Esempio n. 1
0
        public override void ConvertToJavascript(JavascriptConversionContext context)
        {
            var methodCall = context.Node as MethodCallExpression;

            if (methodCall != null)
            {
                if (methodCall.Method.DeclaringType == typeof(string))
                {
                    switch (methodCall.Method.Name)
                    {
                    case "Concat":
                    {
                        context.PreventDefault();
                        var writer = context.GetWriter();
                        using (writer.Operation(JavascriptOperationTypes.Concat))
                        {
                            if (methodCall.Arguments.Count == 0)
                            {
                                writer.Write("''");
                            }
                            else
                            {
                                if (GetTypeOfExpression(methodCall.Arguments[0]) != typeof(string))
                                {
                                    writer.Write("''+");
                                }
                                context.WriteMany('+', methodCall.Arguments);
                            }
                        }

                        return;
                    }

                    case "Join":
                    {
                        context.PreventDefault();
                        var writer = context.GetWriter();
                        using (writer.Operation(JavascriptOperationTypes.Call))
                        {
                            using (writer.Operation(JavascriptOperationTypes.IndexerProperty))
                            {
                                var pars = methodCall.Method.GetParameters();
                                if (pars.Length == 4 && pars[1].ParameterType.IsArray && pars[2].ParameterType == typeof(int) && pars[3].ParameterType == typeof(int))
                                {
                                    throw new NotSupportedException("The `String.Join` method with start and count paramaters is not supported.");
                                }

                                if (pars.Length != 2 || !TypeHelpers.IsEnumerableType(pars[1].ParameterType))
                                {
                                    throw new NotSupportedException("This `String.Join` method is not supported.");
                                }

                                // if second parameter is an enumerable, render it directly
                                context.Visitor.Visit(methodCall.Arguments[1]);
                                writer.Write(".join");
                            }

                            writer.Write('(');

                            // separator
                            using (writer.Operation(0))
                                context.Visitor.Visit(methodCall.Arguments[0]);

                            writer.Write(')');
                        }

                        return;
                    }
                    }
                }
            }
        }
