Пример #1
0
        public override BoundNode VisitSequence(BoundSequence node)
        {
            BoundSpillSequenceBuilder valueBuilder = null;
            var value = VisitExpression(ref valueBuilder, node.Value);

            BoundSpillSequenceBuilder builder = null;

            var sideEffects = VisitExpressionList(ref builder, node.SideEffects, forceSpill: valueBuilder != null, sideEffectsOnly: true);

            if (builder == null && valueBuilder == null)
            {
                return(node.Update(node.Locals, sideEffects, value, node.Type));
            }

            if (builder == null)
            {
                builder = new BoundSpillSequenceBuilder();
            }

            PromoteAndAddLocals(builder, node.Locals);
            builder.AddExpressions(sideEffects);
            builder.Include(valueBuilder);

            return(builder.Update(value));
        }
Пример #2
0
 public override BoundNode VisitSequence(BoundSequence node)
 {
     AddAll(node.Locals);
     base.VisitSequence(node);
     RemoveAll(node.Locals);
     return(null);
 }
Пример #3
0
 internal void AddSequence(SyntheticBoundNodeFactory F, BoundSequence sequence)
 {
     locals.AddRange(sequence.Locals);
     foreach (var sideEffect in sequence.SideEffects)
     {
         statements.Add(F.ExpressionStatement(sideEffect));
     }
 }
Пример #4
0
                public override BoundNode VisitSequence(BoundSequence node)
                {
                    var oldScope = _currentScope;

                    _currentScope = CreateOrReuseScope(node, node.Locals);
                    var result = base.VisitSequence(node);

                    _currentScope = oldScope;
                    return(result);
                }
Пример #5
0
        public override BoundNode VisitSequence(BoundSequence node)
        {
            // Spilled local temps do not appear here in a sequence expression, because any temps in a
            // sequence expression that need to be spilled would have been moved up to the
            // statement level by the AwaitLiftingRewriter.
            foreach (var local in node.Locals)
            {
                Debug.Assert(!NeedsProxy(local) || proxies.ContainsKey(local));
            }

            return(base.VisitSequence(node));
        }
Пример #6
0
        // Omit ref feature for COM interop: We can pass arguments by value for ref parameters if we are calling a method/property on an instance of a COM imported type.
        // We should have ignored the 'ref' on the parameter during overload resolution for the given method call.
        // If we had any ref omitted argument for the given call, we create a temporary local and
        // replace the argument with the following BoundSequence: { sideeffects: { temp = argument }, value = { ref temp } }
        // NOTE: The temporary local must be scoped to live across the entire BoundCall node,
        // otherwise the codegen optimizer might re-use the same temporary for multiple ref-omitted arguments for this call.
        private void RewriteArgumentsForComCall(
            ImmutableArray <ParameterSymbol> parameters,
            BoundExpression[] actualArguments, //already re-ordered to match parameters
            ArrayBuilder <RefKind> argsRefKindsBuilder,
            ArrayBuilder <LocalSymbol> temporariesBuilder)
        {
            Debug.Assert(actualArguments != null);
            Debug.Assert(actualArguments.Length == parameters.Length);

            Debug.Assert(argsRefKindsBuilder == null || argsRefKindsBuilder.Count == parameters.Length);

            var argsCount = actualArguments.Length;

            for (int argIndex = 0; argIndex < argsCount; ++argIndex)
            {
                RefKind paramRefKind = parameters[argIndex].RefKind;
                RefKind argRefKind   = argsRefKindsBuilder[argIndex];

                // Rewrite only if the argument was passed with no ref/out and the
                // parameter was declared ref.
                if (argRefKind != RefKind.None || paramRefKind != RefKind.Ref)
                {
                    continue;
                }

                var argument = actualArguments[argIndex];
                if (argument.Kind == BoundKind.Local)
                {
                    var localRefKind = ((BoundLocal)argument).LocalSymbol.RefKind;
                    if (localRefKind == RefKind.Ref)
                    {
                        // Already passing an address from the ref local.
                        continue;
                    }

                    Debug.Assert(localRefKind == RefKind.None);
                }

                BoundAssignmentOperator boundAssignmentToTemp;
                BoundLocal boundTemp = factory.StoreToTemp(argument, out boundAssignmentToTemp);

                actualArguments[argIndex] = new BoundSequence(
                    argument.Syntax,
                    locals: ImmutableArray <LocalSymbol> .Empty,
                    sideEffects: ImmutableArray.Create <BoundExpression>(boundAssignmentToTemp),
                    value: boundTemp,
                    type: boundTemp.Type);
                argsRefKindsBuilder[argIndex] = RefKind.Ref;

                temporariesBuilder.Add(boundTemp.LocalSymbol);
            }
        }
