Exemplo n.º 1
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 && otherType.ConvertedType.SpecialType == SpecialType.System_String)
                    {
                        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, 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(")");
                }
            }
        }
Exemplo n.º 3
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 = symbolInfo.Symbol.OriginalDefinition.As <IMethodSymbol>().UnReduce();

            var translateOpt = MethodTranslation.Get(symbolInfo.Symbol.As <IMethodSymbol>());
            var memberReferenceExpressionOpt = invocationExpression.Expression as MemberAccessExpressionSyntax;
            //var returnTypeHaxe = TypeProcessor.ConvertType(methodSymbol.ReturnType);
            var firstParameter = true;

            var              extensionNamespace = methodSymbol.IsExtensionMethod ? methodSymbol.ContainingNamespace.FullNameWithDot().ToLower() + methodSymbol.ContainingType.Name : null; //null means it's not an extension method, non-null means it is
            string           methodName;
            ExpressionSyntax subExpressionOpt;

            if (methodSymbol.ContainingType.Name == "Enum")
            {
                if (methodSymbol.Name == "Parse")
                {
                    WriteEnumParse(writer, invocationExpression);
                    return;
                }

                if (methodSymbol.Name == "GetValues")
                {
                    WriteEnumGetValues(writer, invocationExpression);
                    return;
                }
            }

            if (expressionSymbol.Symbol is IEventSymbol)
            {
                methodName = "Invoke";                 //Would need to append the number of arguments to this to support events.  However, events are not currently supported
            }
            else if (memberReferenceExpressionOpt != null && memberReferenceExpressionOpt.Expression is PredefinedTypeSyntax)
            {
                switch (methodSymbol.Name)
                {
                case "Parse":
                    var t = TypeProcessor.ConvertType(methodSymbol.ReturnType);

                    if (t == "Bool")
                    {
                        methodName         = "ParseBool";
                        extensionNamespace = "Cs2Hx";
                    }
                    else if (t == "Int" || t == "Float")
                    {
                        methodName         = "parse" + t;
                        extensionNamespace = "Std";
                    }
                    else
                    {
                        throw new Exception("Parse method on " + t + " is not supported.  " + Utility.Descriptor(memberReferenceExpressionOpt));
                    }

                    break;

                case "TryParse":
                    methodName         = "TryParse" + TypeProcessor.ConvertType(methodSymbol.Parameters[1].Type);
                    extensionNamespace = "Cs2Hx";
                    break;

                default:
                    methodName         = methodSymbol.Name;
                    extensionNamespace = "Cs2Hx";
                    break;
                }
            }
            else if (translateOpt != null && translateOpt.ReplaceWith != null)
            {
                methodName = translateOpt.ReplaceWith;
            }
            else if (methodSymbol.MethodKind == MethodKind.DelegateInvoke)
            {
                methodName = null;
            }
            else
            {
                methodName = OverloadResolver.MethodName(methodSymbol);
            }

            if (translateOpt != null && translateOpt.HasComplexReplaceWith)
            {
                translateOpt.DoComplexReplaceWith(writer, memberReferenceExpressionOpt);                 //TODO: What if this is null?
                return;
            }

            if (translateOpt != null && translateOpt.SkipExtensionParameter)
            {
                subExpressionOpt = null;
            }
            else if (methodSymbol.MethodKind == MethodKind.DelegateInvoke)
            {
                subExpressionOpt = invocationExpression.Expression;
            }
            else if (memberReferenceExpressionOpt != null)
            {
                if (memberReferenceExpressionOpt.Expression is PredefinedTypeSyntax)
                {
                    subExpressionOpt = null;
                }
                else
                {
                    subExpressionOpt = memberReferenceExpressionOpt.Expression;
                }
            }
            else
            {
                subExpressionOpt = null;                 //TODO
            }
            //Determine if it's an extension method called in a non-extension way.  In this case, just pretend it's not an extension method
            if (extensionNamespace != null && subExpressionOpt != null && model.GetTypeInfo(subExpressionOpt).ConvertedType.ToString() == methodSymbol.ContainingNamespace + "." + methodSymbol.ContainingType.Name)
            {
                extensionNamespace = null;
            }

            if (translateOpt != null && !string.IsNullOrEmpty(translateOpt.ExtensionNamespace))
            {
                extensionNamespace = translateOpt.ExtensionNamespace;
            }
            else if (translateOpt != null && translateOpt.ExtensionNamespace == "")
            {
                extensionNamespace = null;
            }

            if (extensionNamespace != null)
            {
                writer.Write(extensionNamespace);

                if (methodName != null)
                {
                    writer.Write(".");
                    writer.Write(methodName);
                }

                writer.Write("(");

                if (subExpressionOpt != null)
                {
                    firstParameter = false;

                    if (methodSymbol.IsExtensionMethod)
                    {
                        WriteForEachStatement.CheckWriteEnumerator(writer, subExpressionOpt, true);
                    }
                    else
                    {
                        Core.Write(writer, subExpressionOpt);
                    }
                }
            }
            else
            {
                if (memberReferenceExpressionOpt != null)
                {
                    var memberType     = model.GetTypeInfo(memberReferenceExpressionOpt.Expression).Type;
                    var memberTypeHaxe = TypeProcessor.ConvertType(memberType);

                    //sort calls without any parameters need to get the default sort parameter
                    if (methodName == "sort" && invocationExpression.ArgumentList.Arguments.Count == 0)
                    {
                        Core.Write(writer, memberReferenceExpressionOpt.Expression);
                        writer.Write(".sort(");

                        switch (memberTypeHaxe)
                        {
                        case "Array<Int>":
                            writer.Write("Cs2Hx.SortInts");
                            break;

                        case "Array<Float>":
                            writer.Write("Cs2Hx.SortFloats");
                            break;

                        case "Array<String>":
                            writer.Write("Cs2Hx.SortStrings");
                            break;

                        default:
                            throw new Exception("Unknown default sort type: " + memberTypeHaxe + ".  " + Utility.Descriptor(invocationExpression));
                        }

                        writer.Write(")");
                        return;
                    }

                    //Check against lowercase toString since it gets replaced with the haxe name before we get here
                    if (methodName == "toString")
                    {
                        if (memberType.TypeKind == TypeKind.Enum)
                        {
                            //calling ToString() on an enum forwards to our enum's special ToString method
                            writer.Write(memberType.ContainingNamespace.FullNameWithDot().ToLower());
                            writer.Write(WriteType.TypeName((INamedTypeSymbol)memberType));
                            writer.Write(".ToString(");
                            Core.Write(writer, memberReferenceExpressionOpt.Expression);
                            writer.Write(")");

                            if (invocationExpression.ArgumentList.Arguments.Count > 0)
                            {
                                throw new Exception("Enum's ToString detected with parameters.  These are not supported " + Utility.Descriptor(invocationExpression));
                            }

                            return;
                        }

                        if (memberType.SpecialType == SpecialType.System_Char)
                        {
                            writer.Write("Cs2Hx.CharToString(");
                            Core.Write(writer, memberReferenceExpressionOpt.Expression);
                            writer.Write(")");

                            if (invocationExpression.ArgumentList.Arguments.Count > 0)
                            {
                                throw new Exception("Char's ToString detected with parameters.  These are not supported " + Utility.Descriptor(invocationExpression));
                            }

                            return;
                        }

                        if (memberTypeHaxe == "Int" || memberTypeHaxe == "Float" || memberTypeHaxe == "Bool" || memberType.TypeKind == TypeKind.TypeParameter)
                        {
                            //ToString()'s on primitive types get replaced with Std.string
                            writer.Write("Std.string(");

                            if (memberReferenceExpressionOpt.Expression is ParenthesizedExpressionSyntax)
                            {
                                Core.Write(writer, memberReferenceExpressionOpt.Expression.As <ParenthesizedExpressionSyntax>().Expression);                                //eat a set of parenthesis, just to make the output code a bit nicer.
                            }
                            else
                            {
                                Core.Write(writer, memberReferenceExpressionOpt.Expression);
                            }

                            writer.Write(")");

                            if (invocationExpression.ArgumentList.Arguments.Count > 0)
                            {
                                throw new Exception("Primitive type's ToString detected with parameters.  These are not supported " + Utility.Descriptor(invocationExpression));
                            }

                            return;                             //Skip parameters
                        }
                    }
                }

                if (subExpressionOpt != null)
                {
                    WriteMemberAccessExpression.WriteMember(writer, subExpressionOpt);
                }

                if (subExpressionOpt != null && methodName != null)
                {
                    writer.Write(".");
                }

                writer.Write(methodName);
                writer.Write("(");
            }

            var prms = TranslateParameters(translateOpt, SortArguments(methodSymbol, invocationExpression.ArgumentList.Arguments, invocationExpression, extensionNamespace != null), invocationExpression).ToList();

            //If we invoke a method with type parameters that aren't used in the argument list, the haxe function won't have a way to see what args were used. To give it a way, add those as parameters at the end
            foreach (var typePrm in Utility.PassTypeArgsToMethod(methodSymbol))
            {
                var name = invocationExpression.Expression.ChildNodes().OfType <GenericNameSyntax>().ToList();
                if (name.Count == 0)
                {
                    name = invocationExpression.Expression.DescendantNodesAndSelf().OfType <GenericNameSyntax>().ToList(); //TODO: This will pick up false positives, we need a proper recursion function here
                }
                if (name.Count != 1)
                {
                    throw new Exception("Expected a single generic name, got " + string.Join(", ", name) + "  " + Utility.Descriptor(invocationExpression));
                }
                if (name.Count > 0 && name.Single().TypeArgumentList.Arguments.Count > 0)
                {
                    var typePrmIndex = methodSymbol.TypeParameters.IndexOf(methodSymbol.TypeParameters.Single(o => o == typePrm));
                    var genericVar   = name.Single().TypeArgumentList.Arguments.ElementAt(typePrmIndex);
                    if (genericVar.ToString() == typePrm.ToString())
                    {
                        writer.Write("t" + (typePrmIndex + 1));
                    }
                    else
                    {
                        prms.Add(new TransformedArgument(TypeProcessor.ConvertType(genericVar)));
                    }
                }
            }


            bool inParams = false;

            foreach (var arg in prms)
            {
                if (firstParameter)
                {
                    firstParameter = false;
                }
                else
                {
                    writer.Write(", ");
                }

                if (!inParams && IsParamsArgument(invocationExpression, arg.ArgumentOpt, methodSymbol) && TypeProcessor.ConvertType(model.GetTypeInfo(arg.ArgumentOpt.Expression).Type).StartsWith("Array<") == false)
                {
                    inParams = true;
                    writer.Write("[ ");
                }


                if (arg.ArgumentOpt != null &&
                    arg.ArgumentOpt.RefOrOutKeyword.Kind() != SyntaxKind.None &&
                    model.GetSymbolInfo(arg.ArgumentOpt.Expression).Symbol is IFieldSymbol)
                {
                    throw new Exception("ref/out cannot reference fields, only local variables.  Consider using ref/out on a local variable and then assigning it into the field. " + Utility.Descriptor(invocationExpression));
                }


                //When passing an argument by ref or out, leave off the .Value suffix
                if (arg.ArgumentOpt != null && arg.ArgumentOpt.RefOrOutKeyword.Kind() != SyntaxKind.None)
                {
                    WriteIdentifierName.Go(writer, arg.ArgumentOpt.Expression.As <IdentifierNameSyntax>(), true);
                }
                else if (arg.ArgumentOpt != null)
                {
                    WriteForEachStatement.CheckWriteEnumerator(writer, arg.ArgumentOpt.Expression, false);
                }
                else
                {
                    arg.Write(writer);
                }
            }

            if (inParams)
            {
                writer.Write(" ]");
            }


            writer.Write(")");
        }
