public static ScopeAccessInformation AddStructureExitPoints(
            this ScopeAccessInformation scopeInformation,
            CSharpName structureExitFlagNameIfAny,
            ScopeAccessInformation.ExitableNonScopeDefiningConstructOptions structureExitType)
        {
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (!Enum.IsDefined(typeof(ScopeAccessInformation.ExitableNonScopeDefiningConstructOptions), structureExitType))
            {
                throw new ArgumentOutOfRangeException("structureExitType");
            }

            return(new ScopeAccessInformation(
                       scopeInformation.Parent,
                       scopeInformation.ScopeDefiningParent,
                       scopeInformation.ParentReturnValueNameIfAny,
                       scopeInformation.ErrorRegistrationTokenIfAny,
                       scopeInformation.DirectedWithReferenceIfAny,
                       scopeInformation.ExternalDependencies,
                       scopeInformation.Classes,
                       scopeInformation.Functions,
                       scopeInformation.Properties,
                       scopeInformation.Constants,
                       scopeInformation.Variables,
                       scopeInformation.StructureExitPoints
                       .Add(new ScopeAccessInformation.ExitableNonScopeDefiningConstructDetails(
                                structureExitFlagNameIfAny,
                                structureExitType
                                ))
                       ));
        }
        public static ScopeAccessInformation SetParent(this ScopeAccessInformation scopeInformation, IHaveNestedContent parent)
        {
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }

            if ((parent != scopeInformation.ScopeDefiningParent) && !scopeInformation.ScopeDefiningParent.GetAllNestedBlocks().Contains(parent))
            {
                // The parent must either be the current ScopeDefiningParent or be one of the descendant blocks, otherwise the structure will be invalid
                throw new ArgumentException("The parent must either be the current ScopeDefiningParent or be one of the descendant blocks");
            }
            return(new ScopeAccessInformation(
                       parent,
                       scopeInformation.ScopeDefiningParent,
                       scopeInformation.ParentReturnValueNameIfAny,
                       scopeInformation.ErrorRegistrationTokenIfAny,
                       scopeInformation.DirectedWithReferenceIfAny,
                       scopeInformation.ExternalDependencies,
                       scopeInformation.Classes,
                       scopeInformation.Functions,
                       scopeInformation.Properties,
                       scopeInformation.Constants,
                       scopeInformation.Variables,
                       scopeInformation.StructureExitPoints
                       ));
        }
        private static TranslatedStatementContentDetails Translate(
            ITranslateIndividualStatements statementTranslator,
            Statement statement,
            ScopeAccessInformation scopeAccessInformation,
            ExpressionReturnTypeOptions returnRequirements,
            Action <string> warningLogger)
        {
            if (statementTranslator == null)
            {
                throw new ArgumentNullException("statementTranslator");
            }
            if (statement == null)
            {
                throw new ArgumentNullException("statement");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (!Enum.IsDefined(typeof(ExpressionReturnTypeOptions), returnRequirements))
            {
                throw new ArgumentOutOfRangeException("returnRequirements");
            }
            if (warningLogger == null)
            {
                throw new ArgumentNullException("warningLogger");
            }

            return(statementTranslator.Translate(
                       statement.ToStageTwoParserExpression(scopeAccessInformation, returnRequirements, warningLogger),
                       scopeAccessInformation,
                       returnRequirements
                       ));
        }
        public static ScopeAccessInformation ExtendVariables(this ScopeAccessInformation scopeInformation, NonNullImmutableList <ScopedNameToken> variables)
        {
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (variables == null)
            {
                throw new ArgumentNullException("variables");
            }

            return(new ScopeAccessInformation(
                       scopeInformation.Parent,
                       scopeInformation.ScopeDefiningParent,
                       scopeInformation.ParentReturnValueNameIfAny,
                       scopeInformation.ErrorRegistrationTokenIfAny,
                       scopeInformation.DirectedWithReferenceIfAny,
                       scopeInformation.ExternalDependencies,
                       scopeInformation.Classes,
                       scopeInformation.Functions,
                       scopeInformation.Properties,
                       scopeInformation.Constants,
                       scopeInformation.Variables.AddRange(variables),
                       scopeInformation.StructureExitPoints
                       ));
        }
        private static ScopeAccessInformation AddOutermostScopeVariable(ScopeAccessInformation scopeAccessInformation, string name, int lineIndex)
        {
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentException("Null/blank name specified");
            }
            if (lineIndex < 0)
            {
                throw new ArgumentOutOfRangeException("lineIndex");
            }

            return(new ScopeAccessInformation(
                       scopeAccessInformation.Parent,
                       scopeAccessInformation.ScopeDefiningParent,
                       scopeAccessInformation.ParentReturnValueNameIfAny,
                       scopeAccessInformation.ErrorRegistrationTokenIfAny,
                       scopeAccessInformation.DirectedWithReferenceIfAny,
                       scopeAccessInformation.ExternalDependencies,
                       scopeAccessInformation.Classes,
                       scopeAccessInformation.Functions,
                       scopeAccessInformation.Properties,
                       scopeAccessInformation.Constants,
                       scopeAccessInformation.Variables.Add(new ScopedNameToken(
                                                                name,
                                                                lineIndex,
                                                                VBScriptTranslator.LegacyParser.CodeBlocks.Basic.ScopeLocationOptions.OutermostScope
                                                                )),
                       scopeAccessInformation.StructureExitPoints
                       ));
        }
        /// <summary>
        /// This will never return null, it will raise an exception if unable to satisfy the request (this includes the case of a null expression reference)
        /// </summary>
        public static TranslatedStatementContentDetails Translate(
            this ITranslateIndividualStatements statementTranslator,
            Expression expression,
            ScopeAccessInformation scopeAccessInformation,
            ExpressionReturnTypeOptions returnRequirements,
            Action <string> warningLogger)
        {
            if (statementTranslator == null)
            {
                throw new ArgumentNullException("statementTranslator");
            }
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (!Enum.IsDefined(typeof(ExpressionReturnTypeOptions), returnRequirements))
            {
                throw new ArgumentOutOfRangeException("returnRequirements");
            }
            if (warningLogger == null)
            {
                throw new ArgumentNullException("warningLogger");
            }

            return(Translate(statementTranslator, (Statement)expression, scopeAccessInformation, returnRequirements, warningLogger));
        }
        /// <summary>
        /// This will never return null, it will raise an exception if unable to satisfy the request (this includes the case of a null statement reference)
        /// </summary>
        public static TranslatedStatementContentDetails Translate(
            this ITranslateIndividualStatements statementTranslator,
            Statement statement,
            ScopeAccessInformation scopeAccessInformation,
            Action <string> warningLogger)
        {
            if (statementTranslator == null)
            {
                throw new ArgumentNullException("statementTranslator");
            }
            if (statement == null)
            {
                throw new ArgumentNullException("statement");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (warningLogger == null)
            {
                throw new ArgumentNullException("warningLogger");
            }

            return(Translate(statementTranslator, statement, scopeAccessInformation, ExpressionReturnTypeOptions.None, warningLogger));
        }
        // TODO:
        // - Check "If callExpressionSegment.ZeroArgumentBracketsPresent" TODOs in ValueSettingStatementsTranslator - eg. "a() = 1" vs "a = 1"
        // - TODO: Constant()
        // - TODO: Property() where not within that property's body
        // - TODO: Property() where within that property's body
        // - TODO: Variations where property body is GET and where it isn't

        /// <summary>
        /// This will return an empty ScopeAccessInformation that indicates an outermost scope without any statements - this does not describe a real scenario
        /// but allows us to set up data to exercise the code that the tests here are targetting
        /// </summary>
        private static ScopeAccessInformation GetEmptyScopeAccessInformation()
        {
            return(ScopeAccessInformation.FromOutermostScope(
                       new CSharpName("UnitTestOutermostScope"),
                       new NonNullImmutableList <VBScriptTranslator.LegacyParser.CodeBlocks.ICodeBlock>(),
                       new NonNullImmutableList <NameToken>()
                       ));
        }
コード例 #9
0
        private NonNullImmutableList <FuncByRefMapping> GetByRefArgumentsThatNeedRewriting(
            Expression expression,
            ScopeAccessInformation scopeAccessInformation,
            NonNullImmutableList <FuncByRefMapping> rewrittenReferences,
            bool expressionIsDirectArgumentValue)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (rewrittenReferences == null)
            {
                throw new ArgumentNullException("rewrittenReferences");
            }

            // If we're not within a function or property then there's no work to do since there can be no ByRef arguments in the parent construct to worry about
            var containingFunctionOrProperty = scopeAccessInformation.ScopeDefiningParent as VBScriptTranslator.LegacyParser.CodeBlocks.Basic.AbstractFunctionBlock;

            if (containingFunctionOrProperty == null)
            {
                return(rewrittenReferences);
            }

            // If the containing function/property doesn't have any ByRef arguments then, again, there is nothing to worry about
            var byRefArguments = containingFunctionOrProperty.Parameters.Where(p => p.ByRef).Select(p => p.Name).ToNonNullImmutableList();

            if (!byRefArguments.Any())
            {
                return(rewrittenReferences);
            }

            // By-ref arguments of the containing function need to be aliased when referenced within a lambda (such as when processed with HANDLEERROR) - but, depending
            // upon how they are used, this aliasing can be a one-way affair. If "a" is a by-ref function argument and the lambda-wrapped statement is "F1(a + 1)" then
            // it's not possible for F1 to change the value of "a" and so the alias value does not need to be copied back onto "a" after the lambda has been called
            // (this is a read-only by-ref argument mapping). However, if the lambda-wrapped statement is "F1(a)" then it IS possible for F1 to change the value
            // of "a" - in this case the alias value must be copied over "a" after the lamdba completes (even if it errors, in case the value of the F1 argument
            // was changed before the error occurred). The GetByRefArgumentsThatNeedRewriting below examines arguments passed in to functions to determiner whether
            // any mappings should be read-only or read-write but it will miss an important case - if the "expression" at this point is a simple reference to a
            // by-ref argument then it may be the left hand side of a value-setting statement and that will definitely need to be a read-write alias mapping
            // (otherwise the alias value would be updated within the lambda, after the value-setting was executed, but it wouldn't be mapped back on to the
            // by-ref argument, which would defeat the point of the value-setting statement).
            if (scopeAccessInformation.ErrorRegistrationTokenIfAny != null)
            {
                var nameTokenTargetIfApplicable = TryToGetExpressionAsSingleNameToken(expression);
                if ((nameTokenTargetIfApplicable != null) && byRefArguments.Any(a => a.Content.Equals(nameTokenTargetIfApplicable.Content, StringComparison.OrdinalIgnoreCase)))
                {
                    expressionIsDirectArgumentValue = true;
                }
            }

            return(GetByRefArgumentsThatNeedRewriting(expression.Segments, byRefArguments, scopeAccessInformation, rewrittenReferences, expressionIsDirectArgumentValue));
        }