Пример #7
0
        private BoundNode RewriteCatch(BoundCatchBlock node, ArrayBuilder <BoundExpression> prologue, ArrayBuilder <LocalSymbol> newLocals)
        {
            AddLocals(node.Locals, newLocals);
            var rewrittenCatchLocals = newLocals.ToImmutableAndFree();

            // If exception variable got lifted, IntroduceFrame will give us frame init prologue.
            // It needs to run before the exception variable is accessed.
            // To ensure that, we will make exception variable a sequence that performs prologue as its its sideeffecs.
            BoundExpression rewrittenExceptionSource = null;
            var             rewrittenFilter          = (BoundExpression)this.Visit(node.ExceptionFilterOpt);

            if (node.ExceptionSourceOpt != null)
            {
                rewrittenExceptionSource = (BoundExpression)Visit(node.ExceptionSourceOpt);
                if (prologue.Count > 0)
                {
                    rewrittenExceptionSource = new BoundSequence(
                        rewrittenExceptionSource.Syntax,
                        ImmutableArray.Create <LocalSymbol>(),
                        prologue.ToImmutable(),
                        rewrittenExceptionSource,
                        rewrittenExceptionSource.Type);
                }
            }
            else if (prologue.Count > 0)
            {
                Debug.Assert(rewrittenFilter != null);
                rewrittenFilter = new BoundSequence(
                    rewrittenFilter.Syntax,
                    ImmutableArray.Create <LocalSymbol>(),
                    prologue.ToImmutable(),
                    rewrittenFilter,
                    rewrittenFilter.Type);
            }

            // done with this.
            prologue.Free();

            // rewrite filter and body
            // NOTE: this will proxy all accesses to exception local if that got lifted.
            var exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt);
            var rewrittenBlock   = (BoundBlock)this.Visit(node.Body);

            return(node.Update(
                       rewrittenCatchLocals,
                       rewrittenExceptionSource,
                       exceptionTypeOpt,
                       rewrittenFilter,
                       rewrittenBlock));
        }
Пример #8
0
            public override BoundNode VisitSequence(BoundSequence node)
            {
                if (node.Locals.IsDefaultOrEmpty)
                {
                    // ignore blocks that declare no variables.
                    return(base.VisitSequence(node));
                }

                var previousBlock = PushBlock(node, node.Locals);
                var result        = base.VisitSequence(node);

                PopBlock(previousBlock);
                return(result);
            }
Пример #9
0
        public override BoundNode VisitSequence(BoundSequence node)
        {
            LambdaFrame frame;

            // Test if this frame has captured variables and requires the introduction of a closure class.
            if (frames.TryGetValue(node, out frame))
            {
                return(IntroduceFrame(node, frame, (ArrayBuilder <BoundExpression> prologue, ArrayBuilder <LocalSymbol> newLocals) =>
                {
                    return RewriteSequence(node, prologue, newLocals);
                }));
            }
            else
            {
                return(RewriteSequence(node, ArrayBuilder <BoundExpression> .GetInstance(), ArrayBuilder <LocalSymbol> .GetInstance()));
            }
        }
