public override CodeNode VisitFieldAssignment(FieldAssignmentNode node) { //ensure we're in a FieldAssignmentBlock if (!_InFieldAssignmentBlock) { throw new InvalidOperationException("EndOfStreamCheckNode must occur within a FieldAssignmentBlockNode"); } _InFieldAssignment = true; try { Log($"Handling field {node.FieldIndex}."); //generate the end of stream check ILGen.Ldloc(_Reader); ILGen.Callvirt(_AtEndOfStreamMethod); ILGen.Brtrue(_EndLabel); //declare the local and enable it in the scope of child visiting _FieldLocal = ILGen.DeclareLocal(node.Type); _FieldLocals[node.FieldIndex] = _FieldLocal; _SkipAssignmentLabel = ILGen.DefineLabel(); var assignmentCompleted = ILGen.DefineLabel(); //visit any read node there is Visit(node.ReadNode); //visit any assignment block if (node.AssignmentBlock != null) { Visit(node.AssignmentBlock); } else { Log($"No assignment for {node.FieldIndex}."); } ILGen.Br(assignmentCompleted); ILGen.MarkLabel(_SkipAssignmentLabel); Log($"Assignment skipped."); ILGen.MarkLabel(assignmentCompleted); Log($"Done with {node.FieldIndex}."); _FieldLocal = null; return(node); } finally { _InFieldAssignment = false; } }
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); }