Esempio n. 2
0
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            if (node.Method.IsSpecialName)
            {
                var isIndexer = node.Method.Name == "get_Item" || node.Method.Name == "get_Chars";
                if (isIndexer)
                {
                    using (this.result.Operation(JavascriptOperationTypes.IndexerProperty))
                    {
                        this.Visit(node.Object);
                        this.result.Write('[');

                        using (this.result.Operation(0))
                        {
                            var posStart0 = this.result.Length;
                            foreach (var arg in node.Arguments)
                            {
                                if (this.result.Length != posStart0)
                                {
                                    this.result.Write(',');
                                }

                                this.Visit(arg);
                            }
                        }

                        this.result.Write(']');
                        return(node);
                    }
                }

                if (node.Method.Name == "set_Item")
                {
                    using (this.result.Operation(0))
                    {
                        using (this.result.Operation(JavascriptOperationTypes.AssignRhs))
                        {
                            using (this.result.Operation(JavascriptOperationTypes.IndexerProperty))
                            {
                                this.Visit(node.Object);
                                this.result.Write('[');

                                using (this.result.Operation(0))
                                {
                                    var posStart0 = this.result.Length;
                                    foreach (var arg in node.Arguments)
                                    {
                                        if (this.result.Length != posStart0)
                                        {
                                            this.result.Write(',');
                                        }

                                        this.Visit(arg);
                                    }
                                }

                                this.result.Write(']');
                            }
                        }

                        this.result.Write('=');
                        this.Visit(node.Arguments.Single());
                    }

                    return(node);
                }
            }
            else
            {
                if (node.Method.DeclaringType != null &&
                    (node.Method.Name == "ContainsKey" &&
                     TypeHelpers.IsDictionaryType(node.Method.DeclaringType)))
                {
                    using (this.result.Operation(JavascriptOperationTypes.Call))
                    {
                        using (this.result.Operation(JavascriptOperationTypes.IndexerProperty))
                            this.Visit(node.Object);
                        this.result.Write(".hasOwnProperty(");
                        using (this.result.Operation(0))
                            this.Visit(node.Arguments.Single());
                        this.result.Write(')');
                        return(node);
                    }
                }
            }

            if (node.Method.DeclaringType == typeof(string))
            {
                if (node.Method.Name == "Contains")
                {
                    using (this.result.Operation(JavascriptOperationTypes.Comparison))
                    {
                        using (this.result.Operation(JavascriptOperationTypes.Call))
                        {
                            using (this.result.Operation(JavascriptOperationTypes.IndexerProperty))
                                this.Visit(node.Object);
                            this.result.Write(".indexOf(");
                            using (this.result.Operation(0))
                            {
                                var posStart = this.result.Length;
                                foreach (var arg in node.Arguments)
                                {
                                    if (this.result.Length > posStart)
                                    {
                                        this.result.Write(',');
                                    }
                                    this.Visit(arg);
                                }
                            }

                            this.result.Write(')');
                        }

                        this.result.Write(">=0");
                        return(node);
                    }
                }
            }

            if (node.Method.Name == "ToString" && node.Type == typeof(string) && node.Object != null)
            {
                string methodName = null;
                if (node.Arguments.Count == 0 || typeof(IFormatProvider).IsAssignableFrom(node.Arguments[0].Type))
                {
                    methodName = "toString()";
                }
                else if (TypeHelpers.IsNumericType(node.Object.Type) &&
                         node.Arguments.Count >= 1 &&
                         node.Arguments[0].Type == typeof(string) &&
                         node.Arguments[0].NodeType == ExpressionType.Constant)
                {
                    var str   = (string)((ConstantExpression)node.Arguments[0]).Value;
                    var match = Regex.Match(str, @"^([DEFGNX])(\d*)$", RegexOptions.IgnoreCase);
                    var f     = match.Groups[1].Value.ToUpper();
                    var n     = match.Groups[2].Value;
                    if (f == "D")
                    {
                        methodName = "toString()";
                    }
                    else if (f == "E")
                    {
                        methodName = "toExponential(" + n + ")";
                    }
                    else if (f == "F" || f == "G")
                    {
                        methodName = "toFixed(" + n + ")";
                    }
                    else if (f == "N")
                    {
                        methodName = "toLocaleString()";
                    }
                    else if (f == "X")
                    {
                        methodName = "toString(16)";
                    }
                }

                if (methodName != null)
                {
                    using (this.result.Operation(JavascriptOperationTypes.Call))
                    {
                        using (this.result.Operation(JavascriptOperationTypes.IndexerProperty))
                            this.Visit(node.Object);
                        this.result.WriteFormat(".{0}", methodName);
                        return(node);
                    }
                }
            }

            if (!node.Method.IsStatic)
            {
                throw new NotSupportedException(string.Format("By default, Lambda2Js cannot convert custom instance methods, only static ones. `{0}` is not static.", node.Method.Name));
            }

            using (this.result.Operation(JavascriptOperationTypes.Call))
                if (node.Method.DeclaringType != null)
                {
                    this.result.Write(node.Method.DeclaringType.FullName);
                    this.result.Write('.');
                    this.result.Write(node.Method.Name);
                    this.result.Write('(');

                    var posStart = this.result.Length;
                    using (this.result.Operation(0))
                        foreach (var arg in node.Arguments)
                        {
                            if (this.result.Length != posStart)
                            {
                                this.result.Write(',');
                            }

                            this.Visit(arg);
                        }

                    this.result.Write(')');

                    return(node);
                }

            return(node);
        }
