コード例 #1
0
ファイル: MemberInitAsJson.cs プロジェクト: ppekrol/lambda2js
        public override void ConvertToJavascript(JavascriptConversionContext context)
        {
            var initExpr = context.Node as MemberInitExpression;

            if (initExpr == null)
            {
                return;
            }
            var typeOk1 = this.NewObjectTypes?.Contains(initExpr.Type) ?? false;
            var typeOk2 = this.TypePredicate?.Invoke(initExpr.Type) ?? false;
            var typeOk3 = this.NewObjectTypes == null && this.TypePredicate == null;

            if (!typeOk1 && !typeOk2 && !typeOk3)
            {
                return;
            }
            if (initExpr.NewExpression.Arguments.Count > 0)
            {
                return;
            }
            if (initExpr.Bindings.Any(mb => mb.BindingType != MemberBindingType.Assignment))
            {
                return;
            }

            context.PreventDefault();
            var writer = context.GetWriter();

            using (writer.Operation(0))
            {
                writer.Write('{');

                var posStart = writer.Length;
                foreach (var assignExpr in initExpr.Bindings.Cast <MemberAssignment>())
                {
                    if (writer.Length > posStart)
                    {
                        writer.Write(',');
                    }

                    var metadataProvider = context.Options.GetMetadataProvider();
                    var meta             = metadataProvider.GetMemberMetadata(assignExpr.Member);
                    var memberName       = meta?.MemberName;
                    Debug.Assert(!string.IsNullOrEmpty(memberName), "!string.IsNullOrEmpty(memberName)");
                    if (Regex.IsMatch(memberName, @"^\w[\d\w]*$"))
                    {
                        writer.Write(memberName);
                    }
                    else
                    {
                        writer.WriteLiteral(memberName);
                    }

                    writer.Write(':');
                    context.Visitor.Visit(assignExpr.Expression);
                }

                writer.Write('}');
            }
        }
コード例 #2
0
        /// <summary>
        /// Writes a property accessor.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="propertyName">The property to access. Anything that can be converted to a string.</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static JavascriptConversionContext WriteAccessor(
            [NotNull] this JavascriptConversionContext context,
            [NotNull] string propertyName)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (propertyName == null)
            {
                throw new ArgumentNullException(nameof(propertyName));
            }

            if (Regex.IsMatch(propertyName, @"^\w[\d\w]*$"))
            {
                context.GetWriter().Write('.');
                context.GetWriter().Write(propertyName);
            }
            else if (Regex.IsMatch(propertyName, @"^\d+$"))
            {
                context.GetWriter().Write('[');
                context.GetWriter().Write(propertyName);
                context.GetWriter().Write(']');
            }
            else
            {
                context.GetWriter().Write('[');
                context.GetWriter().WriteLiteral(propertyName);
                context.GetWriter().Write(']');
            }

            return(context);
        }
コード例 #3
0
 /// <summary>
 /// Writes many expression nodes, separated by the given separator.
 /// </summary>
 /// <param name="context">The Javascript conversion context.</param>
 /// <param name="separator">Separator to be used.</param>
 /// <param name="nodes">A list of nodes to write.</param>
 /// <returns>The Javascript conversion context itself, to allow fluent style rendering.</returns>
 public static JavascriptConversionContext WriteMany(
     this JavascriptConversionContext context,
     char separator,
     params Expression[] nodes)
 {
     return(WriteMany(context, separator, (IEnumerable <Expression>)nodes));
 }
コード例 #4
0
        /// <summary>
        /// Writes a single character.
        /// </summary>
        /// <param name="context">The Javascript conversion context.</param>
        /// <param name="ch">Character to write.</param>
        /// <returns>The Javascript conversion context itself, to allow fluent style rendering.</returns>
        public static JavascriptConversionContext Write(this JavascriptConversionContext context, char ch)
        {
            var writer = context.GetWriter();

            writer.Write(ch);
            return(context);
        }
コード例 #5
0
 /// <summary>
 /// Writes many expressions isolated from outer and inner operations by opening, closing and separator characters.
 /// </summary>
 /// <param name="context">The Javascript conversion context.</param>
 /// <param name="opening">First character to render, isolating from outer operation.</param>
 /// <param name="closing">Last character to render, isolating from outer operation.</param>
 /// <param name="separator">Separator character to render, isolating one parameter from the other.</param>
 /// <param name="nodes">Nodes to render.</param>
 public static JavascriptConversionContext WriteManyIsolated(
     this JavascriptConversionContext context,
     char opening,
     char closing,
     char separator,
     params Expression[] nodes)
 {
     return(WriteManyIsolated(context, opening, closing, separator, (IEnumerable <Expression>)nodes));
 }
