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); }
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); }
public virtual CodeNode VisitWriteField(WriteFieldNode node) { //TODO: do this right; throw new NotSupportedException("WriteFieldNodes are too complicated to transform during visiting. :)"); }