Пример #10
0
        private void XsInsertMissingOptionalArguments(SyntaxNode syntax,
                                                      ImmutableArray <ParameterSymbol> parameters,
                                                      BoundExpression[] arguments,
                                                      ArrayBuilder <RefKind> refKinds,
                                                      ArrayBuilder <LocalSymbol> temps,
                                                      ThreeState enableCallerInfo = ThreeState.Unknown
                                                      )
        {
            Debug.Assert(refKinds.Count == arguments.Length);
            for (int p = 0; p < arguments.Length; ++p)
            {
                if (arguments[p] == null || arguments[p].Syntax.XIsMissingArgument)
                {
                    ParameterSymbol parameter = parameters[p];
                    BoundExpression expr      = GetDefaultParameterValue(syntax, parameter, enableCallerInfo);
                    if (expr is BoundDefaultExpression && parameter.Type == _compilation.UsualType())
                    {
                        var flds = _compilation.UsualType().GetMembers("_NIL");
                        var type = new BoundTypeExpression(syntax, null, _compilation.UsualType());
                        expr = _factory.Field(type, (FieldSymbol)flds[0]);
                    }

                    BoundAssignmentOperator boundAssignmentToTemp;
                    BoundLocal boundTemp = _factory.StoreToTemp(expr, out boundAssignmentToTemp);
                    expr = new BoundSequence(
                        syntax,
                        locals: ImmutableArray <LocalSymbol> .Empty,
                        sideEffects: ImmutableArray.Create <BoundExpression>(boundAssignmentToTemp),
                        value: boundTemp,
                        type: boundTemp.Type);
                    refKinds[p] = parameters[p].RefKind;
                    temps.Add(boundTemp.LocalSymbol);


                    arguments[p] = expr;
                    Debug.Assert(arguments[p].Type == parameter.Type);

                    if (parameters[p].RefKind == RefKind.In)
                    {
                        Debug.Assert(refKinds[p] == RefKind.None);
                        refKinds[p] = RefKind.In;
                    }
                }
            }
        }
Пример #11
0
        private BoundSequence RewriteSequence(BoundSequence node, ArrayBuilder <BoundExpression> prologue, ArrayBuilder <LocalSymbol> newLocals)
        {
            AddLocals(node.Locals, newLocals);

            foreach (var expr in node.SideEffects)
            {
                var replacement = (BoundExpression)this.Visit(expr);
                if (replacement != null)
                {
                    prologue.Add(replacement);
                }
            }

            var newValue = (BoundExpression)this.Visit(node.Value);
            var newType  = this.VisitType(node.Type);

            return(node.Update(newLocals.ToImmutableAndFree(), prologue.ToImmutableAndFree(), newValue, newType));
        }
Пример #12
0
        public override BoundNode VisitSequence(BoundSequence node)
        {
            BoundSpillSequence2 ss2 = null;
            var value = VisitExpression(ref ss2, node.Value);

            BoundSpillSequence2 ss1 = null;
            var sideEffects         = VisitExpressionList(ref ss1, node.SideEffects, forceSpill: ss2 != null, sideEffectsOnly: true);

            if (ss1 == null && ss2 == null)
            {
                return(node.Update(node.Locals, sideEffects, value, node.Type));
            }

            if (ss1 == null)
            {
                ss1 = new BoundSpillSequence2();              // possible if sideEffects is empty
            }
            ss1.AddRange(sideEffects, MakeExpressionStatement);
            ss1.AddRange(node.Locals);
            ss1.IncludeSequence(ss2);
            return(ss1.Update(value));
        }
Пример #13
0
        public override BoundNode VisitSequence(BoundSequence node)
        {
            ReadOnlyArray <BoundExpression> sideEffects = (ReadOnlyArray <BoundExpression>) this.VisitList(node.SideEffects);
            BoundExpression value = (BoundExpression)this.Visit(node.Value);
            TypeSymbol      type  = this.VisitType(node.Type);

            if (!RequiresSpill(sideEffects) && value.Kind != BoundKind.SpillSequence)
            {
                return(node.Update(node.Locals, sideEffects, value, type));
            }

            var spillBuilder = new SpillBuilder();

            spillBuilder.Locals.AddRange(node.Locals);

            foreach (var sideEffect in sideEffects)
            {
                spillBuilder.Statements.Add(
                    (sideEffect.Kind == BoundKind.SpillSequence)
                    ? RewriteSpillSequenceAsBlock((BoundSpillSequence)sideEffect)
                    : F.ExpressionStatement(sideEffect));
            }

            BoundExpression newValue;

            if (value.Kind == BoundKind.SpillSequence)
            {
                var awaitEffect = (BoundSpillSequence)value;
                spillBuilder.AddSpill(awaitEffect);
                newValue = awaitEffect.Value;
            }
            else
            {
                newValue = value;
            }

            return(spillBuilder.BuildSequenceAndFree(F, newValue));
        }
