示例#1
0
 public override CodeNode VisitWriteField(WriteFieldNode node)
 {
     _Writer.WriteStartElement("WriteField");
     _Writer.WriteAttributeString("FieldIndex", node.FieldIndex.ToString());
     _Writer.WriteAttributeString("FieldType", node.FieldType.ToString());
     _Writer.WriteAttributeString("Property", node.Property.Name);
     _Writer.WriteAttributeString("OptionalFieldIndex", node.OptionalFieldIndex.ToString());
     _Writer.WriteAttributeString("OptionaFieldMask", string.Format("0x{0:x}", node.OptionaFieldMask));
     _Writer.WriteStartElement("Initialization");
     Visit(node.Initialization);
     _Writer.WriteEndElement();
     _Writer.WriteStartElement("NoValueWriteContingency");
     Visit(node.NoValueWriteContingency);
     _Writer.WriteEndElement();
     _Writer.WriteStartElement("WriteOperation");
     Visit(node.WriteOperation);
     _Writer.WriteEndElement();
     _Writer.WriteEndElement();
     return(node);
 }
示例#2
0
        public override CodeNode VisitWriteField(WriteFieldNode node)
        {
            Log($"Writing field {node.FieldIndex}");
            //TODO: do the right kind of checks for the optional node properties

            //do any initialization (this is typically creating field locals)
            Visit(node.Initialization);

            //initialize the local that indicates whether we have a local value to write
            var hasValueLocal = ILGen.DeclareLocal <bool>();

            ILGen.Ldc_I4_0();
            ILGen.Stloc(hasValueLocal);

            //declare a bunch of labels
            var decideLabel      = ILGen.DefineLabel();
            var skipWritingLabel = ILGen.DefineLabel();
            var doWriteLabel     = ILGen.DefineLabel();

            //if we have a property, we need to read from it
            if (node.Property != null)
            {
                //get the field local
                var fieldLocal = _FieldLocals[node.FieldIndex];
                if (fieldLocal.LocalType != node.FieldType)
                {
                    throw new InvalidOperationException("Field assignment is occuring on a mismatched field local type.");
                }

                Log($"Reading {node.Property.Name} for field {node.FieldIndex}");
                //get the value of the property
                ILGen.Ldloc(_ConcreteRecordLocal);
                ILGen.Callvirt(node.Property.GetGetMethod());

                //generate the check for whether we have a value to write
                if (node.Property.PropertyType.IsValueType)
                {
                    Log($"Processing value type");
                    //it's a value type.  Check for Nullable
                    Type nullable = typeof(Nullable <>).MakeGenericType(node.FieldType);
                    if (node.Property.PropertyType == nullable)
                    {
                        Log($"Checking Nullable for value");
                        //it is nullable, check to see whether we have a value
                        //create a local for the nullable and store the value in it
                        var nullableLocal = ILGen.DeclareLocal(nullable);
                        ILGen.Stloc(nullableLocal);
                        //call .HasValue
                        ILGen.Ldloca(nullableLocal); //load address so we can call methods
                        ILGen.Callvirt(nullable.GetProperty("HasValue").GetGetMethod());
                        //dup and store hasValue
                        ILGen.Dup();
                        ILGen.Stloc(hasValueLocal);

                        //if we don't have a value, branch to decide if we should be writing something anyway
                        ILGen.Brfalse(decideLabel);

                        //otherwise, get the value
                        Log($"Getting Value");
                        ILGen.Ldloca(nullableLocal);
                        ILGen.Callvirt(nullable.GetProperty("Value").GetGetMethod());
                    }
                    //BUG: if we have a field that we persist AND represents the state of another field,
                    // then there should be a merge operation here (or a consistency check should fail earlier).
                    //for now, we assume consistency.
                    //store the value and branch to the write
                    ILGen.Stloc(fieldLocal);
                    ILGen.Br(doWriteLabel);
                }
                else
                {
                    Log($"Processing Reference Type");
                    //copy so we can store (remember we're dup'ing the thing returned from the property)
                    ILGen.Dup();
                    ILGen.Stloc(fieldLocal);

                    Log($"Checking For null");
                    //it's a ref type, check for null (more complicated than I thought)
                    ILGen.Ldnull(); //load null
                    ILGen.Ceq();    //compare to null
                    //0 on stack if has value (it didn't equal null)
                    //now compare with 0
                    ILGen.Ldc_I4_0();
                    ILGen.Ceq(); //compare to 0
                    //1 on stack if has value
                    //dup and store as hasValue
                    ILGen.Dup();
                    ILGen.Stloc(hasValueLocal);

                    //if we don't have a value, branch to decide if we should be writing something anyway
                    ILGen.Brfalse(decideLabel);

                    //branch to the write
                    ILGen.Br(doWriteLabel);
                }
            }

            ILGen.MarkLabel(decideLabel);
            Log($"No value. Deciding whether to write.");

            //at this point, we don't have a value from the record directly,
            //but we may need to write if we have started already
            ILGen.Ldloc(_StartedWriting);
            //we can skip writing if we haven't started already
            ILGen.Brfalse(skipWritingLabel);

            //emit contingency for no value
            if (node.NoValueWriteContingency != null)
            {
                Visit(node.NoValueWriteContingency);
            }

            ILGen.Br(skipWritingLabel);

            //this is where we will start doing real writes
            ILGen.MarkLabel(doWriteLabel);

            //we've started writing, so set that
            ILGen.Ldc_I4_1();
            ILGen.Stloc(_StartedWriting);

            Visit(node.WriteOperation);

            ILGen.MarkLabel(skipWritingLabel);
            Log($"Done writing.");

            //if we have an optional field index, we need to emit code to set it
            if (node.OptionalFieldIndex.HasValue)
            {
                Log($"Setting optional field bits if necessary.");
                var optionalLocal = _FieldLocals[node.OptionalFieldIndex.Value];
                var skipOptField  = ILGen.DefineLabel();
                //if we have a value, skip setting the optional field
                ILGen.Ldloc(hasValueLocal);
                ILGen.Brtrue(skipOptField);
                //load the optional local and the field mask, and "or" them together
                Log($"Setting optional field bits 0x{node.OptionaFieldMask:x}");
                ILGen.Ldloc(optionalLocal);
                ILGen.Ldc_I4_S(node.OptionaFieldMask);
                ILGen.Or();
                //store the value back in the local
                ILGen.Stloc(optionalLocal);
                ILGen.MarkLabel(skipOptField);
                Log($"Done setting optional field bits");
            }

            return(node);
        }
示例#3
0
 public virtual CodeNode VisitWriteField(WriteFieldNode node)
 {
     //TODO: do this right;
     throw new NotSupportedException("WriteFieldNodes are too complicated to transform during visiting. :)");
 }