public static void Go(HaxeWriter writer, InvocationExpressionSyntax invocationExpression) { var model = Program.GetModel(invocationExpression); var symbolInfo = model.GetSymbolInfo(invocationExpression); var expressionSymbol = model.GetSymbolInfo(invocationExpression.Expression); if (symbolInfo.Symbol == null) { throw new Exception("InvocationExpression could not be identified. Are you sure the C# is valid? " + Utility.Descriptor(invocationExpression)); } var methodSymbol = symbolInfo.Symbol.OriginalDefinition.As <IMethodSymbol>().UnReduce(); var translateOpt = MethodTranslation.Get(symbolInfo.Symbol.As <IMethodSymbol>()); var memberReferenceExpressionOpt = invocationExpression.Expression as MemberAccessExpressionSyntax; //var returnTypeHaxe = TypeProcessor.ConvertType(methodSymbol.ReturnType); var firstParameter = true; var extensionNamespace = methodSymbol.IsExtensionMethod ? methodSymbol.ContainingNamespace.FullNameWithDot().ToLower() + methodSymbol.ContainingType.Name : null; //null means it's not an extension method, non-null means it is string methodName; ExpressionSyntax subExpressionOpt; if (methodSymbol.ContainingType.Name == "Enum") { if (methodSymbol.Name == "Parse") { WriteEnumParse(writer, invocationExpression); return; } if (methodSymbol.Name == "GetValues") { WriteEnumGetValues(writer, invocationExpression); return; } } if (expressionSymbol.Symbol is IEventSymbol) { methodName = "Invoke"; //Would need to append the number of arguments to this to support events. However, events are not currently supported } else if (memberReferenceExpressionOpt != null && memberReferenceExpressionOpt.Expression is PredefinedTypeSyntax) { switch (methodSymbol.Name) { case "Parse": var t = TypeProcessor.ConvertType(methodSymbol.ReturnType); if (t == "Bool") { methodName = "ParseBool"; extensionNamespace = "Cs2Hx"; } else if (t == "Int" || t == "Float") { methodName = "parse" + t; extensionNamespace = "Std"; } else { throw new Exception("Parse method on " + t + " is not supported. " + Utility.Descriptor(memberReferenceExpressionOpt)); } break; case "TryParse": methodName = "TryParse" + TypeProcessor.ConvertType(methodSymbol.Parameters[1].Type); extensionNamespace = "Cs2Hx"; break; default: methodName = methodSymbol.Name; extensionNamespace = "Cs2Hx"; break; } } else if (translateOpt != null && translateOpt.ReplaceWith != null) { methodName = translateOpt.ReplaceWith; } else if (methodSymbol.MethodKind == MethodKind.DelegateInvoke) { methodName = null; } else { methodName = OverloadResolver.MethodName(methodSymbol); } if (translateOpt != null && translateOpt.HasComplexReplaceWith) { translateOpt.DoComplexReplaceWith(writer, memberReferenceExpressionOpt); //TODO: What if this is null? return; } if (translateOpt != null && translateOpt.SkipExtensionParameter) { subExpressionOpt = null; } else if (methodSymbol.MethodKind == MethodKind.DelegateInvoke) { subExpressionOpt = invocationExpression.Expression; } else if (memberReferenceExpressionOpt != null) { if (memberReferenceExpressionOpt.Expression is PredefinedTypeSyntax) { subExpressionOpt = null; } else { subExpressionOpt = memberReferenceExpressionOpt.Expression; } } else { subExpressionOpt = null; //TODO } //Determine if it's an extension method called in a non-extension way. In this case, just pretend it's not an extension method if (extensionNamespace != null && subExpressionOpt != null && model.GetTypeInfo(subExpressionOpt).ConvertedType.ToString() == methodSymbol.ContainingNamespace + "." + methodSymbol.ContainingType.Name) { extensionNamespace = null; } if (translateOpt != null && !string.IsNullOrEmpty(translateOpt.ExtensionNamespace)) { extensionNamespace = translateOpt.ExtensionNamespace; } else if (translateOpt != null && translateOpt.ExtensionNamespace == "") { extensionNamespace = null; } if (extensionNamespace != null) { writer.Write(extensionNamespace); if (methodName != null) { writer.Write("."); writer.Write(methodName); } writer.Write("("); if (subExpressionOpt != null) { firstParameter = false; if (methodSymbol.IsExtensionMethod) { WriteForEachStatement.CheckWriteEnumerator(writer, subExpressionOpt, true); } else { Core.Write(writer, subExpressionOpt); } } } else { if (memberReferenceExpressionOpt != null) { var memberType = model.GetTypeInfo(memberReferenceExpressionOpt.Expression).Type; var memberTypeHaxe = TypeProcessor.ConvertType(memberType); //sort calls without any parameters need to get the default sort parameter if (methodName == "sort" && invocationExpression.ArgumentList.Arguments.Count == 0) { Core.Write(writer, memberReferenceExpressionOpt.Expression); writer.Write(".sort("); switch (memberTypeHaxe) { case "Array<Int>": writer.Write("Cs2Hx.SortInts"); break; case "Array<Float>": writer.Write("Cs2Hx.SortFloats"); break; case "Array<String>": writer.Write("Cs2Hx.SortStrings"); break; default: throw new Exception("Unknown default sort type: " + memberTypeHaxe + ". " + Utility.Descriptor(invocationExpression)); } writer.Write(")"); return; } //Check against lowercase toString since it gets replaced with the haxe name before we get here if (methodName == "toString") { if (memberType.TypeKind == TypeKind.Enum) { //calling ToString() on an enum forwards to our enum's special ToString method writer.Write(memberType.ContainingNamespace.FullNameWithDot().ToLower()); writer.Write(WriteType.TypeName((INamedTypeSymbol)memberType)); writer.Write(".ToString("); Core.Write(writer, memberReferenceExpressionOpt.Expression); writer.Write(")"); if (invocationExpression.ArgumentList.Arguments.Count > 0) { throw new Exception("Enum's ToString detected with parameters. These are not supported " + Utility.Descriptor(invocationExpression)); } return; } if (memberType.SpecialType == SpecialType.System_Char) { writer.Write("Cs2Hx.CharToString("); Core.Write(writer, memberReferenceExpressionOpt.Expression); writer.Write(")"); if (invocationExpression.ArgumentList.Arguments.Count > 0) { throw new Exception("Char's ToString detected with parameters. These are not supported " + Utility.Descriptor(invocationExpression)); } return; } if (memberTypeHaxe == "Int" || memberTypeHaxe == "Float" || memberTypeHaxe == "Bool" || memberType.TypeKind == TypeKind.TypeParameter) { //ToString()'s on primitive types get replaced with Std.string writer.Write("Std.string("); if (memberReferenceExpressionOpt.Expression is ParenthesizedExpressionSyntax) { Core.Write(writer, memberReferenceExpressionOpt.Expression.As <ParenthesizedExpressionSyntax>().Expression); //eat a set of parenthesis, just to make the output code a bit nicer. } else { Core.Write(writer, memberReferenceExpressionOpt.Expression); } writer.Write(")"); if (invocationExpression.ArgumentList.Arguments.Count > 0) { throw new Exception("Primitive type's ToString detected with parameters. These are not supported " + Utility.Descriptor(invocationExpression)); } return; //Skip parameters } } } if (subExpressionOpt != null) { WriteMemberAccessExpression.WriteMember(writer, subExpressionOpt); } if (subExpressionOpt != null && methodName != null) { writer.Write("."); } writer.Write(methodName); writer.Write("("); } var prms = TranslateParameters(translateOpt, SortArguments(methodSymbol, invocationExpression.ArgumentList.Arguments, invocationExpression, extensionNamespace != null), invocationExpression).ToList(); //If we invoke a method with type parameters that aren't used in the argument list, the haxe function won't have a way to see what args were used. To give it a way, add those as parameters at the end foreach (var typePrm in Utility.PassTypeArgsToMethod(methodSymbol)) { var name = invocationExpression.Expression.ChildNodes().OfType <GenericNameSyntax>().ToList(); if (name.Count == 0) { name = invocationExpression.Expression.DescendantNodesAndSelf().OfType <GenericNameSyntax>().ToList(); //TODO: This will pick up false positives, we need a proper recursion function here } if (name.Count != 1) { throw new Exception("Expected a single generic name, got " + string.Join(", ", name) + " " + Utility.Descriptor(invocationExpression)); } if (name.Count > 0 && name.Single().TypeArgumentList.Arguments.Count > 0) { var typePrmIndex = methodSymbol.TypeParameters.IndexOf(methodSymbol.TypeParameters.Single(o => o == typePrm)); var genericVar = name.Single().TypeArgumentList.Arguments.ElementAt(typePrmIndex); if (genericVar.ToString() == typePrm.ToString()) { writer.Write("t" + (typePrmIndex + 1)); } else { prms.Add(new TransformedArgument(TypeProcessor.ConvertType(genericVar))); } } } bool inParams = false; foreach (var arg in prms) { if (firstParameter) { firstParameter = false; } else { writer.Write(", "); } if (!inParams && IsParamsArgument(invocationExpression, arg.ArgumentOpt, methodSymbol) && TypeProcessor.ConvertType(model.GetTypeInfo(arg.ArgumentOpt.Expression).Type).StartsWith("Array<") == false) { inParams = true; writer.Write("[ "); } if (arg.ArgumentOpt != null && arg.ArgumentOpt.RefOrOutKeyword.Kind() != SyntaxKind.None && model.GetSymbolInfo(arg.ArgumentOpt.Expression).Symbol is IFieldSymbol) { throw new Exception("ref/out cannot reference fields, only local variables. Consider using ref/out on a local variable and then assigning it into the field. " + Utility.Descriptor(invocationExpression)); } //When passing an argument by ref or out, leave off the .Value suffix if (arg.ArgumentOpt != null && arg.ArgumentOpt.RefOrOutKeyword.Kind() != SyntaxKind.None) { WriteIdentifierName.Go(writer, arg.ArgumentOpt.Expression.As <IdentifierNameSyntax>(), true); } else if (arg.ArgumentOpt != null) { WriteForEachStatement.CheckWriteEnumerator(writer, arg.ArgumentOpt.Expression, false); } else { arg.Write(writer); } } if (inParams) { writer.Write(" ]"); } writer.Write(")"); }
public static void Go(HaxeWriter writer, MethodDeclarationSyntax method) { if (method.Modifiers.Any(SyntaxKind.PartialKeyword) && method.Body == null) { //We only want to render out one of the two partial methods. If there's another, skip this one. if (TypeState.Instance.Partials.SelectMany(o => o.Syntax.As <ClassDeclarationSyntax>().Members) .OfType <MethodDeclarationSyntax>() .Except(method) .Where(o => o.Identifier.ValueText == method.Identifier.ValueText) .Any()) { return; } } if (method.Identifier.ValueText == "GetEnumerator") { return; //skip GetEnumerator methods -- haxe can't enumerate on objects. TODO: Render these out, but convert them to array-returning methods } var methodSymbol = Program.GetModel(method).GetDeclaredSymbol(method); writer.WriteIndent(); if (ShouldUseOverrideKeyword(method, methodSymbol)) { writer.Write("override "); } if (method.Modifiers.Any(SyntaxKind.PublicKeyword) || method.Modifiers.Any(SyntaxKind.ProtectedKeyword) || method.Modifiers.Any(SyntaxKind.InternalKeyword)) { writer.Write("public "); } if (method.Modifiers.Any(SyntaxKind.PrivateKeyword)) { writer.Write("private "); } if (method.Modifiers.Any(SyntaxKind.StaticKeyword)) { writer.Write("static "); } writer.Write("function "); var methodName = OverloadResolver.MethodName(methodSymbol); if (methodName == "ToString") { methodName = "toString"; } writer.Write(methodName); if (method.TypeParameterList != null) { writer.Write("<"); writer.Write(string.Join(", ", method.TypeParameterList.Parameters.Select(o => TypeParameter(o, method.ConstraintClauses)))); writer.Write(">"); } writer.Write("("); var deferredDefaults = new Dictionary <string, ExpressionSyntax>(); var firstParam = true; foreach (var parameter in method.ParameterList.Parameters) { bool isRef = parameter.Modifiers.Any(SyntaxKind.OutKeyword) || parameter.Modifiers.Any(SyntaxKind.RefKeyword); if (firstParam) { firstParam = false; } else { writer.Write(", "); } writer.Write(parameter.Identifier.ValueText); if (isRef) { writer.Write(":CsRef<"); writer.Write(TypeProcessor.ConvertType(parameter.Type)); writer.Write(">"); Program.RefOutSymbols.TryAdd(Program.GetModel(method).GetDeclaredSymbol(parameter), null); } else { writer.Write(TypeProcessor.ConvertTypeWithColon(parameter.Type)); } if (parameter.Default != null) { writer.Write(" = "); if (TypeProcessor.ConvertType(parameter.Type).StartsWith("Nullable")) { writer.Write("null"); deferredDefaults.Add(parameter.Identifier.ValueText, parameter.Default.Value); } else { Core.Write(writer, parameter.Default.Value); } } } writer.Write(")"); writer.Write(TypeProcessor.ConvertTypeWithColon(method.ReturnType)); if (method.Modifiers.Any(SyntaxKind.AbstractKeyword)) { writer.WriteLine(); writer.WriteOpenBrace(); writer.WriteIndent(); if (method.ReturnType.ToString() != "void") { writer.Write("return "); //"return" the throw statement to work around haxe limitations } writer.Write("throw new Exception(\"Abstract item called\");\r\n"); writer.WriteCloseBrace(); } else if (method.Parent is InterfaceDeclarationSyntax) { writer.Write(";\r\n"); } else { writer.WriteLine(); writer.WriteOpenBrace(); foreach (var defer in deferredDefaults) { writer.WriteLine("if (" + defer.Key + " == null)"); writer.Indent++; writer.WriteIndent(); writer.Write(defer.Key); writer.Write(" = "); Core.Write(writer, defer.Value); writer.Write(";\r\n"); writer.Indent--; } if (method.Body != null) { foreach (var statement in method.Body.Statements) { Core.Write(writer, statement); } TriviaProcessor.ProcessTrivias(writer, method.Body.DescendantTrivia()); } writer.WriteCloseBrace(); } }