/// <summary>Checks whether the specified method is applicable given the argument expressions.</summary> /// <param name="signature">The candidate function signature to check.</param> /// <param name="argumentTypes">The argument types to match.</param> /// <returns>An applicable function signature if all argument types can be promoted; 'null' otherwise.</returns> private static bool IsApplicable(FunctionSignature signature, ResourceType[] argumentTypes) { Debug.Assert(signature != null, "signature != null"); Debug.Assert(argumentTypes != null, "argumentTypes != null"); if (signature.ArgumentTypes.Length != argumentTypes.Length) { return false; } for (int i = 0; i < argumentTypes.Length; ++i) { if (!CanPromoteTo(argumentTypes[i], signature.ArgumentTypes[i])) { return false; } } return true; }
/// <summary>Finds the exact fitting function for the specified arguments.</summary> /// <param name="functions">Functions to consider.</param> /// <param name="argumentTypes">Types of the arguments for the function.</param> /// <returns>The exact fitting function; null if no exact match was found.</returns> internal static FunctionSignature FindExactFunctionSignature(FunctionSignature[] functions, ResourceType[] argumentTypes) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(functions != null, "functions != null"); Debug.Assert(argumentTypes != null, "argumentTypes != null"); for (int functionIndex = 0; functionIndex < functions.Length; functionIndex++) { FunctionSignature functionSignature = functions[functionIndex]; bool matchFound = true; if (functionSignature.ArgumentTypes.Length != argumentTypes.Length) { continue; } for (int argumentIndex = 0; argumentIndex < argumentTypes.Length; argumentIndex++) { ResourceType functionSignatureArgumentType = functionSignature.ArgumentTypes[argumentIndex]; ResourceType argumentType = argumentTypes[argumentIndex]; Debug.Assert(functionSignatureArgumentType.ResourceTypeKind == ResourceTypeKind.Primitive, "Only primitive arguments are supported for functions."); if (argumentType.ResourceTypeKind != ResourceTypeKind.Primitive) { matchFound = false; break; } // Since we're working on primitive types only we can compare just references since primitive resource types are atomized. if (!object.ReferenceEquals(argumentType, functionSignatureArgumentType)) { matchFound = false; break; } } if (matchFound) { return functionSignature; } } return null; }
/// <summary>Finds the best methods for the specified arguments given a candidate method enumeration.</summary> /// <param name="signatures">The candidate function signatures.</param> /// <param name="argumentTypes">The argument types to match.</param> /// <returns>The number of "best match" methods.</returns> private static int FindBestSignature(FunctionSignature[] signatures, ResourceType[] argumentTypes) { Debug.Assert(signatures != null, "signatures != null"); Debug.Assert(argumentTypes != null, "argumentTypes != null"); Debug.Assert(argumentTypes.All(t => t == null || t.ResourceTypeKind == ResourceTypeKind.Primitive), "All argument types must be primitive or null."); Debug.Assert(signatures.All(s => s.ArgumentTypes != null && s.ArgumentTypes.All(t => t.ResourceTypeKind == ResourceTypeKind.Primitive)), "All signatures must have only primitive argument types."); List<FunctionSignature> applicableSignatures = signatures.Where(signature => IsApplicable(signature, argumentTypes)).ToList(); if (applicableSignatures.Count > 1) { applicableSignatures = FindBestApplicableSignatures(applicableSignatures, argumentTypes); } int result = applicableSignatures.Count; if (result == 1) { // TODO: deal with the situation that we started off with all non-open types // and end up with all open types; see RequestQueryParser.FindBestMethod. // Ignored for now since we don't support open types yet. FunctionSignature signature = applicableSignatures[0]; for (int i = 0; i < argumentTypes.Length; i++) { argumentTypes[i] = signature.ArgumentTypes[i]; } result = 1; } else if (result > 1) { // We may have the case for operators (which C# doesn't) in which we have a nullable operand // and a non-nullable operand. We choose to convert the one non-null operand to nullable in that // case (the binary expression will lift to null). if (argumentTypes.Length == 2 && result == 2 && TypeUtils.GetNonNullableType(applicableSignatures[0].ArgumentTypes[0].InstanceType) == TypeUtils.GetNonNullableType(applicableSignatures[1].ArgumentTypes[0].InstanceType)) { FunctionSignature nullableMethod = TypeUtils.TypeAllowsNull(applicableSignatures[0].ArgumentTypes[0].InstanceType) ? applicableSignatures[0] : applicableSignatures[1]; argumentTypes[0] = nullableMethod.ArgumentTypes[0]; argumentTypes[1] = nullableMethod.ArgumentTypes[1]; // TODO: why is this necessary? We keep it here for now since the product has it but assert // that nothing new was found. int signatureCount = FindBestSignature(signatures, argumentTypes); Debug.Assert(signatureCount == 1, "signatureCount == 1"); Debug.Assert(argumentTypes[0] == nullableMethod.ArgumentTypes[0], "argumentTypes[0] == nullableMethod.ArgumentTypes[0]"); Debug.Assert(argumentTypes[1] == nullableMethod.ArgumentTypes[1], "argumentTypes[1] == nullableMethod.ArgumentTypes[1]"); return signatureCount; } } return result; }
/// <summary>Finds the best fitting function for the specified arguments.</summary> /// <param name="functions">Functions to consider.</param> /// <param name="argumentTypes">Types of the arguments for the function.</param> /// <returns>The best fitting function; null if none found or ambiguous.</returns> internal static FunctionSignature FindBestFunctionSignature(FunctionSignature[] functions, ResourceType[] argumentTypes) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(functions != null, "functions != null"); Debug.Assert(argumentTypes != null, "argumentTypes != null"); List<FunctionSignature> applicableFunctions = new List<FunctionSignature>(functions.Length); // Build a list of applicable functions (and cache their promoted arguments). foreach (FunctionSignature candidate in functions) { if (candidate.ArgumentTypes.Length != argumentTypes.Length) { continue; } bool argumentsMatch = true; for (int i = 0; i < candidate.ArgumentTypes.Length; i++) { if (!CanPromoteTo(argumentTypes[i], candidate.ArgumentTypes[i])) { argumentsMatch = false; break; } } if (argumentsMatch) { applicableFunctions.Add(candidate); } } // Return the best applicable function. if (applicableFunctions.Count == 0) { // No matching function. return null; } else if (applicableFunctions.Count == 1) { return applicableFunctions[0]; } else { // Find a single function which is better than all others. int bestFunctionIndex = -1; for (int i = 0; i < applicableFunctions.Count; i++) { bool betterThanAllOthers = true; for (int j = 0; j < applicableFunctions.Count; j++) { if (i != j && MatchesArgumentTypesBetterThan(argumentTypes, applicableFunctions[j].ArgumentTypes, applicableFunctions[i].ArgumentTypes)) { betterThanAllOthers = false; break; } } if (betterThanAllOthers) { if (bestFunctionIndex == -1) { bestFunctionIndex = i; } else { // Ambiguous. return null; } } } if (bestFunctionIndex == -1) { return null; } return applicableFunctions[bestFunctionIndex]; } }