/// <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)) )); }
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) )); }
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); }