Exemplo n.º 4
0
        public static void Go(HaxeWriter writer, CastExpressionSyntax expression)
        {
            var model = Program.GetModel(expression);

            var symbol = model.GetSymbolInfo(expression);

            var castingFrom = model.GetTypeInfo(expression.Expression).Type;

            if (castingFrom == null)
            {
                castingFrom = model.GetTypeInfo(expression).Type;
            }

            var castingFromStr  = TypeProcessor.GenericTypeName(castingFrom);
            var castingFromHaxe = TypeProcessor.ConvertType((ITypeSymbol)castingFrom);
            var destType        = model.GetTypeInfo(expression.Type).Type;
            var destTypeHaxe    = TypeProcessor.TryConvertType(expression.Type);



            var subExpression = expression.Expression;

            //Since we output parenthesis, we can eat one set of them
            if (subExpression is ParenthesizedExpressionSyntax)
            {
                subExpression = subExpression.As <ParenthesizedExpressionSyntax>().Expression;
            }


            if (symbol.Symbol != null && castingFromHaxe != "Int" && castingFromHaxe != "String" && castingFromHaxe != "Bool" && castingFromHaxe != "Float")
            {
                //when the symbol is non-null, this indicates we're calling a cast operator function
                WriteCastOperator(writer, expression, (IMethodSymbol)symbol.Symbol, destTypeHaxe);
            }
            else if (destTypeHaxe.StartsWith("Nullable"))
            {
                //Casting to a nullable type results in creation of that nullable type. No cast necessary.
                writer.Write("new ");
                writer.Write(destTypeHaxe);
                writer.Write("(");

                if (subExpression.Kind() != SyntaxKind.NullLiteralExpression)
                {
                    Core.Write(writer, subExpression);
                }

                writer.Write(")");
            }
            else if (subExpression.Kind() == SyntaxKind.NullLiteralExpression)
            {
                //no cast necessary for null. haxe can infer the type better than C#
                writer.Write("null");
            }
            else if (destTypeHaxe == null)
            {
                //Sometimes roslyn can't determine the type for some reason. Just fall back to haxe's dynamic cast
                writer.Write("cast(");
                Core.Write(writer, subExpression);
                writer.Write(")");
            }
            else if (castingFromHaxe == "Dynamic")
            {
                //casts to and from dynamic will be automatic, however we have to manually specify the type.  Otherwise, we saw cases on the js target where haxe would not translate properties from "X" to "get_X()".
                //The only way I've found to specify the type of an expression is to in-line an anonymous function.  Haxe optimizes this away so there's no runtime hit.
                writer.Write("(function():" + destTypeHaxe + " return ");
                Core.Write(writer, expression.Expression);
                writer.Write(")()");
            }
            else if (destTypeHaxe == "Int" && castingFromHaxe == "Int" && expression.DescendantNodes().OfType <BinaryExpressionSyntax>().Where(o => o.OperatorToken.Kind() == SyntaxKind.SlashToken).None())
            {
                //Eat casts from Int to Int.  Enums getting casted to int fall here, and since we use ints to represent enums anyway, it's not necessary.  However, if we contain the division operator, and since haxe division always produces floating points and C# integer division produces integers, we can't rely on the C# expression type so cast anyway.
                Core.Write(writer, expression.Expression);
            }
            else if (destTypeHaxe.Contains("<"))
            {
                //Eat casts with type parameters.  haxe doesn't allow this.
                Core.Write(writer, expression.Expression);
            }
            else if (destTypeHaxe == "Int")
            {
                //Casting from int to float is handled by Std.int in haxe
                writer.Write("Std.int(");
                Core.Write(writer, subExpression);
                writer.Write(")");
            }
            else if (destTypeHaxe == "Float" && castingFromHaxe == "Int")
            {
                //Eat casts from Int to Float.  C# does this so it can do floating division, but in haxe all division is floating so there's no reason to do it.
                Core.Write(writer, expression.Expression);
            }
            else if (destTypeHaxe == castingFromHaxe)
            {
                //Eat casts between identical types
                Core.Write(writer, expression.Expression);
            }
            else if (destType.TypeKind == TypeKind.TypeParameter)
            {
                //ignore casts to template types
                Core.Write(writer, expression.Expression);
            }
            else
            {
                writer.Write("cast(");
                Core.Write(writer, subExpression);
                writer.Write(", ");
                writer.Write(destTypeHaxe);
                writer.Write(")");
            }
        }