Пример #14
0
 public override BoundNode VisitSequence(BoundSequence node)
 {
     try
     {
         if (!node.Locals.IsNullOrEmpty)
         {
             foreach (var l in node.Locals)
             {
                 localsDefined.Add(l);
             }
         }
         return(base.VisitSequence(node));
     }
     finally
     {
         if (!node.Locals.IsNullOrEmpty)
         {
             foreach (var l in node.Locals)
             {
                 localsDefined.Remove(l);
             }
         }
     }
 }
Пример #15
0
        private BoundNode RewriteWithRefOperand(
            bool isPrefix,
            bool isChecked,
            ArrayBuilder <LocalSymbol> tempSymbols,
            ArrayBuilder <BoundExpression> tempInitializers,
            SyntaxNode syntax,
            BoundExpression operand,
            TypeSymbol operandType,
            BoundExpression boundTemp,
            BoundExpression newValue)
        {
            var tempValue      = isPrefix ? newValue : MakeRValue(operand);
            var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false);

            var operandValue = isPrefix ? boundTemp : newValue;
            var tempAssignedAndOperandValue = new BoundSequence(
                syntax,
                ImmutableArray <LocalSymbol> .Empty,
                ImmutableArray.Create <BoundExpression>(tempAssignment),
                operandValue,
                tempValue.Type);

            // prefix:  operand = Seq{temp = (T)(operand + 1);  temp;}
            // postfix: operand = Seq{temp = operand;        ;  (T)(temp + 1);}
            BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false);

            // prefix:  Seq{operand initializers; operand = Seq{temp = (T)(operand + 1);  temp;}          result: temp}
            // postfix: Seq{operand initializers; operand = Seq{temp = operand;        ;  (T)(temp + 1);} result: temp}
            tempInitializers.Add(operandAssignment);
            return(new BoundSequence(
                       syntax: syntax,
                       locals: tempSymbols.ToImmutableAndFree(),
                       sideEffects: tempInitializers.ToImmutableAndFree(),
                       value: boundTemp,
                       type: operandType));
        }
Пример #16
0
        /// <summary>
        /// Process tempStores and add them as sideeffects to arguments where needed. The return
        /// value tells how many temps are actually needed. For unnecessary temps the corresponding
        /// temp store will be cleared.
        /// </summary>
        private static int MergeArgumentsAndSideEffects(
            BoundExpression[] arguments,
            ArrayBuilder <RefKind> refKinds,
            ArrayBuilder <BoundAssignmentOperator> tempStores)
        {
            Debug.Assert(arguments != null);
            Debug.Assert(refKinds != null);
            Debug.Assert(tempStores != null);

            int tempsRemainedInUse = tempStores.Count;

            // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E()
            // and arguments: t0, t2, t1, t4, 10, t5
            //
            // We wish to produce arguments list: A(), SEQ(t1=B(), C()), t1, D(), 10, E()
            //
            // Our algorithm essentially finds temp stores that must happen before given argument
            // load, and if there are any they become side effects of the given load.
            // Stores immediately followed by loads of the same thing can be eliminated.
            //
            // Constraints:
            //    Stores must happen before corresponding loads.
            //    Stores cannot move relative to other stores. If arg was movable it would not need a temp.

            int firstUnclaimedStore = 0;

            for (int a = 0; a < arguments.Length; ++a)
            {
                var argument = arguments[a];

                // if argument is a load, search for corresponding store. if store is found, extract
                // the actual expression we were storing and add it as an argument - this one does
                // not need a temp. if there are any unclaimed stores before the found one, add them
                // as side effects that precede this arg, they cannot happen later.
                if (argument.Kind == BoundKind.Local)
                {
                    var correspondingStore = -1;
                    for (int i = firstUnclaimedStore; i < tempStores.Count; i++)
                    {
                        if (tempStores[i].Left == argument)
                        {
                            correspondingStore = i;
                            break;
                        }
                    }

                    // store found?
                    if (correspondingStore != -1)
                    {
                        var value = tempStores[correspondingStore].Right;

                        // When we created the temp, we dropped the argument RefKind
                        // since the local contained its own RefKind. Since we're removing
                        // the temp, the argument RefKind needs to be restored.
                        refKinds[a] = ((BoundLocal)argument).LocalSymbol.RefKind;

                        // the matched store will not need to go into sideffects, only ones before it will
                        // remove the store to signal that we are not using its temp.
                        tempStores[correspondingStore] = null;
                        tempsRemainedInUse--;

                        // no need for sideeffects?
                        // just combine store and load
                        if (correspondingStore == firstUnclaimedStore)
                        {
                            arguments[a] = value;
                        }
                        else
                        {
                            var sideffects = new BoundExpression[correspondingStore - firstUnclaimedStore];
                            for (int s = 0; s < sideffects.Length; s++)
                            {
                                sideffects[s] = tempStores[firstUnclaimedStore + s];
                            }

                            arguments[a] = new BoundSequence(
                                value.Syntax,
                                // this sequence does not own locals. Note that temps that
                                // we use for the rewrite are stored in one arg and loaded
                                // in another so they must live in a scope above.
                                ImmutableArray <LocalSymbol> .Empty,
                                sideffects.AsImmutableOrNull(),
                                value,
                                value.Type);
                        }

                        firstUnclaimedStore = correspondingStore + 1;
                    }
                }
            }

            Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all sideeffects were claimed");
            return(tempsRemainedInUse);
        }
