Esempio n. 1
0
        private static void WriteEnumGetValues(HaxeWriter writer, InvocationExpressionSyntax invocationExpression)
        {
            if (!(invocationExpression.ArgumentList.Arguments[0].Expression is TypeOfExpressionSyntax))
            {
                throw new Exception("Expected a typeof() expression as the first parameter of Enum.GetValues " + Utility.Descriptor(invocationExpression));
            }

            var type = Program.GetModel(invocationExpression).GetTypeInfo(invocationExpression.ArgumentList.Arguments[0].Expression.As <TypeOfExpressionSyntax>().Type).Type;

            writer.Write(type.ContainingNamespace.FullNameWithDot().ToLower());
            writer.Write(WriteType.TypeName((INamedTypeSymbol)type));
            writer.Write(".Values()");
        }
Esempio n. 2
0
        private static void Generate(IEnumerable <string> extraTranslation)
        {
            Console.WriteLine("Parsing...");
            var sw = Stopwatch.StartNew();


            TranslationManager.Init(extraTranslation);

            if (!Directory.Exists(OutDir))
            {
                Directory.CreateDirectory(OutDir);
            }

            var allTypes = Compilation.SyntaxTrees
                           .SelectMany(o => o.GetRoot().DescendantNodes().OfType <BaseTypeDeclarationSyntax>())
                           .Select(o => new { Syntax = o, Symbol = GetModel(o).GetDeclaredSymbol(o), TypeName = WriteType.TypeName(GetModel(o).GetDeclaredSymbol(o)) })
                           .GroupBy(o => o.Symbol.ContainingNamespace.FullName() + "." + o.TypeName)
                           .ToList();

            Utility.Parallel(Compilation.SyntaxTrees.ToList(), tree =>
            {
                foreach (var n in TriviaProcessor.DoNotWrite(tree))
                {
                    DoNotWrite.TryAdd(n, null);
                }
            });

            Console.WriteLine("Parsed in " + sw.Elapsed + ". Writing out haxe...");
            sw.Restart();

            Compilation.SyntaxTrees.SelectMany(o => o.GetRoot().DescendantNodes().OfType <AnonymousObjectCreationExpressionSyntax>())
            .Select(o => new { Syntax = o, Name = WriteAnonymousObjectCreationExpression.TypeName(o) })
            .GroupBy(o => o.Name)
            .Parallel(o => WriteAnonymousObjectCreationExpression.WriteAnonymousType(o.First().Syntax));


            allTypes.Parallel(type =>
            {
                TypeState.Instance          = new TypeState();
                TypeState.Instance.TypeName = type.First().TypeName;
                TypeState.Instance.Partials = type.Select(o => new TypeState.SyntaxAndSymbol {
                    Symbol = o.Symbol, Syntax = o.Syntax
                })
                                              .Where(o => !DoNotWrite.ContainsKey(o.Syntax))
                                              .ToList();

                if (TypeState.Instance.Partials.Count > 0)
                {
                    WriteType.Go();
                }
            });

            WriteConstructor.WriteConstructorsHelper(allTypes.SelectMany(o => o).Where(o => !DoNotWrite.ContainsKey(o.Syntax)).Select(o => o.Symbol));

            Console.WriteLine("Haxe written out in " + sw.Elapsed);
        }
