/* public JsExpression[] TranslateArguments(MethodSymbol method, params JsExpression[] args) { return TranslateArguments(method, (x, i) => x is JsArrayExpression); } */ public JsExpression[] TranslateArguments(SyntaxNode context, IMethodSymbol method, Func<JsExpression, int, bool> isArgumentArray, Func<JsExpression, int, string> getArgumentName, params JsExpression[] args) { var isExported = method.IsExported(); // The number of arguments may differ from the number of parameters, due to default parameters and the params keyword. // This queue holds all the remaining arguments, and the matching of parameters to arguments happens in sequence, // consuming from the queue. var remainingArguments = new Queue<JsExpression>(args); var argumentsByName = args .Select((x, i) => new { Name = getArgumentName(x, i), Argument = x }) .Where(x => x.Name != null) .ToDictionary(x => x.Name, x => x.Argument); if (argumentsByName.Any()) { var newArguments = new List<JsExpression>(); foreach (var parameter in method.Parameters) { if (!parameter.HasExplicitDefaultValue) { newArguments.Add(remainingArguments.Dequeue()); } else { if (!argumentsByName.ContainsKey(parameter.Name)) { if (parameter.GetAttributes().Any(x => Equals(x.AttributeClass, Context.Instance.CallerMemberNameAttribute))) { var value = context.GetContainingMemberName(); newArguments.Add(value != null ? Js.Literal(value) : GetExplicitDefaultValue(parameter)); } else { newArguments.Add(GetExplicitDefaultValue(parameter)); } } else { var argument = argumentsByName[parameter.Name]; newArguments.Add(argument); } } } remainingArguments = new Queue<JsExpression>(newArguments); } var arguments = new List<JsExpression>(); foreach (var parameter in method.Parameters) { // params parameters require special handling if (parameter.IsParams) { // If there's only one argument for the params parameter, it could be either an array containing // the the params arguments, or just a single argument. if (remainingArguments.Count == 1) { var argument = remainingArguments.Peek(); var argIndex = args.Length - remainingArguments.Count; if (isArgumentArray(argument, argIndex)) { // If it's an array and exported, we just pass it as is, since the argument must ultimately be an array. if (isExported) { arguments.Add(argument); } // If it's NOT exported, then we simply pass them as ordinary arguments, since that's how this "params" // concept works over there (it's not abstracted into an array). else { foreach (var element in ((JsArrayExpression)argument).Elements) { arguments.Add(element); } } remainingArguments.Dequeue(); continue; } } // If exported, then add all the rest of the arguments as an array if (isExported) { arguments.Add(MakeArray(Js.Array(remainingArguments.ToArray()), (IArrayTypeSymbol)parameter.Type)); } // Otherwise, add all the rest of the arguments as ordinary arguments per the comment earlier about non exported types. else { while (remainingArguments.Any()) arguments.Add(remainingArguments.Dequeue()); } } else if (!remainingArguments.Any()) { // If not exported, then it's a C# to Javascript transfer, and in Javascript land, default arguments are // always undefined. Thus we don't want to add default arguments for non-exported methods. if (isExported) { if (parameter.GetAttributes().Any(x => Equals(x.AttributeClass, Context.Instance.CallerMemberNameAttribute))) { var value = context.GetContainingMemberName(); arguments.Add(value != null ? Js.Literal(value) : GetExplicitDefaultValue(parameter)); } else { arguments.Add(GetExplicitDefaultValue(parameter)); } } } else { arguments.Add(remainingArguments.Dequeue()); } } return arguments.ToArray(); }