コード例 #10
0
        private TranslationResult TryToTranslateValueSettingStatementAsSimpleFunctionValueReturner(
            TranslationResult translationResult,
            ICodeBlock block,
            ScopeAccessInformation scopeAccessInformation,
            int indentationDepth)
        {
            if (translationResult == null)
            {
                throw new ArgumentNullException("translationResult");
            }
            if (block == null)
            {
                throw new ArgumentNullException("block");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            var valueSettingStatement = block as ValueSettingStatement;

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

            var translatedStatementContentDetails = _statementTranslator.Translate(
                valueSettingStatement.Expression,
                scopeAccessInformation,
                (valueSettingStatement.ValueSetType == ValueSettingStatement.ValueSetTypeOptions.Set)
                                        ? ExpressionReturnTypeOptions.Reference
                                        : ExpressionReturnTypeOptions.Value,
                _logger.Warning
                );
            var undeclaredVariables = translatedStatementContentDetails.VariablesAccessed
                                      .Where(v => !scopeAccessInformation.IsDeclaredReference(v, _nameRewriter));

            foreach (var undeclaredVariable in undeclaredVariables)
            {
                _logger.Warning("Undeclared variable: \"" + undeclaredVariable.Content + "\" (line " + (undeclaredVariable.LineIndex + 1) + ")");
            }

            return(translationResult
                   .Add(new TranslatedStatement(
                            "return " + translatedStatementContentDetails.TranslatedContent + ";",
                            indentationDepth,
                            valueSettingStatement.Expression.Tokens.First().LineIndex
                            ))
                   .AddUndeclaredVariables(undeclaredVariables));
        }
        public void UndeclaredSetTargetsWithinFunctionsAreScopeRestrictedToThatFunction()
        {
            // The ValueSettingStatementsTranslator wasn't using the ScopeAccessInformation's GetNameOfTargetContainerIfAnyRequired extension method and
            // was incorrectly applying the logic that it should have gotten for free by using that method - if an undeclared variable was being accessed
            // within a method (for the to-set target) then it was being mapped back to the "Environment References" class instead of being treated as
            // local to the function.
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("a", 0)
            });
            var expressionToSetTo = new Expression(new IToken[]
            {
                new NumericValueToken("1", 0)
            });
            var valueSettingStatement = new ValueSettingStatement(
                expressionToSet,
                expressionToSetTo,
                ValueSettingStatement.ValueSetTypeOptions.Let
                );

            var containingFunction = new FunctionBlock(
                isPublic: true,
                isDefault: false,
                name: new NameToken("F1", 0),
                parameters: new AbstractFunctionBlock.Parameter[0],
                statements: new[] { valueSettingStatement }
                );

            var expected = new TranslatedStatementContentDetails(
                "a = (Int16)1",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("a", 0) })
                );
            var scopeAccessInformation = GetEmptyScopeAccessInformation();

            scopeAccessInformation = new ScopeAccessInformation(
                containingFunction,   // parent
                containingFunction,   // scopeDefiningParent
                new CSharpName("F1"), // parentReturnValueName
                scopeAccessInformation.ErrorRegistrationTokenIfAny,
                scopeAccessInformation.DirectedWithReferenceIfAny,
                scopeAccessInformation.ExternalDependencies,
                scopeAccessInformation.Classes,
                scopeAccessInformation.Functions.Add(new ScopedNameToken("F1", 0, ScopeLocationOptions.WithinFunctionOrPropertyOrWith)),
                scopeAccessInformation.Properties,
                scopeAccessInformation.Constants,
                scopeAccessInformation.Variables,
                scopeAccessInformation.StructureExitPoints
                );
            var actual = GetDefaultValueSettingStatementTranslator().Translate(valueSettingStatement, scopeAccessInformation);

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
コード例 #12
0
        private CSharpName GetEarlyExitNameIfRequired(ForEachBlock forEachBlock, ScopeAccessInformation scopeAccessInformation)
        {
            if (forEachBlock == null)
            {
                throw new ArgumentNullException("forEachBlock");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }

            if (!forEachBlock.ContainsLoopThatContainsMismatchedExitThatMustBeHandledAtThisLevel())
            {
                return(null);
            }

            return(_tempNameGenerator(new CSharpName("exitFor"), scopeAccessInformation));
        }
コード例 #13
0
        /// <summary>
        /// If we're within a function that has a ByRef argument a0 and that argument is passed into another function, where that argument is ByRef, then we will encounter
        /// problems since the mechanism for dealing with ByRef arguments is to generate an arguments handler with lambdas for updating the variable after the call completes,
        /// but "ref" arguments may not be included in lambdas in C#. So the a0 reference must be copied into a temporary variable that is updated after the second function
        /// call ends (even if it errors, since the argument may have been altered before the error). This function will identify which variables in an expression must be
        /// rewritten in this manner. Similar logic must be applied if a0 is referenced within error-trapped code since the translated code in that case will also be executed
        /// in a lambda - in that case, however, a0 will not need to be overwritten by the alias after the work completes, it is a "read only" mapping. If the specified
        /// scopeAccessInformation reference does not indicate that the expression is within a function (or property) then there will be work to perform.
        /// </summary>
        public NonNullImmutableList <FuncByRefMapping> GetByRefArgumentsThatNeedRewriting(
            Expression expression,
            ScopeAccessInformation scopeAccessInformation,
            NonNullImmutableList <FuncByRefMapping> rewrittenReferences)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (rewrittenReferences == null)
            {
                throw new ArgumentNullException("rewrittenReferences");
            }

            return(GetByRefArgumentsThatNeedRewriting(expression, scopeAccessInformation, rewrittenReferences, expressionIsDirectArgumentValue: false));
        }
        /// <summary>
        /// If the parent is scope-defining then both the parent and scopeDefiningParent references will be set to it, this is a convenience method to
        /// save having to specify it explicitly for both (for cases where the parent scope - if any - does not have a return value)
        /// </summary>
        public static ScopeAccessInformation Extend(
            this ScopeAccessInformation scopeInformation,
            IDefineScope parent,
            NonNullImmutableList <ICodeBlock> blocks)
        {
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }
            if (blocks == null)
            {
                throw new ArgumentNullException("blocks");
            }

            return(Extend(scopeInformation, parent, null, null, blocks));
        }
コード例 #15
0
        /// <summary>
        /// This will never return null, it will raise an exception if unable to satisfy the request (this includes the case of a null statement reference)
        /// </summary>
        public static VBScriptTranslator.StageTwoParser.ExpressionParsing.Expression ToStageTwoParserExpression(
            this VBScriptTranslator.LegacyParser.CodeBlocks.Basic.Statement statement,
            ScopeAccessInformation scopeAccessInformation,
            ExpressionReturnTypeOptions returnRequirements,
            Action <string> warningLogger)
        {
            if (statement == null)
            {
                throw new ArgumentNullException("statement");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (!Enum.IsDefined(typeof(ExpressionReturnTypeOptions), returnRequirements))
            {
                throw new ArgumentOutOfRangeException("returnRequirements");
            }
            if (warningLogger == null)
            {
                throw new ArgumentNullException("warningLogger");
            }

            // The BracketStandardisedTokens property should only be used if this is a non-value-returning statement (eg. "Test" or "Test 1"
            // or "Test(a)", which would be translated into "Test()", "Test(1)" or "Test((a))", respectively) since that is the only time
            // that brackets appear "optional". When this statement's return value is considered (eg. the "Test(1)" in "a = Test(1)"), the
            // brackets will already be in a format in valid VBScript that matches what would be expected in C#.
            var expressions =
                VBScriptTranslator.StageTwoParser.ExpressionParsing.ExpressionGenerator.Generate(
                    (returnRequirements == ExpressionReturnTypeOptions.None) ? statement.GetBracketStandardisedTokens(scopeAccessInformation.DirectedWithReferenceIfAny?.AsToken()) : statement.Tokens,
                    (scopeAccessInformation.DirectedWithReferenceIfAny == null) ? null : scopeAccessInformation.DirectedWithReferenceIfAny.AsToken(),
                    warningLogger
                    ).ToArray();

            if (expressions.Length != 1)
            {
                throw new ArgumentException("Statement translation should always result in a single expression being generated");
            }
            return(expressions[0]);
        }
コード例 #16
0
        private TranslationResult Translate(
            NonNullImmutableList <ICodeBlock> blocks,
            ScopeAccessInformation scopeAccessInformation,
            bool isSingleReturnValueStatementFunction,
            int indentationDepth)
        {
            if (blocks == null)
            {
                throw new ArgumentNullException("block");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            NonNullImmutableList <BlockTranslationAttempter> blockTranslators;

            if (isSingleReturnValueStatementFunction)
            {
                blockTranslators = new NonNullImmutableList <BlockTranslationAttempter>()
                                   .Add(TryToTranslateValueSettingStatementAsSimpleFunctionValueReturner)
                                   .Add(base.TryToTranslateBlankLine)
                                   .Add(base.TryToTranslateComment);
            }
            else
            {
                blockTranslators = base.GetWithinFunctionBlockTranslators();
            }

            return(base.TranslateCommon(
                       blockTranslators,
                       blocks,
                       scopeAccessInformation,
                       indentationDepth
                       ));
        }
        /// <summary>
        /// If the parent is scope-defining then both the parent and scopeDefiningParent references will be set to it, this is a convenience method to
        /// save having to specify it explicitly for both
        /// </summary>
        public static ScopeAccessInformation Extend(
            this ScopeAccessInformation scopeInformation,
            IDefineScope parent,
            CSharpName parentReturnValueNameIfAny,
            CSharpName errorRegistrationTokenIfAny,
            NonNullImmutableList <ICodeBlock> blocks)
        {
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }
            if (blocks == null)
            {
                throw new ArgumentNullException("blocks");
            }

            return(Extend(scopeInformation, parent, parent, parentReturnValueNameIfAny, errorRegistrationTokenIfAny, blocks));
        }
コード例 #18
0
        public static ScopeAccessInformation SetErrorRegistrationToken(this ScopeAccessInformation scopeAccessInformation, CSharpName errorRegistrationTokenIfAny)
        {
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }

            return(new ScopeAccessInformation(
                       scopeAccessInformation.Parent,
                       scopeAccessInformation.ScopeDefiningParent,
                       scopeAccessInformation.ParentReturnValueNameIfAny,
                       errorRegistrationTokenIfAny,
                       scopeAccessInformation.DirectedWithReferenceIfAny,
                       scopeAccessInformation.ExternalDependencies,
                       scopeAccessInformation.Classes,
                       scopeAccessInformation.Functions,
                       scopeAccessInformation.Properties,
                       scopeAccessInformation.Constants,
                       scopeAccessInformation.Variables,
                       scopeAccessInformation.StructureExitPoints
                       ));
        }
