Ejemplo n.º 1
0
        private bool TryExtractMethodFromAttributeData(
            IMethodSymbol method,
            INamedTypeSymbol attributeType,
            bool synchroneous,
            [NotNullWhen(returnValue: true)] out ReactMethod?reactMethod)
        {
            if (TryFindAttribute(method, attributeType, out var attr))
            {
                string?name = null;
                if (attr.ConstructorArguments.Length > 0)
                {
                    name = attr.ConstructorArguments[0].Value as string;
                }

                foreach (var namedArgument in attr.NamedArguments)
                {
                    switch (namedArgument.Key)
                    {
                    case nameof(ReactMethodAttribute.MethodName):
                        name = namedArgument.Value.Value as string;
                        break;

                    default:
                        var location = attr.ApplicationSyntaxReference?.SyntaxTree.GetLocation(attr.ApplicationSyntaxReference.Span);
                        m_diagnostics.Add(Diagnostic.Create(
                                              DiagnosticDescriptors.UnexpectedPropertyInAttribute,
                                              location ?? Location.None,
                                              namedArgument.Key,
                                              attr.AttributeClass?.Name));
                        reactMethod = null;
                        return(false);
                    }
                }

                name ??= method.Name;

                reactMethod = new ReactMethod(method, name, synchroneous);
                return(true);
            }

            reactMethod = null;
            return(false);
        }