Пример #17
0
        private BoundExpression OptimizeLiftedUnaryOperator(
            UnaryOperatorKind operatorKind,
            SyntaxNode syntax,
            MethodSymbol method,
            BoundExpression loweredOperand,
            TypeSymbol type)
        {
            if (NullableNeverHasValue(loweredOperand))
            {
                return(new BoundDefaultOperator(syntax, null, type));
            }

            // Second, another simple optimization. If we know that the operand is never null
            // then we can obtain the non-null value and skip generating the temporary. That is,
            // "~(new int?(M()))" is the same as "new int?(~M())".

            BoundExpression neverNull = NullableAlwaysHasValue(loweredOperand);

            if (neverNull != null)
            {
                return(GetLiftedUnaryOperatorConsequence(operatorKind, syntax, method, type, neverNull));
            }

            var conditionalLeft = loweredOperand as BoundLoweredConditionalAccess;

            // NOTE: we could in theory handle side-effecting loweredRight here too
            //       by including it as a part of whenNull, but there is a concern
            //       that it can lead to code duplication
            var optimize = conditionalLeft != null &&
                           (conditionalLeft.WhenNullOpt == null || conditionalLeft.WhenNullOpt.IsDefaultValue());

            if (optimize)
            {
                var result = LowerLiftedUnaryOperator(operatorKind, syntax, method, conditionalLeft.WhenNotNull, type);

                return(conditionalLeft.Update(
                           conditionalLeft.Receiver,
                           conditionalLeft.HasValueMethodOpt,
                           whenNotNull: result,
                           whenNullOpt: null,
                           id: conditionalLeft.Id,
                           type: result.Type
                           ));
            }

            // This optimization is analogous to DistributeLiftedConversionIntoLiftedOperand.

            // Suppose we have a lifted unary conversion whose operand is itself a lifted operation.
            // That is, we have something like:
            //
            // int? r = - (M() + N());
            //
            // where M() and N() return nullable ints. We would simply codegen this as first
            // creating the nullable int result of M() + N(), then checking it for nullity,
            // and then doing the unary minus. That is:
            //
            // int? m = M();
            // int? n = N();
            // int? t = m.HasValue && n.HasValue ? new int?(m.Value + n.Value) : new int?();
            // int? r = t.HasValue ? new int?(-t.Value) : new int?();
            //
            // However, we also observe that we can distribute the unary minus into both branches of
            // the conditional:
            //
            // int? m = M();
            // int? n = N();
            // int? r = m.HasValue && n.HasValue ? - (new int?(m.Value + n.Value))) : - new int?();
            //
            // And we already optimize those! So we could reduce this to:
            //
            // int? m = M();
            // int? n = N();
            // int? r = m.HasValue && n.HasValue ? new int?(- (m.Value + n.Value)) : new int?());
            //
            // which avoids entirely the creation of the unnecessary nullable int and the unnecessary
            // extra null check.

            if (loweredOperand.Kind == BoundKind.Sequence)
            {
                BoundSequence seq = (BoundSequence)loweredOperand;
                if (seq.Value.Kind == BoundKind.ConditionalOperator)
                {
                    BoundConditionalOperator conditional = (BoundConditionalOperator)seq.Value;
                    Debug.Assert(seq.Type == conditional.Type);
                    Debug.Assert(conditional.Type == conditional.Consequence.Type);
                    Debug.Assert(conditional.Type == conditional.Alternative.Type);

                    if (NullableAlwaysHasValue(conditional.Consequence) != null && NullableNeverHasValue(conditional.Alternative))
                    {
                        return(new BoundSequence(
                                   syntax,
                                   seq.Locals,
                                   seq.SideEffects,
                                   RewriteConditionalOperator(
                                       syntax,
                                       conditional.Condition,
                                       MakeUnaryOperator(operatorKind, syntax, method, conditional.Consequence, type),
                                       MakeUnaryOperator(operatorKind, syntax, method, conditional.Alternative, type),
                                       ConstantValue.NotAvailable,
                                       type),
                                   type));
                    }
                }
            }

            return(null);
        }
