📄 assign.cs
字号:
//// assign.cs: Assignments.//// Author:// Miguel de Icaza (miguel@ximian.com)// Martin Baulig (martin@ximian.com)//// (C) 2001, 2002, 2003 Ximian, Inc.// (C) 2004 Novell, Inc//using System;using System.Reflection;using System.Reflection.Emit;namespace Mono.CSharp { /// <summary> /// This interface is implemented by expressions that can be assigned to. /// </summary> /// <remarks> /// This interface is implemented by Expressions whose values can not /// store the result on the top of the stack. /// /// Expressions implementing this (Properties, Indexers and Arrays) would /// perform an assignment of the Expression "source" into its final /// location. /// /// No values on the top of the stack are expected to be left by /// invoking this method. /// </remarks> public interface IAssignMethod { // // This is an extra version of Emit. If leave_copy is `true' // A copy of the expression will be left on the stack at the // end of the code generated for EmitAssign // void Emit (EmitContext ec, bool leave_copy); // // This method does the assignment // `source' will be stored into the location specified by `this' // if `leave_copy' is true, a copy of `source' will be left on the stack // if `prepare_for_load' is true, when `source' is emitted, there will // be data on the stack that it can use to compuatate its value. This is // for expressions like a [f ()] ++, where you can't call `f ()' twice. // void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load); /* For simple assignments, this interface is very simple, EmitAssign is called with source as the source expression and leave_copy and prepare_for_load false. For compound assignments it gets complicated. EmitAssign will be called as before, however, prepare_for_load will be true. The @source expression will contain an expression which calls Emit. So, the calls look like: this.EmitAssign (ec, source, false, true) -> source.Emit (ec); -> [...] -> this.Emit (ec, false); -> end this.Emit (ec, false); -> end [...] end source.Emit (ec); end this.EmitAssign (ec, source, false, true) When prepare_for_load is true, EmitAssign emits a `token' on the stack that Emit will use for its state. Let's take FieldExpr as an example. assume we are emitting f ().y += 1; Here is the call tree again. This time, each call is annotated with the IL it produces: this.EmitAssign (ec, source, false, true) call f dup Binary.Emit () this.Emit (ec, false); ldfld y end this.Emit (ec, false); IntConstant.Emit () ldc.i4.1 end IntConstant.Emit add end Binary.Emit () stfld end this.EmitAssign (ec, source, false, true) Observe two things: 1) EmitAssign left a token on the stack. It was the result of f (). 2) This token was used by Emit leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy of the expression at that point in evaluation. This is used for pre/post inc/dec and for a = x += y. Let's do the above example with leave_copy true in EmitAssign this.EmitAssign (ec, source, true, true) call f dup Binary.Emit () this.Emit (ec, false); ldfld y end this.Emit (ec, false); IntConstant.Emit () ldc.i4.1 end IntConstant.Emit add end Binary.Emit () dup stloc temp stfld ldloc temp end this.EmitAssign (ec, source, true, true) And with it true in Emit this.EmitAssign (ec, source, false, true) call f dup Binary.Emit () this.Emit (ec, true); ldfld y dup stloc temp end this.Emit (ec, true); IntConstant.Emit () ldc.i4.1 end IntConstant.Emit add end Binary.Emit () stfld ldloc temp end this.EmitAssign (ec, source, false, true) Note that these two examples are what happens for ++x and x++, respectively. */ } /// <summary> /// An Expression to hold a temporary value. /// </summary> /// <remarks> /// The LocalTemporary class is used to hold temporary values of a given /// type to "simulate" the expression semantics on property and indexer /// access whose return values are void. /// /// The local temporary is used to alter the normal flow of code generation /// basically it creates a local variable, and its emit instruction generates /// code to access this value, return its address or save its value. /// /// If `is_address' is true, then the value that we store is the address to the /// real value, and not the value itself. /// /// This is needed for a value type, because otherwise you just end up making a /// copy of the value on the stack and modifying it. You really need a pointer /// to the origional value so that you can modify it in that location. This /// Does not happen with a class because a class is a pointer -- so you always /// get the indirection. /// /// The `is_address' stuff is really just a hack. We need to come up with a better /// way to handle it. /// </remarks> public class LocalTemporary : Expression, IMemoryLocation { LocalBuilder builder; bool is_address; public LocalTemporary (EmitContext ec, Type t) : this (ec, t, false) {} public LocalTemporary (EmitContext ec, Type t, bool is_address) { type = t; eclass = ExprClass.Value; loc = Location.Null; builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (t): t); this.is_address = is_address; } public LocalTemporary (LocalBuilder b, Type t) { type = t; eclass = ExprClass.Value; loc = Location.Null; builder = b; } public void Release (EmitContext ec) { ec.FreeTemporaryLocal (builder, type); builder = null; } public override Expression DoResolve (EmitContext ec) { return this; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; ig.Emit (OpCodes.Ldloc, builder); // we need to copy from the pointer if (is_address) LoadFromPtr (ig, type); } // NB: if you have `is_address' on the stack there must // be a managed pointer. Otherwise, it is the type from // the ctor. public void Store (EmitContext ec) { ILGenerator ig = ec.ig; ig.Emit (OpCodes.Stloc, builder); } public void AddressOf (EmitContext ec, AddressOp mode) { // if is_address, than this is just the address anyways, // so we just return this. ILGenerator ig = ec.ig; if (is_address) ig.Emit (OpCodes.Ldloc, builder); else ig.Emit (OpCodes.Ldloca, builder); } public bool PointsToAddress { get { return is_address; } } } /// <summary> /// The Assign node takes care of assigning the value of source into /// the expression represented by target. /// </summary> public class Assign : ExpressionStatement { protected Expression target, source, real_source; protected LocalTemporary temp = null, real_temp = null; protected Assign embedded = null; protected bool is_embedded = false; protected bool must_free_temp = false; public Assign (Expression target, Expression source) : this (target, source, target.Location) { } public Assign (Expression target, Expression source, Location l) { this.target = target; this.source = this.real_source = source; this.loc = l; } protected Assign (Assign embedded, Location l) : this (embedded.target, embedded.source, l) { this.is_embedded = true; } protected virtual Assign GetEmbeddedAssign (Location loc) { return new Assign (this, loc); } public Expression Target { get { return target; } set { target = value; } } public Expression Source { get { return source; } set { source = value; } } public static void error70 (EventInfo ei, Location l) { Report.Error (70, l, "The event `" + TypeManager.CSharpSignature (ei) + "' can only appear on the left hand side of += or -= (except when" + " used from within the type `" + ei.DeclaringType + "')"); } // // Will return either `this' or an instance of `New'. // public override Expression DoResolve (EmitContext ec) { // Create an embedded assignment if our source is an assignment. if (source is Assign) source = embedded = ((Assign) source).GetEmbeddedAssign (loc); real_source = source = source.Resolve (ec); if (source == null) { // Ensure that we don't propagate the error as spurious "uninitialized variable" errors. target = target.ResolveLValue (ec, EmptyExpression.Null, Location); return null; } //
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -