private void Cache(bool hasObject, CallbackArguments args, IOverloadableMemberDescriptor bestOverload) { int lowestHits = int.MaxValue; OverloadCacheItem found = null; for (int i = 0; i < m_Cache.Length; i++) { if (m_Cache[i] == null) { found = new OverloadCacheItem { ArgsDataType = new List <DataType>(), ArgsUserDataType = new List <Type>() }; m_Cache[i] = found; break; } if (m_Cache[i].HitIndexAtLastHit < lowestHits) { lowestHits = m_Cache[i].HitIndexAtLastHit; found = m_Cache[i]; } } if (found == null) { // overflow.. m_Cache = new OverloadCacheItem[CACHE_SIZE]; found = new OverloadCacheItem { ArgsDataType = new List <DataType>(), ArgsUserDataType = new List <Type>() }; m_Cache[0] = found; m_CacheHits = 0; } found.Method = bestOverload; found.HitIndexAtLastHit = ++m_CacheHits; found.ArgsDataType.Clear(); found.HasObject = hasObject; for (int i = 0; i < args.Count; i++) { found.ArgsDataType.Add(args[i].Type); if (args[i].Type == DataType.UserData) { found.ArgsUserDataType.Add(args[i].UserData.Descriptor.Type); } else { found.ArgsUserDataType.Add(null); } } }
private void AddMemberTo(Dictionary <string, IMemberDescriptor> members, string name, IMemberDescriptor desc) { IOverloadableMemberDescriptor odesc = desc as IOverloadableMemberDescriptor; if (odesc != null) { if (members.ContainsKey(name)) { OverloadedMethodMemberDescriptor overloads = members[name] as OverloadedMethodMemberDescriptor; if (overloads != null) { overloads.AddOverload(odesc); } else { throw new ArgumentException(string.Format("Multiple members named {0} are being added to type {1} and one or more of these members do not support overloads.", name, this.Type.FullName)); } } else { members.Add(name, new OverloadedMethodMemberDescriptor(name, this.Type, odesc)); } } else { if (members.ContainsKey(name)) { throw new ArgumentException(string.Format("Multiple members named {0} are being added to type {1} and one or more of these members do not support overloads.", name, this.Type.FullName)); } else { members.Add(name, desc); } } }
/// <summary> /// Initializes a new instance of the <see cref="OverloadedMethodMemberDescriptor" /> class. /// </summary> /// <param name="name">The name.</param> /// <param name="declaringType">The declaring type.</param> /// <param name="descriptor">The descriptor of the first overloaded method.</param> public OverloadedMethodMemberDescriptor(string name, Type declaringType, IOverloadableMemberDescriptor descriptor) : this(name, declaringType) { m_Overloads.Add(descriptor); }
/// <summary> /// Calculates the score for the overload. /// </summary> /// <param name="context">The context.</param> /// <param name="args">The arguments.</param> /// <param name="method">The method.</param> /// <param name="isExtMethod">if set to <c>true</c>, is an extension method.</param> /// <returns></returns> private int CalcScoreForOverload(ScriptExecutionContext context, CallbackArguments args, IOverloadableMemberDescriptor method, bool isExtMethod) { int totalScore = ScriptToClrConversions.WEIGHT_EXACT_MATCH; int argsBase = args.IsMethodCall ? 1 : 0; int argsCnt = argsBase; bool varArgsUsed = false; for (int i = 0; i < method.Parameters.Length; i++) { if (isExtMethod && i == 0) { continue; } if (method.Parameters[i].IsOut) { continue; } Type parameterType = method.Parameters[i].Type; if ((parameterType == typeof(Script)) || (parameterType == typeof(ScriptExecutionContext)) || (parameterType == typeof(CallbackArguments))) { continue; } if (i == method.Parameters.Length - 1 && method.VarArgsArrayType != null) { int varargCnt = 0; DynValue firstArg = null; int scoreBeforeVargars = totalScore; // update score for varargs while (true) { var arg = args.RawGet(argsCnt, false); if (arg == null) { break; } if (firstArg == null) { firstArg = arg; } argsCnt += 1; varargCnt += 1; int score = CalcScoreForSingleArgument(method.Parameters[i], method.VarArgsElementType, arg, isOptional: false); totalScore = Math.Min(totalScore, score); } // check if exact-match if (varargCnt == 1) { if (firstArg.Type == DataType.UserData && firstArg.UserData.Object != null) { if (Framework.Do.IsAssignableFrom(method.VarArgsArrayType, firstArg.UserData.Object.GetType())) { totalScore = scoreBeforeVargars; continue; } } } // apply varargs penalty to score if (varargCnt == 0) { totalScore = Math.Min(totalScore, ScriptToClrConversions.WEIGHT_VARARGS_EMPTY); } varArgsUsed = true; } else { var arg = args.RawGet(argsCnt, false) ?? DynValue.Void; int score = CalcScoreForSingleArgument(method.Parameters[i], parameterType, arg, method.Parameters[i].HasDefaultValue); totalScore = Math.Min(totalScore, score); argsCnt += 1; } } if (totalScore > 0) { if ((args.Count - argsBase) <= method.Parameters.Length) { totalScore += ScriptToClrConversions.WEIGHT_NO_EXTRA_PARAMS_BONUS; totalScore *= 1000; } else if (varArgsUsed) { totalScore -= ScriptToClrConversions.WEIGHT_VARARGS_MALUS; totalScore *= 1000; } else { totalScore *= 1000; totalScore -= ScriptToClrConversions.WEIGHT_EXTRA_PARAMS_MALUS * ((args.Count - argsBase) - method.Parameters.Length); totalScore = Math.Max(1, totalScore); } } #if DEBUG_OVERLOAD_RESOLVER System.Diagnostics.Debug.WriteLine(string.Format("[OVERLOAD] : Score {0} for method {1}", totalScore, method.SortDiscriminant)); #endif return(totalScore); }
/// <summary> /// Performs the overloaded call. /// </summary> /// <param name="script">The script.</param> /// <param name="obj">The object.</param> /// <param name="context">The context.</param> /// <param name="args">The arguments.</param> /// <returns></returns> /// <exception cref="ScriptRuntimeException">function call doesn't match any overload</exception> private DynValue PerformOverloadedCall(Script script, object obj, ScriptExecutionContext context, CallbackArguments args) { bool extMethodCacheNotExpired = IgnoreExtensionMethods || (obj == null) || m_ExtensionMethodVersion == UserData.GetExtensionMethodsChangeVersion(); // common case, let's optimize for it if (m_Overloads.Count == 1 && m_ExtOverloads.Count == 0 && extMethodCacheNotExpired) { return(m_Overloads[0].Execute(script, obj, context, args)); } if (m_Unsorted) { m_Overloads.Sort(new OverloadableMemberDescriptorComparer()); m_Unsorted = false; } if (extMethodCacheNotExpired) { for (int i = 0; i < m_Cache.Length; i++) { if (m_Cache[i] != null && CheckMatch(obj != null, args, m_Cache[i])) { #if DEBUG_OVERLOAD_RESOLVER System.Diagnostics.Debug.WriteLine(string.Format("[OVERLOAD] : CACHED! slot {0}, hits: {1}", i, m_CacheHits)); #endif return(m_Cache[i].Method.Execute(script, obj, context, args)); } } } // resolve on overloads first int maxScore = 0; IOverloadableMemberDescriptor bestOverload = null; for (int i = 0; i < m_Overloads.Count; i++) { if (obj != null || m_Overloads[i].IsStatic) { int score = CalcScoreForOverload(context, args, m_Overloads[i], false); if (score > maxScore) { maxScore = score; bestOverload = m_Overloads[i]; } } } if (!IgnoreExtensionMethods && (obj != null)) { if (!extMethodCacheNotExpired) { m_ExtensionMethodVersion = UserData.GetExtensionMethodsChangeVersion(); m_ExtOverloads = UserData.GetExtensionMethodsByNameAndType(this.Name, this.DeclaringType); } for (int i = 0; i < m_ExtOverloads.Count; i++) { int score = CalcScoreForOverload(context, args, m_ExtOverloads[i], true); if (score > maxScore) { maxScore = score; bestOverload = m_ExtOverloads[i]; } } } if (bestOverload != null) { Cache(obj != null, args, bestOverload); return(bestOverload.Execute(script, obj, context, args)); } throw new ScriptRuntimeException("function call doesn't match any overload"); }
/// <summary> /// Adds an overload. /// </summary> /// <param name="overload">The overload.</param> public void AddOverload(IOverloadableMemberDescriptor overload) { m_Overloads.Add(overload); m_Unsorted = true; }