コード例 #6
0
        /// <summary>
        /// Writes many expressions isolated from outer and inner operations by opening, closing and separator characters.
        /// </summary>
        /// <param name="context">The Javascript conversion context.</param>
        /// <param name="opening">First character to render, isolating from outer operation.</param>
        /// <param name="closing">Last character to render, isolating from outer operation.</param>
        /// <param name="separator">Separator character to render, isolating one parameter from the other.</param>
        /// <param name="nodes">Nodes to render.</param>
        public static JavascriptConversionContext WriteManyIsolated(
            this JavascriptConversionContext context,
            char opening,
            char closing,
            char separator,
            IEnumerable <Expression> nodes)
        {
            var writer = context.GetWriter();

            writer.Write(opening);
            using (writer.Operation(0))
                context.WriteMany(separator, nodes);
            writer.Write(closing);
            return(context);
        }
コード例 #7
0
        /// <summary>
        /// Writes many expression nodes, separated by the given separator.
        /// </summary>
        /// <param name="context">The Javascript conversion context.</param>
        /// <param name="separator">Separator to be used.</param>
        /// <param name="nodes">A list of nodes to write.</param>
        /// <returns>The Javascript conversion context itself, to allow fluent style rendering.</returns>
        public static JavascriptConversionContext WriteMany(
            this JavascriptConversionContext context,
            char separator,
            IEnumerable <Expression> nodes)
        {
            var writer = context.GetWriter();
            int count  = 0;

            foreach (var node in nodes)
            {
                if (count++ > 0)
                {
                    writer.Write(separator);
                }

                context.Visitor.Visit(node);
            }
            return(context);
        }
コード例 #8
0
        public override Expression Visit(Expression node)
        {
            var context = new JavascriptConversionContext(node, this, this.result, this.Options);

            foreach (var each in this.extensions)
            {
                each.ConvertToJavascript(context);

                #region Supported will be removed in v2
#if V1
                if (context.gotWriter && context.Node != node)
                {
                    throw new Exception(
                              "Cannot both write and return a new node. Either write javascript code, or return a new node.");
                }
#endif
                #endregion
                if (context.preventDefault || context.gotWriter)
                {
                    // canceling any further action with the current node
                    return(node);
                }

                #region Supported will be removed in v2
#if V1
                if (context.Node != node)
                {
                    // a new node must be completelly revisited
                    return(this.Visit(context.Node));
                }
#endif
                #endregion
            }

            // nothing happened, continue to the default conversion behavior
            return(base.Visit(node));
        }