コード例 #19
0
        private TranslationResult Translate(
            NonNullImmutableList <ICodeBlock> blocks,
            ScopeAccessInformation scopeAccessInformation,
            bool supportsExit,
            CSharpName earlyExitNameIfAny,
            int indentationDepth)
        {
            if (blocks == null)
            {
                throw new ArgumentNullException("block");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (!supportsExit && (earlyExitNameIfAny != null))
            {
                throw new ArgumentException("earlyExitNameIfAny should always be null if supportsExit is false");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            // Add a StructureExitPoint entry for the current loop so that the "early-exit" logic described in the Translate method above is possible
            if (supportsExit)
            {
                scopeAccessInformation = scopeAccessInformation.AddStructureExitPoints(
                    earlyExitNameIfAny,
                    ScopeAccessInformation.ExitableNonScopeDefiningConstructOptions.Do
                    );
            }
            return(base.TranslateCommon(
                       base.GetWithinFunctionBlockTranslators(),
                       blocks,
                       scopeAccessInformation,
                       indentationDepth
                       ));
        }
コード例 #20
0
        public static bool IsDeclaredReference(this ScopeAccessInformation scopeInformation, NameToken target, VBScriptNameRewriter nameRewriter)
        {
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (target == null)
            {
                throw new ArgumentNullException("target");
            }
            if (nameRewriter == null)
            {
                throw new ArgumentNullException("nameRewriter");
            }

            // TargetCurrentClassToken indicates a "Me" reference, which is always valid - TODO: Move this into TryToGetDeclaredReferenceDetails?
            if (target is TargetCurrentClassToken)
            {
                return(true);
            }

            return(TryToGetDeclaredReferenceDetails(scopeInformation, target, nameRewriter) != null);
        }
コード例 #21
0
        public TranslationResult Translate(ForEachBlock forEachBlock, ScopeAccessInformation scopeAccessInformation, int indentationDepth)
        {
            if (forEachBlock == null)
            {
                throw new ArgumentNullException("forEachBlock");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            // The approach here is to get an IEnumerator reference and then loop over it in a "while (true)" loop, exiting when there are no more items. It would
            // feel more natural to use a C# foreach loop but the loop variable may not be restricted in scope to the loop (in fact, in VBScript this is very unlikely)
            // and so a "Type and identifier are both required in a foreach statement" compile error would result - "foreach (i in a)" is not valid, it must be of the
            // form "foreach (var i in a)" which only works if "i" is limited in scope to that loop. The "while (true)" structure also works better when error-trapping
            // may be enabled since the call-MoveNext-and-set-loop-variable-to-enumerator-Current-value-if-not-reached-end-of-data can be bypassed entirely if an error
            // was caught while evaluating the enumerator (in which case the loop should be processed once but the loop variable not set).

            // Note: The looped-over content must be of type "Reference" since VBScript won't enumerate over strings, for example, whereas C# would be happy to.
            // However, to make the output marginally easier to read, the ENUMERABLE method will deal with this logic and so the ExpressionReturnTypeOptions
            // value passed to the statement translator is "NotSpecified".

            var loopSourceContent = _statementTranslator.Translate(forEachBlock.LoopSrc, scopeAccessInformation, ExpressionReturnTypeOptions.NotSpecified, _logger.Warning);
            var undeclaredVariablesInLoopSourceContent = loopSourceContent.GetUndeclaredVariablesAccessed(scopeAccessInformation, _nameRewriter);

            foreach (var undeclaredVariable in undeclaredVariablesInLoopSourceContent)
            {
                _logger.Warning("Undeclared variable: \"" + undeclaredVariable.Content + "\" (line " + (undeclaredVariable.LineIndex + 1) + ")");
            }
            var translationResult = TranslationResult.Empty.AddUndeclaredVariables(undeclaredVariablesInLoopSourceContent);
            var enumerationContentVariableName  = _tempNameGenerator(new CSharpName("enumerationContent"), scopeAccessInformation);
            var enumeratorInitialisationContent = string.Format(
                "{0} = {1}.ENUMERABLE({2}).GetEnumerator();",
                enumerationContentVariableName.Name,
                _supportRefName.Name,
                loopSourceContent.TranslatedContent
                );

            if (!scopeAccessInformation.IsDeclaredReference(forEachBlock.LoopVar, _nameRewriter))
            {
                translationResult = translationResult.AddUndeclaredVariables(new[] { forEachBlock.LoopVar });
            }
            var rewrittenLoopVarName   = _nameRewriter.GetMemberAccessTokenName(forEachBlock.LoopVar);
            var loopVarTargetContainer = scopeAccessInformation.GetNameOfTargetContainerIfAnyRequired(forEachBlock.LoopVar, _envRefName, _outerRefName, _nameRewriter);

            if (loopVarTargetContainer != null)
            {
                rewrittenLoopVarName = loopVarTargetContainer.Name + "." + rewrittenLoopVarName;
            }
            if (scopeAccessInformation.ErrorRegistrationTokenIfAny == null)
            {
                translationResult = translationResult.Add(new TranslatedStatement(
                                                              "var " + enumeratorInitialisationContent,
                                                              indentationDepth,
                                                              forEachBlock.LoopVar.LineIndex
                                                              ));
            }
            else
            {
                // If ON ERROR RESUME NEXT wraps a FOR EACH loop and there is an error in evaluating the enumerator, then the loop will be entered once. The
                // loop variable will not be altered - eg.
                //
                //   On Error Resume Next
                //   Dim i: For Each i in "12"
                //     WScript.Echo "We're in the loop! i is " & TypeName(i)
                //   Next
                //
                // VBScript can not enumerate a string, so the loop errors. But the ON ERROR RESUME NEXT causes the loop to be processed - only once. This is
                // approached by calling _.ENUMERABLE before the loop construct, setting a temporary variable to be the returned enumerable inside a call to
                // "HANDLEERROR" - meaning that it will be left as null if it fails. Null values are replaced with a single-item array, where the element is
                // the current value of the loop variable - so its value is not altered when the loop is entered.
                // - Note: The "error-trapping" wrapper functions ("HANDLEERROR") are used around code that MAY have error-trapping enabled (there are cases
                //   where we can't know at translation time whether it will have been turned off with ON ERROR GOTO 0 or not - and there are probably some
                //   cases that could be picked up if the translation process was more intelligent). If the above example had an ON ERROR GOTO 0 between the
                //   ON ERROR RESUME NEXT and the FOR EACH loop then the error (about trying to enumerate a string) would be raised and so the FOR EACH would
                //   not be entered, and so the single-element "fallback array" would never come to exist. If errors WERE still being captured, then the
                //   translated FOR EACH loop WOULD be entered and enumerated through once (without the value of the loop variable being altered, as
                //   is consistent with VBScript)
                translationResult = translationResult
                                    .Add(new TranslatedStatement(
                                             string.Format(
                                                 "IEnumerator {0} = null;",
                                                 enumerationContentVariableName.Name
                                                 ),
                                             indentationDepth,
                                             forEachBlock.LoopVar.LineIndex
                                             ))
                                    .Add(new TranslatedStatement(
                                             string.Format(
                                                 "{0}.HANDLEERROR({1}, () => {{",
                                                 _supportRefName.Name,
                                                 scopeAccessInformation.ErrorRegistrationTokenIfAny.Name
                                                 ),
                                             indentationDepth,
                                             forEachBlock.LoopVar.LineIndex
                                             ))
                                    .Add(new TranslatedStatement(
                                             enumeratorInitialisationContent,
                                             indentationDepth + 1,
                                             forEachBlock.LoopVar.LineIndex
                                             ))
                                    .Add(new TranslatedStatement("});", indentationDepth, forEachBlock.LoopVar.LineIndex));
            }
            translationResult = translationResult
                                .Add(new TranslatedStatement("while (true)", indentationDepth, forEachBlock.LoopVar.LineIndex))
                                .Add(new TranslatedStatement("{", indentationDepth, forEachBlock.LoopVar.LineIndex));
            if (scopeAccessInformation.ErrorRegistrationTokenIfAny != null)
            {
                // If error-trapping is enabled and an error was indeed trapped while trying evaluate the enumerator, then the enumerator will be null.
                // In this case, the loop should be executed once but the loop variable not set to anything. When this happens, there is no point trying
                // to call MoveNext (since the enumerator is null) and the loop-variable-setting should be skipped. So an is-null check is wrapper around
                // that work. If error-trapping is not enabled then this check is not required and a level of nesting in the translated output can be
                // avoided.
                translationResult = translationResult
                                    .Add(new TranslatedStatement(
                                             string.Format(
                                                 "if ({0} != null)",
                                                 enumerationContentVariableName.Name
                                                 ),
                                             indentationDepth + 1,
                                             forEachBlock.LoopVar.LineIndex
                                             ))
                                    .Add(new TranslatedStatement("{", indentationDepth + 1, forEachBlock.LoopVar.LineIndex));
                indentationDepth++;
            }
            translationResult = translationResult
                                .Add(new TranslatedStatement(string.Format(
                                                                 "if (!{0}.MoveNext())",
                                                                 enumerationContentVariableName.Name
                                                                 ),
                                                             indentationDepth + 1,
                                                             forEachBlock.LoopVar.LineIndex
                                                             ))
                                .Add(new TranslatedStatement("break;", indentationDepth + 2, forEachBlock.LoopVar.LineIndex))
                                .Add(new TranslatedStatement(
                                         string.Format(
                                             "{0} = {1}.Current;",
                                             rewrittenLoopVarName,
                                             enumerationContentVariableName.Name
                                             ),
                                         indentationDepth + 1,
                                         forEachBlock.LoopVar.LineIndex
                                         ));
            if (scopeAccessInformation.ErrorRegistrationTokenIfAny != null)
            {
                // If error-trapping may be enabled then the above MoveNext and set-to-Current work was wrapped in a condition which must be closed
                translationResult = translationResult.Add(new TranslatedStatement("}", indentationDepth, forEachBlock.LoopVar.LineIndex));
                indentationDepth--;
            }
            var earlyExitNameIfAny = GetEarlyExitNameIfRequired(forEachBlock, scopeAccessInformation);

            if (earlyExitNameIfAny != null)
            {
                translationResult = translationResult.Add(new TranslatedStatement(
                                                              string.Format("var {0} = false;", earlyExitNameIfAny.Name),
                                                              indentationDepth + 1,
                                                              forEachBlock.LoopVar.LineIndex
                                                              ));
            }
            translationResult = translationResult.Add(
                Translate(
                    forEachBlock.Statements.ToNonNullImmutableList(),
                    scopeAccessInformation.SetParent(forEachBlock),
                    earlyExitNameIfAny,
                    indentationDepth + 1
                    )
                );
            if (scopeAccessInformation.ErrorRegistrationTokenIfAny != null)
            {
                // If error-trapping was enabled and an error caught, then the loop should be processed once and only once. The enumerator reference
                // will be null - so check for that and exit if so. If there is no chance that error-trapping is enabled then this condition is not
                // required and there is no point emitting it.
                translationResult = translationResult
                                    .Add(new TranslatedStatement(
                                             string.Format(
                                                 "if ({0} == null)",
                                                 enumerationContentVariableName.Name
                                                 ),
                                             indentationDepth + 1,
                                             forEachBlock.LoopVar.LineIndex
                                             ))
                                    .Add(new TranslatedStatement("break;", indentationDepth + 2, forEachBlock.LoopVar.LineIndex));
            }
            translationResult = translationResult.Add(new TranslatedStatement("}", indentationDepth, forEachBlock.LoopVar.LineIndex));

            var earlyExitFlagNamesToCheck = scopeAccessInformation.StructureExitPoints
                                            .Where(e => e.ExitEarlyBooleanNameIfAny != null)
                                            .Select(e => e.ExitEarlyBooleanNameIfAny.Name);

            if (earlyExitFlagNamesToCheck.Any())
            {
                // Perform early-exit checks for any scopeAccessInformation.StructureExitPoints - if this is FOR loop inside a DO..LOOP loop and an
                // EXIT DO was encountered within the FOR that must refer to the containing DO, then the FOR loop will have been broken out of, but
                // also a flag set that means that we must break further to get out of the DO loop.
                translationResult = translationResult
                                    .Add(new TranslatedStatement(
                                             "if (" + string.Join(" || ", earlyExitFlagNamesToCheck) + ")",
                                             indentationDepth,
                                             forEachBlock.LoopVar.LineIndex
                                             ))
                                    .Add(new TranslatedStatement(
                                             "break;",
                                             indentationDepth + 1,
                                             forEachBlock.LoopVar.LineIndex
                                             ));
            }
            return(translationResult);
        }
コード例 #22
0
        public TranslationResult Translate(WithBlock withBlock, ScopeAccessInformation scopeAccessInformation, int indentationDepth)
        {
            if (withBlock == null)
            {
                throw new ArgumentNullException("withBlock");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            var translatedTargetReference = _statementTranslator.Translate(withBlock.Target, scopeAccessInformation, ExpressionReturnTypeOptions.Reference, _logger.Warning);
            var undeclaredVariables       = translatedTargetReference.VariablesAccessed
                                            .Where(v => !scopeAccessInformation.IsDeclaredReference(v, _nameRewriter));

            foreach (var undeclaredVariable in undeclaredVariables)
            {
                _logger.Warning("Undeclared variable: \"" + undeclaredVariable.Content + "\" (line " + (undeclaredVariable.LineIndex + 1) + ")");
            }

            var targetName = base._tempNameGenerator(new CSharpName("with"), scopeAccessInformation);
            var withBlockContentTranslationResult = Translate(
                withBlock.Content.ToNonNullImmutableList(),
                new ScopeAccessInformation(
                    withBlock,
                    scopeAccessInformation.ScopeDefiningParent,
                    scopeAccessInformation.ParentReturnValueNameIfAny,
                    scopeAccessInformation.ErrorRegistrationTokenIfAny,
                    new ScopeAccessInformation.DirectedWithReferenceDetails(
                        targetName,
                        withBlock.Target.Tokens.First().LineIndex
                        ),
                    scopeAccessInformation.ExternalDependencies,
                    scopeAccessInformation.Classes,
                    scopeAccessInformation.Functions,
                    scopeAccessInformation.Properties,
                    scopeAccessInformation.Constants,
                    scopeAccessInformation.Variables,
                    scopeAccessInformation.StructureExitPoints
                    ),
                indentationDepth
                );

            return(new TranslationResult(
                       withBlockContentTranslationResult.TranslatedStatements
                       .Insert(
                           new TranslatedStatement(
                               string.Format(
                                   "var {0} = {1};",
                                   targetName.Name,
                                   translatedTargetReference.TranslatedContent
                                   ),
                               indentationDepth,
                               withBlock.Target.Tokens.First().LineIndex
                               ),
                           0
                           ),
                       withBlockContentTranslationResult.ExplicitVariableDeclarations,
                       withBlockContentTranslationResult.UndeclaredVariablesAccessed.AddRange(undeclaredVariables)
                       ));
        }
コード例 #23
0
        private TranslationResult TranslateForClass(NonNullImmutableList <ICodeBlock> blocks, ScopeAccessInformation scopeAccessInformation, int indentationDepth)
        {
            if (blocks == null)
            {
                throw new ArgumentNullException("block");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            // Note: DIM statements are allowed between in the space inside a CLASS but outside of any FUNCTION or PROPERTY within that CLASS, but CONST is not
            return(base.TranslateCommon(
                       new BlockTranslationAttempter[]
            {
                base.TryToTranslateBlankLine,
                base.TryToTranslateComment,
                base.TryToTranslateDim,
                base.TryToTranslateFunctionPropertyOrSub
            }.ToNonNullImmutableList(),
                       blocks,
                       scopeAccessInformation,
                       indentationDepth
                       ));
        }
コード例 #24
0
        public NonNullImmutableList <TranslatedStatement> Translate(NonNullImmutableList <ICodeBlock> blocks)
        {
            if (blocks == null)
            {
                throw new ArgumentNullException("blocks");
            }

            // There are some date literal values that need to be validated at runtime (they may vary by culture - if they include a month name, basically, which will
            // depend upon the current language). If any of these literals are invalid for the runtime culture then no further processing may take place (this is the
            // same as the VBScript interpreter refusing to process a script when it reads it, in whatever culture is active when it executes). We need to build this
            // list before we remove any duplicate functions since, although VBScript will "overwrite" functions with the same name in the outermost scope, if the
            // functions it overwrites / ignores contained any invalid date literals, the interpreter will still not execute. We'll use this data further down..
            var dateLiteralsToValidateAtRuntime = EnumerateAllDateLiteralTokens(blocks)
                                                  .Where(d => d.RequiresRuntimeValidation)
                                                  .GroupBy(d => d.Content)
                                                  .Select(g => new { DateLiteralValue = g.Key, LineNumbers = g.Select(d => d.LineIndex + 1) })
                                                  .ToArray();

            // Note: There is no need to check for identically-named classes since that would cause a "Name Redefined" error even if Option Explicit was not enabled
            blocks = RemoveDuplicateFunctions(blocks);

            // Group the code blocks that need to be executed (the functions from the outermost scope need to go into a "global references" class
            // which will appear after any other classes, which will appear after everything else)
            var commentBuffer      = new NonNullImmutableList <CommentStatement>();
            var annotatedFunctions = new NonNullImmutableList <Annotated <AbstractFunctionBlock> >();
            var annotatedClasses   = new NonNullImmutableList <Annotated <ClassBlock> >();
            var otherBlocks        = new NonNullImmutableList <ICodeBlock>();

            foreach (var block in blocks)
            {
                var comment = block as CommentStatement;
                if (comment != null)
                {
                    commentBuffer = commentBuffer.Add(comment);
                    continue;
                }
                var functionBlock = block as AbstractFunctionBlock;
                if (functionBlock != null)
                {
                    annotatedFunctions = annotatedFunctions.Add(new Annotated <AbstractFunctionBlock>(commentBuffer, functionBlock));
                    commentBuffer      = new NonNullImmutableList <CommentStatement>();
                    continue;
                }
                var classBlock = block as ClassBlock;
                if (classBlock != null)
                {
                    annotatedClasses = annotatedClasses.Add(new Annotated <ClassBlock>(commentBuffer, classBlock));
                    commentBuffer    = new NonNullImmutableList <CommentStatement>();
                    continue;
                }
                otherBlocks   = otherBlocks.AddRange(commentBuffer).Add(block);
                commentBuffer = new NonNullImmutableList <CommentStatement>();
            }
            if (commentBuffer.Any())
            {
                otherBlocks = otherBlocks.AddRange(commentBuffer);
            }

            // Ensure that functions are in valid configurations - properties are not valid outside of classes and any non-public functions will
            // be translated INTO public functions (since this there are no private external functions in VBScript)
            annotatedFunctions = annotatedFunctions
                                 .Select(f =>
            {
                if (f.CodeBlock is PropertyBlock)
                {
                    throw new ArgumentException("Property encountered in OuterMostScope - these may only appear within classes: " + f.CodeBlock.Name.Content);
                }
                if (!f.CodeBlock.IsPublic)
                {
                    _logger.Warning("OuterScope function \"" + f.CodeBlock.Name.Content + "\" is private, this is invalid and will be changed to public");
                    if (f.CodeBlock is FunctionBlock)
                    {
                        return(new Annotated <AbstractFunctionBlock>(
                                   f.LeadingComments,
                                   new FunctionBlock(true, f.CodeBlock.IsDefault, f.CodeBlock.Name, f.CodeBlock.Parameters, f.CodeBlock.Statements)
                                   ));
                    }
                    else if (f.CodeBlock is SubBlock)
                    {
                        return(new Annotated <AbstractFunctionBlock>(
                                   f.LeadingComments,
                                   new SubBlock(true, f.CodeBlock.IsDefault, f.CodeBlock.Name, f.CodeBlock.Parameters, f.CodeBlock.Statements)
                                   ));
                    }
                    else
                    {
                        throw new ArgumentException("Unsupported AbstractFunctionBlock type: " + f.GetType());
                    }
                }
                return(f);
            })
                                 .ToNonNullImmutableList();

            var scopeAccessInformation = ScopeAccessInformation.FromOutermostScope(
                _startClassName,                 // A placeholder name is required for an OutermostScope instance and so is required by this method
                blocks,
                _externalDependencies
                );

            if (blocks.DoesScopeContainOnErrorResumeNext())
            {
                scopeAccessInformation = scopeAccessInformation.SetErrorRegistrationToken(
                    _tempNameGenerator(new CSharpName("errOn"), scopeAccessInformation)
                    );
            }

            var outerExecutableBlocksTranslationResult = Translate(
                otherBlocks.ToNonNullImmutableList(),
                scopeAccessInformation,
                3                 // indentationDepth
                );
            var explicitVariableDeclarationsFromWithOuterScope = outerExecutableBlocksTranslationResult.ExplicitVariableDeclarations;

            outerExecutableBlocksTranslationResult = new TranslationResult(
                outerExecutableBlocksTranslationResult.TranslatedStatements,
                new NonNullImmutableList <VariableDeclaration>(),
                outerExecutableBlocksTranslationResult.UndeclaredVariablesAccessed
                );

            var translatedStatements = new NonNullImmutableList <TranslatedStatement>();

            if (_outputType == OutputTypeOptions.Executable)
            {
                translatedStatements = translatedStatements.AddRange(new[]
                {
                    new TranslatedStatement("using System;", 0, 0),
                    new TranslatedStatement("using System.Collections;", 0, 0)
                });
                if (dateLiteralsToValidateAtRuntime.Any())
                {
                    // System.Collections.ObjectModel is only required for the ReadOnlyCollection, which is only used when there are date literals that need validating at runtime
                    translatedStatements = translatedStatements.Add(new TranslatedStatement("using System.Collections.ObjectModel;", 0, 0));
                }
                translatedStatements = translatedStatements.Add(new TranslatedStatement("using System.Runtime.InteropServices;", 0, 0));
                translatedStatements = translatedStatements.AddRange(new[]
                {
                    new TranslatedStatement("using " + typeof(IProvideVBScriptCompatFunctionalityToIndividualRequests).Namespace + ";", 0, 0),
                    new TranslatedStatement("using " + typeof(SourceClassName).Namespace + ";", 0, 0),
                    new TranslatedStatement("using " + typeof(SpecificVBScriptException).Namespace + ";", 0, 0),
                    new TranslatedStatement("using " + typeof(TranslatedPropertyIReflectImplementation).Namespace + ";", 0, 0),
                    new TranslatedStatement("", 0, 0),
                    new TranslatedStatement("namespace " + _startNamespace.Name, 0, 0),
                    new TranslatedStatement("{", 0, 0),
                    new TranslatedStatement("public class " + _startClassName.Name, 1, 0),
                    new TranslatedStatement("{", 1, 0),
                    new TranslatedStatement("private readonly " + typeof(IProvideVBScriptCompatFunctionalityToIndividualRequests).Name + " " + _supportRefName.Name + ";", 2, 0),
                    new TranslatedStatement("public " + _startClassName.Name + "(" + typeof(IProvideVBScriptCompatFunctionalityToIndividualRequests).Name + " compatLayer)", 2, 0),
                    new TranslatedStatement("{", 2, 0),
                    new TranslatedStatement("if (compatLayer == null)", 3, 0),
                    new TranslatedStatement("throw new ArgumentNullException(\"compatLayer\");", 4, 0),
                    new TranslatedStatement(_supportRefName.Name + " = compatLayer;", 3, 0),
                    new TranslatedStatement("}", 2, 0)
                });
                translatedStatements = translatedStatements.AddRange(new[]
                {
                    new TranslatedStatement("", 0, 0),
                    new TranslatedStatement(
                        string.Format(
                            "public {0} {1}()",
                            _outerClassName.Name,
                            _startMethodName.Name
                            ),
                        2,
                        0
                        ),
                    new TranslatedStatement("{", 2, 0),
                    new TranslatedStatement(
                        string.Format(
                            "return {0}(new {1}());",
                            _startMethodName.Name,
                            _envClassName.Name
                            ),
                        3,
                        0
                        ),
                    new TranslatedStatement("}", 2, 0),
                    new TranslatedStatement(
                        string.Format(
                            "public {0} {1}({2} env)",
                            _outerClassName.Name,
                            _startMethodName.Name,
                            _envClassName.Name
                            ),
                        2,
                        0
                        ),
                    new TranslatedStatement("{", 2, 0),
                    new TranslatedStatement("if (env == null)", 3, 0),
                    new TranslatedStatement("throw new ArgumentNullException(\"env\");", 4, 0),
                    new TranslatedStatement("", 0, 0),
                    new TranslatedStatement(
                        string.Format("var {0} = env;", _envRefName.Name),
                        3,
                        0
                        ),
                    new TranslatedStatement(
                        string.Format("var {0} = new {1}({2}, {3});", _outerRefName.Name, _outerClassName.Name, _supportRefName.Name, _envRefName.Name),
                        3,
                        0
                        )
                });
                if (dateLiteralsToValidateAtRuntime.Any())
                {
                    // When rendering in full Executable format (not just in WithoutScaffolding), if there were any date literals in the source content that could not
                    // be confirmed as valid at translation time (meaning they include a month name, which will vary in validity depending upon the culture of the
                    // environment at runtime) then these literals need to be validated each run before any other work is attempted. (This is the equivalent of
                    // the VBScript interpreter reading the script for every execution and validating date literals against the current culture - if it finds
                    // any of them to be invalid then it will raise a syntax error and not attempt to execute any of the script).
                    translatedStatements = translatedStatements.Add(new TranslatedStatement(
                                                                        string.Format(
                                                                            "{0}.ValidateAgainstCurrentCulture({1});",
                                                                            _runtimeDateLiteralValidatorClassName.Name,
                                                                            _supportRefName.Name
                                                                            ),
                                                                        3,
                                                                        0
                                                                        ));
                }
                translatedStatements = translatedStatements.Add(new TranslatedStatement("", 0, 0));
            }

            if (scopeAccessInformation.ErrorRegistrationTokenIfAny != null)
            {
                translatedStatements = translatedStatements
                                       .Add(new TranslatedStatement(
                                                string.Format(
                                                    "var {0} = {1}.GETERRORTRAPPINGTOKEN();",
                                                    scopeAccessInformation.ErrorRegistrationTokenIfAny.Name,
                                                    _supportRefName.Name
                                                    ),
                                                3,
                                                0
                                                ));
            }
            translatedStatements = translatedStatements.AddRange(
                outerExecutableBlocksTranslationResult.TranslatedStatements
                );
            if (scopeAccessInformation.ErrorRegistrationTokenIfAny != null)
            {
                translatedStatements = translatedStatements
                                       .Add(new TranslatedStatement(
                                                string.Format(
                                                    "{0}.RELEASEERRORTRAPPINGTOKEN({1});",
                                                    _supportRefName.Name,
                                                    scopeAccessInformation.ErrorRegistrationTokenIfAny.Name
                                                    ),
                                                3,
                                                0
                                                ));
            }
            if (_outputType == OutputTypeOptions.Executable)
            {
                // Close the main "TranslatedProgram" function and then write out the runtime-date-literal-validation logic and the global references class (when a complete executable
                // is not required, none of this is of much benefit and detracts from the core of what's being translated - most tests will specify WithoutScaffolding rather than
                // Executable so that just the real meat of the source code is generated)
                translatedStatements = translatedStatements.AddRange(new[]
                {
                    new TranslatedStatement(string.Format("return {0};", _outerRefName.Name), 3, 0),
                    new TranslatedStatement("}", 2, 0),
                    new TranslatedStatement("", 0, 0)
                });

                if (dateLiteralsToValidateAtRuntime.Any())
                {
                    // Declare a static readonly immutable set of date literals that need validating against the current culture before any request can do any actual work
                    translatedStatements = translatedStatements.AddRange(new[]
                    {
                        new TranslatedStatement(
                            string.Format(
                                "private static class {0}",
                                _runtimeDateLiteralValidatorClassName.Name
                                ),
                            2,
                            0
                            ),
                        new TranslatedStatement("{", 2, 0),
                        new TranslatedStatement("private static readonly ReadOnlyCollection<Tuple<string, int[]>> _literalsToValidate =", 3, 0),
                        new TranslatedStatement("new ReadOnlyCollection<Tuple<string, int[]>>(new[] {", 3, 0)
                    });
                    foreach (var indexedDateLiteralToValidate in dateLiteralsToValidateAtRuntime.Select((d, i) => new { Index = i, DateLiteralValue = d.DateLiteralValue, LineNumbers = d.LineNumbers }))
                    {
                        translatedStatements = translatedStatements.Add(new TranslatedStatement(
                                                                            string.Format(
                                                                                "Tuple.Create({0}, new[] {{ {1} }}){2}",
                                                                                indexedDateLiteralToValidate.DateLiteralValue.ToLiteral(),
                                                                                string.Join <int>(", ", indexedDateLiteralToValidate.LineNumbers),
                                                                                (indexedDateLiteralToValidate.Index < (dateLiteralsToValidateAtRuntime.Length - 1)) ? "," : ""
                                                                                ),
                                                                            4,
                                                                            0
                                                                            ));
                    }
                    translatedStatements = translatedStatements.Add(new TranslatedStatement("});", 3, 0));
                    translatedStatements = translatedStatements.Add(new TranslatedStatement("", 0, 0));

                    // Declare the function that reads the data above and performs the validation work
                    translatedStatements = translatedStatements.AddRange(new[]
                    {
                        new TranslatedStatement("public static void ValidateAgainstCurrentCulture(IProvideVBScriptCompatFunctionalityToIndividualRequests compatLayer)", 3, 0),
                        new TranslatedStatement("{", 3, 0),
                        new TranslatedStatement("if (compatLayer == null)", 4, 0),
                        new TranslatedStatement("throw new ArgumentNullException(\"compatLayer\");", 5, 0),
                        new TranslatedStatement("foreach (var dateLiteralValueAndLineNumbers in _literalsToValidate)", 4, 0),
                        new TranslatedStatement("{", 4, 0),
                        new TranslatedStatement(
                            string.Format(
                                "try {{ compatLayer.DateLiteralParser.Parse(dateLiteralValueAndLineNumbers.Item1); }}",
                                _runtimeDateLiteralValidatorClassName.Name
                                ),
                            5,
                            0
                            ),
                        new TranslatedStatement("catch", 5, 0),
                        new TranslatedStatement("{", 5, 0),
                        new TranslatedStatement("throw new SyntaxError(string.Format(", 6, 0),
                        new TranslatedStatement("\"Invalid date literal #{0}# on line{1} {2}\",", 7, 0),
                        new TranslatedStatement("dateLiteralValueAndLineNumbers.Item1,", 7, 0),
                        new TranslatedStatement("(dateLiteralValueAndLineNumbers.Item2.Length == 1) ? \"\" : \"s\",", 7, 0),
                        new TranslatedStatement("string.Join<int>(\", \", dateLiteralValueAndLineNumbers.Item2)", 7, 0),
                        new TranslatedStatement("));", 6, 0),
                        new TranslatedStatement("}", 5, 0),
                        new TranslatedStatement("}", 4, 0),
                        new TranslatedStatement("}", 3, 0),
                        new TranslatedStatement("}", 2, 0),
                        new TranslatedStatement("", 0, 0)
                    });
                }

                translatedStatements = translatedStatements.AddRange(new[]
                {
                    new TranslatedStatement("public class " + _outerClassName.Name, 2, 0),
                    new TranslatedStatement("{", 2, 0),
                    new TranslatedStatement("private readonly " + typeof(IProvideVBScriptCompatFunctionalityToIndividualRequests).Name + " " + _supportRefName.Name + ";", 3, 0),
                    new TranslatedStatement("private readonly " + _outerClassName.Name + " " + _outerRefName.Name + ";", 3, 0),
                    new TranslatedStatement("private readonly " + _envClassName.Name + " " + _envRefName.Name + ";", 3, 0),
                    new TranslatedStatement("public " + _outerClassName.Name + "(" + typeof(IProvideVBScriptCompatFunctionalityToIndividualRequests).Name + " compatLayer, " + _envClassName.Name + " env)", 3, 0),
                    new TranslatedStatement("{", 3, 0),
                    new TranslatedStatement("if (compatLayer == null)", 4, 0),
                    new TranslatedStatement("throw new ArgumentNullException(\"compatLayer\");", 5, 0),
                    new TranslatedStatement("if (env == null)", 4, 0),
                    new TranslatedStatement("throw new ArgumentNullException(\"env\");", 5, 0),
                    new TranslatedStatement(_supportRefName.Name + " = compatLayer;", 4, 0),
                    new TranslatedStatement(_envRefName.Name + " = env;", 4, 0),
                    new TranslatedStatement(_outerRefName.Name + " = this;", 4, 0)
                });

                // Note: Any repeated "explicitVariableDeclarationsFromWithOuterScope" entries are ignored - this makes the ReDim translation process easier (where ReDim statements
                // may target already-declared variables or they may be considered to implicitly declare them) but it means that the Dim translation has to do some extra work to
                // pick up on "Name redefined" scenarios.
                var variableInitialisationStatements = new NonNullImmutableList <TranslatedStatement>();
                foreach (var explicitVariableDeclaration in explicitVariableDeclarationsFromWithOuterScope)
                {
                    var variableInitialisationStatement = new TranslatedStatement(
                        base.TranslateVariableInitialisation(explicitVariableDeclaration, ScopeLocationOptions.OutermostScope),
                        4,
                        explicitVariableDeclaration.Name.LineIndex
                        );
                    if (!variableInitialisationStatements.Any(s => s.Content == variableInitialisationStatement.Content))
                    {
                        variableInitialisationStatements = variableInitialisationStatements.Add(variableInitialisationStatement);
                    }
                }
                translatedStatements = translatedStatements.AddRange(variableInitialisationStatements);
                translatedStatements = translatedStatements
                                       .Add(
                    new TranslatedStatement("}", 3, 0)
                    );
                if (explicitVariableDeclarationsFromWithOuterScope.Any())
                {
                    translatedStatements = translatedStatements.Add(new TranslatedStatement("", 0, 0));
                    var variableDeclarationStatements = new NonNullImmutableList <TranslatedStatement>();
                    foreach (var explicitVariableDeclaration in explicitVariableDeclarationsFromWithOuterScope)
                    {
                        var variableDeclarationStatement = new TranslatedStatement(
                            "public object " + _nameRewriter.GetMemberAccessTokenName(explicitVariableDeclaration.Name) + " { get; set; }",
                            3,
                            explicitVariableDeclaration.Name.LineIndex
                            );
                        if (!variableDeclarationStatements.Any(s => s.Content == variableDeclarationStatement.Content))
                        {
                            variableDeclarationStatements = variableDeclarationStatements.Add(variableDeclarationStatement);
                        }
                    }
                    translatedStatements = translatedStatements.AddRange(variableDeclarationStatements);
                }
            }
            foreach (var annotatedFunctionBlock in annotatedFunctions)
            {
                // Note that any variables that were accessed by code in the outermost scope, but that were not explicitly declared, are considered to be IMPLICITLY declared.
                // So, if they are referenced within any of the functions, they must share the same reference unless explicit declared within those functions (so if "a" is
                // set in the outermost scope and then a function has a statement "ReDim a(2)", that "a" reference should be that one in the outermost scope). To achieve
                // this, the "implicitly declared" outermost scope variables are added to the ExternalDependencies set in the ScopeAccessInformation instance provided
                // to the function block translator. The same must be done for class block translation (see below).
                translatedStatements = translatedStatements.Add(new TranslatedStatement("", 1, 0));
                translatedStatements = translatedStatements.AddRange(
                    Translate(
                        annotatedFunctionBlock.LeadingComments.Cast <ICodeBlock>().Concat(new[] { annotatedFunctionBlock.CodeBlock }).ToNonNullImmutableList(),
                        scopeAccessInformation.ExtendExternalDependencies(outerExecutableBlocksTranslationResult.UndeclaredVariablesAccessed),
                        3                         // indentationDepth
                        ).TranslatedStatements
                    );
            }
            var classBlocksTranslationResult = Translate(
                TrimTrailingBlankLines(
                    annotatedClasses.SelectMany(c => c.LeadingComments.Cast <ICodeBlock>().Concat(new[] { c.CodeBlock })).ToNonNullImmutableList()
                    ),
                scopeAccessInformation.ExtendExternalDependencies(outerExecutableBlocksTranslationResult.UndeclaredVariablesAccessed), // See comment above relating to ExternalDependencies for function blocks
                2                                                                                                                      // indentationDepth
                );

            if (_outputType == OutputTypeOptions.Executable)
            {
                translatedStatements = translatedStatements.AddRange(new[]
                {
                    new TranslatedStatement("}", 2, 0),
                    new TranslatedStatement("", 0, 0)
                });

                // This has to be generated after all of the Translate calls to ensure that the UndeclaredVariablesAccessed data for all of the TranslationResults is available
                var allEnvironmentVariablesAccessed =
                    scopeAccessInformation.ExternalDependencies
                    .AddRange(
                        outerExecutableBlocksTranslationResult
                        .Add(classBlocksTranslationResult)
                        .UndeclaredVariablesAccessed
                        );
                translatedStatements = translatedStatements.AddRange(new[]
                {
                    new TranslatedStatement("public class " + _envClassName.Name, 2, 0),
                    new TranslatedStatement("{", 2, 0)
                });
                var allEnvironmentVariableNames = allEnvironmentVariablesAccessed.Select(v => new { RewrittenName = _nameRewriter.GetMemberAccessTokenName(v), LineIndex = v.LineIndex });
                var environmentVariableNamesThatHaveBeenAccountedFor = new HashSet <string>();
                foreach (var v in allEnvironmentVariableNames)
                {
                    if (environmentVariableNamesThatHaveBeenAccountedFor.Contains(v.RewrittenName))
                    {
                        continue;
                    }
                    translatedStatements = translatedStatements.Add(
                        new TranslatedStatement("public object " + v.RewrittenName + " { get; set; }", 3, v.LineIndex)
                        );
                    environmentVariableNamesThatHaveBeenAccountedFor.Add(v.RewrittenName);
                }
                translatedStatements = translatedStatements.Add(
                    new TranslatedStatement("}", 2, 0)
                    );
            }

            if (classBlocksTranslationResult.TranslatedStatements.Any())
            {
                translatedStatements = translatedStatements.Add(
                    new TranslatedStatement("", 0, 0)
                    );
                translatedStatements = translatedStatements.AddRange(
                    classBlocksTranslationResult.TranslatedStatements
                    );
            }

            if (_outputType == OutputTypeOptions.Executable)
            {
                translatedStatements = translatedStatements.AddRange(new[]
                {
                    new TranslatedStatement("}", 1, 0),                     // Close outer class
                    new TranslatedStatement("}", 0, 0),                     // Close namespace
                });
            }

            // Moving the functions and classes around can sometimes leaving extraneous blank lines in their wake. This tidies them up. (This would also result in
            // runs of blank lines in the source being reduced to a single line, not just runs that were introduced by the rearranging, but I can't see how that
            // could be a big problem).
            return(RemoveRunsOfBlankLines(translatedStatements));
        }
コード例 #25
0
        private TranslationResult Translate(NonNullImmutableList <ICodeBlock> blocks, ScopeAccessInformation scopeAccessInformation, int indentationDepth)
        {
            if (blocks == null)
            {
                throw new ArgumentNullException("block");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            return(base.TranslateCommon(
                       base.GetWithinFunctionBlockTranslators(),
                       blocks,
                       scopeAccessInformation,
                       indentationDepth
                       ));
        }
コード例 #26
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)
                       ));
        }
コード例 #27
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))
                       ));
        }
