private void DecomposeMethodCall(MethodCallNode methodCallNode, bool isYielding, bool mayPanic) { NIType outputType; // TODO: try to use something like Unit or Void NIType emptyOutputType = NITypes.Boolean; switch (methodCallNode.OutputTerminals.Count) { case 0: outputType = emptyOutputType; break; case 1: outputType = methodCallNode.OutputTerminals[0].GetTrueVariable().Type; break; default: outputType = methodCallNode.OutputTerminals.Select(t => t.GetTrueVariable().Type).DefineTupleType(); break; } AutoBorrowNodeFacade methodCallNodeFacade = AutoBorrowNodeFacade.GetNodeFacade(methodCallNode); AwaitNode awaitNode = null; if (isYielding) { CreateMethodCallPromise createMethodCallPromise = new CreateMethodCallPromise(methodCallNode.ParentDiagram, methodCallNode.Signature, methodCallNode.TargetName); AutoBorrowNodeFacade createMethodCallPromiseFacade = AutoBorrowNodeFacade.GetNodeFacade(createMethodCallPromise); foreach (var terminalPair in methodCallNode.InputTerminals.Zip(createMethodCallPromise.InputTerminals)) { Terminal methodCallTerminal = terminalPair.Key, createMethodCallPromiseTerminal = terminalPair.Value; methodCallTerminal.ConnectedTerminal.ConnectTo(createMethodCallPromiseTerminal); createMethodCallPromiseFacade[createMethodCallPromiseTerminal] = methodCallNodeFacade[methodCallTerminal]; } Terminal methodCallOutputTerminal = methodCallNode.OutputTerminals.FirstOrDefault(); NIType awaitOutputType = mayPanic ? outputType.CreatePanicResult() : outputType; NIType promiseType = awaitOutputType.CreateMethodCallPromise(); Terminal promiseTerminal = createMethodCallPromise.OutputTerminals[0]; createMethodCallPromiseFacade[promiseTerminal] = new SimpleTerminalFacade( promiseTerminal, createMethodCallPromise.GetTypeVariableSet().CreateTypeVariableReferenceFromNIType(promiseType)); awaitNode = ConnectNewNodeToOutputTerminalWithOutputFacade(createMethodCallPromise, diagram => new AwaitNode(diagram), awaitOutputType); VariableSet variableSet = awaitNode.GetVariableSet(); TypeVariableReference outputTypeVariable = awaitNode.OutputTerminal.GetTrueVariable().TypeVariableReference; TypeVariableReference pollResultTypeVariable = variableSet.TypeVariableSet.CreateReferenceToOptionType(outputTypeVariable); awaitNode.PollResultVariable = variableSet.CreateNewVariable( diagramId: 0, variableType: pollResultTypeVariable, mutable: false); } Node outputNode = awaitNode; if (mayPanic) { Node panicOrContinueInput = awaitNode; if (!isYielding) { // Create PanickingMethodCall as input to PanicOrContinue throw new NotImplementedException("Calling non-yielding panicking methods not supported yet."); } outputNode = ConnectNewNodeToOutputTerminalWithOutputFacade( panicOrContinueInput, diagram => new PanicOrContinueNode(diagram), outputType); } Terminal outputNodeTerminal = outputNode.OutputTerminals[0]; switch (methodCallNode.OutputTerminals.Count) { case 0: // no method call output terminals; drop the result of the await InsertDropTransform.InsertDropForVariable(outputNode.ParentDiagram, LiveVariable.FromTerminal(outputNodeTerminal), _unificationResultFactory); break; case 1: ConnectOutputTerminal(methodCallNode.OutputTerminals[0], outputNodeTerminal); break; default: { // for >1 method call output terminals, decompose the tuple result of the await and match each // decomposed terminal to a method call terminal DecomposeTupleNode decomposeTupleNode = InsertDropTransform.InsertDecompositionForTupleVariable( methodCallNode.ParentDiagram, LiveVariable.FromTerminal(outputNodeTerminal), _unificationResultFactory); foreach (var pair in methodCallNode.OutputTerminals.Zip(decomposeTupleNode.OutputTerminals)) { Terminal methodCallTerminal = pair.Key, decomposeTupleTerminal = pair.Value; ConnectOutputTerminal(methodCallTerminal, decomposeTupleTerminal); } break; } } methodCallNode.RemoveFromGraph(); }
private void ConnectOutputTerminal(Terminal outputTerminal, Terminal newOutputTerminal) { AutoBorrowNodeFacade nodeToReplaceFacade = AutoBorrowNodeFacade.GetNodeFacade(outputTerminal.ParentNode); AutoBorrowNodeFacade newTerminalNodeFacade = AutoBorrowNodeFacade.GetNodeFacade(newOutputTerminal.ParentNode); if (outputTerminal.IsConnected) { outputTerminal.ConnectedTerminal.ConnectTo(newOutputTerminal); newTerminalNodeFacade[newOutputTerminal] = nodeToReplaceFacade[outputTerminal]; } else { InsertDropTransform.InsertDropForVariable(newOutputTerminal.ParentDiagram, LiveVariable.FromTerminal(newOutputTerminal), _unificationResultFactory); } }