Exemplo n.º 5
0
        public static string TypeParameter(TypeParameterSyntax prm, IEnumerable <TypeParameterConstraintClauseSyntax> constraints)
        {
            var identifier = prm.Identifier.ValueText;

            var constraint = constraints.SingleOrDefault(o => o.Name.Identifier.ValueText == identifier);

            if (constraint == null)
            {
                return(identifier);
            }

            return(identifier + ": (" + string.Join(", ", constraint.Constraints.OfType <TypeConstraintSyntax>().ToList().Select(o => TypeProcessor.ConvertType(o.Type))) + ")");
        }
Exemplo n.º 6
0
        public static void Go(HaxeWriter writer, MethodDeclarationSyntax method)
        {
            if (method.Modifiers.Any(SyntaxKind.PartialKeyword) && method.Body == null)
            {
                //We only want to render out one of the two partial methods.  If there's another, skip this one.
                if (TypeState.Instance.Partials.SelectMany(o => o.Syntax.As <ClassDeclarationSyntax>().Members)
                    .OfType <MethodDeclarationSyntax>()
                    .Except(method)
                    .Where(o => o.Identifier.ValueText == method.Identifier.ValueText)
                    .Any())
                {
                    return;
                }
            }

            if (method.Identifier.ValueText == "GetEnumerator")
            {
                return;                 //skip GetEnumerator methods -- haxe can't enumerate on objects.  TODO: Render these out, but convert them to array-returning methods
            }
            var methodSymbol = Program.GetModel(method).GetDeclaredSymbol(method);

            writer.WriteIndent();

            if (ShouldUseOverrideKeyword(method, methodSymbol))
            {
                writer.Write("override ");
            }
            if (method.Modifiers.Any(SyntaxKind.PublicKeyword) || method.Modifiers.Any(SyntaxKind.ProtectedKeyword) || method.Modifiers.Any(SyntaxKind.InternalKeyword))
            {
                writer.Write("public ");
            }
            if (method.Modifiers.Any(SyntaxKind.PrivateKeyword))
            {
                writer.Write("private ");
            }
            if (method.Modifiers.Any(SyntaxKind.StaticKeyword))
            {
                writer.Write("static ");
            }

            writer.Write("function ");
            var methodName = OverloadResolver.MethodName(methodSymbol);

            if (methodName == "ToString")
            {
                methodName = "toString";
            }

            writer.Write(methodName);

            if (method.TypeParameterList != null)
            {
                writer.Write("<");
                writer.Write(string.Join(", ", method.TypeParameterList.Parameters.Select(o => TypeParameter(o, method.ConstraintClauses))));
                writer.Write(">");
            }

            writer.Write("(");
            var deferredDefaults = new Dictionary <string, ExpressionSyntax>();

            var firstParam = true;

            foreach (var parameter in method.ParameterList.Parameters)
            {
                bool isRef = parameter.Modifiers.Any(SyntaxKind.OutKeyword) || parameter.Modifiers.Any(SyntaxKind.RefKeyword);

                if (firstParam)
                {
                    firstParam = false;
                }
                else
                {
                    writer.Write(", ");
                }

                writer.Write(parameter.Identifier.ValueText);

                if (isRef)
                {
                    writer.Write(":CsRef<");
                    writer.Write(TypeProcessor.ConvertType(parameter.Type));
                    writer.Write(">");

                    Program.RefOutSymbols.TryAdd(Program.GetModel(method).GetDeclaredSymbol(parameter), null);
                }
                else
                {
                    writer.Write(TypeProcessor.ConvertTypeWithColon(parameter.Type));
                }

                if (parameter.Default != null)
                {
                    writer.Write(" = ");

                    if (TypeProcessor.ConvertType(parameter.Type).StartsWith("Nullable"))
                    {
                        writer.Write("null");
                        deferredDefaults.Add(parameter.Identifier.ValueText, parameter.Default.Value);
                    }
                    else
                    {
                        Core.Write(writer, parameter.Default.Value);
                    }
                }
            }

            writer.Write(")");
            writer.Write(TypeProcessor.ConvertTypeWithColon(method.ReturnType));

            if (method.Modifiers.Any(SyntaxKind.AbstractKeyword))
            {
                writer.WriteLine();
                writer.WriteOpenBrace();
                writer.WriteIndent();

                if (method.ReturnType.ToString() != "void")
                {
                    writer.Write("return ");                     //"return" the throw statement to work around haxe limitations
                }
                writer.Write("throw new Exception(\"Abstract item called\");\r\n");
                writer.WriteCloseBrace();
            }
            else if (method.Parent is InterfaceDeclarationSyntax)
            {
                writer.Write(";\r\n");
            }
            else
            {
                writer.WriteLine();
                writer.WriteOpenBrace();

                foreach (var defer in deferredDefaults)
                {
                    writer.WriteLine("if (" + defer.Key + " == null)");
                    writer.Indent++;
                    writer.WriteIndent();
                    writer.Write(defer.Key);
                    writer.Write(" = ");
                    Core.Write(writer, defer.Value);
                    writer.Write(";\r\n");
                    writer.Indent--;
                }

                if (method.Body != null)
                {
                    foreach (var statement in method.Body.Statements)
                    {
                        Core.Write(writer, statement);
                    }

                    TriviaProcessor.ProcessTrivias(writer, method.Body.DescendantTrivia());
                }

                writer.WriteCloseBrace();
            }
        }