Пример #18
0
        private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node, bool used)
        {
            Debug.Assert(TypeSymbol.Equals(node.Right.Type, node.Operator.RightType, TypeCompareKind.ConsiderEverything2));
            BoundExpression loweredRight = VisitExpression(node.Right);

            var temps = ArrayBuilder <LocalSymbol> .GetInstance();

            var stores = ArrayBuilder <BoundExpression> .GetInstance();

            var  kind           = node.Operator.Kind;
            bool isChecked      = kind.IsChecked();
            bool isDynamic      = kind.IsDynamic();
            var  binaryOperator = kind.Operator();

            // This will be filled in with the LHS that uses temporaries to prevent
            // double-evaluation of side effects.
            BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Left, stores, temps, isDynamic);
            var             lhsRead        = MakeRValue(transformedLHS);
            BoundExpression rewrittenAssignment;

            if (node.Left.Kind == BoundKind.DynamicMemberAccess &&
                (binaryOperator == BinaryOperatorKind.Addition || binaryOperator == BinaryOperatorKind.Subtraction))
            {
                // If this could be an event assignment at runtime, we need to rewrite to the following form:
                // Original:
                //   receiver.EV += handler
                // Rewritten:
                //   dynamic memberAccessReceiver = receiver;
                //   bool isEvent = Runtime.IsEvent(memberAccessReceiver, "EV");
                //   dynamic storeNonEvent = !isEvent ? memberAccessReceiver.EV : null;
                //   var loweredRight = handler; // Only necessary if handler can change values, or is something like a lambda
                //   isEvent ? add_Event(memberAccessReceiver, "EV", loweredRight) : transformedLHS = storeNonEvent + loweredRight;
                //
                // This is to ensure that if handler is something like a lambda, we evaluate fully evaluate the left
                // side before storing the lambda to a temp for use in both possible branches.
                // The first store to memberAccessReceiver has already been taken care of above by TransformCompoundAssignmentLHS

                var eventTemps = ArrayBuilder <LocalSymbol> .GetInstance();

                var sequence = ArrayBuilder <BoundExpression> .GetInstance();

                //   dynamic memberAccessReceiver = receiver;
                var memberAccess = (BoundDynamicMemberAccess)transformedLHS;

                //   bool isEvent = Runtime.IsEvent(memberAccessReceiver, "EV");
                var isEvent = _factory.StoreToTemp(_dynamicFactory.MakeDynamicIsEventTest(memberAccess.Name, memberAccess.Receiver).ToExpression(), out BoundAssignmentOperator isEventAssignment);
                eventTemps.Add(isEvent.LocalSymbol);
                sequence.Add(isEventAssignment);

                // dynamic storeNonEvent = !isEvent ? memberAccessReceiver.EV : null;
                lhsRead = _factory.StoreToTemp(lhsRead, out BoundAssignmentOperator receiverAssignment);
                eventTemps.Add(((BoundLocal)lhsRead).LocalSymbol);
                var storeNonEvent = _factory.StoreToTemp(_factory.Conditional(_factory.Not(isEvent), receiverAssignment, _factory.Null(receiverAssignment.Type), receiverAssignment.Type), out BoundAssignmentOperator nonEventStore);
                eventTemps.Add(storeNonEvent.LocalSymbol);
                sequence.Add(nonEventStore);

                // var loweredRight = handler;
                if (CanChangeValueBetweenReads(loweredRight))
                {
                    loweredRight = _factory.StoreToTemp(loweredRight, out BoundAssignmentOperator possibleHandlerAssignment);
                    eventTemps.Add(((BoundLocal)loweredRight).LocalSymbol);
                    sequence.Add(possibleHandlerAssignment);
                }

                // add_Event(t1, "add_EV");
                var invokeEventAccessor = _dynamicFactory.MakeDynamicEventAccessorInvocation(
                    (binaryOperator == BinaryOperatorKind.Addition ? "add_" : "remove_") + memberAccess.Name,
                    memberAccess.Receiver,
                    loweredRight);

                // transformedLHS = storeNonEvent + loweredRight
                rewrittenAssignment = rewriteAssignment(lhsRead);

                // Final conditional
                var condition = _factory.Conditional(isEvent, invokeEventAccessor.ToExpression(), rewrittenAssignment, rewrittenAssignment.Type);

                rewrittenAssignment = new BoundSequence(node.Syntax, eventTemps.ToImmutableAndFree(), sequence.ToImmutableAndFree(), condition, condition.Type);
            }
            else
            {
                rewrittenAssignment = rewriteAssignment(lhsRead);
            }

            BoundExpression result = (temps.Count == 0 && stores.Count == 0) ?
                                     rewrittenAssignment :
                                     new BoundSequence(
                node.Syntax,
                temps.ToImmutable(),
                stores.ToImmutable(),
                rewrittenAssignment,
                rewrittenAssignment.Type);

            temps.Free();
            stores.Free();
            return(result);

            BoundExpression rewriteAssignment(BoundExpression leftRead)
            {
                SyntaxNode syntax = node.Syntax;

                // OK, we now have the temporary declarations, the temporary stores, and the transformed left hand side.
                // We need to generate
                //
                // xlhs = (FINAL)((LEFT)xlhs op rhs)
                //
                // And then wrap it up with the generated temporaries.
                //
                // (The right hand side has already been converted to the type expected by the operator.)

                BoundExpression opLHS = isDynamic ? leftRead : MakeConversionNode(
                    syntax: syntax,
                    rewrittenOperand: leftRead,
                    conversion: node.LeftConversion,
                    rewrittenType: node.Operator.LeftType,
                    @checked: isChecked);

                BoundExpression operand = MakeBinaryOperator(syntax, node.Operator.Kind, opLHS, loweredRight, node.Operator.ReturnType, node.Operator.Method, isCompoundAssignment: true);

                BoundExpression opFinal = MakeConversionNode(
                    syntax: syntax,
                    rewrittenOperand: operand,
                    conversion: node.FinalConversion,
                    rewrittenType: node.Left.Type,
                    explicitCastInCode: isDynamic,
                    @checked: isChecked);

                return(MakeAssignmentOperator(syntax, transformedLHS, opFinal, node.Left.Type, used: used, isChecked: isChecked, isCompoundAssignment: true));
            }
        }