Ejemplo n.º 2
0
        internal StatementSyntax AddMethod(ReactMethod method)
        {
            var statements = new List <StatementSyntax>();

            var parameters = method.Method.Parameters;

            var readArgsArguments = new List <ArgumentSyntax>(parameters.Length);

            readArgsArguments.Add(Argument(IdentifierName(ReactNativeNames.ReaderLocalName)));
            var args = new List <ExpressionSyntax>(parameters.Length);

            // generates:
            //   (out ArgType0 arg0, out ArgType1 arg1, ...);
            // as well as
            //   (arg0, arg1, ... )
            for (int i = 0; i < method.EffectiveParameters.Count; i++)
            {
                var param        = method.EffectiveParameters[i];
                var variableName = "arg" + i.ToString(CultureInfo.InvariantCulture);

                readArgsArguments.Add(Argument(
                                          nameColon: null,
                                          refKindKeyword: Token(SyntaxKind.OutKeyword),
                                          expression: DeclarationExpression(
                                              param.Type.ToTypeSyntax(),
                                              SingleVariableDesignation(
                                                  Identifier(variableName))
                                              )
                                          ));

                args.Add(IdentifierName(variableName));
            }

            switch (method.ReturnStyle)
            {
            case ReactMethod.MethodReturnStyle.Promise:
                args.Add(ObjectCreationExpression(
                             ReactTypes.ReactPromise.Construct(method.EffectiveReturnType),
                             IdentifierName(ReactNativeNames.WriterLocalName),
                             IdentifierName(ReactNativeNames.ResolveLocalName),
                             IdentifierName(ReactNativeNames.RejectLocalName)
                             ));
                break;

            case ReactMethod.MethodReturnStyle.Callback:
                args.Add(GeneratePromiseInvocation(isReject: false));
                break;

            case ReactMethod.MethodReturnStyle.TwoCallbacks:
                args.Add(GeneratePromiseInvocation(isReject: false));
                args.Add(GeneratePromiseInvocation(isReject: true));
                break;
            }

            // generates:
            //  reader.ReadArgs( ... )
            statements.Add(InvocationStatement(
                               MemberAccessExpression(ReactTypes.JSValueReader, ReactNativeNames.ReadArgsMethodName),
                               readArgsArguments));

            var methodCall = InvocationStatement(
                MemberAccessExpression(ReactNativeNames.Module, Identifier(method.Method.Name)),
                args.ToArray()
                );

            if (method.Method.ReturnsVoid)
            {
                // generate:
                //   module.MyMethod(arg0, arg1, ...)
                statements.Add(methodCall);
            }
            else
            {
                // generate:
                //   MyResult result = module.MyMethod(arg0, arg1, ...);
                statements.Add(
                    LocalDeclarationStatement(
                        method.Method.ReturnType,
                        ReactNativeNames.ResultLocalName,
                        methodCall.Expression));

                if (method.ReturnStyle == ReactMethod.MethodReturnStyle.Task)
                {
                    // generate:
                    //  ReactTaskExtension.ContinueWith(result, writer, resolve, reject);
                    statements.Add(InvocationStatement(
                                       MemberAccessExpression(ReactTypes.ReactTaskExtensions, ReactNativeNames.ContinueWith),
                                       IdentifierName(ReactNativeNames.ResultLocalName),
                                       IdentifierName(ReactNativeNames.WriterLocalName),
                                       IdentifierName(ReactNativeNames.ResolveLocalName),
                                       IdentifierName(ReactNativeNames.RejectLocalName)
                                       ));
                }
                else
                {
                    if (method.IsSynchronous)
                    {
                        // generate:
                        //   writer.WriteValue(result);
                        var writeValue = InvocationExpression(
                            MemberAccessExpression(ReactTypes.JSValueWriter, ReactNativeNames.WriteValueMethodName),
                            IdentifierName(ReactNativeNames.WriterLocalName),
                            IdentifierName(ReactNativeNames.ResultLocalName));

                        statements.Add(ExpressionStatement(writeValue));
                    }
                    else
                    {
                        // generate:
                        //   writer.WriteArgs(result);
                        var writeArgs = InvocationExpression(
                            MemberAccessExpression(ReactTypes.JSValueWriter, ReactNativeNames.WriteArgsMethodName),
                            IdentifierName(ReactNativeNames.WriterLocalName),
                            IdentifierName(ReactNativeNames.ResultLocalName));

                        // generate:
                        //   resolve(.. writeargs ..);
                        statements.Add(
                            InvocationStatement(
                                IdentifierName(ReactNativeNames.ResolveLocalName),
                                writeArgs
                                ));
                    }
                }
            }

            if (method.IsSynchronous)
            {
                // generate:
                //   moduleBuilder.AddSyncMethod(
                //    "MyMethod",
                //    (
                //    IJSValueReader reader,
                //    IJSValueWriter writer) =>
                //    {
                //       .... statements
                //    }
                return(InvocationStatement(
                           MemberAccessExpression(ReactNativeNames.ModuleBuilder, ReactNativeNames.AddSyncMethod),
                           LiteralExpression(method.Name),
                           ParenthesizedLambdaExpression(
                               parameterList:
                               ParameterList(
                                   Parameter(ReactNativeNames.ReaderLocalName)
                                   .WithType(ReactTypes.IJSValueReader.ToTypeSyntax()),
                                   Parameter(ReactNativeNames.WriterLocalName)
                                   .WithType(ReactTypes.IJSValueWriter.ToTypeSyntax())),
                               block: Block(statements),
                               expressionBody: null)));
            }
            else
            {
                // generate:
                //   moduleBuilder.AddMethod(
                //    "MyMethod",
                //    MethodReturnType.xxx,
                //    (
                //    IJSValueReader reader,
                //    IJSValueWriter writer,
                //    MethodResultCallback resolve,
                //    MethodResultCallback reject) =>
                //    {
                //       .... statements
                //    }
                return(InvocationStatement(
                           MemberAccessExpression(
                               ReactNativeNames.ModuleBuilder,
                               method.IsSynchronous ? ReactNativeNames.AddSyncMethod : ReactNativeNames.AddMethod),
                           LiteralExpression(method.Name),
                           MemberAccessExpression(
                               SyntaxKind.SimpleMemberAccessExpression,
                               ReactTypes.MethodReturnType.ToTypeSyntax(),
                               SyntaxFactory.IdentifierName(GetMethodReturnTypeFromStyle(method.ReturnStyle))),
                           ParenthesizedLambdaExpression(
                               parameterList:
                               ParameterList(
                                   Parameter(ReactNativeNames.ReaderLocalName)
                                   .WithType(ReactTypes.IJSValueReader.ToTypeSyntax()),
                                   Parameter(ReactNativeNames.WriterLocalName)
                                   .WithType(ReactTypes.IJSValueWriter.ToTypeSyntax()),
                                   Parameter(ReactNativeNames.ResolveLocalName)
                                   .WithType(ReactTypes.MethodResultCallback.ToTypeSyntax()),
                                   Parameter(ReactNativeNames.RejectLocalName)
                                   .WithType(ReactTypes.MethodResultCallback.ToTypeSyntax())),
                               block: Block(
                                   statements),
                               expressionBody: null)));
            }
        }
