protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            bool writeValue;

            SanityCheck(ctx.Model, property, Tail, out writeValue, ctx.NonPublic, ctx.AllowInternal(property));
            if (Helpers.IsValueType(ExpectedType) && valueFrom == null)
            {
                throw new InvalidOperationException("Attempt to mutate struct on the head of the stack; changes would be lost");
            }

            using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom))
            {
                if (Tail.RequiresOldValue)
                {
                    ctx.LoadAddress(loc, ExpectedType); // stack is: old-addr
                    ctx.LoadValue(property);            // stack is: old-value
                }
                Type propertyType = property.PropertyType;
                ctx.ReadNullCheckedTail(propertyType, Tail, null); // stack is [new-value]

                if (writeValue)
                {
                    using (Compiler.Local newVal = new Compiler.Local(ctx, property.PropertyType))
                    {
                        ctx.StoreValue(newVal);                                // stack is empty

                        Compiler.CodeLabel allDone = new Compiler.CodeLabel(); // <=== default structs
                        if (!Helpers.IsValueType(propertyType))
                        {                                                      // if the tail returns a null, intepret that as *no assign*
                            allDone = ctx.DefineLabel();
                            ctx.LoadValue(newVal);                             // stack is: new-value
                            ctx.BranchIfFalse(@allDone, true);                 // stack is empty
                        }
                        // assign the value
                        ctx.LoadAddress(loc, ExpectedType); // parent-addr
                        ctx.LoadValue(newVal);              // parent-obj|new-value
                        if (shadowSetter == null)
                        {
                            ctx.StoreValue(property); // empty
                        }
                        else
                        {
                            ctx.EmitCall(shadowSetter); // empty
                        }
                        if (!Helpers.IsValueType(propertyType))
                        {
                            ctx.MarkLabel(allDone);
                        }
                    }
                }
                else
                { // don't want return value; drop it if anything there
                    // stack is [new-value]
                    if (Tail.ReturnsValue)
                    {
                        ctx.DiscardValue();
                    }
                }
            }
        }
Beispiel #2
0
        protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            bool writeValue;

            SanityCheck(ctx.Model, property, Tail, out writeValue, ctx.NonPublic, ctx.AllowInternal(property));
            if (ExpectedType.IsValueType && valueFrom == null)
            {
                throw new InvalidOperationException("Attempt to mutate struct on the head of the stack; changes would be lost");
            }

            ctx.LoadAddress(valueFrom, ExpectedType); // stack is: old-addr
            if (writeValue && Tail.RequiresOldValue)
            {                                         // need to read and write
                ctx.CopyValue();
            }
            // stack is: [old-addr]|old-addr
            if (Tail.RequiresOldValue)
            {
                ctx.LoadValue(property); // stack is: [old-addr]|old-value
            }
            Type propertyType = property.PropertyType;

            ctx.ReadNullCheckedTail(propertyType, Tail, null); // stack is [old-addr]|[new-value]

            if (writeValue)
            {
                // stack is old-addr|new-value
                Compiler.CodeLabel @skip = new Compiler.CodeLabel(), allDone = new Compiler.CodeLabel(); // <=== default structs
                if (!propertyType.IsValueType)
                {                                                                                        // if the tail returns a null, intepret that as *no assign*
                    ctx.CopyValue();                                                                     // old-addr|new-value|new-value
                    @skip   = ctx.DefineLabel();
                    allDone = ctx.DefineLabel();
                    ctx.BranchIfFalse(@skip, true); // old-addr|new-value
                }

                if (shadowSetter == null)
                {
                    ctx.StoreValue(property);
                }
                else
                {
                    ctx.EmitCall(shadowSetter);
                }
                if (!propertyType.IsValueType)
                {
                    ctx.Branch(allDone, true);

                    ctx.MarkLabel(@skip); // old-addr|new-value
                    ctx.DiscardValue();
                    ctx.DiscardValue();

                    ctx.MarkLabel(allDone);
                }
            }
            else
            { // don't want return value; drop it if anything there
                // stack is [new-value]
                if (Tail.ReturnsValue)
                {
                    ctx.DiscardValue();
                }
            }
        }
Beispiel #3
0
        protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this, _property.Name))
            {
                var  g = ctx.G;
                bool canSet;
                SanityCheck(ctx.Model, _property, Tail, out canSet, ctx.NonPublic, ctx.AllowInternal(_property), true);

                using (Compiler.Local loc = ctx.GetLocalWithValueForEmitRead(this, valueFrom))
                    using (Compiler.Local oldVal = canSet ? new Compiler.Local(ctx, _property.PropertyType) : null)
                        using (Compiler.Local newVal = new Compiler.Local(ctx, _property.PropertyType))
                        {
                            Compiler.Local valueForTail = null;
                            if (Tail.RequiresOldValue)
                            {
                                ctx.LoadAddress(loc, ExpectedType);
                                ctx.LoadValue(_property);
                                if (!oldVal.IsNullRef())
                                {
                                    if (Tail.RequiresOldValue) // we need it later
                                    {
                                        ctx.CopyValue();
                                    }
                                    ctx.StoreValue(oldVal);
                                }
                            }

                            if (Tail.RequiresOldValue) // !here! <-- we need value again
                            {
                                if (!Tail.EmitReadReturnsValue)
                                {
                                    ctx.StoreValue(newVal);
                                    valueForTail = newVal;
                                } // otherwise valueForTail = null (leave value on stack)
                            }

                            Tail.EmitRead(ctx, valueForTail);

                            // otherwise newVal was passed to EmitRead so already has necessary data
                            if (Tail.EmitReadReturnsValue)
                            {
                                ctx.StoreValue(newVal);
                            }

                            // check-condition:
                            //(!Tail.RequiresOldValue // always set where can't check oldVal
                            //                        // and if it's value type or nullable with changed null/not null or ref
                            //    || (Helpers.IsValueType(property.PropertyType) && oldVal != null && newVal != null)
                            //    || !ReferenceEquals(oldVal, newVal)
                            //   ))
                            if (canSet)
                            {
                                bool check = Tail.RequiresOldValue;
                                if (check)
                                {
                                    var condition = !g.StaticFactory.InvokeReferenceEquals(oldVal, newVal);

                                    if (Helpers.IsValueType(_property.PropertyType))
                                    {
                                        condition = (oldVal.AsOperand != null && newVal.AsOperand != null) || condition;
                                    }

                                    g.If(condition);
                                }

                                ctx.LoadAddress(loc, ExpectedType);
                                ctx.LoadValue(newVal);
                                if (_shadowSetter == null)
                                {
                                    ctx.StoreValue(_property);
                                }
                                else
                                {
                                    ctx.EmitCall(_shadowSetter);
                                }

                                if (check)
                                {
                                    g.End();
                                }
                            }

                            if (EmitReadReturnsValue)
                            {
                                ctx.LoadValue(loc);
                            }
                        }
            }
        }