Ejemplo n.º 1
0
        /// <summary>
        /// Generate the GetEnumerator() method for iterators and GetAsyncEnumerator() for async-iterators.
        /// </summary>
        protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSymbol getEnumeratorMethod, ref BoundExpression managedThreadId, int initialState)
        {
            // Produces:
            //    {StateMachineType} result;
            //    if (this.initialThreadId == {managedThreadId} && this.state == -2)
            //    {
            //        this.state = {initialState};
            //        extraReset
            //        result = this;
            //    }
            //    else
            //    {
            //        result = new {StateMachineType}({initialState});
            //    }
            //    result.parameter = this.parameterProxy; // copy all of the parameter proxies

            // The implementation doesn't depend on the method body of the iterator method.
            // Only on its parameters and staticness.
            var getEnumerator = OpenMethodImplementation(
                getEnumeratorMethod,
                hasMethodBodyDependency: false);

            var bodyBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            // {StateMachineType} result;
            var resultVariable = F.SynthesizedLocal(stateMachineType, null);
            // result = new {StateMachineType}({initialState})
            BoundStatement makeIterator = F.Assignment(F.Local(resultVariable), F.New(stateMachineType.Constructor, F.Literal(initialState)));

            var thisInitialized = F.GenerateLabel("thisInitialized");

            if ((object)initialThreadIdField != null)
            {
                managedThreadId = MakeCurrentThreadId();

                var thenBuilder = ArrayBuilder <BoundStatement> .GetInstance(4);

                thenBuilder.Add(
                    // this.state = {initialState};
                    F.Assignment(F.Field(F.This(), stateField), F.Literal(initialState)));

                thenBuilder.Add(
                    // result = this;
                    F.Assignment(F.Local(resultVariable), F.This()));

                var extraReset = GetExtraResetForIteratorGetEnumerator();
                if (extraReset != null)
                {
                    thenBuilder.Add(extraReset);
                }

                thenBuilder.Add(
                    method.IsStatic || method.ThisParameter.Type.IsReferenceType ? // if this is a reference type, no need to copy it since it is not assignable
                    F.Goto(thisInitialized) :                                      // goto thisInitialized
                    (BoundStatement)F.StatementList());

                makeIterator = F.If(
                    // if (this.state == -2 && this.initialThreadId == Thread.CurrentThread.ManagedThreadId)
                    condition: F.LogicalAnd(
                        F.IntEqual(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)),
                        F.IntEqual(F.Field(F.This(), initialThreadIdField), managedThreadId)),
                    thenClause: F.Block(thenBuilder.ToImmutableAndFree()),
                    elseClauseOpt: makeIterator);
            }

            bodyBuilder.Add(makeIterator);

            // Initialize all the parameter copies
            var copySrc  = initialParameters;
            var copyDest = nonReusableLocalProxies;

            if (!method.IsStatic)
            {
                // starting with "this"
                CapturedSymbolReplacement proxy;
                if (copyDest.TryGetValue(method.ThisParameter, out proxy))
                {
                    bodyBuilder.Add(
                        F.Assignment(
                            proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable)),
                            copySrc[method.ThisParameter].Replacement(F.Syntax, stateMachineType => F.This())));
                }
            }

            bodyBuilder.Add(F.Label(thisInitialized));

            foreach (var parameter in method.Parameters)
            {
                CapturedSymbolReplacement proxy;
                if (copyDest.TryGetValue(parameter, out proxy))
                {
                    bodyBuilder.Add(
                        F.Assignment(
                            proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable)),
                            copySrc[parameter].Replacement(F.Syntax, stateMachineType => F.This())));
                }
            }

            bodyBuilder.Add(F.Return(F.Local(resultVariable)));
            F.CloseMethod(F.Block(ImmutableArray.Create(resultVariable), bodyBuilder.ToImmutableAndFree()));
            return(getEnumerator);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Generate the GetEnumerator() method for iterators and GetAsyncEnumerator() for async-iterators.
        /// </summary>
        protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSymbol getEnumeratorMethod, ref BoundExpression managedThreadId, int initialState)
        {
            // Produces:
            //    {StateMachineType} result;
            //    if (this.initialThreadId == {managedThreadId} && this.state == -2)
            //    {
            //        this.state = {initialState};
            //        extraReset
            //        result = this;
            //    }
            //    else
            //    {
            //        result = new {StateMachineType}({initialState});
            //    }
            //
            //    // Initialize each parameter fields
            //    result.parameter = this.parameterProxy;
            //      OR
            //    if (token.Equals(default)) { result.parameter = this.parameterProxy; } else { result.parameter = token; } // for async-enumerable parameters marked with [EnumeratorCancellation]

            // The implementation doesn't depend on the method body of the iterator method.
            // Only on its parameters and staticness.
            var getEnumerator = OpenMethodImplementation(
                getEnumeratorMethod,
                hasMethodBodyDependency: false);

            var bodyBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            // {StateMachineType} result;
            var resultVariable = F.SynthesizedLocal(stateMachineType, null);
            // result = new {StateMachineType}({initialState})
            BoundStatement makeIterator = F.Assignment(F.Local(resultVariable), F.New(stateMachineType.Constructor, F.Literal(initialState)));

            var thisInitialized = F.GenerateLabel("thisInitialized");

            if ((object)initialThreadIdField != null)
            {
                managedThreadId = MakeCurrentThreadId();

                var thenBuilder = ArrayBuilder <BoundStatement> .GetInstance(4);

                thenBuilder.Add(
                    // this.state = {initialState};
                    F.Assignment(F.Field(F.This(), stateField), F.Literal(initialState)));

                thenBuilder.Add(
                    // result = this;
                    F.Assignment(F.Local(resultVariable), F.This()));

                var extraReset = GetExtraResetForIteratorGetEnumerator();
                if (extraReset != null)
                {
                    thenBuilder.Add(extraReset);
                }

                if (method.IsStatic || method.ThisParameter.Type.IsReferenceType)
                {
                    // if this is a reference type, no need to copy it since it is not assignable
                    thenBuilder.Add(
                        // goto thisInitialized;
                        F.Goto(thisInitialized));
                }

                makeIterator = F.If(
                    // if (this.state == -2 && this.initialThreadId == Thread.CurrentThread.ManagedThreadId)
                    condition: F.LogicalAnd(
                        F.IntEqual(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)),
                        F.IntEqual(F.Field(F.This(), initialThreadIdField), managedThreadId)),
                    thenClause: F.Block(thenBuilder.ToImmutableAndFree()),
                    elseClauseOpt: makeIterator);
            }

            bodyBuilder.Add(makeIterator);

            // Initialize all the parameter copies
            var copySrc  = initialParameters;
            var copyDest = nonReusableLocalProxies;

            if (!method.IsStatic)
            {
                // starting with "this"
                CapturedSymbolReplacement proxy;
                if (copyDest.TryGetValue(method.ThisParameter, out proxy))
                {
                    bodyBuilder.Add(
                        F.Assignment(
                            proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable)),
                            copySrc[method.ThisParameter].Replacement(F.Syntax, stateMachineType => F.This())));
                }
            }

            bodyBuilder.Add(F.Label(thisInitialized));

            foreach (var parameter in method.Parameters)
            {
                CapturedSymbolReplacement proxy;
                if (copyDest.TryGetValue(parameter, out proxy))
                {
                    // result.parameter = this.parameterProxy;
                    BoundExpression left = proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable));
                    BoundStatement  copy = F.Assignment(
                        left,
                        copySrc[parameter].Replacement(F.Syntax, stateMachineType => F.This()));

                    if (this.method.IsAsync)
                    {
                        ParameterSymbol tokenParameter = getEnumeratorMethod.Parameters[0];
                        if (hasEnumeratorCancellationAttribute(parameter))
                        {
                            // For any async-enumerable parameter marked with [EnumeratorCancellation] attribute, conditionally copy GetAsyncEnumerator's cancellation token parameter instead
                            // if (token.Equals(default))
                            //     result.parameter = this.parameterProxy;
                            // else
                            //     result.parameter = token;

                            copy = F.If(
                                // if (token.Equals(default))
                                F.Call(F.Parameter(tokenParameter), WellKnownMember.System_Threading_CancellationToken__Equals, F.Default(tokenParameter.Type)),
                                // result.parameter = this.parameterProxy;
                                copy,
                                // result.parameter = token;
                                F.Assignment(left, F.Parameter(tokenParameter)));
                        }
                    }

                    bodyBuilder.Add(copy);
                }
            }

            bodyBuilder.Add(F.Return(F.Local(resultVariable)));
            F.CloseMethod(F.Block(ImmutableArray.Create(resultVariable), bodyBuilder.ToImmutableAndFree()));
            return(getEnumerator);