Ejemplo n.º 3
0
        private bool TryExtractMethodFromAttributeData(
            IMethodSymbol method,
            INamedTypeSymbol attributeType,
            bool synchronous,
            [NotNullWhen(returnValue: true)] out ReactMethod?reactMethod)
        {
            Contract.Requires(m_reactTypes != null);

            if (TryFindAttribute(method, attributeType, out var attr))
            {
                string?name = null;
                if (attr.ConstructorArguments.Length > 0)
                {
                    name = attr.ConstructorArguments[0].Value as string;
                }

                foreach (var namedArgument in attr.NamedArguments)
                {
                    switch (namedArgument.Key)
                    {
                    case nameof(ReactMethodAttribute.MethodName):
                        name = namedArgument.Value.Value as string;
                        break;

                    default:
                        var location = attr.ApplicationSyntaxReference?.SyntaxTree.GetLocation(attr.ApplicationSyntaxReference.Span);
                        m_diagnostics.Add(Diagnostic.Create(
                                              DiagnosticDescriptors.UnexpectedPropertyInAttribute,
                                              location ?? Location.None,
                                              namedArgument.Key,
                                              attr.AttributeClass?.Name));
                        reactMethod = null;
                        return(false);
                    }
                }

                ReactMethod.MethodReturnStyle?returnStyle = null;

                // Compute the effective parameters
                var         effectiveParameters = new List <IParameterSymbol>();
                ITypeSymbol effectiveReturnType = m_reactTypes.SystemVoid;
                var         parameters          = method.Parameters;
                for (int i = 0; i < parameters.Length; i++)
                {
                    var  param          = parameters[i];
                    bool skipReadingArg = false;

                    if (!synchronous) // IsSynchronous methods don't have support for the callbacks
                    {
                        if (i == parameters.Length - 1)
                        {
                            if (IsPromiseType(parameters[i].Type, out var promiseGenericType))
                            {
                                returnStyle         = ReactMethod.MethodReturnStyle.Promise;
                                effectiveReturnType = promiseGenericType;
                                skipReadingArg      = true;
                            }

                            // if the last argument is a callback, assume this is the resolve or resolve function
                            if (IsSingleArgCallback(parameters[i].Type, out var currentCallBackType))
                            {
                                ITypeSymbol?previousCallBackType = null;
                                var         isReject             = parameters.Length >= 2 &&
                                                                   IsSingleArgCallback(parameters[i - 1].Type, out previousCallBackType);
                                effectiveReturnType = isReject
                                  ? previousCallBackType !
                                  : currentCallBackType !;
                                returnStyle = isReject
                                  ? ReactMethod.MethodReturnStyle.TwoCallbacks
                                  : ReactMethod.MethodReturnStyle.Callback;

                                skipReadingArg = true;
                            }
                        }
                        else if (i == parameters.Length - 2)
                        {
                            // if the last 2 argument are callbacks, assume this is the resolve function
                            if (IsSingleArgCallback(parameters[i].Type, out var resolveCallBackType) &&
                                IsSingleArgCallback(parameters[i + 1].Type, out _))
                            {
                                effectiveReturnType = resolveCallBackType;
                                returnStyle         = ReactMethod.MethodReturnStyle.TwoCallbacks;
                                skipReadingArg      = true;
                            }
                        }
                    }

                    if (!skipReadingArg)
                    {
                        effectiveParameters.Add(param);
                    }
                }

                if (returnStyle == null)
                {
                    if (IsTaskType(method.ReturnType, out var taskReturnType))
                    {
                        if (synchronous)
                        {
                            m_diagnostics.Add(Diagnostic.Create(
                                                  DiagnosticDescriptors.MethodShouldNotReturnTaskWhenSynchronous,
                                                  method.GetLocation() ?? Location.None,
                                                  method.Name,
                                                  attr.AttributeClass?.Name));
                            reactMethod = null;
                            return(false);
                        }

                        effectiveReturnType = taskReturnType;
                        returnStyle         = ReactMethod.MethodReturnStyle.Task;
                    }
                    else if (method.ReturnsVoid)
                    {
                        returnStyle = ReactMethod.MethodReturnStyle.Void;
                    }
                    else
                    {
                        returnStyle = ReactMethod.MethodReturnStyle.ReturnValue;
                    }
                }

                name ??= method.Name;

                reactMethod = new ReactMethod(method, name, returnStyle.Value, effectiveReturnType, effectiveParameters, synchronous);
                return(true);
            }

            reactMethod = null;
            return(false);
        }