Пример #19
0
        private BoundNode RewriteCatch(BoundCatchBlock node, ArrayBuilder <BoundExpression> prologue, ArrayBuilder <LocalSymbol> newLocals)
        {
            LocalSymbol rewrittenCatchLocal = null;

            if (newLocals.Count > 0)
            {
                Debug.Assert(newLocals.Count == 1, "must be only one local that is the frame reference");
                Debug.Assert(this.proxies.ContainsKey(node.LocalOpt), "original local should be proxied");

                // getting new locals means that our original local was lifted into a closure
                // and instead of an actual local catch will own frame reference.
                rewrittenCatchLocal = newLocals[0];
            }
            else if (node.LocalOpt != null)
            {
                // local was not lifted, but its type may need to be rewritten
                // this happens when it has a generic type which needs to be rewritten
                // when lambda body was moved to a separate method.
                var origLocal = node.LocalOpt;
                Debug.Assert(!this.proxies.ContainsKey(origLocal), "captured local should not need rewriting");

                var newType = VisitType(origLocal.Type);
                if (newType == origLocal.Type)
                {
                    // keeping same local
                    rewrittenCatchLocal = origLocal;
                }
                else
                {
                    //  need a local of a different type
                    rewrittenCatchLocal = new SynthesizedLocal(CurrentMethod, newType, origLocal.Name, declarationKind: LocalDeclarationKind.Catch);
                    localMap.Add(origLocal, rewrittenCatchLocal);
                }
            }

            // If exception variable got lifted, IntroduceFrame will give us frame init prologue.
            // It needs to run before the exception variable is accessed.
            // To ensure that, we will make exception variable a sequence that performs prologue as its its sideeffecs.
            BoundExpression rewrittenExceptionSource = null;

            if (node.ExceptionSourceOpt != null)
            {
                rewrittenExceptionSource = (BoundExpression)Visit(node.ExceptionSourceOpt);
                if (prologue.Count > 0)
                {
                    rewrittenExceptionSource = new BoundSequence(
                        rewrittenExceptionSource.Syntax,
                        ImmutableArray.Create <LocalSymbol>(),
                        prologue.ToImmutable(),
                        rewrittenExceptionSource,
                        rewrittenExceptionSource.Type);
                }
            }

            // done with these.
            newLocals.Free();
            prologue.Free();

            // rewrite filter and body
            // NOTE: this will proxy all accesses to exception local if that got lifted.
            var exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt);
            var rewrittenFilter  = (BoundExpression)this.Visit(node.ExceptionFilterOpt);
            var rewrittenBlock   = (BoundBlock)this.Visit(node.Body);

            return(node.Update(
                       rewrittenCatchLocal,
                       rewrittenExceptionSource,
                       exceptionTypeOpt,
                       rewrittenFilter,
                       rewrittenBlock));
        }
 public override BoundNode VisitSequence(BoundSequence node)
 {
     AddVariables(node.Locals);
     return(base.VisitSequence(node));
 }