コード例 #9
0
        public override void ConvertToJavascript(JavascriptConversionContext context)
        {
            var cte = context.Node as ConstantExpression;

            if (cte != null && cte.Type.GetTypeInfo().IsEnum)
            {
                context.PreventDefault();
                var  writer        = context.GetWriter();
                long remaining     = Convert.ToInt64(cte.Value);
                var  flagsAsString = (this.opts & EnumOptions.FlagsAsStringWithSeparator) != 0;
                var  flagsAsOrs    = (this.opts & EnumOptions.FlagsAsNumericOrs) != 0;
                var  flagsAsArray  = (this.opts & EnumOptions.FlagsAsArray) != 0;
                var  isFlags       = cte.Type.GetTypeInfo().IsDefined(typeof(FlagsAttribute), false);

                // when value is zero
                if (remaining == 0 && (!isFlags || !(flagsAsString || flagsAsOrs || flagsAsArray)))
                {
                    if (WriteSingleEnumItem(context, writer, 0, cte.Value, false))
                    {
                        return;
                    }
                }

                // reading enum composition
                var values   = Enum.GetValues(cte.Type);
                var selected = new List <int>();
                for (int itV = 0; itV < values.Length; itV++)
                {
                    var val = Convert.ToInt64(values.GetValue(values.Length - itV - 1));
                    if ((val & remaining) == val)
                    {
                        remaining &= ~val;
                        selected.Add(values.Length - itV - 1);

                        if (!isFlags)
                        {
                            break;
                        }
                    }
                }

                // selecting enum case
                if (isFlags)
                {
                    var cnt = selected.Count + (remaining != 0 ? 1 : 0);


                    PrecedenceController xpto = null;
                    string start     = "";
                    string separator = "";
                    string end       = "";

                    if (flagsAsString)
                    {
                        xpto      = writer.Operation(JavascriptOperationTypes.Literal);
                        start     = "\"";
                        separator = "|";
                        end       = "\"";
                    }
                    else if (flagsAsArray)
                    {
                        xpto      = writer.Operation(0);
                        start     = "[";
                        separator = ",";
                        end       = "]";
                    }
                    else if (flagsAsOrs && cnt > 1)
                    {
                        xpto      = writer.Operation(JavascriptOperationTypes.Or);
                        start     = "";
                        separator = "|";
                        end       = "";
                    }
                    else if (cnt > 1)
                    {
                        throw new NotSupportedException("When converting flags enums to JavaScript, a flags option must be specified.");
                    }

                    using (xpto)
                    {
                        writer.Write(start);
                        var pos0 = writer.Length;
                        for (int itIdx = 0; itIdx < selected.Count; itIdx++)
                        {
                            if (pos0 != writer.Length)
                            {
                                writer.Write(separator);
                            }

                            var index   = selected[itIdx];
                            var enumVal = values.GetValue(index);
                            var val     = Convert.ToInt64(enumVal);
                            WriteSingleEnumItem(context, writer, val, enumVal, flagsAsString);
                        }

                        if (remaining != 0)
                        {
                            if (pos0 != writer.Length)
                            {
                                writer.Write(separator);
                            }
                            WriteSingleEnumItem(context, writer, remaining, remaining, flagsAsString);
                        }
                        writer.Write(end);
                    }
                }
                else
                {
                    if (remaining != 0)
                    {
                        var enumVal = cte.Value;
                        var val     = Convert.ToInt64(cte.Value);
                        WriteSingleEnumItem(context, writer, val, enumVal, false);
                    }
                    else
                    {
                        foreach (var index in selected)
                        {
                            var enumVal = values.GetValue(index);
                            var val     = Convert.ToInt64(enumVal);
                            WriteSingleEnumItem(context, writer, val, enumVal, false);
                        }
                    }
                }
            }
        }
コード例 #10
0
        private bool WriteSingleEnumItem(
            JavascriptConversionContext context,
            JavascriptWriter writer,
            long numValue,
            object enumValue,
            bool inStringAlready)
        {
            if ((this.opts & EnumOptions.UseStrings) != 0)
            {
                if (inStringAlready)
                {
                    if ((this.opts & EnumOptions.UseNumbers) != 0)
                    {
                        writer.WriteLiteralStringContent(numValue.ToString());
                    }
                    else
                    {
                        var str = enumValue.ToString();
                        if (numValue == 0 && str == "0")
                        {
                            writer.WriteLiteralStringContent("");
                        }
                        else
                        {
                            writer.WriteLiteralStringContent(str);
                        }
                    }
                    return(true);
                }

                using (writer.Operation(JavascriptOperationTypes.Literal))
                {
                    if ((this.opts & EnumOptions.UseNumbers) != 0)
                    {
                        writer.WriteLiteral(numValue.ToString());
                    }
                    else
                    {
                        var str = enumValue.ToString();
                        if (numValue == 0 && str == "0")
                        {
                            writer.WriteLiteral("");
                        }
                        else
                        {
                            writer.WriteLiteral(str);
                        }
                    }
                    return(true);
                }
            }

            if ((this.opts & EnumOptions.UseNumbers) != 0)
            {
                using (writer.Operation(JavascriptOperationTypes.Literal))
                    writer.WriteLiteral(numValue);
                return(true);
            }

            if ((this.opts & EnumOptions.UseStaticFields) != 0)
            {
                using (writer.Operation(JavascriptOperationTypes.IndexerProperty))
                    context.Write(enumValue.GetType().Name).WriteAccessor(enumValue.ToString());
                return(true);
            }

            return(false);
        }
コード例 #11
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;
                    }
                    }
                }
            }
        }
コード例 #12
0
 /// <summary>
 /// Writes many expression nodes, separated by the given separator.
 /// </summary>
 /// <param name="context">The Javascript conversion context.</param>
 /// <param name="node">The node to write.</param>
 /// <returns>The Javascript conversion context itself, to allow fluent style rendering.</returns>
 public static JavascriptConversionContext Write(this JavascriptConversionContext context, Expression node)
 {
     context.Visitor.Visit(node);
     return(context);
 }
コード例 #13
0
 public static JavascriptConversionContext WriteFormat(this JavascriptConversionContext context, string format, params object[] values)
 {
     context.GetWriter().WriteFormat(format, values);
     return(context);
 }
