Ejemplo n.º 1
0
        public bool TryUnwrapJsniExpression(IMethodSymbol method, ExpressionSyntax originalTarget, ExpressionSyntax[] originalArguments, JsExpression target, JsExpression[] arguments, out JsExpression result)
        {
            // Special compiler handler of Jsni -- these are special methods that translate into otherwise inexpressible javascript
            if (Equals(method.ContainingType, Context.Instance.JsniType))
            {
                if (method.ReducedFrom != null && !Equals(method.ReducedFrom, method))
                {
                    method = method.ReducedFrom;
                    arguments = new[] { target }.Concat(arguments).ToArray();
                    originalArguments = new[] { originalTarget }.Concat(originalArguments).ToArray();
                }
                switch (method.Name)
                {
                    case "apply":
                    {
                        if (method.TypeArguments.Any())
                        {
                            // First argument is an Action -- we want to deconstruct it to extract the target and method name.
                            var body = originalArguments[0].GetBody();
                            var lambdaInvocation = (InvocationExpressionSyntax)body;
                            var lambdaMethod = (IMethodSymbol)ModelExtensions.GetSymbolInfo(transformer.model, lambdaInvocation).Symbol;
                            result = Type(lambdaMethod.ContainingType).Member("prototype").Member(lambdaMethod.GetMemberName()).Member("apply").Invoke(arguments[1], arguments[2]);
                            return true;
                        }
                        else
                        {
                            result = arguments[0].Member("apply").Invoke(arguments[1], arguments[2]);
                            return true;
                        }
                    }
                    case "call":
                    {
                        if (method.TypeArguments.Any())
                        {
                            // First argument is an Action -- we want to deconstruct it to extract the target and method name.
                            var body = originalArguments[0].GetBody();
                            var lambdaInvocation = (InvocationExpressionSyntax)body;
                            var lambdaMethod = (IMethodSymbol)ModelExtensions.GetSymbolInfo(transformer.model, lambdaInvocation).Symbol;
                            result = Type(lambdaMethod.ContainingType).Member("prototype").Member(lambdaMethod.GetMemberName()).Member("call").Invoke(arguments.Skip(1).ToArray());
                            return true;
                        }
                        else
                        {
                            // First argument is an Action -- we want to deconstruct it to extract the target and method name.
                            result = arguments[0].Member("call").Invoke(arguments.Skip(1).ToArray());
                            return true;
                        }
                    }
                    case "type":
                        result = method.TypeArguments.Any() ? 
                            Type(method.TypeArguments.Single()) : 
                            ((JsMemberReferenceExpression)((JsInvocationExpression)arguments[0]).Target).Target;
                        return true;
                    case "new":
                    case "@new":
                        result = Js.New(arguments[0].Parenthetical(), arguments.Skip(1).ToArray());
                        return true;
                    case "array":
                        result = Js.Array(arguments);
                        return true;
                    case "invoke":
                        result = arguments[0].Invoke(arguments.Skip(1).ToArray());
                        return true;
                    case "memberset":
                    {
                        var memberName = GetConstantString(originalArguments[1]);
                        result = arguments[0].Member(memberName).Assign(arguments[2]);
                        return true;
                    }
                    case "member":
                    {
                        var memberName = GetConstantString(originalArguments[1]);
                        result = arguments[0].Member(memberName);
                        return true;
                    }
                    case "function":
                    case "procedure":
                        var nameArguments = originalArguments.Skip(1).ToArray();
                        var nameOverrides = nameArguments.Select(x => GetConstantString(x)).ToArray();

                        var lambda = originalArguments[0];
                        var lambdaSymbol = (IMethodSymbol)transformer.model.GetSymbolInfo(lambda).Symbol;
                        var lambdaParameters = lambda.GetParameters();

                        var jsParameters = lambdaParameters.Select(x => Js.Parameter(x.Identifier.ToString())).Cast<IJsDeclaration>().ToArray();
                        for (var i = 0; i < nameOverrides.Length; i++)
                        {
                            jsParameters[i] = new WrappedParent(jsParameters[i]) { Name = nameOverrides[i] };
                        }
                        for (var i = 0; i < jsParameters.Length; i++)
                        {
                            var parameter = jsParameters[i];
                            var symbol = lambdaSymbol.Parameters[i];
                            transformer.DeclareInCurrentScope(symbol, parameter);
                        }

                        var jsBody = new JsBlockStatement();
                        transformer.PushOutput(jsBody);
                        var lambdaBody = lambda.GetBody();

                        var lambdaNode = lambdaBody.Accept(transformer);

                        JsStatement lambdaStatement;
                        if (lambdaNode is JsExpression)
                        {
                            switch (method.Name)
                            {
                                case "function":
                                    lambdaStatement = ((JsExpression)lambdaNode).Return();
                                    break;
                                default:
                                    lambdaStatement = ((JsExpression)lambdaNode).Express();
                                    break;
                            }
                        }
                        else
                        {
                            lambdaStatement = (JsStatement)lambdaNode;
                        }

                        result = Js.Function(jsParameters).Body(lambdaStatement);

                        transformer.PopOutput();

                        return true;
                    case "arguments":
                        result = Js.Reference("arguments");
                        return true;
                    case "this":
                    case "@this":
                        result = Js.This();
                        return true;
                    case "delete":
                        var deleteTarget = arguments[0];
                        result = deleteTarget.Delete();
                        return true;
                    case "_typeof":
                        var typeofTarget = arguments[0];
                        result = Js.TypeOf(typeofTarget);
                        return true;
                    case "reference":
                        result = Js.Reference(GetConstantString(originalArguments[0]));
                        return true;
                    case "parseInt":
                        result = Js.Reference("parseInt").Invoke(arguments);
                        return true;
                    case "parseFloat":
                        result = Js.Reference("parseFloat").Invoke(arguments);
                        return true;
                    case "encodeURIComponent":
                        result = Js.Reference("encodeURIComponent").Invoke(arguments);
                        return true;
                    case "decodeURIComponent":
                        result = Js.Reference("decodeURIComponent").Invoke(arguments);
                        return true;
                    case "getComputedStyle":
                        result = Js.Reference("getComputedStyle").Invoke(arguments);
                        return true;
                    case "isNaN":
                        result = Js.Reference("isNaN").Invoke(arguments);
                        return true;
                    case "instanceof":
                        result = Js.InstanceOf(arguments[0], arguments[1]).Parenthetical();
                        return true;
                    case "object":
                        // Deconstruct the object passed in into a JS object
                        var obj = (AnonymousObjectCreationExpressionSyntax)originalArguments[0];
                        result = Js.Object(
                            obj.Initializers.Select(x => Js.Item(
                                x.NameEquals.Name.ToString(),
                                (JsExpression)x.Expression.Accept(transformer)
                            )).ToArray()
                        );
                        if (arguments.Length > 1 && originalArguments[1].IsTrue())
                        {
                            result = result.Compact();
                        }
                        return true;
                    case "regex":
                        result = new JsRegexExpression(GetConstantString(originalArguments[0]));
                        if (originalArguments.Length > 1)
                            ((JsRegexExpression)result).Suffix = GetConstantString(originalArguments[1]);
                        return true;
                    case "in":
                        result = arguments[0].In(arguments[1]);
                        return true;
                    case "setTimeout":
                        result = Js.Reference("setTimeout").Invoke(arguments[0], arguments[1]);
                        return true;
                    case "setInterval":
                        result = Js.Reference("setInterval").Invoke(arguments[0], arguments[1]);
                        return true;
                    case "clearTimeout":
                        result = Js.Reference("clearTimeout").Invoke(arguments[0]);
                        return true;
                    case "clearInterval":
                        result = Js.Reference("clearInterval").Invoke(arguments[0]);
                        return true;
                    case "code":
                        result = new JsNativeExpression(GetConstantString(originalArguments[0]));
                        return true;
                }
            }            
            result = null;
            return false;
        }