Exemplo n.º 7
0
        public static void Go()
        {
            var partials = TypeState.Instance.Partials;
            var first    = partials.First();


            using (var writer = new HaxeWriter(first.Symbol.ContainingNamespace.FullName(), TypeState.Instance.TypeName))
            {
                var bases = partials
                            .Select(o => o.Syntax.BaseList)
                            .Where(o => o != null)
                            .SelectMany(o => o.Types)
                            .Select(o => Program.GetModel(o).GetTypeInfo(o.Type).ConvertedType)
                            .Distinct()
                            .ToList();

                var interfaces = bases.Where(o => o.TypeKind == TypeKind.Interface).ToList();

                TypeState.Instance.DerivesFromObject = bases.Count == interfaces.Count;

                writer.WriteLine("package " + first.Symbol.ContainingNamespace.FullName().ToLower() + @";");

                WriteImports.Go(writer);

                switch (first.Syntax.Kind())
                {
                case SyntaxKind.ClassDeclaration:
                case SyntaxKind.StructDeclaration:
                case SyntaxKind.EnumDeclaration:
                    writer.Write("class ");
                    break;

                case SyntaxKind.InterfaceDeclaration:
                    writer.Write("interface ");
                    break;

                default:
                    throw new Exception(first.Syntax.Kind().ToString());
                }

                writer.Write(TypeState.Instance.TypeName);



                if (first.Syntax is TypeDeclarationSyntax)
                {
                    //Look for generic arguments
                    var genericArgs = partials
                                      .Select(o => o.Syntax)
                                      .Cast <TypeDeclarationSyntax>()
                                      .Where(o => o.TypeParameterList != null)
                                      .SelectMany(o => o.TypeParameterList.Parameters)
                                      .ToList();

                    if (genericArgs.Count > 0)
                    {
                        writer.Write("<");
                        writer.Write(string.Join(", ", genericArgs.Select(o => WriteMethod.TypeParameter(o, partials.SelectMany(z => z.Syntax.As <TypeDeclarationSyntax>().ConstraintClauses)))));
                        writer.Write(">");
                    }

                    foreach (var baseType in bases)
                    {
                        var baseTypeHaxe = TypeProcessor.ConvertType(baseType);

                        if (baseTypeHaxe.StartsWith("Array<"))
                        {
                            if (baseType.ToString().StartsWith("System.Collections.Generic.IEnumerable<"))
                            {
                                writer.Write(" implements ");
                                writer.Write("system.collections.generic.IEnumerable<" + baseTypeHaxe.RemoveFromStartOfString("Array<"));
                            }


                            continue;
                        }

                        writer.Write(" ");

                        if (baseType.TypeKind == TypeKind.Interface && first.Syntax.Kind() != SyntaxKind.InterfaceDeclaration)
                        {
                            writer.Write("implements ");
                            writer.Write(baseTypeHaxe);
                        }
                        else
                        {
                            writer.Write("extends ");
                            writer.Write(baseTypeHaxe);
                        }
                    }
                }

                writer.Write("\r\n");

                writer.WriteOpenBrace();

                if (first.Syntax is EnumDeclarationSyntax)
                {
                    WriteEnumBody.Go(writer, TypeState.Instance.Partials.Select(o => o.Syntax).Cast <EnumDeclarationSyntax>().SelectMany(o => o.Members).Where(o => !Program.DoNotWrite.ContainsKey(o)));
                }
                else
                {
                    TypeState.Instance.AllMembers = partials.Select(o => o.Syntax).Cast <TypeDeclarationSyntax>().SelectMany(o => o.Members).Where(o => !Program.DoNotWrite.ContainsKey(o)).ToList();

                    foreach (var partial in partials)
                    {
                        foreach (var member in partial.Syntax.As <TypeDeclarationSyntax>().Members)
                        {
                            if (!(member is ClassDeclarationSyntax) && !(member is EnumDeclarationSyntax))
                            {
                                Core.Write(writer, member);
                            }
                        }
                    }

                    if (first.Syntax.Kind() != SyntaxKind.InterfaceDeclaration)
                    {
                        //Normally constructors will be written as we traverse the tree.  However, if there are no constructors, we must manually write them out since there are cases where we need a constructor in haxe while C# had none.
                        var ctors         = TypeState.Instance.AllMembers.OfType <ConstructorDeclarationSyntax>().ToList();
                        var instanceCtors = ctors.Where(o => !o.Modifiers.Any(SyntaxKind.StaticKeyword));

                        if (instanceCtors.Count() > 1)
                        {
                            throw new Exception("Haxe does not support overloaded constructors.  Consider changing all but one to static Create methods " + Utility.Descriptor(first.Syntax));
                        }

                        if (ctors.None(o => o.Modifiers.Any(SyntaxKind.StaticKeyword)))
                        {
                            WriteConstructor.WriteStaticConstructor(writer, null);
                        }
                        if (instanceCtors.None())
                        {
                            WriteConstructor.WriteInstanceConstructor(writer, null);
                        }
                    }
                }

                writer.WriteCloseBrace();
            }
        }
Exemplo n.º 8
0
        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 methodSymbol = model.GetSymbolInfo(expression).Symbol.As <IMethodSymbol>();

            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);


                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, false), expression))
                {
                    if (first)
                    {
                        first = false;
                    }
                    else
                    {
                        writer.Write(", ");
                    }

                    param.Write(writer);
                }

                writer.Write(")");
            }
        }