コード例 #28
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);
        }
コード例 #29
0
        private IEnumerable <TranslatedStatement> TranslateClassHeader(
            ClassBlock classBlock,
            ScopeAccessInformation scopeAccessInformation,
            NonNullImmutableList <VariableDeclaration> explicitVariableDeclarationsFromWithinClass,
            NameToken classInitializeMethodNameIfAny,
            NameToken classTerminateMethodNameIfAny,
            int indentationDepth)
        {
            if (classBlock == null)
            {
                throw new ArgumentNullException("classBlock");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (explicitVariableDeclarationsFromWithinClass == null)
            {
                throw new ArgumentNullException("explicitVariableDeclarationsFromWithClass");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            // C# doesn't support nameed indexed properties, so if there are any Get properties with a parameter or any Let/Set properties
            // with multiple parameters (they need at least; the value to set) then we'll have to get creative - see notes in the
            // TranslatedPropertyIReflectImplementation class
            string inheritanceChainIfAny;

            if (classBlock.Statements.Where(s => s is PropertyBlock).Cast <PropertyBlock>().Any(p => p.IsPublic && p.IsIndexedProperty()))
            {
                inheritanceChainIfAny = " : " + typeof(TranslatedPropertyIReflectImplementation).Name;                 // Assume that the namespace is available for this attribute
            }
            else
            {
                inheritanceChainIfAny = "";
            }

            var className = _nameRewriter.GetMemberAccessTokenName(classBlock.Name);
            IEnumerable <TranslatedStatement> classInitializeCallStatements;

            if (classInitializeMethodNameIfAny == null)
            {
                classInitializeCallStatements = new TranslatedStatement[0];
            }
            else
            {
                // When Class_Initialize is called, it is treated as if ON ERROR RESUME NEXT is applied around the call - the first error will
                // result in the method exiting, but the error won't be propagated up (so the caller will continue as if it hadn't happened).
                // Wrapping the call in a try..catch simulates this behaviour (there is similar required for Class_Terminate).
                classInitializeCallStatements = new[] {
                    new TranslatedStatement(
                        "try { " + _nameRewriter.GetMemberAccessTokenName(classInitializeMethodNameIfAny) + "(); }",
                        indentationDepth + 2,
                        classBlock.Name.LineIndex
                        ),
                    new TranslatedStatement("catch(Exception e)", indentationDepth + 2, classBlock.Name.LineIndex),
                    new TranslatedStatement("{", indentationDepth + 2, classBlock.Name.LineIndex),
                    new TranslatedStatement(_supportRefName.Name + ".SETERROR(e);", indentationDepth + 3, classBlock.Name.LineIndex),
                    new TranslatedStatement("}", indentationDepth + 2, classBlock.Name.LineIndex)
                };
            }
            TranslatedStatement[] disposeImplementationStatements;
            CSharpName            disposedFlagNameIfAny;

            if (classTerminateMethodNameIfAny == null)
            {
                disposeImplementationStatements = new TranslatedStatement[0];
                disposedFlagNameIfAny           = null;
            }
            else
            {
                // If this class has a Clas_Terminate method, then the closest facsimile is a finalizer. However, in C# the garbage collector means
                // that the execution of the finalizer is non-deterministic, while the VBScript interpreter's garbage collector resulted in these
                // being executed predictably. In case a translated class requires this behaviour be maintained somehow, the best thing to do is
                // to make the class implement IDisposable. Translated calling code will not know when to call Dispose on the classes written
                // here, but if they are called from any other C# code written directly against the translated output, having the option of
                // taking advantage of the IDisposable interface may be useful.
                // - Note that when the finalizer is executed, the call to Dispose (which then calls the Class_Terminate method) is wrapped in
                //   a try..catch for the same reason as the Class_Initialize call, as explained above. The only difference is that here, when
                //   we call _.SETERROR(e) there is - technically - a chance that the "_" reference will have been finalised. Managed references
                //   should not be accessed in the finaliser if we're following the rules to the letter, but this entire structure around trying
                //   to emulate Class_Terminate with a finaliser is a series of compromises, so this is the best we can do. The SETERROR call is
                //   ALSO wrapped in a try..catch, just in case that reference really is no longer available.
                // - Also note that IDisposable's public Dispose() method is explicitly implemented and that the method that this and the
                //   finaliser call is named by "_tempNameGenerator" reference and that it is "private" instead of the more common (for
                //   correct implementations of the disposable pattern) "protected virtual". This is explained below, where the class
                //   header is generated.
                disposedFlagNameIfAny = _tempNameGenerator(new CSharpName("_disposed"), scopeAccessInformation);
                var disposeMethodName = _tempNameGenerator(new CSharpName("Dispose"), scopeAccessInformation);
                disposeImplementationStatements = new[]
                {
                    new TranslatedStatement("~" + className + "()", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("{", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("try { " + disposeMethodName.Name + "(false); }", indentationDepth + 2, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("catch(Exception e)", indentationDepth + 2, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("{", indentationDepth + 2, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("try { " + _supportRefName.Name + ".SETERROR(e); } catch { }", indentationDepth + 3, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("}", indentationDepth + 2, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("}", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("void IDisposable.Dispose()", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("{", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement(disposeMethodName.Name + "(true);", indentationDepth + 2, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("GC.SuppressFinalize(this);", indentationDepth + 2, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("}", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("private void " + disposeMethodName.Name + "(bool disposing)", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("{", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("if (" + disposedFlagNameIfAny.Name + ")", indentationDepth + 2, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("return;", indentationDepth + 3, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("if (disposing)", indentationDepth + 2, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement(_nameRewriter.GetMemberAccessTokenName(classTerminateMethodNameIfAny) + "();", indentationDepth + 3, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement(disposedFlagNameIfAny.Name + " = true;", indentationDepth + 2, classTerminateMethodNameIfAny.LineIndex),
                    new TranslatedStatement("}", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex)
                };
                if (inheritanceChainIfAny == "")
                {
                    inheritanceChainIfAny = " : ";
                }
                else
                {
                    inheritanceChainIfAny += ", ";
                }
                inheritanceChainIfAny += "IDisposable";
            }

            // The class is sealed to make the IDisposable implementation easier (where appropriate) - the recommended way to implement IDisposable
            // is for there to be a "protected virtual void Dispose(bool disposing)" method, for use by the IDisposable's public Dispose() method,
            // by the finaliser and by any derived types. However, there may be a "Dispose" method that comes from the VBScript source. We can work
            // around this by explicitly implementating "IDisposable.Dispose" and by using the "_tempNameGenerator" reference to get a safe-to-use
            // method name, for the boolean argument "Dispose" method - but then it won't follow the recommended pattern and be a method name
            // "Dispose" that derived types can use. The easiest way around that is to make the classes sealed and then there are no derived
            // types to worry about (this could be seen to be a limitation on the translated code, but since it's all being translated from
            // VBScript where all types are dynamic, one class could just be swapped out for another entirely different one as long as it
            // has the same methods and properties on it).
            var classHeaderStatements = new List <TranslatedStatement>
            {
                new TranslatedStatement("[ComVisible(true)]", indentationDepth, classBlock.Name.LineIndex),
                new TranslatedStatement("[SourceClassName(" + classBlock.Name.Content.ToLiteral() + ")]", indentationDepth, classBlock.Name.LineIndex),
                new TranslatedStatement("public sealed class " + className + inheritanceChainIfAny, indentationDepth, classBlock.Name.LineIndex),
                new TranslatedStatement("{", indentationDepth, classBlock.Name.LineIndex),
                new TranslatedStatement("private readonly " + typeof(IProvideVBScriptCompatFunctionalityToIndividualRequests).Name + " " + _supportRefName.Name + ";", indentationDepth + 1, classBlock.Name.LineIndex),
                new TranslatedStatement("private readonly " + _envClassName.Name + " " + _envRefName.Name + ";", indentationDepth + 1, classBlock.Name.LineIndex),
                new TranslatedStatement("private readonly " + _outerClassName.Name + " " + _outerRefName.Name + ";", indentationDepth + 1, classBlock.Name.LineIndex)
            };

            if (disposedFlagNameIfAny != null)
            {
                classHeaderStatements.Add(
                    new TranslatedStatement("private bool " + disposedFlagNameIfAny.Name + ";", indentationDepth + 1, classBlock.Name.LineIndex)
                    );
            }
            classHeaderStatements.AddRange(new[] {
                new TranslatedStatement(
                    string.Format(
                        "public {0}({1} compatLayer, {2} env, {3} outer)",
                        className,
                        typeof(IProvideVBScriptCompatFunctionalityToIndividualRequests).Name,
                        _envClassName.Name,
                        _outerClassName.Name
                        ),
                    indentationDepth + 1,
                    classBlock.Name.LineIndex
                    ),
                new TranslatedStatement("{", indentationDepth + 1, classBlock.Name.LineIndex),
                new TranslatedStatement("if (compatLayer == null)", indentationDepth + 2, classBlock.Name.LineIndex),
                new TranslatedStatement("throw new ArgumentNullException(\"compatLayer\");", indentationDepth + 3, classBlock.Name.LineIndex),
                new TranslatedStatement("if (env == null)", indentationDepth + 2, classBlock.Name.LineIndex),
                new TranslatedStatement("throw new ArgumentNullException(\"env\");", indentationDepth + 3, classBlock.Name.LineIndex),
                new TranslatedStatement("if (outer == null)", indentationDepth + 2, classBlock.Name.LineIndex),
                new TranslatedStatement("throw new ArgumentNullException(\"outer\");", indentationDepth + 3, classBlock.Name.LineIndex),
                new TranslatedStatement(_supportRefName.Name + " = compatLayer;", indentationDepth + 2, classBlock.Name.LineIndex),
                new TranslatedStatement(_envRefName.Name + " = env;", indentationDepth + 2, classBlock.Name.LineIndex),
                new TranslatedStatement(_outerRefName.Name + " = outer;", indentationDepth + 2, classBlock.Name.LineIndex)
            });
            if (disposedFlagNameIfAny != null)
            {
                classHeaderStatements.Add(
                    new TranslatedStatement(disposedFlagNameIfAny.Name + " = false;", indentationDepth + 2, classBlock.Name.LineIndex)
                    );
            }
            classHeaderStatements.AddRange(
                explicitVariableDeclarationsFromWithinClass.Select(
                    v => new TranslatedStatement(
                        base.TranslateVariableInitialisation(v, ScopeLocationOptions.WithinClass),
                        indentationDepth + 2,
                        v.Name.LineIndex
                        )
                    )
                );
            classHeaderStatements.AddRange(classInitializeCallStatements);
            classHeaderStatements.Add(
                new TranslatedStatement("}", indentationDepth + 1, classBlock.Name.LineIndex)
                );
            if (disposeImplementationStatements.Any())
            {
                classHeaderStatements.Add(new TranslatedStatement("", indentationDepth + 1, classTerminateMethodNameIfAny.LineIndex));
                classHeaderStatements.AddRange(disposeImplementationStatements);
            }
            if (explicitVariableDeclarationsFromWithinClass.Any())
            {
                classHeaderStatements.Add(new TranslatedStatement("", indentationDepth + 1, explicitVariableDeclarationsFromWithinClass.Min(v => v.Name.LineIndex)));
                classHeaderStatements.AddRange(
                    explicitVariableDeclarationsFromWithinClass.Select(declaredVariableToInitialise =>
                                                                       new TranslatedStatement(
                                                                           string.Format(
                                                                               "{0} object {1} {{ get; set; }}",
                                                                               (declaredVariableToInitialise.Scope == VariableDeclarationScopeOptions.Private) ? "private" : "public",
                                                                               _nameRewriter.GetMemberAccessTokenName(declaredVariableToInitialise.Name)
                                                                               ),
                                                                           indentationDepth + 1,
                                                                           declaredVariableToInitialise.Name.LineIndex
                                                                           )
                                                                       )
                    );
            }
            return(classHeaderStatements);
        }
コード例 #30
0
        public TranslationResult Translate(ClassBlock classBlock, ScopeAccessInformation scopeAccessInformation, int indentationDepth)
        {
            if (classBlock == null)
            {
                throw new ArgumentNullException("classBlock");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            // Outside of classes, functions may be declared multiple times - in which case the last one will take precedence and it will be as if the
            // others never existed. Within classes, however, duplicated names are not allowed and would result in a "Name redefined" compile-time error.

            /* TODO: Fix this to allow properties and functions - no name may be present for both and none may have multiple get, let, set accessors
             * var classFunctionsWithDuplicatedNames = classBlock.Statements
             *      .Where(statement => statement is AbstractFunctionBlock)
             *      .Cast<AbstractFunctionBlock>()
             *      .GroupBy(function => function.Name.Content, StringComparer.OrdinalIgnoreCase)
             *      .Where(group => group.Count() > 1)
             *      .Select(group => group.Key);
             * if (classFunctionsWithDuplicatedNames.Any())
             *      throw new ArgumentException("The following function name are repeated with class " + classBlock.Name.Content + " (which is invalid): " + string.Join(", ", classFunctionsWithDuplicatedNames));
             */

            // Apply Class_Initialize / Class_Terminate validation - if they appear then they must be SUBs (not FUNCTIONs) and they may not have arguments
            var classInitializeMethodIfAny = classBlock.Statements
                                             .Where(statement => statement is AbstractFunctionBlock)
                                             .Cast <AbstractFunctionBlock>()
                                             .FirstOrDefault(function => function.Name.Content.Equals("Class_Initialize", StringComparison.OrdinalIgnoreCase));

            if (classInitializeMethodIfAny != null)
            {
                if (!(classInitializeMethodIfAny is SubBlock) || classInitializeMethodIfAny.Parameters.Any())
                {
                    throw new ArgumentException("A class' Class_Initialize may only be a Sub (not a Function) with zero arguments - this is not the case in class " + classBlock.Name.Content);
                }
                if (!classInitializeMethodIfAny.Statements.Any(s => !(s is INonExecutableCodeBlock)))
                {
                    // If Class_Initialize doesn't do anything then there's no point adding the complexity of calling it (same for Class_Terminate,
                    // except that there's even more complexity that can be avoided in that case - see below)
                    classInitializeMethodIfAny = null;
                }
            }
            var classTerminateMethodIfAny = classBlock.Statements
                                            .Where(statement => statement is AbstractFunctionBlock)
                                            .Cast <AbstractFunctionBlock>()
                                            .FirstOrDefault(function => function.Name.Content.Equals("Class_Terminate", StringComparison.OrdinalIgnoreCase));

            if (classTerminateMethodIfAny != null)
            {
                if (!(classTerminateMethodIfAny is SubBlock) || classTerminateMethodIfAny.Parameters.Any())
                {
                    throw new ArgumentException("A class' Class_Terminate may only be a Sub (not a Function) with zero arguments - this is not the case in class " + classBlock.Name.Content);
                }
                if (!classTerminateMethodIfAny.Statements.Any(s => !(s is INonExecutableCodeBlock)))
                {
                    // If Class_Terminate doesn't do anything then there's no point adding the complexity involved in supporting it
                    classTerminateMethodIfAny = null;
                }
            }

            // Any error-trapping in the parent scope will not apply within the class and will have to be set explicitly within the methods and
            // properties if required
            scopeAccessInformation = scopeAccessInformation.SetErrorRegistrationToken(null);

            var classContentTranslationResult = TranslateForClass(
                classBlock.Statements.ToNonNullImmutableList(),
                scopeAccessInformation.Extend(classBlock, classBlock.Statements.ToNonNullImmutableList()),
                indentationDepth + 1
                );

            if (classContentTranslationResult.UndeclaredVariablesAccessed.Any())
            {
                // Valid VBScript would not allow an undeclared variables to be present at this point. Any undeclared variables would be become
                // implicitly declared within functions or properties and may not exist at all outside of them (the only things allows within a
                // class are explicit variable declarations - Dim / Private / Public - and functions / properties).
                throw new ArgumentException("Invalid content - it should not be possible for there to be any undeclared variables within a class that aren't within one of its functions or properties");
            }
            var explicitVariableDeclarationsFromWithClass = classContentTranslationResult.ExplicitVariableDeclarations;

            classContentTranslationResult = new TranslationResult(
                classContentTranslationResult.TranslatedStatements,
                new NonNullImmutableList <VariableDeclaration>(),     // The ExplicitVariableDeclarations will be translated separately below
                new NonNullImmutableList <NameToken>()                // We've just confirmed that there will be no UndeclaredVariablesAccessed references
                );
            var translatedClassHeaderContent = TranslateClassHeader(
                classBlock,
                scopeAccessInformation,
                explicitVariableDeclarationsFromWithClass,
                (classInitializeMethodIfAny == null) ? null : classInitializeMethodIfAny.Name,
                (classTerminateMethodIfAny == null) ? null : classTerminateMethodIfAny.Name,
                indentationDepth
                );

            if (classContentTranslationResult.TranslatedStatements.Any())
            {
                translatedClassHeaderContent = translatedClassHeaderContent.Concat(new[] { new TranslatedStatement("", 0, classBlock.Name.LineIndex) });
            }
            var lineIndexOfLastStatement = classContentTranslationResult.TranslatedStatements.Any()
                                ? classContentTranslationResult.TranslatedStatements.Max(s => s.LineIndexOfStatementStartInSource)
                                : classBlock.Name.LineIndex;

            return(TranslationResult.Empty
                   .Add(translatedClassHeaderContent)
                   .Add(classContentTranslationResult)
                   .Add(new TranslatedStatement("}", indentationDepth, lineIndexOfLastStatement)));
        }