예제 #1
0
		/// <summary>
		///   Creates a deterministic or nondeterministic fault effect body.
		/// </summary>
		private BlockSyntax CreateBody(IMethodSymbol method, BlockSyntax originalBody, SyntaxNode baseEffect)
		{
			var lineAdjustedOriginalBody = originalBody.AppendLineDirective(-1).PrependLineDirective(originalBody.GetLineNumber());
			var componentType = _typeLookup[method.ContainingType.BaseType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)];
			var faultEffectType = _typeLookup[method.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)];
			var faults = _faults[componentType];
			var baseStatements = !method.ReturnsVoid
				? new[] { Syntax.ReturnStatement(baseEffect) }
				: new[] { Syntax.ExpressionStatement(baseEffect), Syntax.ReturnStatement() };

			IMethodSymbol methodSymbol;
			BlockSyntax body = null;

			if (_methodLookup.TryGetValue(method.OverriddenMethod.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), out methodSymbol))
			{
				var priorityFaults = faults.Where(fault => fault.GetPriority(Compilation) == method.ContainingType.GetPriority(Compilation)).ToArray();
				var overridingEffects = priorityFaults.Where(f => f.GetMembers().OfType<IMethodSymbol>().Any(m => m.Overrides(methodSymbol))).ToArray();
				var overrideCount = overridingEffects.Length;

				if (overrideCount > 1)
				{
					var fieldName = _faultChoiceFields[Tuple.Create(methodSymbol, priorityFaults[0].GetPriority(Compilation))];
					var effectIndex = Array.IndexOf(priorityFaults, faultEffectType);
					var choiceField = Syntax.MemberAccessExpression(Syntax.ThisExpression(), fieldName);

					var levelCondition = Syntax.ValueNotEqualsExpression(choiceField, Syntax.LiteralExpression(effectIndex));
					var ifStatement = Syntax.IfStatement(levelCondition, baseStatements).NormalizeWhitespace().WithTrailingNewLines(1);

					if (overridingEffects.Last().Equals(faultEffectType))
					{
						var levelChoiceVariable = "levelChoice".ToSynthesized();
						var levelCountVariable = "levelCount".ToSynthesized();

						var writer = new CodeWriter();
						writer.AppendLine("unsafe");
						writer.AppendBlockStatement(() =>
						{
							writer.AppendLine($"var {levelChoiceVariable} = stackalloc int[{overrideCount}];");
							writer.AppendLine($"var {levelCountVariable} = 0;");

							for (var i = 0; i < overrideCount; ++i)
							{
								var effectType = overridingEffects[i].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
								var index = Array.IndexOf(priorityFaults, overridingEffects[i]);

								writer.AppendLine($"if ({_tryActivate}((({effectType})this).{"fault".ToSynthesized()}))");
								writer.IncreaseIndent();
								writer.AppendLine($"{levelChoiceVariable}[{levelCountVariable}++] = {index};");
								writer.DecreaseIndent();
								writer.NewLine();
							}

							writer.AppendLine($"{fieldName} = {levelCountVariable} == 0 ? - 1 : {levelChoiceVariable}[ChooseIndex({levelCountVariable})];");
						});

						var selectionStatement = SyntaxFactory.ParseStatement(writer.ToString());
						body = SyntaxFactory.Block(selectionStatement, (StatementSyntax)ifStatement, lineAdjustedOriginalBody);
					}
					else
						body = SyntaxFactory.Block((StatementSyntax)ifStatement, lineAdjustedOriginalBody);
				}
			}

			if (body == null)
			{
				var writer = new CodeWriter();
				writer.AppendLine($"if (!{_tryActivate}(this.{"fault".ToSynthesized()}))");
				writer.AppendBlockStatement(() =>
				{
					// Optimization: If we're normalizing a non-void returning method without ref/out parameters and
					// the fault effect simply returns a constant value of primitive type, we generate code to check whether the non-fault
					// value for the case that the fault is not activated (which is always the first case) actually differs 
					// from the constant value returned by the fault effect when the fault is activated. If both values are
					// the same, the activation of the fault will have no effect, so we can undo it, reducing the number
					// of transitions that have to be checked
					var signatureAllowsOptimization =
						!method.ReturnsVoid && CanBeCompared(method.ReturnType) && method.Parameters.All(parameter => parameter.RefKind == RefKind.None);
					var faultEffectReturn = originalBody.Statements.Count == 1 ? originalBody.Statements[0] as ReturnStatementSyntax : null;
					var isConstantValue = faultEffectReturn != null && SemanticModel.GetConstantValue(faultEffectReturn.Expression).HasValue;

					if (signatureAllowsOptimization && isConstantValue)
					{
						writer.AppendLine($"var {"tmp".ToSynthesized()} = {baseEffect.ToFullString()};");
						writer.AppendLine($"if ({"tmp".ToSynthesized()} == {faultEffectReturn.Expression.ToFullString()})");
						writer.AppendBlockStatement(() => { writer.AppendLine($"{_undoActivation}(this.{"fault".ToSynthesized()});"); });
						writer.AppendLine($"return {"tmp".ToSynthesized()};");
					}
					else
					{
						foreach (var statement in baseStatements)
							writer.AppendLine(statement.NormalizeWhitespace().ToFullString());
					}
				});

				writer.NewLine();
				body = SyntaxFactory.Block(SyntaxFactory.ParseStatement(writer.ToString()), lineAdjustedOriginalBody);
			}

			return body.PrependLineDirective(-1);
		}
