コード例 #1
0
        /// <summary>
        /// If a function or property only contains a single executable block, which is a return statement, then this can be translated into a simple return
        /// statement in the C# output (as opposed to having to maintain a temporary variable for the return value in case there are various manipulations
        /// of it or error-handling or any other VBScript oddnes required)
        /// </summary>
        private bool IsSingleReturnValueStatementFunctionWithoutAnyByRefMappings(AbstractFunctionBlock functionBlock, ScopeAccessInformation scopeAccessInformation)
        {
            if (functionBlock == null)
            {
                throw new ArgumentNullException("functionBlock");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }

            var executableStatements = functionBlock.Statements.Where(s => !(s is INonExecutableCodeBlock));

            if (executableStatements.Count() != 1)
            {
                return(false);
            }

            var valueSettingStatement = executableStatements.Single() as ValueSettingStatement;

            if (valueSettingStatement == null)
            {
                return(false);
            }

            if (valueSettingStatement.ValueToSet.Tokens.Count() != 1)
            {
                return(false);
            }

            var valueToSetTokenAsNameToken = valueSettingStatement.ValueToSet.Tokens.Single() as NameToken;

            if (valueToSetTokenAsNameToken == null)
            {
                return(false);
            }

            if (_nameRewriter.GetMemberAccessTokenName(valueToSetTokenAsNameToken) != _nameRewriter.GetMemberAccessTokenName(functionBlock.Name))
            {
                return(false);
            }

            // If there is no return value (ie. it's a SUB or a LET/SET PROPERTY accessor) then this can't apply (not only can this simple single-line
            // return format not be used but a runtime error is required if the value-setting statement targets the name of a SUB)
            if (!functionBlock.HasReturnValue)
            {
                return(false);
            }

            // If any values need aliasing in order to perform this "one liner" then it won't be possible to represent it a simple one-line return, it will
            // need a try..finally setting up to create the alias(es), use where required and then map the values back over the original(s).
            scopeAccessInformation = scopeAccessInformation.Extend(functionBlock, functionBlock.Statements.ToNonNullImmutableList());
            var byRefArgumentMapper = new FuncByRefArgumentMapper(_nameRewriter, _tempNameGenerator, _logger);
            var byRefArgumentsToMap = byRefArgumentMapper.GetByRefArgumentsThatNeedRewriting(
                valueSettingStatement.Expression.ToStageTwoParserExpression(scopeAccessInformation, ExpressionReturnTypeOptions.NotSpecified, _logger.Warning),
                scopeAccessInformation,
                new NonNullImmutableList <FuncByRefMapping>()
                );

            if (byRefArgumentsToMap.Any())
            {
                return(false);
            }

            return(!valueSettingStatement.Expression.Tokens.Any(
                       t => (t is NameToken) && (_nameRewriter.GetMemberAccessTokenName(t) == _nameRewriter.GetMemberAccessTokenName(functionBlock.Name))
                       ));
        }
