/// <summary> /// Writes a literal value to the output. /// </summary> /// <param name="value">Value to write.</param> /// <returns>The <see cref="JavascriptWriter"/>, allowing a fluent style.</returns> public JavascriptWriter WriteLiteral(object value) { if (value is string || value is char) { this.result.Append('"'); this.result.Append(value.ToString() .Replace("\\", "\\\\") .Replace("\r", "\\r") .Replace("\n", "\\n") .Replace("\t", "\\t") .Replace("\0", "\\0") .Replace("\"", "\\\"")); this.result.Append('"'); } else if (TypeHelpers.IsNumericType(value.GetType())) { if (TypeHelpers.IsIntegerType(value.GetType())) { this.result.Append(value); } else if (value is float || value is double || value is decimal) { this.result.AppendFormat("{0:E}", value); } } return(this); }
protected override Expression VisitConstant(ConstantExpression node) { if (TypeHelpers.IsNumericType(node.Type)) { using (this.result.Operation(JavascriptOperationTypes.Literal)) this.result.Write(Convert.ToString(node.Value, CultureInfo.InvariantCulture)); } else if (node.Type == typeof(string)) { using (this.result.Operation(JavascriptOperationTypes.Literal)) this.WriteStringLiteral((string)node.Value); } else if (node.Type == typeof(Regex)) { using (this.result.Operation(JavascriptOperationTypes.Literal)) { this.result.Write('/'); this.result.Write(node.Value); this.result.Write("/g"); } } else if (node.Value == null) { this.result.Write("null"); } return(node); }
/// <summary> /// Writes a literal value to the output. /// </summary> /// <param name="value">Value to write.</param> /// <returns>The <see cref="JavascriptWriter"/>, allowing a fluent style.</returns> public JavascriptWriter WriteLiteral(object value) { if (value is string || value is char) { this.result.Append('"'); this.WriteLiteralStringContent(value); this.result.Append('"'); } else if (TypeHelpers.IsNumericType(value.GetType())) { if (TypeHelpers.IsIntegerType(value.GetType())) { this.result.Append(value); } else if (value is float || value is double || value is decimal) { this.result.AppendFormat("{0:E}", value); } } return(this); }
public static void WriteOperator(StringBuilder result, ExpressionType nodeType, Type type) { switch (nodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: result.Append('+'); break; case ExpressionType.AddAssign: case ExpressionType.AddAssignChecked: result.Append("+="); break; case ExpressionType.And: result.Append("&"); break; case ExpressionType.AndAlso: result.Append("&&"); break; case ExpressionType.AndAssign: result.Append("&="); break; case ExpressionType.ArrayIndex: break; case ExpressionType.ArrayLength: result.Append(".length"); break; case ExpressionType.Assign: result.Append("="); break; case ExpressionType.Block: break; case ExpressionType.Call: break; case ExpressionType.Coalesce: break; case ExpressionType.Conditional: break; case ExpressionType.Constant: break; case ExpressionType.Convert: break; case ExpressionType.ConvertChecked: break; case ExpressionType.DebugInfo: break; case ExpressionType.Decrement: result.Append("--"); break; case ExpressionType.Default: break; case ExpressionType.Divide: result.Append("/"); break; case ExpressionType.DivideAssign: result.Append("/="); break; case ExpressionType.Dynamic: break; case ExpressionType.Equal: result.Append("==="); break; case ExpressionType.ExclusiveOr: result.Append("^"); break; case ExpressionType.ExclusiveOrAssign: result.Append("^="); break; case ExpressionType.Extension: break; case ExpressionType.Goto: break; case ExpressionType.GreaterThan: result.Append(">"); break; case ExpressionType.GreaterThanOrEqual: result.Append(">="); break; case ExpressionType.Increment: result.Append("++"); break; case ExpressionType.Index: break; case ExpressionType.Invoke: break; case ExpressionType.IsFalse: break; case ExpressionType.IsTrue: break; case ExpressionType.Label: break; case ExpressionType.Lambda: break; case ExpressionType.LeftShift: result.Append("<<"); break; case ExpressionType.LeftShiftAssign: result.Append("<<="); break; case ExpressionType.LessThan: result.Append("<"); break; case ExpressionType.LessThanOrEqual: result.Append("<="); break; case ExpressionType.ListInit: break; case ExpressionType.Loop: break; case ExpressionType.MemberAccess: break; case ExpressionType.MemberInit: break; case ExpressionType.Modulo: result.Append("%"); break; case ExpressionType.ModuloAssign: result.Append("%="); break; case ExpressionType.Multiply: result.Append("*"); break; case ExpressionType.MultiplyAssign: result.Append("*="); break; case ExpressionType.MultiplyAssignChecked: break; case ExpressionType.MultiplyChecked: break; case ExpressionType.Negate: case ExpressionType.NegateChecked: result.Append("-"); break; case ExpressionType.New: result.Append("new"); break; case ExpressionType.NewArrayBounds: break; case ExpressionType.NewArrayInit: break; case ExpressionType.Not: if (TypeHelpers.IsNumericType(type) || type.GetTypeInfo().IsEnum) { result.Append("~"); } else { result.Append("!"); } break; case ExpressionType.NotEqual: result.Append("!=="); break; case ExpressionType.OnesComplement: result.Append("~"); break; case ExpressionType.Or: result.Append("|"); break; case ExpressionType.OrAssign: result.Append("|="); break; case ExpressionType.OrElse: result.Append("||"); break; case ExpressionType.Parameter: break; case ExpressionType.Power: break; case ExpressionType.PowerAssign: break; case ExpressionType.PostDecrementAssign: case ExpressionType.PreDecrementAssign: result.Append("--"); break; case ExpressionType.PostIncrementAssign: case ExpressionType.PreIncrementAssign: result.Append("++"); break; case ExpressionType.Quote: break; case ExpressionType.RightShift: result.Append(">>"); break; case ExpressionType.RightShiftAssign: result.Append(">>="); break; case ExpressionType.RuntimeVariables: break; case ExpressionType.Subtract: result.Append("-"); break; case ExpressionType.SubtractAssign: result.Append("-="); break; case ExpressionType.SubtractAssignChecked: result.Append("--"); break; case ExpressionType.SubtractChecked: result.Append("-"); break; case ExpressionType.Switch: break; case ExpressionType.Throw: break; case ExpressionType.Try: break; case ExpressionType.TypeAs: break; case ExpressionType.TypeEqual: break; case ExpressionType.TypeIs: break; case ExpressionType.UnaryPlus: break; case ExpressionType.Unbox: break; default: throw new ArgumentOutOfRangeException(); } }
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); }
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"); } } }