예제 #2
0
        /// <summary>
        ///   Creates a deterministic or nondeterministic fault effect body.
        /// </summary>
        private BlockSyntax CreateBody(IMethodSymbol method, BlockSyntax originalBody, SyntaxNode baseEffect)
        {
            var lineAdjustedOriginalBody = originalBody.AppendLineDirective(-1).PrependLineDirective(originalBody.GetLineNumber());
            var componentType            = _typeLookup[method.ContainingType.BaseType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)];
            var faultEffectType          = _typeLookup[method.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)];
            var faults         = _faults[componentType];
            var baseStatements = !method.ReturnsVoid
                                ? new[] { Syntax.ReturnStatement(baseEffect) }
                                : new[] { Syntax.ExpressionStatement(baseEffect), Syntax.ReturnStatement() };

            IMethodSymbol methodSymbol;
            BlockSyntax   body = null;

            if (_methodLookup.TryGetValue(method.OverriddenMethod.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), out methodSymbol))
            {
                var priorityFaults    = faults.Where(fault => fault.GetPriority(Compilation) == method.ContainingType.GetPriority(Compilation)).ToArray();
                var overridingEffects = priorityFaults.Where(f => f.GetMembers().OfType <IMethodSymbol>().Any(m => m.Overrides(methodSymbol))).ToArray();
                var overrideCount     = overridingEffects.Length;

                if (overrideCount > 1)
                {
                    var fieldName   = _faultChoiceFields[Tuple.Create(methodSymbol, priorityFaults[0].GetPriority(Compilation))];
                    var effectIndex = Array.IndexOf(priorityFaults, faultEffectType);
                    var choiceField = Syntax.MemberAccessExpression(Syntax.ThisExpression(), fieldName);

                    var levelCondition = Syntax.ValueNotEqualsExpression(choiceField, Syntax.LiteralExpression(effectIndex));
                    var ifStatement    = Syntax.IfStatement(levelCondition, baseStatements).NormalizeWhitespace().WithTrailingNewLines(1);

                    if (overridingEffects.Last().Equals(faultEffectType))
                    {
                        var levelChoiceVariable = "levelChoice".ToSynthesized();
                        var levelCountVariable  = "levelCount".ToSynthesized();

                        var writer = new CodeWriter();
                        writer.AppendLine("unsafe");
                        writer.AppendBlockStatement(() =>
                        {
                            writer.AppendLine($"var {levelChoiceVariable} = stackalloc int[{overrideCount}];");
                            writer.AppendLine($"var {levelCountVariable} = 0;");

                            for (var i = 0; i < overrideCount; ++i)
                            {
                                var effectType = overridingEffects[i].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
                                var index      = Array.IndexOf(priorityFaults, overridingEffects[i]);

                                writer.AppendLine($"if ({_tryActivate}((({effectType})this).{"fault".ToSynthesized()}))");
                                writer.IncreaseIndent();
                                writer.AppendLine($"{levelChoiceVariable}[{levelCountVariable}++] = {index};");
                                writer.DecreaseIndent();
                                writer.NewLine();
                            }

                            writer.AppendLine($"{fieldName} = {levelCountVariable} == 0 ? - 1 : {levelChoiceVariable}[ChooseIndex({levelCountVariable})];");
                        });

                        var selectionStatement = SyntaxFactory.ParseStatement(writer.ToString());
                        body = SyntaxFactory.Block(selectionStatement, (StatementSyntax)ifStatement, lineAdjustedOriginalBody);
                    }
                    else
                    {
                        body = SyntaxFactory.Block((StatementSyntax)ifStatement, lineAdjustedOriginalBody);
                    }
                }
            }

            if (body == null)
            {
                var writer = new CodeWriter();
                writer.AppendLine($"if (!{_tryActivate}(this.{"fault".ToSynthesized()}))");
                writer.AppendBlockStatement(() =>
                {
                    // Optimization: If we're normalizing a non-void returning method without ref/out parameters and
                    // the fault effect simply returns a constant value of primitive type, we generate code to check whether the non-fault
                    // value for the case that the fault is not activated (which is always the first case) actually differs
                    // from the constant value returned by the fault effect when the fault is activated. If both values are
                    // the same, the activation of the fault will have no effect, so we can undo it, reducing the number
                    // of transitions that have to be checked
                    var signatureAllowsOptimization =
                        !method.ReturnsVoid && CanBeCompared(method.ReturnType) && method.Parameters.All(parameter => parameter.RefKind == RefKind.None);
                    var faultEffectReturn = originalBody.Statements.Count == 1 ? originalBody.Statements[0] as ReturnStatementSyntax : null;
                    var isConstantValue   = faultEffectReturn != null && SemanticModel.GetConstantValue(faultEffectReturn.Expression).HasValue;

                    if (signatureAllowsOptimization && isConstantValue)
                    {
                        writer.AppendLine($"var {"tmp".ToSynthesized()} = {baseEffect.ToFullString()};");
                        writer.AppendLine($"if ({"tmp".ToSynthesized()} == {faultEffectReturn.Expression.ToFullString()})");
                        writer.AppendBlockStatement(() => { writer.AppendLine($"{_undoActivation}(this.{"fault".ToSynthesized()});"); });
                        writer.AppendLine($"return {"tmp".ToSynthesized()};");
                    }
                    else
                    {
                        foreach (var statement in baseStatements)
                        {
                            writer.AppendLine(statement.NormalizeWhitespace().ToFullString());
                        }
                    }
                });

                writer.NewLine();
                body = SyntaxFactory.Block(SyntaxFactory.ParseStatement(writer.ToString()), lineAdjustedOriginalBody);
            }

            return(body.PrependLineDirective(-1));
        }