コード例 #2
0
        public TranslationResult Translate(AbstractFunctionBlock functionBlock, ScopeAccessInformation scopeAccessInformation, int indentationDepth)
        {
            if (functionBlock == null)
            {
                throw new ArgumentNullException("functionBlock");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            var isSingleReturnValueStatementFunction = IsSingleReturnValueStatementFunctionWithoutAnyByRefMappings(functionBlock, scopeAccessInformation);
            var returnValueName = functionBlock.HasReturnValue
                                ? _tempNameGenerator(new CSharpName("retVal"), scopeAccessInformation.Extend(functionBlock, functionBlock.Statements.ToNonNullImmutableList())) // Ensure call Extend so that ScopeDefiningParent is the current function
                                : null;
            var translationResult = TranslationResult.Empty.Add(
                TranslateFunctionHeader(
                    functionBlock,
                    scopeAccessInformation,
                    returnValueName,
                    indentationDepth
                    )
                );
            CSharpName errorRegistrationTokenIfAny;

            if (functionBlock.Statements.ToNonNullImmutableList().DoesScopeContainOnErrorResumeNext())
            {
                errorRegistrationTokenIfAny = _tempNameGenerator(new CSharpName("errOn"), scopeAccessInformation);
                translationResult           = translationResult.Add(new TranslatedStatement(
                                                                        string.Format(
                                                                            "var {0} = {1}.GETERRORTRAPPINGTOKEN();",
                                                                            errorRegistrationTokenIfAny.Name,
                                                                            _supportRefName.Name
                                                                            ),
                                                                        indentationDepth + 1,
                                                                        functionBlock.Name.LineIndex
                                                                        ));
            }
            else
            {
                errorRegistrationTokenIfAny = null;
            }
            translationResult = translationResult.Add(
                Translate(
                    functionBlock.Statements.ToNonNullImmutableList(),
                    scopeAccessInformation.Extend(
                        functionBlock,
                        returnValueName,
                        errorRegistrationTokenIfAny,
                        functionBlock.Statements.ToNonNullImmutableList()
                        ),
                    isSingleReturnValueStatementFunction,
                    indentationDepth + 1
                    )
                );
            var lineIndexForClosingScaffolding = translationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource;

            if (errorRegistrationTokenIfAny != null)
            {
                translationResult = translationResult.Add(new TranslatedStatement(
                                                              string.Format(
                                                                  "{0}.RELEASEERRORTRAPPINGTOKEN({1});",
                                                                  _supportRefName.Name,
                                                                  errorRegistrationTokenIfAny.Name
                                                                  ),
                                                              indentationDepth + 1,
                                                              lineIndexForClosingScaffolding
                                                              ));
            }
            if (functionBlock.HasReturnValue && !isSingleReturnValueStatementFunction)
            {
                // If this is an empty function then just render "return null" (TranslateFunctionHeader won't declare the return value reference)
                translationResult = translationResult
                                    .Add(new TranslatedStatement(
                                             string.Format(
                                                 "return {0};",
                                                 functionBlock.Statements.Any() ? returnValueName.Name : "null"
                                                 ),
                                             indentationDepth + 1,
                                             lineIndexForClosingScaffolding
                                             ));
            }
            return(translationResult.Add(
                       new TranslatedStatement("}", indentationDepth, lineIndexForClosingScaffolding)
                       ));
        }
コード例 #3
0
        private IEnumerable <TranslatedStatement> TranslateFunctionHeader(AbstractFunctionBlock functionBlock, ScopeAccessInformation scopeAccessInformation, CSharpName returnValueNameIfAny, int indentationDepth)
        {
            if (functionBlock == null)
            {
                throw new ArgumentNullException("functionBlock");
            }
            if (functionBlock.HasReturnValue && (returnValueNameIfAny == null))
            {
                throw new ArgumentException("returnValueNameIfAny must not be null if functionBlock.HasReturnValue is true");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            var content = new StringBuilder();

            content.Append(functionBlock.IsPublic ? "public" : "private");
            content.Append(" ");
            content.Append(functionBlock.HasReturnValue ? "object" : "void");
            content.Append(" ");
            content.Append(_nameRewriter.GetMemberAccessTokenName(functionBlock.Name));
            content.Append("(");
            var numberOfParameters = functionBlock.Parameters.Count();

            for (var index = 0; index < numberOfParameters; index++)
            {
                var parameter = functionBlock.Parameters.ElementAt(index);
                if (parameter.ByRef)
                {
                    content.Append("ref ");
                }
                content.Append("object ");
                content.Append(_nameRewriter.GetMemberAccessTokenName(parameter.Name));
                if (index < (numberOfParameters - 1))
                {
                    content.Append(", ");
                }
            }
            content.Append(")");

            var translatedStatements = new List <TranslatedStatement>();

            if (functionBlock.IsDefault)
            {
                translatedStatements.Add(new TranslatedStatement("[" + typeof(IsDefault).FullName + "]", indentationDepth, functionBlock.Name.LineIndex));
            }
            var property = functionBlock as PropertyBlock;

            if (property != null)
            {
                // All property blocks that are translated into C# methods needs to be decorated with the [TranslatedProperty] attribute. The [TranslatedProperty] attribute
                // was originally intended only for indexed properties (which C# can only support one of per class but VBScript classes can have as many as they like) but
                // a class with an indexed property will be emitted to inherit from TranslatedPropertyIReflectImplementation, which will try to identify properties based
                // upon the presence of [TranslatedProperty] attributes - if some (ie. indexed properties) have these and others (non-indexed properties) don't then it
                // will result in runtime failures. So we could apply the attribute to indexed properties and all properties within classes that have at least one
                // indexed property but that feels like complications for little benefit so I think it's easier to just put it on ALL from-property methods.
                translatedStatements.Add(
                    new TranslatedStatement(
                        string.Format(
                            "[TranslatedProperty({0})]",                             // Note: Safe to assume that using statements are present for the namespace that contains TranslatedProperty
                            property.Name.Content.ToLiteral()
                            ),
                        indentationDepth,
                        functionBlock.Name.LineIndex
                        )
                    );
            }
            translatedStatements.Add(new TranslatedStatement(content.ToString(), indentationDepth, functionBlock.Name.LineIndex));
            translatedStatements.Add(new TranslatedStatement("{", indentationDepth, functionBlock.Name.LineIndex));
            if (functionBlock.HasReturnValue && functionBlock.Statements.Any() && !IsSingleReturnValueStatementFunctionWithoutAnyByRefMappings(functionBlock, scopeAccessInformation))
            {
                translatedStatements.Add(new TranslatedStatement(
                                             base.TranslateVariableInitialisation(
                                                 new VariableDeclaration(
                                                     new DoNotRenameNameToken(
                                                         returnValueNameIfAny.Name,
                                                         functionBlock.Name.LineIndex
                                                         ),
                                                     VariableDeclarationScopeOptions.Private,
                                                     null    // Not declared as an array
                                                     ),
                                                 ScopeLocationOptions.WithinFunctionOrPropertyOrWith
                                                 ),
                                             indentationDepth + 1,
                                             functionBlock.Name.LineIndex
                                             ));
            }
            return(translatedStatements);
        }