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;
            }
        }
示例#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);
        }