Esempio n. 3
0
        protected override Expression VisitListInit(ListInitExpression node)
        {
            // Detecting a new dictionary
            if (TypeHelpers.IsDictionaryType(node.Type))
            {
                using (this.result.Operation(0))
                {
                    this.result.Write('{');

                    var posStart = this.result.Length;
                    foreach (var init in node.Initializers)
                    {
                        if (this.result.Length > posStart)
                        {
                            this.result.Write(',');
                        }

                        if (init.Arguments.Count != 2)
                        {
                            throw new NotSupportedException(
                                      "Objects can only be initialized with methods that receive pairs: key -> name");
                        }

                        var nameArg = init.Arguments[0];
                        if (nameArg.NodeType != ExpressionType.Constant || nameArg.Type != typeof(string))
                        {
                            throw new NotSupportedException("The key of an object must be a constant string value");
                        }

                        var name = (string)((ConstantExpression)nameArg).Value;
                        if (Regex.IsMatch(name, @"^\w[\d\w]*$"))
                        {
                            this.result.Write(name);
                        }
                        else
                        {
                            this.WriteStringLiteral(name);
                        }

                        this.result.Write(':');
                        this.Visit(init.Arguments[1]);
                    }

                    this.result.Write('}');
                }

                return(node);
            }

            // Detecting a new dictionary
            if (TypeHelpers.IsListType(node.Type))
            {
                using (this.result.Operation(0))
                {
                    this.result.Write('[');

                    var posStart = this.result.Length;
                    foreach (var init in node.Initializers)
                    {
                        if (this.result.Length > posStart)
                        {
                            this.result.Write(',');
                        }

                        if (init.Arguments.Count != 1)
                        {
                            throw new Exception(
                                      "Arrays can only be initialized with methods that receive a single parameter for the value");
                        }

                        this.Visit(init.Arguments[0]);
                    }

                    this.result.Write(']');
                }

                return(node);
            }

            return(node);
        }
Esempio n. 4
0
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression == null)
            {
                var decl = node.Member.DeclaringType;
                if (decl == typeof(string))
                {
                    if (node.Member.Name == "Empty")
                    {
                        using (this.result.Operation(JavascriptOperationTypes.Literal))
                            this.result.Write("\"\"");
                        return(node);
                    }
                }
            }

            using (this.result.Operation(node))
            {
                var metadataProvider = this.Options.GetMetadataProvider();
                var pos = this.result.Length;
                if (node.Expression == null)
                {
                    var decl = node.Member.DeclaringType;
                    if (decl != null)
                    {
                        // TODO: there should be a way to customize the name of types through metadata
                        this.result.Write(decl.FullName);
                        this.result.Write('.');
                        this.result.Write(decl.Name);
                    }
                }
                else if (node.Expression != this.contextParameter)
                {
                    this.Visit(node.Expression);
                }
                else
                {
                    this.usedScopeMembers = this.usedScopeMembers ?? new List <string>();
                    var meta = metadataProvider.GetMemberMetadata(node.Member);
                    Debug.Assert(!string.IsNullOrEmpty(meta?.MemberName), "!string.IsNullOrEmpty(meta?.MemberName)");
                    this.usedScopeMembers.Add(meta?.MemberName ?? node.Member.Name);
                }

                if (this.result.Length > pos)
                {
                    this.result.Write('.');
                }

                var propInfo = node.Member as PropertyInfo;
                if (propInfo?.DeclaringType != null &&
                    node.Type == typeof(int) &&
                    node.Member.Name == "Count" &&
                    TypeHelpers.IsListType(propInfo.DeclaringType))
                {
                    this.result.Write("length");
                }
                else
                {
                    var meta = metadataProvider.GetMemberMetadata(node.Member);
                    Debug.Assert(!string.IsNullOrEmpty(meta?.MemberName), "!string.IsNullOrEmpty(meta?.MemberName)");
                    this.result.Write(meta?.MemberName);
                }

                return(node);
            }
        }
