Пример #1
0
 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);
     }
 }
Пример #2
0
 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);
     }
 }
Пример #3
0
        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);
            }
        }
Пример #4
0
        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(")");
            }
        }
Пример #5
0
        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++;
            }
        }
Пример #6
0
        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(")");
        }
Пример #7
0
        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(")");
                }
            }
        }
Пример #9
0
        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(")");
        }
Пример #10
0
        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(")");
        }