Пример #21
0
        public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
        {
            Debug.Assert(node != null);

            // Rewrite the arguments.
            // NOTE: We may need additional argument rewriting such as generating a params array,
            //       re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc.
            // NOTE: This is done later by MakeArguments, for now we just lower each argument.
            var rewrittenArguments = VisitList(node.Arguments);

            // We have already lowered each argument, but we may need some additional rewriting for the arguments,
            // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc.
            ImmutableArray <LocalSymbol> temps;
            ImmutableArray <RefKind>     argumentRefKindsOpt = node.ArgumentRefKindsOpt;

            rewrittenArguments = MakeArguments(
                node.Syntax,
                rewrittenArguments,
                node.Constructor,
                node.Constructor,
                node.Expanded,
                node.ArgsToParamsOpt,
                ref argumentRefKindsOpt,
                out temps);

            BoundExpression rewrittenObjectCreation;

            if (_inExpressionLambda)
            {
                if (!temps.IsDefaultOrEmpty)
                {
                    throw ExceptionUtilities.UnexpectedValue(temps.Length);
                }

                rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, argumentRefKindsOpt, MakeObjectCreationInitializerForExpressionTree(node.InitializerExpressionOpt), changeTypeOpt: node.Constructor.ContainingType);

                if (node.Type.IsInterfaceType())
                {
                    Debug.Assert(TypeSymbol.Equals(rewrittenObjectCreation.Type, ((NamedTypeSymbol)node.Type).ComImportCoClass, TypeCompareKind.ConsiderEverything2));
                    rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false);
                }

                return(rewrittenObjectCreation);
            }

            rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, argumentRefKindsOpt, newInitializerExpression: null, changeTypeOpt: node.Constructor.ContainingType);

            // replace "new S()" with a default struct ctor with "default(S)"
            if (node.Constructor.IsDefaultValueTypeConstructor())
            {
                rewrittenObjectCreation = new BoundDefaultExpression(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type !);
            }

            if (!temps.IsDefaultOrEmpty)
            {
                rewrittenObjectCreation = new BoundSequence(
                    node.Syntax,
                    temps,
                    ImmutableArray <BoundExpression> .Empty,
                    rewrittenObjectCreation,
                    node.Type);
            }

            if (node.Type.IsInterfaceType())
            {
                Debug.Assert(TypeSymbol.Equals(rewrittenObjectCreation.Type, ((NamedTypeSymbol)node.Type).ComImportCoClass, TypeCompareKind.ConsiderEverything2));
                rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false);
            }

            if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors)
            {
                return(rewrittenObjectCreation);
            }

            return(MakeExpressionWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type));
        }