private ExpressionSyntax GenCall(FuzzType type, bool allowNew) { Debug.Assert(!(type is RefType), "Cannot GenCall to ref type -- use GenExistingLValue for that"); FuncGenerator func; if (allowNew && Random.FlipCoin(Options.GenNewMethodProb)) { type = type ?? Types.PickType(Options.ReturnTypeIsByRefProb); func = new FuncGenerator(_funcs, Random, Types, Statics, _genChecksumSiteId); func.Generate(type, true); } else { IEnumerable <FuncGenerator> funcs = _funcs.Skip(_funcIndex + 1); if (type != null) { funcs = funcs.Where(f => f.ReturnType.IsCastableTo(type) || (f.ReturnType is RefType rt && rt.InnerType.IsCastableTo(type))); } List <FuncGenerator> list = funcs.ToList(); if (list.Count == 0) { return(null); } func = Random.NextElement(list); type = type ?? func.ReturnType; } ArgumentSyntax[] args = GenArgs(func, 0, out _); InvocationExpressionSyntax invoc = InvocationExpression( IdentifierName(func.Name), ArgumentList( SeparatedList(args))); if (func.ReturnType == type || func.ReturnType is RefType retRt && retRt.InnerType == type) { return(invoc); } return(CastExpression(type.GenReferenceTo(), invoc)); }
private ArgumentSyntax[] GenArgs(FuncGenerator funcToCall, int minRefEscapeScope, out int argsMinRefEscapeScope) { ArgumentSyntax[] args = new ArgumentSyntax[funcToCall.Parameters.Length]; argsMinRefEscapeScope = int.MaxValue; for (int i = 0; i < args.Length; i++) { FuzzType paramType = funcToCall.Parameters[i].Type; if (paramType is RefType rt) { LValueInfo lv = GenLValue(rt.InnerType, minRefEscapeScope); argsMinRefEscapeScope = Math.Min(argsMinRefEscapeScope, lv.RefEscapeScope); args[i] = Argument(lv.Expression).WithRefKindKeyword(Token(SyntaxKind.RefKeyword)); } else { args[i] = Argument(GenExpression(paramType)); } } return(args); }
private ExpressionSyntax GenCall(FuzzType type, bool allowNew) { Debug.Assert(!(type is RefType), "Cannot GenCall to ref type -- use GenExistingLValue for that"); FuncGenerator func; if (allowNew && Random.FlipCoin(Options.GenNewFunctionProb) && !Options.FuncGenRejection.Reject(_funcs.Count, Random.Rng)) { type = type ?? Types.PickType(Options.ReturnTypeIsByRefProb); func = new FuncGenerator(_funcs, Random, Types, Statics, _genChecksumSiteId); func.Generate(type, true); } else { IEnumerable <FuncGenerator> funcs = _funcs .Skip(_funcIndex + 1) .Where(candidate => { // Make sure we do not get too many leaf calls. Here we compute what the new transitive // number of calls would be to each function, and if it's too much, reject this candidate. // Note that we will never reject calling a leaf function directly, even if the +1 puts // us over the cap. That is intentional. We only want to limit the exponential growth // which happens when functions call functions multiple times, and those functions also // call functions multiple times. foreach (var(transFunc, transNumCall) in candidate._callCountMap) { _callCountMap.TryGetValue(transFunc, out long curNumCalls); if (curNumCalls + transNumCall > Options.SingleFunctionMaxTotalCalls) { return(false); } } return(true); }); if (type != null) { funcs = funcs.Where(f => f.ReturnType.IsCastableTo(type) || (f.ReturnType is RefType rt && rt.InnerType.IsCastableTo(type))); } List <FuncGenerator> list = funcs.ToList(); if (list.Count == 0) { return(null); } func = Random.NextElement(list); type = type ?? func.ReturnType; } // Update transitive call counts before generating args, so we decrease chance of // calling the same methods in the arg expressions. _callCountMap.TryGetValue(func._funcIndex, out long numCalls); _callCountMap[func._funcIndex] = numCalls + 1; foreach (var(transFunc, transNumCalls) in func._callCountMap) { _callCountMap.TryGetValue(transFunc, out long curNumCalls); _callCountMap[transFunc] = curNumCalls + transNumCalls; } ArgumentSyntax[] args = GenArgs(func, 0, out _); InvocationExpressionSyntax invoc = InvocationExpression( IdentifierName(func.Name), ArgumentList( SeparatedList(args))); if (func.ReturnType == type || func.ReturnType is RefType retRt && retRt.InnerType == type) { return(invoc); } return(CastExpression(type.GenReferenceTo(), invoc)); }
private LValueInfo GenExistingLValue(FuzzType type, int minRefEscapeScope) { Debug.Assert(type == null || !(type is RefType)); LValueKind kind = (LValueKind)Options.ExistingLValueDist.Sample(Random.Rng); if (kind == LValueKind.RefReturningCall) { List <FuncGenerator> refReturningFuncs = new List <FuncGenerator>(); foreach (FuncGenerator func in _funcs.Skip(_funcIndex + 1)) { if (!(func.ReturnType is RefType rt)) { continue; } if (type == null || type == rt.InnerType) { refReturningFuncs.Add(func); } } // If we don't have a ref-returning function, fall back to local/static by retrying. // The reason we don't always fall back to trying other options is that otherwise // we may stack overflow in cases like ref int M(ref int a), if we have no locals // or statics of type int. We would keep generating method calls in this case. // This is kind of a hack, although it is pretty natural to pick statics/locals for // lvalues, so it is not that big of a deal. if (refReturningFuncs.Count == 0) { return(GenExistingLValue(type, minRefEscapeScope)); } FuncGenerator funcToCall = Random.NextElement(refReturningFuncs); // When calling a func that returns a by-ref, we need to take into account that the C# compiler // performs 'escape-analysis' on by-refs. If we want to produce a non-local by-ref we are only // allowed to pass non-local by-refs. The following example illustrates the analysis performed // by the compiler: // ref int M(ref int b, ref int c) { // int a = 2; // return ref Max(ref a, ref b); // compiler error, returned by-ref could point to 'a' and escape // return ref Max(ref b, ref c); // ok, returned ref cannot point to local // } // ref int Max(ref int a, ref int b) { ... } // This is the reason for the second arg passed to GenArgs. For example, if we want a call to return // a ref that may escape, we need a minimum ref escape scope of 1, since 0 could pass refs to locals, // and the result of that call would not be valid to return. ArgumentSyntax[] args = GenArgs(funcToCall, minRefEscapeScope, out int argsMinRefEscapeScope); InvocationExpressionSyntax invoc = InvocationExpression( IdentifierName(funcToCall.Name), ArgumentList( SeparatedList( args))); return(new LValueInfo(invoc, ((RefType)funcToCall.ReturnType).InnerType, argsMinRefEscapeScope)); } List <LValueInfo> lvalues = CollectVariablePaths(type, minRefEscapeScope, kind == LValueKind.Local, kind == LValueKind.Static); if (lvalues.Count == 0) { // We typically fall back to generating a static, so just try to find a static if we found no local. if (kind != LValueKind.Local) { return(null); } lvalues = CollectVariablePaths(type, minRefEscapeScope, false, true); if (lvalues.Count == 0) { return(null); } } return(Random.NextElement(lvalues)); }
internal void GenerateMethods(Func <string> genChecksumSiteId) { FuncGenerator gen = new FuncGenerator(_funcs, Random, Types, Statics, genChecksumSiteId); gen.Generate(null, false); }