public JsExpression CompileToJavascript(DataContextStack dataContext, Expression expression) { var currentContextVariable = new JsTemporaryVariableParameter(new JsIdentifierExpression("ko").Member("contextFor").Invoke(new JsSymbolicParameter(CommandBindingExpression.SenderElementParameter))); var resultPromiseVariable = new JsTemporaryVariableParameter(new JsNewExpression("DotvvmPromise")); var senderVariable = new JsTemporaryVariableParameter(new JsSymbolicParameter(CommandBindingExpression.SenderElementParameter)); var visitor = new ExtractExpressionVisitor(ex => ex.NodeType == ExpressionType.Call && ex is MethodCallExpression methodCall && javascriptTranslator.TryTranslateMethodCall(methodCall.Object, methodCall.Arguments.ToArray(), methodCall.Method, dataContext) == null); var rootCallback = visitor.Visit(expression); var js = SouldCompileCallback(rootCallback) ? new JsSymbolicParameter(resultPromiseVariable).Member("resolve").Invoke(javascriptTranslator.CompileToJavascript(rootCallback, dataContext)) : null; foreach (var param in visitor.ParameterOrder.Reverse <ParameterExpression>()) { js = js ?? new JsSymbolicParameter(resultPromiseVariable).Member("resolve").Invoke(new JsIdentifierExpression(param.Name)); var callback = new JsFunctionExpression(new[] { new JsIdentifier(param.Name) }, new JsBlockStatement(new JsExpressionStatement(js))); var method = visitor.Replaced[param] as MethodCallExpression; js = CompileMethodCall(method, dataContext, callback); } foreach (var sp in js.Descendants.OfType <JsSymbolicParameter>()) { if (sp.Symbol == JavascriptTranslator.KnockoutContextParameter) { sp.Symbol = currentContextVariable; } else if (sp.Symbol == JavascriptTranslator.KnockoutViewModelParameter) { sp.ReplaceWith(new JsSymbolicParameter(currentContextVariable).Member("$data")); } else if (sp.Symbol == CommandBindingExpression.SenderElementParameter) { sp.Symbol = senderVariable; } } return(new JsBinaryExpression(js, BinaryOperatorType.Sequence, new JsSymbolicParameter(resultPromiseVariable))); }
public JsExpression CompileToJavascript(DataContextStack dataContext, Expression expression) { var currentContextVariable = new JsTemporaryVariableParameter(new JsIdentifierExpression("ko").Member("contextFor").Invoke(new JsSymbolicParameter(CommandBindingExpression.SenderElementParameter))); // var resultPromiseVariable = new JsNewExpression("DotvvmPromise")); var senderVariable = new JsTemporaryVariableParameter(new JsSymbolicParameter(CommandBindingExpression.SenderElementParameter)); var visitor = new ExtractExpressionVisitor(ex => { if (ex.NodeType == ExpressionType.Call && ex is MethodCallExpression methodCall) { if (javascriptTranslator.TryTranslateMethodCall(methodCall.Object, methodCall.Arguments.ToArray(), methodCall.Method, dataContext) is JsExpression jsTranslation) { if (jsTranslation.Annotation <ResultIsPromiseAnnotation>() is ResultIsPromiseAnnotation promiseAnnotation) { return(p => new BindingParameterAnnotation(extensionParameter: new JavascriptTranslationVisitor.FakeExtensionParameter(_ => new JsIdentifierExpression(p.Name).WithAnnotations(promiseAnnotation.ResultAnnotations), p.Name, new ResolvedTypeDescriptor(p.Type)))); } else { return(null); } } return(p => new BindingParameterAnnotation(extensionParameter: new JavascriptTranslationVisitor.FakeExtensionParameter(_ => new JsIdentifierExpression(p.Name).WithAnnotation(new ViewModelInfoAnnotation(p.Type)), p.Name, new ResolvedTypeDescriptor(p.Type)))); } return(null); }); var rootCallback = visitor.Visit(expression); var js = SouldCompileCallback(rootCallback) ? new JsIdentifierExpression("resolve").Invoke(javascriptTranslator.CompileToJavascript(rootCallback, dataContext)) : null; foreach (var param in visitor.ParameterOrder.Reverse <ParameterExpression>()) { js = js ?? new JsIdentifierExpression("resolve").Invoke(new JsIdentifierExpression(param.Name)); var callback = new JsFunctionExpression(new[] { new JsIdentifier(param.Name) }, new JsBlockStatement(new JsExpressionStatement(js))); var method = visitor.Replaced[param] as MethodCallExpression; js = CompileMethodCall(method, dataContext, callback); } foreach (var sp in js.Descendants.OfType <JsSymbolicParameter>()) { if (sp.Symbol == JavascriptTranslator.KnockoutContextParameter) { sp.Symbol = currentContextVariable; } else if (sp.Symbol == JavascriptTranslator.KnockoutViewModelParameter) { sp.ReplaceWith(new JsSymbolicParameter(currentContextVariable).Member("$data")); } else if (sp.Symbol == CommandBindingExpression.SenderElementParameter) { sp.Symbol = senderVariable; } } if (js is JsInvocationExpression invocation && invocation.Target is JsIdentifierExpression identifier && identifier.Identifier == "resolve") { // optimize `new Promise(function (resolve) { resolve(x) })` to `Promise.resolve(x)` identifier.ReplaceWith(new JsIdentifierExpression("Promise").Member("resolve")); return(js); }
public JsExpression TryTranslateCall(LazyTranslatedExpression context, LazyTranslatedExpression[] arguments, MethodInfo method) { var tmp = new JsTemporaryVariableParameter(); return(new JsBinaryExpression( new JsAssignmentExpression(new JsSymbolicParameter(tmp), new JsInvocationExpression(new JsIdentifierExpression("dotvvm").Member("globalize").Member("parseDotvvmDate"), context.JsExpression())), BinaryOperatorType.Sequence, new JsNewExpression("Date", new JsInvocationExpression(new JsMemberAccessExpression(new JsSymbolicParameter(tmp), "getFullYear")), new JsInvocationExpression(new JsMemberAccessExpression(new JsSymbolicParameter(tmp), "getMonth")), new JsBinaryExpression(new JsInvocationExpression(new JsMemberAccessExpression(new JsSymbolicParameter(tmp), "getDate")), BinaryOperatorType.Plus, arguments[0].JsExpression()), new JsInvocationExpression(new JsMemberAccessExpression(new JsSymbolicParameter(tmp), "getHours")), new JsInvocationExpression(new JsMemberAccessExpression(new JsSymbolicParameter(tmp), "getMinutes")), new JsInvocationExpression(new JsMemberAccessExpression(new JsSymbolicParameter(tmp), "getSeconds")), new JsInvocationExpression(new JsMemberAccessExpression(new JsSymbolicParameter(tmp), "getMilliseconds")) ) )); }
public override void VisitAssignmentExpression(JsAssignmentExpression assignmentExpression) { base.VisitAssignmentExpression(assignmentExpression); // change assignment to observable property to observable invocation // only do for RestultIsObservable, not ResultMayBeObservable if (assignmentExpression.Left.HasAnnotation <ResultIsObservableAnnotation>()) { var value = assignmentExpression.Right.Detach(); var assignee = assignmentExpression.Left.Detach(); assignee.RemoveAnnotations <ResultIsObservableAnnotation>(); if (value.IsComplexType()) { assignmentExpression.ReplaceWith(_ => new JsIdentifierExpression("dotvvm").Member("serialization").Member("deserialize").Invoke(value, assignee)); } else { // A = B -> A(B) assignee.RemoveAnnotations <MayBeNullAnnotation>(); JsExpression newExpression = new JsInvocationExpression(assignee, value) .WithAnnotation(ObservableSetterInvocationAnnotation.Instance); if (assignmentExpression.Parent is JsExpression resultConsumer) { // assignment's result if (assignee is JsMemberAccessExpression memberAccess) { // x.A(B) -> x.A(B).A newExpression = AddAnnotations(newExpression.Member(memberAccess.MemberName).Invoke().WithAnnotation(ObservableUnwrapInvocationAnnotation.Instance), value); } else { // f() = CC -> f()(_a = CC), _a var tmp = new JsTemporaryVariableParameter(); value.ReplaceWith(_ => new JsAssignmentExpression(new JsSymbolicParameter(tmp), value)); newExpression = AddAnnotations(new JsBinaryExpression(newExpression, BinaryOperatorType.Sequence, new JsSymbolicParameter(tmp)), value); } } assignmentExpression.ReplaceWith(newExpression); } } }