예제 #1
0
        protected virtual IEnumerable<Instruction> ImplementWait(Instruction instruction, TypeReference waitFutureType)
        {
            //FIXME: We're reordering instructions to keep the stack balanced before/after continuation.
            // Better to save/restore the stack instead? ->
            //  Foo (MayThrow (), GetSomeFuture ().Wait ());
            // Normally, if MayThrow () throws an exception, GetSomeFuture () is not called. But because we're
            // reordering, GetSomeFuture *is* called, the future is waited, and then MayThrow throws on the continuation.

            //FIXME: Support multiple predecessors here!
            var lastStack = cfg.FindLastStackItem (block, instruction, IsBarrier).Single (); // this is the Future
            // these are the items on the stack before the Future. they will be moved til after the continuation
            var continuationInst = cfg.FindStackHeight (block, lastStack, 0, IsBarrier).Single ();

            IList<Instruction> precontinuationStack = null;
            if (continuationInst != lastStack) {
                precontinuationStack = ExtractInstructionRange (continuationInst, lastStack);

                // FIXME: Currently, we can't handle any jumps into this critical area
                if (precontinuationStack.Any (i => method.Body.IsJumpTarget (i)))
                    throw new NotSupportedException ("Support for jumps into continuation critical area not implemented");
            }

            // Insert ldarg.0 for CoroutineFuture.Chain
            // FIXME: Right now, we're depending on the C# compiler's behavior re. not separating arg0 from call
            InsertBefore (lastStack, Instruction.Create (OpCodes.Ldarg_0));

            var chainInstructions = new List<Instruction> ();
            chainInstructions.Add (Instruction.Create (OpCodes.Call, chain));

            var continuation = Instruction.Create (OpCodes.Ret);
            continuations.Add (continuation);

            var afterContinuation = Instruction.Create (OpCodes.Ldarg_0);

            // CoroutineFuture.Chain returns false if we don't need continuation
            chainInstructions.Add (Instruction.Create (OpCodes.Brfalse_S, afterContinuation));

            chainInstructions.Add (continuation);
            chainInstructions.Add (afterContinuation);

            // this is the instruction that will follow if the future is not faulted
            var notFaulted = Instruction.Create (OpCodes.Nop);

            // check for exceptions
            chainInstructions.Add (Instruction.Create (OpCodes.Call, checkException));
            chainInstructions.Add (Instruction.Create (OpCodes.Ldarg_0));
            chainInstructions.Add (Instruction.Create (OpCodes.Ldfld, chainedFld));
            chainInstructions.Add (Instruction.Create (OpCodes.Callvirt, module.Import (getException)));
            chainInstructions.Add (Instruction.Create (OpCodes.Brfalse, notFaulted));

            // mark exception as handled and throw it again right there
            chainInstructions.Add (Instruction.Create (OpCodes.Ldarg_0));
            chainInstructions.Add (Instruction.Create (OpCodes.Ldfld, chainedFld));
            chainInstructions.Add (Instruction.Create (OpCodes.Dup));
            chainInstructions.Add (Instruction.Create (OpCodes.Ldc_I4, -1));
            chainInstructions.Add (Instruction.Create (OpCodes.Call, module.Import (setStatus)));
            chainInstructions.Add (Instruction.Create (OpCodes.Callvirt, module.Import (getException)));
            chainInstructions.Add (Instruction.Create (OpCodes.Throw));

            // redirect jumps from our phony Wait call to the chain call.
            method.Body.Instructions.RedirectJumps (instruction, chainInstructions [0]);

            // -- add continuation to InstructionMap
            InstructionMap.Add (chainInstructions [0], chainInstructions);

            yield return notFaulted;

            // move anything on the stack before the Wait to after the continuation
            if (precontinuationStack != null) {
                foreach (var inst in precontinuationStack)
                    yield return inst;

            }

            // load the continuation result if there is one

            // FIXME: Eventually, we prolly don't want to do this if we're just going to pop the result..
            //   but we'd have to get rid of the pop instruction too...
            if (waitFutureType != null /* && instruction.Next.OpCode != OpCodes.Pop */) {
                var genWaitFuture = waitFutureType.CopyGeneric (coroutineType, skews);
                yield return Instruction.Create (OpCodes.Ldarg_0);
                yield return Instruction.Create (OpCodes.Ldfld, chainedFld);
                yield return Instruction.Create (OpCodes.Isinst, genWaitFuture);
                yield return Instruction.Create (OpCodes.Call, module.ImportFrom (getValue, genWaitFuture));
            }

            // reset chained to null
            yield return Instruction.Create (OpCodes.Ldarg_0);
            yield return Instruction.Create (OpCodes.Ldnull);
            yield return Instruction.Create (OpCodes.Stfld, chainedFld);
        }
예제 #2
0
 // -----
 // We are taking a method implementation and extracting it into its own class.
 // If the method is generic, we need to make that class generic and update any
 // type refs to the old method's generic parameters to refer to the new class's
 // generic parameters instead.
 protected TypeReference FixTypeToken(TypeReference token)
 {
     return module.Import (token.CopyGeneric (coroutineType, skews));
 }