Esempio n. 1
0
        protected override bool DoDefineMembers()
        {
            TypeSpec   bt;
            bool       has_task_return_type = false;
            var        istate_machine       = Module.PredefinedTypes.IAsyncStateMachine;
            MethodSpec set_statemachine;

            if (return_type.IsCustomTaskType())
            {
                //
                // TODO: Would be nice to cache all this on per-type basis
                //
                var btypes = Compiler.BuiltinTypes;
                bt = return_type.MemberDefinition.GetAsyncMethodBuilder();
                TypeSpec bt_inflated;
                if (return_type.IsGeneric)
                {
                    bt_inflated = bt.MakeGenericType(Module, bt.MemberDefinition.TypeParameters);
                }
                else
                {
                    bt_inflated = bt;
                }

                var set_result_sign = MemberFilter.Method("SetResult", 0, ParametersCompiled.CreateFullyResolved(bt.MemberDefinition.TypeParameters), btypes.Void);
                set_result = new PredefinedMember <MethodSpec> (Module, bt, set_result_sign).Resolve(Location);

                var set_exception_sign = MemberFilter.Method("SetException", 0, ParametersCompiled.CreateFullyResolved(btypes.Exception), btypes.Void);
                set_exception = new PredefinedMember <MethodSpec> (Module, bt, set_exception_sign).Resolve(Location);

                var builder_factory_sign = MemberFilter.Method("Create", 0, ParametersCompiled.EmptyReadOnlyParameters, bt_inflated);
                builder_factory = new PredefinedMember <MethodSpec> (Module, bt, builder_factory_sign).Resolve(Location);
                if (builder_factory?.IsStatic == false)
                {
                    throw new NotImplementedException("report better error message");
                }

                var builder_start_sign = MemberFilter.Method("Start", 1, new ParametersImported(
                                                                 new [] {
                    new ParameterData(null, Parameter.Modifier.REF),
                },
                                                                 new [] {
                    new TypeParameterSpec(0, null, SpecialConstraint.None, Variance.None, null),
                }, false),
                                                             btypes.Void);
                builder_start = new PredefinedMember <MethodSpec> (Module, bt, builder_start_sign).Resolve(Location);

                if (!istate_machine.Define())
                {
                    return(false);
                }

                var set_statemachine_sign = MemberFilter.Method("SetStateMachine", 0, ParametersCompiled.CreateFullyResolved(istate_machine.TypeSpec), btypes.Void);
                set_statemachine = new PredefinedMember <MethodSpec> (Module, bt, set_statemachine_sign).Resolve(Location);;

                var task_sign = MemberFilter.Property("Task", return_type.MemberDefinition as TypeSpec);
                task = new PredefinedMember <PropertySpec> (Module, bt, task_sign).Resolve(Location);

                if (set_result == null || set_exception == null || builder_factory == null || builder_start == null || set_statemachine == null || task == null ||
                    !Module.PredefinedTypes.INotifyCompletion.Define())
                {
                    return(false);
                }

                has_task_return_type = return_type.IsGeneric;
            }
            else
            {
                PredefinedType builder_type;
                PredefinedMember <MethodSpec> bf;
                PredefinedMember <MethodSpec> bs;
                PredefinedMember <MethodSpec> sr;
                PredefinedMember <MethodSpec> se;
                PredefinedMember <MethodSpec> sm;
                var pred_members = Module.PredefinedMembers;

                if (return_type.Kind == MemberKind.Void)
                {
                    builder_type = Module.PredefinedTypes.AsyncVoidMethodBuilder;
                    bf           = pred_members.AsyncVoidMethodBuilderCreate;
                    bs           = pred_members.AsyncVoidMethodBuilderStart;
                    sr           = pred_members.AsyncVoidMethodBuilderSetResult;
                    se           = pred_members.AsyncVoidMethodBuilderSetException;
                    sm           = pred_members.AsyncVoidMethodBuilderSetStateMachine;
                }
                else if (return_type == Module.PredefinedTypes.Task.TypeSpec)
                {
                    builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilder;
                    bf           = pred_members.AsyncTaskMethodBuilderCreate;
                    bs           = pred_members.AsyncTaskMethodBuilderStart;
                    sr           = pred_members.AsyncTaskMethodBuilderSetResult;
                    se           = pred_members.AsyncTaskMethodBuilderSetException;
                    sm           = pred_members.AsyncTaskMethodBuilderSetStateMachine;
                    task         = pred_members.AsyncTaskMethodBuilderTask.Get();
                }
                else
                {
                    builder_type         = Module.PredefinedTypes.AsyncTaskMethodBuilderGeneric;
                    bf                   = pred_members.AsyncTaskMethodBuilderGenericCreate;
                    bs                   = pred_members.AsyncTaskMethodBuilderGenericStart;
                    sr                   = pred_members.AsyncTaskMethodBuilderGenericSetResult;
                    se                   = pred_members.AsyncTaskMethodBuilderGenericSetException;
                    sm                   = pred_members.AsyncTaskMethodBuilderGenericSetStateMachine;
                    task                 = pred_members.AsyncTaskMethodBuilderGenericTask.Get();
                    has_task_return_type = true;
                }

                set_result      = sr.Get();
                set_exception   = se.Get();
                builder_factory = bf.Get();
                builder_start   = bs.Get();

                set_statemachine = sm.Get();

                if (!builder_type.Define() || !istate_machine.Define() || set_result == null || builder_factory == null ||
                    set_exception == null || set_statemachine == null || builder_start == null ||
                    !Module.PredefinedTypes.INotifyCompletion.Define())
                {
                    Report.Error(1993, Location,
                                 "Cannot find compiler required types for asynchronous functions support. Are you targeting the wrong framework version?");
                    return(base.DoDefineMembers());
                }

                bt = builder_type.TypeSpec;
            }

            //
            // Inflate generic Task types
            //
            if (has_task_return_type)
            {
                var task_return_type = return_type.TypeArguments;
                if (mutator != null)
                {
                    task_return_type = mutator.Mutate(task_return_type);
                }

                bt               = bt.MakeGenericType(Module, task_return_type);
                set_result       = MemberCache.GetMember(bt, set_result);
                set_exception    = MemberCache.GetMember(bt, set_exception);
                set_statemachine = MemberCache.GetMember(bt, set_statemachine);

                if (task != null)
                {
                    task = MemberCache.GetMember(bt, task);
                }
            }

            builder = AddCompilerGeneratedField("$builder", new TypeExpression(bt, Location));

            Field rfield;

            if (has_task_return_type && HasAwaitInsideFinally)
            {
                //
                // Special case async block with return value from finally clause. In such case
                // we rewrite all return expresison stores to stfld to $return. Instead of treating
                // returns outside of finally and inside of finally differently.
                //
                rfield = AddCompilerGeneratedField("$return", new TypeExpression(bt.TypeArguments [0], Location));
            }
            else
            {
                rfield = null;
            }

            var set_state_machine = new Method(this, new TypeExpression(Compiler.BuiltinTypes.Void, Location),
                                               Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN | Modifiers.PUBLIC,
                                               new MemberName("SetStateMachine"),
                                               ParametersCompiled.CreateFullyResolved(
                                                   new Parameter(new TypeExpression(istate_machine.TypeSpec, Location), "stateMachine", Parameter.Modifier.NONE, null, Location),
                                                   istate_machine.TypeSpec),
                                               null);

            ToplevelBlock block = new ToplevelBlock(Compiler, set_state_machine.ParameterInfo, Location);

            block.IsCompilerGenerated = true;
            set_state_machine.Block   = block;

            Members.Add(set_state_machine);

            if (!base.DoDefineMembers())
            {
                return(false);
            }

            //
            // Fabricates SetStateMachine method
            //
            // public void SetStateMachine (IAsyncStateMachine stateMachine)
            // {
            //    $builder.SetStateMachine (stateMachine);
            // }
            //
            var mg = MethodGroupExpr.CreatePredefined(set_statemachine, bt, Location);

            mg.InstanceExpression = new FieldExpr(builder, Location);

            var param_reference = block.GetParameterReference(0, Location);

            param_reference.Type   = istate_machine.TypeSpec;
            param_reference.eclass = ExprClass.Variable;

            var args = new Arguments(1);

            args.Add(new Argument(param_reference));
            set_state_machine.Block.AddStatement(new StatementExpression(new Invocation(mg, args)));

            if (has_task_return_type)
            {
                if (rfield != null)
                {
                    HoistedReturnValue = new FieldExpr(rfield, Location)
                    {
                        InstanceExpression = new CompilerGeneratedThis(CurrentType, Location.Null)
                    };
                }
                else
                {
                    HoistedReturnValue = TemporaryVariableReference.Create(bt.TypeArguments [0], StateMachineMethod.Block, Location);
                }
            }

            return(true);
        }