Esempio n. 3
0
        public static void Go(HaxeWriter writer, ExpressionSyntax left, SyntaxToken operatorToken, ExpressionSyntax right)
        {
            //Check for index assignments, for example dictionary[4] = 3;
            if (left is ElementAccessExpressionSyntax && operatorToken.Kind() == SyntaxKind.EqualsToken)
            {
                var elementAccess = (ElementAccessExpressionSyntax)left;

                var typeHaxe = TypeProcessor.ConvertType(Program.GetModel(left).GetTypeInfo(elementAccess.Expression).ConvertedType);

                if (!typeHaxe.StartsWith("Array<"))                 //arrays are the only thing haxe allows assignments into via indexing
                {
                    WriteIndexingExpression(writer, operatorToken, right, elementAccess);
                    return;
                }
            }


            //Check for improper nullable access, unless it's assignment or string concat, which work fine.  Note that if they're trying to add nullable number types, it won't catch it since we can't tell if it's string concatentation or addition.  But haxe will fail to build, so it should still be caught easily enough.
            if (operatorToken.Kind() != SyntaxKind.EqualsToken && operatorToken.Kind() != SyntaxKind.PlusToken)
            {
                var model = Program.GetModel(left);
                Func <ExpressionSyntax, bool> isNullable = e =>
                {
                    var t = model.GetTypeInfo(e).Type;
                    return(t != null && t.Name == "Nullable");
                };

                if (isNullable(left) || isNullable(right))
                {
                    throw new Exception("When using nullable types, you must use the .Value and .HasValue properties instead of the object directly " + Utility.Descriptor(left.Parent));
                }
            }

            if (operatorToken.Kind() == SyntaxKind.PlusEqualsToken || operatorToken.Kind() == SyntaxKind.MinusEqualsToken)
            {
                //Check for event subscription/removal
                var leftSymbol = Program.GetModel(left).GetSymbolInfo(left);
                if (leftSymbol.Symbol is IEventSymbol)
                {
                    Core.Write(writer, left);
                    if (operatorToken.Kind() == SyntaxKind.PlusEqualsToken)
                    {
                        writer.Write(".Add(");
                    }
                    else
                    {
                        writer.Write(".Remove(");
                    }
                    Core.Write(writer, right);
                    writer.Write(")");
                    return;
                }
            }

            if (operatorToken.Kind() == SyntaxKind.AsKeyword)
            {
                var leftStr = Utility.TryGetIdentifier(left);

                if (leftStr == null)
                {
                    throw new Exception("The \"as\" keyword can only be used on simple names.  Declare it as a local variable. " + Utility.Descriptor(left.Parent));
                }

                var typeHaxe = TypeProcessor.ConvertType(right);

                writer.Write("(Std.is(" + leftStr + ", " + typeHaxe + ") ? cast(" + leftStr + ", " + typeHaxe + ") : null)");
            }
            else if (operatorToken.Kind() == SyntaxKind.IsKeyword)
            {
                writer.Write("Std.is(");
                Core.Write(writer, left);
                writer.Write(", ");
                writer.Write(TypeProcessor.RemoveGenericArguments(TypeProcessor.ConvertType(right)));
                writer.Write(")");
            }
            else if (operatorToken.Kind() == SyntaxKind.QuestionQuestionToken)
            {
                writer.Write("Cs2Hx.Coalesce(");
                Core.Write(writer, left);
                writer.Write(", ");
                Core.Write(writer, right);
                writer.Write(")");
            }
            else
            {
                var leftType  = Program.GetModel(left).GetTypeInfo(left);
                var rightType = Program.GetModel(right).GetTypeInfo(right);

                if ((operatorToken.Kind() == SyntaxKind.EqualsEqualsToken || operatorToken.Kind() == SyntaxKind.ExclamationEqualsToken) &&
                    leftType.ConvertedType.SpecialType == SpecialType.System_Boolean &&
                    rightType.ConvertedType.SpecialType == SpecialType.System_Boolean)
                {
                    //Anytime we == or != booleans, we need to take special care when dealing with the js target.  haxe seems to leave some variables as undefined, which works fine as booleans in most comparisons, but not when comparing against each other such as "x == false"
                    if (operatorToken.Kind() == SyntaxKind.ExclamationEqualsToken)
                    {
                        writer.Write("!");
                    }
                    writer.Write("Cs2Hx.BoolCompare(");
                    Core.Write(writer, left);
                    writer.Write(", ");
                    Core.Write(writer, right);
                    writer.Write(")");
                    return;
                }

                Action <ExpressionSyntax, ExpressionSyntax> write = (e, otherSide) =>
                {
                    var type      = Program.GetModel(left).GetTypeInfo(e);
                    var otherType = Program.GetModel(left).GetTypeInfo(otherSide);
                    //Check for enums being converted to strings by string concatenation
                    if (operatorToken.Kind() == SyntaxKind.PlusToken && type.Type.TypeKind == TypeKind.Enum)
                    {
                        writer.Write(type.Type.ContainingNamespace.FullNameWithDot().ToLower());
                        writer.Write(WriteType.TypeName(type.Type.As <INamedTypeSymbol>()));
                        writer.Write(".ToString(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && type.Type.SpecialType == SpecialType.System_Char && otherType.Type.SpecialType != SpecialType.System_Char && otherType.Type.SpecialType != SpecialType.System_Int32)
                    {
                        //Chars are integers in haxe, so when string-concatening them we must convert them to strings
                        writer.Write("Cs2Hx.CharToString(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && !(e is BinaryExpressionSyntax) && type.Type.SpecialType == SpecialType.System_String && CouldBeNullString(Program.GetModel(e), e))
                    {
                        //In .net, concatenating a null string does not alter the output. However, in haxe's js target, it produces the "null" string. To counter this, we must check non-const strings.
                        writer.Write("system.Cs2Hx.NullCheck(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else
                    {
                        Core.Write(writer, e);
                    }
                };

                write(left, right);
                writer.Write(" ");
                writer.Write(operatorToken.ToString());                 //we can do this since haxe operators work just like C# operators
                writer.Write(" ");
                write(right, left);
            }
        }
        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(")");
        }
Esempio n. 5
0
        private static string ConvertTypeUncached(ITypeSymbol typeSymbol)
        {
            if (typeSymbol.IsAnonymousType)
            {
                return(WriteAnonymousObjectCreationExpression.TypeName(typeSymbol.As <INamedTypeSymbol>()));
            }

            var array = typeSymbol as IArrayTypeSymbol;

            if (array != null)
            {
                if (array.ElementType.ToString() == "byte")
                {
                    return("haxe.io.Bytes");                    //byte arrays become haxe.io.Bytes
                }
                else
                {
                    return("Array<" + (ConvertType(array.ElementType) ?? "Dynamic") + ">");
                }
            }

            var typeInfoStr = typeSymbol.ToString();

            var named = typeSymbol as INamedTypeSymbol;

            if (typeSymbol.TypeKind == TypeKind.TypeParameter)
            {
                return(typeSymbol.Name);
            }

            if (typeSymbol.TypeKind == TypeKind.Delegate)
            {
                var dlg = named.DelegateInvokeMethod.As <IMethodSymbol>();
                if (dlg.Parameters.Length == 0)
                {
                    return("(Void -> " + ConvertType(dlg.ReturnType) + ")");
                }
                else
                {
                    return("(" + string.Join("", dlg.Parameters.ToList().Select(o => ConvertType(o.Type) + " -> ")) + ConvertType(dlg.ReturnType) + ")");
                }
            }

            if (typeSymbol.TypeKind == TypeKind.Enum)
            {
                return("Int");                //enums are always ints
            }
            if (named != null && named.Name == "Nullable" && named.ContainingNamespace.ToString() == "System")
            {
                //Nullable types get replaced by our Nullable_ alternatives
                var nullableType = ConvertType(named.TypeArguments.Single());
                if (nullableType == "Int" || nullableType == "Bool" || nullableType == "Float")
                {
                    return("Nullable_" + nullableType);
                }
                else
                {
                    return("Nullable<" + nullableType + ">");
                }
            }

            var typeStr = GenericTypeName(typeSymbol);


            var trans = TypeTranslation.Get(typeStr);

            if (named != null && named.IsGenericType && !named.IsUnboundGenericType && TypeArguments(named).Any() && (trans == null || trans.SkipGenericTypes == false))
            {
                //Generic type
                var genericTypeRoot          = ConvertType(named.ConstructUnboundGenericType());
                var genericTypeArgs          = TypeArguments(named).ToList();
                var genericTypeArgsConverted = genericTypeArgs.Select(o => ConvertType(o) ?? "Dynamic").ToList();

                if (genericTypeRoot == "system.collections.generic.Dictionary" || genericTypeRoot == "system.collections.generic.HashSet")
                {
                    //Cs2hx does not support the GetHashCode() or Equals() functions, and therefore will only work correctly with basic types as the keys of dictionaries and hash sets.  We should throw on any improper usage since it may not run the same as the original C#
                    var hashArg = genericTypeArgsConverted[0];

                    if (genericTypeArgs[0].TypeKind != TypeKind.TypeParameter && hashArg != "Int" && hashArg != "String" && hashArg != "Dynamic") //TODO: Is Dynamic really ok in this list?  This will happen on a Dictionary<object, ...>, which could be a problem unless it was used carefully.
                    {
                        throw new Exception("Improper hash type: " + hashArg + " used on " + genericTypeRoot);                                    //TODO: How can we provide code location?
                    }
                }

                return(genericTypeRoot + "<" + string.Join(", ", genericTypeArgsConverted) + ">");
            }

            switch (typeStr)
            {
            case "System.Void":
                return("Void");

            case "System.Boolean":
                return("Bool");

            case "System.Object":
                return("Dynamic");

            case "System.Int64":
            case "System.UInt64":
            case "System.Single":
            case "System.Double":
                return("Float");

            case "System.String":
                return("String");

            case "System.Int32":
            case "System.UInt32":
            case "System.Byte":
            case "System.Int16":
            case "System.UInt16":
            case "System.Char":
                return("Int");


            case "System.Collections.Generic.List<>":
            case "System.Collections.Generic.IList<>":
            case "System.Collections.Generic.Queue<>":
            case "System.Collections.Generic.IEnumerable<>":
            case "System.Collections.Generic.Dictionary<,>.ValueCollection":
            case "System.Collections.Generic.Dictionary<,>.KeyCollection":
            case "System.Collections.Generic.ICollection<>":
            case "System.Linq.IOrderedEnumerable<>":
            case "System.Collections.IEnumerable":
            case "System.Collections.Specialized.NameObjectCollectionBase.KeysCollection":
                return("Array");

            case "System.Array":
                return(null);                        //in haxe, unlike C#, array must always have type arguments.  To work around this, just avoid printing the type anytime we see a bare Array type in C#. haxe will infer it.

            case "System.Collections.Generic.LinkedList<>":
                return("List");

            default:


                if (trans != null)
                {
                    return(trans.As <Translations.TypeTranslation>().Replace(named));
                }

                if (named != null)
                {
                    return(typeSymbol.ContainingNamespace.FullNameWithDot().ToLower() + WriteType.TypeName(named));
                }

                //This type does not get translated and gets used as-is
                return(typeSymbol.ContainingNamespace.FullNameWithDot().ToLower() + typeSymbol.Name);
            }
        }
Esempio n. 6
0
        public static void WriteConstructorsHelper(IEnumerable <INamedTypeSymbol> allTypes, string nameArg)
        {
            foreach (var t in allTypes.Select(o => o.ContainingNamespace.FullNameWithDot().ToLower() + WriteType.TypeName(o)))
            {
                AllTypes.Add(t);
            }

            var name = string.IsNullOrWhiteSpace(nameArg) ? "Constructors" : nameArg;


            using (var writer = new HaxeWriter("", name))
            {
                writer.WriteLine(@"/*
This file serves two purposes:  
    1)  It imports every type that CS2HX generated.  haXe will ignore 
        any types that aren't used by haXe code, so this ensures haXe 
        compiles all of your code.

    2)  It lists all the static constructors.  haXe doesn't have the 
        concept of static constructors, so CS2HX generated cctor()
        methods.  You must call these manually.  If you call
        Constructors.init(), all static constructors will be called 
        at once.
*/
package ;");

                foreach (var type in AllTypes.OrderBy(o => o))
                {
                    writer.WriteLine("import " + type + ";");
                }
                writer.WriteLine("import system.TimeSpan;");

                writer.WriteLine("class " + name);
                writer.WriteOpenBrace();

                writer.WriteLine("public static function init()");
                writer.WriteOpenBrace();
                writer.WriteLine("TimeSpan.cctor();");
                foreach (var cctor in StaticConstructors.OrderBy(o => o))
                {
                    writer.WriteLine(cctor + ".cctor();");
                }
                writer.WriteCloseBrace();
                writer.WriteCloseBrace();
            }
        }
Esempio n. 7
0
        public static void WriteMember(HaxeWriter writer, ExpressionSyntax expression)
        {
            var symbol = Program.GetModel(expression).GetSymbolInfo(expression).Symbol;

            if (symbol is INamedTypeSymbol)
            {
                var translateOpt = TypeTranslation.Get(symbol.ContainingNamespace.FullNameWithDot() + symbol.Name);

                if (translateOpt != null)
                {
                    writer.Write(translateOpt.ReplaceWith);
                }
                else
                {
                    writer.Write(symbol.ContainingNamespace.FullNameWithDot().ToLower() + WriteType.TypeName(symbol.As <INamedTypeSymbol>()));
                }
            }
            else
            {
                Core.Write(writer, expression);
            }
        }
Esempio n. 8
0
        private static string ConvertTypeUncached(ITypeSymbol typeSymbol)
        {
            if (typeSymbol.IsAnonymousType)
            {
                return(WriteAnonymousObjectCreationExpression.TypeName(typeSymbol.As <INamedTypeSymbol>()));
            }

            var array = typeSymbol as IArrayTypeSymbol;

            if (array != null)
            {
                if (array.ElementType.ToString() == "byte")
                {
                    return("haxe.io.Bytes");                    //byte arrays become haxe.io.Bytes
                }
                else
                {
                    return("Array<" + (ConvertType(array.ElementType) ?? "Dynamic") + ">");
                }
            }

            var typeInfoStr = typeSymbol.ToString();

            var named = typeSymbol as INamedTypeSymbol;

            if (typeSymbol.TypeKind == TypeKind.TypeParameter)
            {
                return(typeSymbol.Name);
            }

            if (typeSymbol.TypeKind == TypeKind.Delegate)
            {
                var dlg = named.DelegateInvokeMethod.As <IMethodSymbol>();
                if (dlg.Parameters.Length == 0)
                {
                    return("(Void -> " + ConvertType(dlg.ReturnType) + ")");
                }
                else
                {
                    return("(" + string.Join("", dlg.Parameters.ToList().Select(o => ConvertType(o.Type) + " -> ")) + ConvertType(dlg.ReturnType) + ")");
                }
            }

            if (typeSymbol.TypeKind == TypeKind.Enum)
            {
                return("Int");                //enums are always ints
            }
            if (named != null && named.Name == "Nullable" && named.ContainingNamespace.ToString() == "System")
            {
                //Nullable types get replaced by our Nullable_ alternatives
                var nullableType = ConvertType(named.TypeArguments.Single());
                if (nullableType == "Int" || nullableType == "Bool" || nullableType == "Float")
                {
                    return("Nullable_" + nullableType);
                }
                else
                {
                    return("Nullable<" + nullableType + ">");
                }
            }

            var typeStr = GenericTypeName(typeSymbol);

            var trans = TypeTranslation.Get(typeStr);

            if (named != null && named.IsGenericType && !named.IsUnboundGenericType && TypeArguments(named).Any() && (trans == null || trans.SkipGenericTypes == false))
            {
                return(ConvertType(named.ConstructUnboundGenericType()) + "<" + string.Join(", ", TypeArguments(named).Select(o => ConvertType(o) ?? "Dynamic")) + ">");
            }


            switch (typeStr)
            {
            case "System.Void":
                return("Void");

            case "System.Boolean":
                return("Bool");

            case "System.Object":
                return("Dynamic");

            case "System.Int64":
            case "System.UInt64":
            case "System.Single":
            case "System.Double":
                return("Float");

            case "System.String":
                return("String");

            case "System.Int32":
            case "System.UInt32":
            case "System.Byte":
            case "System.Int16":
            case "System.UInt16":
            case "System.Char":
                return("Int");


            case "System.Collections.Generic.List<>":
            case "System.Collections.Generic.IList<>":
            case "System.Collections.Generic.Queue<>":
            case "System.Collections.Generic.IEnumerable<>":
            case "System.Collections.Generic.Dictionary<,>.ValueCollection":
            case "System.Collections.Generic.Dictionary<,>.KeyCollection":
            case "System.Collections.Generic.ICollection<>":
            case "System.Linq.IOrderedEnumerable<>":
            case "System.Collections.IEnumerable":
            case "System.Collections.Specialized.NameObjectCollectionBase.KeysCollection":
                return("Array");

            case "System.Array":
                return(null);                        //in haxe, unlike C#, array must always have type arguments.  To work around this, just avoid printing the type anytime we see a bare Array type in C#. haxe will infer it.

            case "System.Collections.Generic.LinkedList<>":
                return("List");

            default:


                if (trans != null)
                {
                    return(trans.As <Translations.TypeTranslation>().Replace(named));
                }

                if (named != null)
                {
                    return(typeSymbol.ContainingNamespace.FullNameWithDot().ToLower() + WriteType.TypeName(named));
                }

                //This type does not get translated and gets used as-is
                return(typeSymbol.ContainingNamespace.FullNameWithDot().ToLower() + typeSymbol.Name);
            }
        }
Esempio n. 9
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(")");
        }