コード例 #14
0
 /// <summary>
 /// Writes a string to the output.
 /// </summary>
 /// <param name="context">The Javascript conversion context.</param>
 /// <param name="str">String to write.</param>
 /// <returns>The Javascript conversion context itself, to allow fluent style rendering.</returns>
 public static JavascriptConversionContext Write(this JavascriptConversionContext context, string str)
 {
     context.GetWriter().Write(str);
     return(context);
 }
コード例 #15
0
 /// <summary>
 /// Encloses the following write calls in an operation context,
 /// that will automatically write precedence operators '(' and ')' if needed,
 /// depening on the operations stack.
 /// <para>
 /// For example, a sum (+)
 /// inside a multiplication (*) requires the precedence operators.
 /// </para>
 /// <para>
 /// To isolate operations, you can pass 0 to the <paramref name="op"/> parameter,
 /// making neither the current nor the inner operation need precedence operators.
 /// </para>
 /// </summary>
 /// <param name="context">Context of the conversion.</param>
 /// <param name="op">The operation that you want to render in the scope.</param>
 /// <returns>A disposable object that renders the ending ')' when needed.</returns>
 public static IDisposable Operation(
     this JavascriptConversionContext context,
     JavascriptOperationTypes op)
 {
     return(context.GetWriter().Operation(op));
 }
コード例 #16
0
ファイル: LinqMethods.cs プロジェクト: timbrown81/lambda2js
        public override void ConvertToJavascript(JavascriptConversionContext context)
        {
            var methodCall = context.Node as MethodCallExpression;

            if (methodCall != null)
            {
                if (methodCall.Method.DeclaringType == typeof(Enumerable))
                {
                    switch (methodCall.Method.Name)
                    {
                    case "Select":
                    {
                        var writer = context.GetWriter();
                        using (writer.Operation(JavascriptOperationTypes.Call))
                        {
                            using (writer.Operation(JavascriptOperationTypes.IndexerProperty))
                            {
                                // public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
                                // public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
                                var pars = methodCall.Method.GetParameters();
                                if (pars.Length != 2)
                                {
                                    throw new NotSupportedException("The `Enumerable.Select` method must have 2 parameters.");
                                }

                                context.Visitor.Visit(methodCall.Arguments[0]);
                                writer.Write(".map");
                            }

                            writer.Write('(');

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

                            writer.Write(')');
                        }

                        return;
                    }

                    case "Where":
                    {
                        var writer = context.GetWriter();
                        using (writer.Operation(JavascriptOperationTypes.Call))
                        {
                            using (writer.Operation(JavascriptOperationTypes.IndexerProperty))
                            {
                                // public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
                                // public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
                                var pars = methodCall.Method.GetParameters();
                                if (pars.Length != 2)
                                {
                                    throw new NotSupportedException("The `Enumerable.Where` method must have 2 parameters.");
                                }

                                context.Visitor.Visit(methodCall.Arguments[0]);
                                writer.Write(".filter");
                            }

                            writer.Write('(');

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

                            writer.Write(')');
                        }

                        return;
                    }

                    case "ToArray":
                    {
                        // Ecma Script 6+: use spread operator
                        // Other: use array `slice`
                        if (context.Options.ScriptVersion.Supports(JavascriptSyntax.ArraySpread))
                        {
                            var writer = context.GetWriter();
                            using (writer.Operation(0))
                            {
                                writer.Write('[');
                                writer.Write("...");
                                using (writer.Operation(JavascriptOperationTypes.ParamIsolatedLhs))
                                    context.Visitor.Visit(methodCall.Arguments[0]);
                                writer.Write(']');
                            }
                        }
                        else
                        {
                            var writer = context.GetWriter();
                            using (writer.Operation(JavascriptOperationTypes.Call))
                            {
                                using (writer.Operation(JavascriptOperationTypes.IndexerProperty))
                                {
                                    context.Visitor.Visit(methodCall.Arguments[0]);
                                    writer.Write(".slice");
                                }

                                writer.Write('(');
                                writer.Write(')');
                            }
                        }

                        return;
                    }
                    }
                }
            }
        }
コード例 #17
0
 /// <summary>
 /// When overridden allows the control of expression nodes to JavaScript code.
 /// </summary>
 /// <param name="context">JavaScript conversion context, that allows to write text to the output stream.</param>
 public abstract void ConvertToJavascript(JavascriptConversionContext context);
コード例 #18
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");
                }
            }
        }