Ejemplo n.º 4
0
        internal StatementSyntax AddMethod(ReactMethod method)
        {
            var statements = new List <StatementSyntax>();

            string methodReturnType = "Void";

            var parameters = method.Method.Parameters;

            var readArgsArguments = new List <ArgumentSyntax>(parameters.Length);

            readArgsArguments.Add(Argument(IdentifierName(ReactNativeNames.ReaderLocalName)));
            var args = new List <ExpressionSyntax>(parameters.Length);

            // generates:
            //   (out ArgType0 arg0, out ArgType1 arg1, ...);
            // as well as
            //   (arg0, arg1, ... )
            for (int i = 0; i < parameters.Length; i++)
            {
                var  param          = parameters[i];
                bool skipReadingArg = false;

                if (!method.IsSynchronous) // IsSynchronous methods don't have support for the callbacks
                {
                    if (i == parameters.Length - 1)
                    {
                        if (IsPromiseType(parameters[i].Type, out var typeParameter))
                        {
                            // outArgs: Do not add an extra out parameter to be extracted from the JS side

                            // generates:
                            //    new ReactPromise<TResult>(writer, resolve, reject)
                            // to (arg0, arg1, ...) to be passed when calling the remoted method.
                            args.Add(ObjectCreationExpression(
                                         m_reactTypes.ReactPromise.Construct(typeParameter),
                                         IdentifierName(ReactNativeNames.WriterLocalName),
                                         IdentifierName(ReactNativeNames.ResolveLocalName),
                                         IdentifierName(ReactNativeNames.RejectLocalName)
                                         ));
                            methodReturnType = "Promise";
                            skipReadingArg   = true;
                        }

                        // if the last argument is a callback, assume this is the resolve or resolve function
                        if (IsSingleArgCallback(parameters[i].Type, out _))
                        {
                            var isReject = parameters.Length >= 2 &&
                                           IsSingleArgCallback(parameters[i - 1].Type, out _);
                            args.Add(GeneratePromiseInvocation(isReject));
                            methodReturnType = isReject ? "TwoCallbacks" : "Callback";
                            skipReadingArg   = true;
                        }
                    }
                    else if (i == parameters.Length - 2)
                    {
                        // if the last 2 argument is a callback, assume this is the resolve function
                        if (IsSingleArgCallback(parameters[i].Type, out _) &&
                            IsSingleArgCallback(parameters[i + 1].Type, out _))
                        {
                            args.Add(GeneratePromiseInvocation(isReject: false));
                            skipReadingArg = true;
                        }
                    }
                }

                if (!skipReadingArg)
                {
                    var variableName = "arg" + i.ToString(CultureInfo.InvariantCulture);

                    readArgsArguments.Add(Argument(
                                              nameColon: null,
                                              refKindKeyword: Token(SyntaxKind.OutKeyword),
                                              expression: DeclarationExpression(
                                                  param.Type.ToTypeSyntax(),
                                                  SingleVariableDesignation(
                                                      Identifier(variableName))
                                                  )
                                              ));

                    args.Add(IdentifierName(variableName));
                }
            }

            // generates:
            //  reader.ReadArgs( ... )
            statements.Add(InvocationStatement(
                               MemberAccessExpression(m_reactTypes.JSValueReader, ReactNativeNames.ReadArgsMethodName),
                               readArgsArguments));

            var methodCall = InvocationStatement(
                MemberAccessExpression(ReactNativeNames.Module, Identifier(method.Method.Name)),
                args.ToArray()
                );

            if (method.Method.ReturnsVoid)
            {
                // generate:
                //   module.MyMethod(arg0, arg1, ...)
                statements.Add(methodCall);
            }
            else
            {
                methodReturnType = "Callback";
                // generate:
                //   MyResult result = module.MyMethod(arg0, arg1, ...);
                statements.Add(
                    LocalDeclarationStatement(
                        method.Method.ReturnType,
                        ReactNativeNames.ResultLocalName,
                        methodCall.Expression));
                // generate:
                //   writer.WriteArgs(result);
                var writeArgs = InvocationExpression(
                    MemberAccessExpression(m_reactTypes.JSValueWriter, ReactNativeNames.WriteArgsMethodName),
                    IdentifierName(ReactNativeNames.WriterLocalName),
                    IdentifierName(ReactNativeNames.ResultLocalName));

                if (method.IsSynchronous)
                {
                    statements.Add(ExpressionStatement(writeArgs));
                }
                else
                {
                    // generate:
                    //   resolve(.. writeargs ..);
                    statements.Add(
                        InvocationStatement(
                            IdentifierName(ReactNativeNames.ResolveLocalName),
                            writeArgs
                            ));
                }
            }

            if (method.IsSynchronous)
            {
                // generate:
                //   moduleBuilder.AddSyncMethod(
                //    "MyMethod",
                //    (
                //    IJSValueReader reader,
                //    IJSValueWriter writer) =>
                //    {
                //       .... statements
                //    }
                return(InvocationStatement(
                           MemberAccessExpression(ReactNativeNames.ModuleBuilder, ReactNativeNames.AddSyncMethod),
                           LiteralExpression(method.Name),
                           ParenthesizedLambdaExpression(
                               parameterList:
                               ParameterList(
                                   Parameter(ReactNativeNames.ReaderLocalName)
                                   .WithType(m_reactTypes.IJSValueReader.ToTypeSyntax()),
                                   Parameter(ReactNativeNames.WriterLocalName)
                                   .WithType(m_reactTypes.IJSValueWriter.ToTypeSyntax())),
                               block: Block(statements),
                               expressionBody: null)));
            }
            else
            {
                // generate:
                //   moduleBuilder.AddMethod(
                //    "MyMethod",
                //    MethodReturnType.xxx,
                //    (
                //    IJSValueReader reader,
                //    IJSValueWriter writer,
                //    MethodResultCallback resolve,
                //    MethodResultCallback reject) =>
                //    {
                //       .... statements
                //    }
                return(InvocationStatement(
                           MemberAccessExpression(
                               ReactNativeNames.ModuleBuilder,
                               method.IsSynchronous ? ReactNativeNames.AddSyncMethod : ReactNativeNames.AddMethod),
                           LiteralExpression(method.Name),
                           MemberAccessExpression(
                               SyntaxKind.SimpleMemberAccessExpression,
                               m_reactTypes.MethodReturnType.ToTypeSyntax(),
                               SyntaxFactory.IdentifierName(methodReturnType)),
                           ParenthesizedLambdaExpression(
                               parameterList:
                               ParameterList(
                                   Parameter(ReactNativeNames.ReaderLocalName)
                                   .WithType(m_reactTypes.IJSValueReader.ToTypeSyntax()),
                                   Parameter(ReactNativeNames.WriterLocalName)
                                   .WithType(m_reactTypes.IJSValueWriter.ToTypeSyntax()),
                                   Parameter(ReactNativeNames.ResolveLocalName)
                                   .WithType(m_reactTypes.MethodResultCallback.ToTypeSyntax()),
                                   Parameter(ReactNativeNames.RejectLocalName)
                                   .WithType(m_reactTypes.MethodResultCallback.ToTypeSyntax())),
                               block: Block(
                                   statements),
                               expressionBody: null)));
            }
        }