Esempio n. 5
0
        public override void ConvertToJavascript(JavascriptConversionContext context)
        {
            var methodCall = context.Node as MethodCallExpression;

            if (methodCall != null)
            {
                if (methodCall.Method.DeclaringType == typeof(Math))
                {
                    NameLength jsInfo;
                    if (membersMap.TryGetValue(methodCall.Method.Name, out jsInfo))
                    {
                        if (methodCall.Arguments.Count == jsInfo.length)
                        {
                            using (context.Operation(JavascriptOperationTypes.Call))
                            {
                                using (context.Operation(JavascriptOperationTypes.IndexerProperty))
                                    context.Write("Math." + jsInfo.name);
                                context.WriteManyIsolated('(', ')', ',', methodCall.Arguments);
                            }

                            return;
                        }
                        else if (methodCall.Method.Name == "Log" &&
                                 methodCall.Arguments.Count == 2)
                        {
                            // JavaScript does not support `Math.log` with 2 parameters,
                            // But it is easy enough for us to give a little help!
                            using (context.Operation(JavascriptOperationTypes.MulDivMod))
                            {
                                using (context.Operation(JavascriptOperationTypes.Call))
                                {
                                    using (context.Operation(JavascriptOperationTypes.IndexerProperty))
                                        context.Write("Math.log");
                                    context.Write('(');
                                    using (context.Operation(0))
                                        context.Write(methodCall.Arguments[0]);
                                    context.Write(')');
                                }

                                context.Write('/');

                                using (context.Operation(JavascriptOperationTypes.Call))
                                {
                                    using (context.Operation(JavascriptOperationTypes.IndexerProperty))
                                        context.Write("Math.log");
                                    context.Write('(');
                                    using (context.Operation(0))
                                        context.Write(methodCall.Arguments[1]);
                                    context.Write(')');
                                }
                            }

                            return;
                        }
                        else if (methodCall.Method.Name == "Round" &&
                                 methodCall.Arguments.Count == 2 &&
                                 TypeHelpers.IsNumericType(methodCall.Arguments[1].Type))
                        {
                            // We won't support `Math.Round` with two parameters by default.
                            // To do it, we'd have to repeat an input value in the expression (unacceptable):
                            //      Math.Round(A, B) => Math.round(A * Math.pow(10, B)) / Math.pow(10, B)
                            // Or start helping with hacky things (acceptable, but not by default):
                            //      Math.Round(A, B) => (function(a, b) { return Math.round(a * b) / b; })(A, Math.pow(10, B));
                            if (this.round2)
                            {
                                using (context.Operation(JavascriptOperationTypes.Call))
                                {
                                    context.WriteLambda <Func <double, double, double> >((a, b) => Math.Round(a * b) / b);
                                    context.Write('(');
                                    using (context.Operation(0))
                                        context.Write(methodCall.Arguments[0]);
                                    context.Write(',');
                                    using (context.Operation(0))
                                        using (context.Operation(JavascriptOperationTypes.Call))
                                        {
                                            using (context.Operation(JavascriptOperationTypes.IndexerProperty))
                                                context.Write("Math.pow");
                                            context.Write('(');
                                            context.Write("10");
                                            context.Write(',');
                                            using (context.Operation(0))
                                                context.Write(methodCall.Arguments[1]);
                                            context.Write(')');
                                        }

                                    context.Write(')');

                                    return;
                                }
                            }
                        }
                    }
                }
            }

            // E and PI are constant values, they will never result in
            // a member access expression. We will have to catch the
            // exact numbers, and convert them instead.
            var constVal = context.Node as ConstantExpression;

            if (constVal != null)
            {
                if (constVal.Value.Equals(Math.E))
                {
                    using (context.Operation(JavascriptOperationTypes.IndexerProperty))
                        context.Write("Math.E");
                }
                else if (constVal.Value.Equals(Math.PI))
                {
                    using (context.Operation(JavascriptOperationTypes.IndexerProperty))
                        context.Write("Math.PI");
                }
            }
        }