private static IEnumerable <TransformedArgument> TranslateParameters(MethodTranslation translateOpt, IEnumerable <ArgumentSyntax> list, ObjectCreationExpressionSyntax invoke) { if (translateOpt == null) { return(list.Select(o => new TransformedArgument(o))); } else if (translateOpt is MethodTranslation) { return(translateOpt.As <MethodTranslation>().TranslateParameters(list, invoke)); } else { throw new Exception("Need handler for " + translateOpt.GetType().Name); } }
private static IEnumerable <TransformedArgument> TranslateParameters(MethodTranslation translateOpt, IEnumerable <TransformedArgument> list, InvocationExpressionSyntax invoke) { if (translateOpt == null) { return(list); } else if (translateOpt is Translations.MethodTranslation) { return(translateOpt.As <Translations.MethodTranslation>().TranslateParameters(list, invoke.Expression)); } else { throw new Exception("Need handler for " + translateOpt.GetType().Name); } }
private static void WriteTypeParameters(ScalaWriter writer, MethodTranslation translateOpt, string typeParameters, InvocationExpressionSyntax invoke) { if (translateOpt != null) { if (translateOpt.WriteTypeParameters(writer, invoke)) { return; } } if (typeParameters != null) { writer.Write(typeParameters); } }
public static void Go(HaxeWriter writer, ObjectCreationExpressionSyntax expression) { if (expression.ArgumentList == null) { throw new Exception("Types must be initialized with parenthesis. Object initialization syntax is not supported. " + Utility.Descriptor(expression)); } var model = Program.GetModel(expression); var type = model.GetTypeInfo(expression).Type; if (type.SpecialType == SpecialType.System_Object) { //new object() results in the CsObject type being made. This is only really useful for locking writer.Write("new CsObject()"); } else { var methodSymbol = model.GetSymbolInfo(expression).Symbol.As <IMethodSymbol>(); var translateOpt = MethodTranslation.Get(methodSymbol); writer.Write("new "); writer.Write(TypeProcessor.ConvertType(expression.Type)); writer.Write("("); bool first = true; foreach (var param in TranslateParameters(translateOpt, WriteInvocationExpression.SortArguments(methodSymbol, expression.ArgumentList.Arguments, expression), expression)) { if (first) { first = false; } else { writer.Write(", "); } param.Write(writer); } writer.Write(")"); } }
public static void WriteParameters(HaxeWriter writer, BaseMethodDeclarationSyntax method, IMethodSymbol methodSymbol, out Dictionary <string, ExpressionSyntax> deferredDefaults) { deferredDefaults = new Dictionary <string, ExpressionSyntax>(); var prms = method.ParameterList.Parameters.Select(o => new TransformedArgument(o)).ToList(); var translateOpt = MethodTranslation.Get(methodSymbol); if (translateOpt != null) { prms = translateOpt.As <MethodTranslation>().TranslateParameters(prms, null).ToList(); } var firstParam = true; foreach (var parameter in prms) { bool isRef = parameter.ParameterOpt != null && (parameter.ParameterOpt.Modifiers.Any(SyntaxKind.OutKeyword) || parameter.ParameterOpt.Modifiers.Any(SyntaxKind.RefKeyword)); if (parameter.StringOpt != null) { continue; //these are only used on invocations } if (firstParam) { firstParam = false; } else { writer.Write(", "); } writer.Write(parameter.ParameterOpt.Identifier.ValueText); if (isRef) { writer.Write(":CsRef<"); writer.Write(TypeProcessor.ConvertType(parameter.ParameterOpt.Type)); writer.Write(">"); Program.RefOutSymbols.TryAdd(Program.GetModel(method).GetDeclaredSymbol(parameter.ParameterOpt), null); } else { writer.Write(TypeProcessor.ConvertTypeWithColon(parameter.ParameterOpt.Type)); } if (parameter.ParameterOpt.Default != null) { writer.Write(" = "); if (TypeProcessor.ConvertType(parameter.ParameterOpt.Type).StartsWith("Nullable")) { writer.Write("null"); deferredDefaults.Add(parameter.ParameterOpt.Identifier.ValueText, parameter.ParameterOpt.Default.Value); } else { Core.Write(writer, parameter.ParameterOpt.Default.Value); } } } int tIndex = 1; foreach (var genericVar in Utility.PassTypeArgsToMethod(methodSymbol)) { if (firstParam) { firstParam = false; } else { writer.Write(", "); } writer.Write("t" + tIndex.ToString()); writer.Write(":Class<"); writer.Write(TypeProcessor.ConvertType(genericVar)); writer.Write(">"); tIndex++; } }
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 = (IMethodSymbol)symbolInfo.Symbol; var origMethodSymbol = methodSymbol.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; if (methodSymbol.TypeArguments.Any(o => o.SpecialType == SpecialType.System_Char) && origMethodSymbol.ContainingType.Name != "Enumerable") { throw new Exception("Char cannot be passed as a type argument. methodName=" + origMethodSymbol.Name + " " + Utility.Descriptor(invocationExpression)); //the called function could call .ToString on it, which would give the int representation instead of the char. We could fix this by wrapping chars in a class but that would hit performance, so instead just re-write the offending C#. Skip this requirement for System.Linq.Enumerable because it is typically just mapping to a lambda, such as Select or TakeWhile, and our lambda can handle it correctly. Ideally we'd detect if the type parameter is only used in a lambda } var extensionNamespace = origMethodSymbol.IsExtensionMethod ? origMethodSymbol.ContainingNamespace.FullNameWithDot().ToLower() + origMethodSymbol.ContainingType.Name : null; //null means it's not an extension method, non-null means it is string methodName; ExpressionSyntax subExpressionOpt; if (origMethodSymbol.ContainingType.Name == "Enum") { if (origMethodSymbol.Name == "Parse") { WriteEnumParse(writer, invocationExpression); return; } if (origMethodSymbol.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 (origMethodSymbol.Name) { case "Parse": var t = TypeProcessor.ConvertType(origMethodSymbol.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(origMethodSymbol.Parameters[1].Type); extensionNamespace = "Cs2Hx"; break; default: methodName = origMethodSymbol.Name; extensionNamespace = "Cs2Hx"; break; } } else if (translateOpt != null && translateOpt.ReplaceWith != null) { methodName = translateOpt.ReplaceWith; } else if (origMethodSymbol.MethodKind == MethodKind.DelegateInvoke) { methodName = null; } else { methodName = OverloadResolver.MethodName(origMethodSymbol); } 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 (origMethodSymbol.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() == origMethodSymbol.ContainingNamespace + "." + origMethodSymbol.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 (origMethodSymbol.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(origMethodSymbol, 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(origMethodSymbol)) { 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 = origMethodSymbol.TypeParameters.IndexOf(origMethodSymbol.TypeParameters.Single(o => SymbolEqualityComparer.Default.Equals(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, origMethodSymbol) && 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(ScalaWriter writer, ObjectCreationExpressionSyntax expression) { var model = Program.GetModel(expression); var type = model.GetTypeInfo(expression).Type; if (expression.Initializer != null) { throw new Exception("Object initializers are not supported " + Utility.Descriptor(expression)); } if (type.SpecialType == SpecialType.System_Object) { //new object() results in the CsObject type being made. This is only really useful for locking writer.Write("new CsObject()"); } else if (type.SpecialType == SpecialType.System_String) { //new String() writer.Write("System.CsScala.NewString("); bool first = true; foreach (var param in expression.ArgumentList.Arguments) { if (first) { first = false; } else { writer.Write(", "); } Core.Write(writer, param.Expression); } writer.Write(")"); } else if (type.OriginalDefinition is INamedTypeSymbol && type.OriginalDefinition.As <INamedTypeSymbol>().SpecialType == SpecialType.System_Nullable_T) { //new'ing up a Nullable<T> has special sematics in C#. If we're calling this with no parameters, just use null. Otherwise just use the parameter. if (expression.ArgumentList.Arguments.Count == 0) { writer.Write("null"); } else { Core.Write(writer, expression.ArgumentList.Arguments.Single().Expression); } } else { var methodSymbol = model.GetSymbolInfo(expression).Symbol.As <IMethodSymbol>(); var translateOpt = MethodTranslation.Get(methodSymbol); if (translateOpt != null && translateOpt.ExtensionNamespace != null) { writer.Write(translateOpt.ExtensionNamespace); writer.Write("."); writer.Write(translateOpt.ReplaceWith); } else { writer.Write("new "); writer.Write(TypeProcessor.ConvertType(expression.Type)); } writer.Write("("); if (expression.ArgumentList != null) { bool first = true; foreach (var param in TranslateParameters(translateOpt, expression.ArgumentList.Arguments, expression)) { if (first) { first = false; } else { writer.Write(", "); } param.Write(writer); } } writer.Write(")"); } }
public static void Go(HaxeWriter writer, ObjectCreationExpressionSyntax expression) { if (expression.ArgumentList == null || expression.Initializer != null) { throw new Exception("Object initialization syntax is not supported. " + Utility.Descriptor(expression)); } var model = Program.GetModel(expression); var type = model.GetTypeInfo(expression).Type; var methodSymbolUntyped = model.GetSymbolInfo(expression).Symbol; if (methodSymbolUntyped == null) { throw new Exception("methodSymbolUntyped is null"); } var methodSymbol = (IMethodSymbol)methodSymbolUntyped; if (type.SpecialType == SpecialType.System_DateTime && expression.ArgumentList.Arguments.Count == 1) { throw new Exception("You cannot use the DateTime constructor with one argument (ticks). .net Ticks and Haxe Ticks have different meanings, so this would result in problems. " + Utility.Descriptor(expression)); } if (type.SpecialType == SpecialType.System_Object) { //new object() results in the CsObject type being made. This is only really useful for locking writer.Write("new CsObject()"); } else if (type.SpecialType == SpecialType.System_String) { //new String() writer.Write("Cs2Hx.NewString("); bool first = true; foreach (var param in WriteInvocationExpression.SortArguments(methodSymbol, expression.ArgumentList.Arguments, expression, false)) { if (first) { first = false; } else { writer.Write(", "); } param.Write(writer); } writer.Write(")"); } else { var translateOpt = MethodTranslation.Get(methodSymbol); var convertedType = TypeProcessor.ConvertType(expression.Type); if (convertedType == "String") { //Normally, writing "new String(" in C# will fall under the above check which calls Cs2Hx.NewString. However, if a translation changes a type into a string, such as with guids, it falls here. It's important not to ever write "new String(" in haxe since that makes copies of strings which don't compare properly with ==. So just embed the string straight. Core.Write(writer, expression.ArgumentList.Arguments.Single().Expression); } else { writer.Write("new "); writer.Write(convertedType); writer.Write("("); bool first = true; foreach (var param in TranslateParameters(translateOpt, WriteInvocationExpression.SortArguments(methodSymbol, expression.ArgumentList.Arguments, expression, false), expression)) { if (first) { first = false; } else { writer.Write(", "); } param.Write(writer); } writer.Write(")"); } } }
public static void Go(ScalaWriter 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("symbolInfo.Symbol null at " + Utility.Descriptor(invocationExpression)); } if (symbolInfo.Symbol.OriginalDefinition == null) { throw new Exception("symbolInfo.Symbol.OriginalDefinition null at " + 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 firstParameter = true; var extensionNamespace = methodSymbol.IsExtensionMethod ? methodSymbol.ContainingNamespace.FullNameWithDot() + methodSymbol.ContainingType.Name : null; //null means it's not an extension method, non-null means it is string methodName; string typeParameters = null; ExpressionSyntax subExpressionOpt; if (methodSymbol.ContainingType.Name == "Enum") { if (methodSymbol.Name == "Parse") { WriteEnumParse(writer, invocationExpression); return; } if (methodSymbol.Name == "TryParse") { WriteEnumTryParse(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": if (invocationExpression.ArgumentList.Arguments.Count > 1) { throw new Exception("Multiple arguments to .Parse methods are not supported: " + Utility.Descriptor(invocationExpression)); } Core.Write(writer, invocationExpression.ArgumentList.Arguments.Single().Expression); writer.Write(".trim().to"); //we must trim strings before parsing them. In C#, a string like "4 " parses just fine, but if we don't trim this same string would false to parse in java writer.Write(TypeProcessor.ConvertType(methodSymbol.ReturnType)); return; case "TryParse": methodName = "TryParse" + TypeProcessor.ConvertType(methodSymbol.Parameters[1].Type); extensionNamespace = "CsScala"; break; default: methodName = methodSymbol.Name; extensionNamespace = "CsScala"; 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); 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; } //When the code specifically names generic arguments, include them in the method name var genNameExpression = invocationExpression.Expression as GenericNameSyntax; if (genNameExpression == null && memberReferenceExpressionOpt != null) { genNameExpression = memberReferenceExpressionOpt.Name as GenericNameSyntax; } if (genNameExpression != null && genNameExpression.TypeArgumentList.Arguments.Count > 0) { typeParameters = "[" + string.Join(", ", genNameExpression.TypeArgumentList.Arguments.Select(TypeProcessor.ConvertType)) + "]"; } //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; } var memberType = memberReferenceExpressionOpt == null ? null : model.GetTypeInfo(memberReferenceExpressionOpt.Expression).Type; var isNullableEnum = memberType != null && (memberType.Name == "Nullable" && memberType.ContainingNamespace.FullName() == "System") && memberType.As <INamedTypeSymbol>().TypeArguments.Single().TypeKind == TypeKind.Enum; if (isNullableEnum && methodSymbol.Name == "ToString") { extensionNamespace = null; //override Translations.xml for nullable enums. We want them to convert to the enum's ToString method methodName = "toString"; } if (extensionNamespace != null) { writer.Write(extensionNamespace); if (methodName != null) { writer.Write("."); writer.Write(methodName); } WriteTypeParameters(writer, translateOpt, typeParameters, invocationExpression); writer.Write("("); if (subExpressionOpt != null) { firstParameter = false; Core.Write(writer, subExpressionOpt); } } else { if (memberReferenceExpressionOpt != null) { //Check against lowercase toString since it gets replaced with the lowered version before we get here if (methodName == "toString") { if (memberType.TypeKind == TypeKind.Enum || isNullableEnum) { var enumType = memberType.TypeKind == TypeKind.Enum ? memberType : memberType.As <INamedTypeSymbol>().TypeArguments.Single(); //calling ToString() on an enum forwards to our enum's special ToString method writer.Write(enumType.ContainingNamespace.FullNameWithDot()); writer.Write(WriteType.TypeName((INamedTypeSymbol)enumType)); 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_Byte) { //Calling ToString on a byte needs to take special care since it's signed in the JVM writer.Write("System.CsScala.ByteToString("); Core.Write(writer, memberReferenceExpressionOpt.Expression); writer.Write(")"); if (invocationExpression.ArgumentList.Arguments.Count > 0) { throw new Exception("Byte's ToString detected with parameters. These are not supported " + Utility.Descriptor(invocationExpression)); } return; } } } if (subExpressionOpt != null) { WriteMemberAccessExpression.WriteMember(writer, subExpressionOpt); if (methodName != null) { writer.Write("."); } } else if (methodSymbol.IsStatic && extensionNamespace == null) { writer.Write(methodSymbol.ContainingNamespace.FullNameWithDot()); writer.Write(WriteType.TypeName(methodSymbol.ContainingType)); writer.Write("."); } writer.Write(methodName); WriteTypeParameters(writer, translateOpt, typeParameters, invocationExpression); writer.Write("("); } bool inParams = false; bool foundParamsArray = false; foreach (var arg in TranslateParameters(translateOpt, invocationExpression.ArgumentList.Arguments, invocationExpression)) { if (firstParameter) { firstParameter = false; } else { writer.Write(", "); } if (!inParams && IsParamsArgument(invocationExpression, arg.ArgumentOpt, methodSymbol)) { foundParamsArray = true; if (!TypeProcessor.ConvertType(model.GetTypeInfo(arg.ArgumentOpt.Expression).Type).StartsWith("Array[")) { inParams = true; writer.Write("Array("); } } 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 { arg.Write(writer); } } if (inParams) { writer.Write(")"); } else if (!foundParamsArray && methodSymbol.Parameters.Any() && methodSymbol.Parameters.Last().IsParams) { writer.Write(", Array()"); //params method called without any params argument. Send an empty array. } writer.Write(")"); }
public static void Go(HaxeWriter writer, InvocationExpressionSyntax invocationExpression) { var model = Program.GetModel(invocationExpression); var symbolInfo = model.GetSymbolInfo(invocationExpression); var expressionSymbol = model.GetSymbolInfo(invocationExpression.Expression); 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("("); } bool inParams = false; foreach (var arg in TranslateParameters(translateOpt, SortArguments(methodSymbol, invocationExpression.ArgumentList.Arguments, invocationExpression), invocationExpression)) { 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(")"); }