Example #1
0
 public void Rewriter_that_changes_node_returns_new_tree()
 {
     var subject = InfoGroup().AddRules(Rule("rule1"), Rule("rule2"));
     var result  = LambdaRewriter.Visit(subject, node => node switch
     {
         RuleNode r => r.WithName(r.Name + r.Name),
         _ => node
     });
Example #2
0
        // Internal for testing
        internal string Parse(string code, IReadOnlyCollection<Type> moduleTypes, IEnumerable<string> namespaces)
        {
            // Rewrite the lambda shorthand expressions
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(kind: SourceCodeKind.Script));
            LambdaRewriter lambdaRewriter = new LambdaRewriter(moduleTypes.Select(x => x.Name));
            SyntaxNode syntaxNode = lambdaRewriter.Visit(syntaxTree.GetRoot());

            // "Lift" class and method declarations
            LiftingWalker liftingWalker = new LiftingWalker();
            liftingWalker.Visit(syntaxNode);

            // Get the using statements
            string usingStatements = string.Join(Environment.NewLine, namespaces.Select(x => "using " + x + ";"));

            // Get the methods to instantiate each module
            Dictionary<string, string> moduleNames = new Dictionary<string, string>();
            string moduleMethods = string.Join(Environment.NewLine,
                moduleTypes.Select(x => GenerateModuleConstructorMethods(x, moduleNames)));

            // Return the fully parsed script
            return 
                $@"// Generated: bring all module namespaces in scope
                {usingStatements}

                // Input: using directives
                {liftingWalker.UsingDirectives}

                public class {ScriptClassName} : ScriptBase
                {{
                    public {ScriptClassName}(Engine engine) : base(engine) {{ }}

                    public override void Run()
                    {{
                        // Input: script code
{liftingWalker.ScriptCode}
                    }}

                    // Input: lifted methods
{liftingWalker.MethodDeclarations}

                    // Generated: methods for module instantiation
                    {moduleMethods} 
                }}

                // Input: lifted object declarations
{liftingWalker.TypeDeclarations}

                public static class ScriptExtensionMethods
                {{
                    // Input: lifted extension methods
{liftingWalker.ExtensionMethodDeclarations}
                }}";
        }
Example #3
0
        internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
        {
            ImmutableArray <LocalSymbol> declaredLocalsArray;
            var body        = _generateMethodBody(this, diagnostics, out declaredLocalsArray);
            var compilation = compilationState.Compilation;

            _lazyReturnType = CalculateReturnType(compilation, body);

            // Can't do this until the return type has been computed.
            TypeParameterChecker.Check(this, _allTypeParameters);

            if (diagnostics.HasAnyErrors())
            {
                return;
            }

            DiagnosticsPass.IssueDiagnostics(compilation, body, diagnostics, this);
            if (diagnostics.HasAnyErrors())
            {
                return;
            }

            // Check for use-site diagnostics (e.g. missing types in the signature).
            DiagnosticInfo useSiteDiagnosticInfo = null;

            this.CalculateUseSiteDiagnostic(ref useSiteDiagnosticInfo);
            if (useSiteDiagnosticInfo != null && useSiteDiagnosticInfo.Severity == DiagnosticSeverity.Error)
            {
                diagnostics.Add(useSiteDiagnosticInfo, this.Locations[0]);
                return;
            }

            try
            {
                var declaredLocals = PooledHashSet <LocalSymbol> .GetInstance();

                try
                {
                    // Rewrite local declaration statement.
                    body = (BoundStatement)LocalDeclarationRewriter.Rewrite(compilation, _container, declaredLocals, body, declaredLocalsArray);

                    // Verify local declaration names.
                    foreach (var local in declaredLocals)
                    {
                        Debug.Assert(local.Locations.Length > 0);
                        var name = local.Name;
                        if (name.StartsWith("$", StringComparison.Ordinal))
                        {
                            diagnostics.Add(ErrorCode.ERR_UnexpectedCharacter, local.Locations[0], name[0]);
                            return;
                        }
                    }

                    // Rewrite references to placeholder "locals".
                    body = (BoundStatement)PlaceholderLocalRewriter.Rewrite(compilation, _container, declaredLocals, body, diagnostics);

                    if (diagnostics.HasAnyErrors())
                    {
                        return;
                    }
                }
                finally
                {
                    declaredLocals.Free();
                }

                var syntax            = body.Syntax;
                var statementsBuilder = ArrayBuilder <BoundStatement> .GetInstance();

                statementsBuilder.Add(body);
                // Insert an implicit return statement if necessary.
                if (body.Kind != BoundKind.ReturnStatement)
                {
                    statementsBuilder.Add(new BoundReturnStatement(syntax, RefKind.None, expressionOpt: null));
                }

                var localsBuilder = ArrayBuilder <LocalSymbol> .GetInstance();

                var localsSet = PooledHashSet <LocalSymbol> .GetInstance();

                foreach (var local in this.LocalsForBinding)
                {
                    Debug.Assert(!localsSet.Contains(local));
                    localsBuilder.Add(local);
                    localsSet.Add(local);
                }
                foreach (var local in this.Locals)
                {
                    if (!localsSet.Contains(local))
                    {
                        Debug.Assert(!localsSet.Contains(local));
                        localsBuilder.Add(local);
                        localsSet.Add(local);
                    }
                }
                localsSet.Free();

                body = new BoundBlock(syntax, localsBuilder.ToImmutableAndFree(), statementsBuilder.ToImmutableAndFree())
                {
                    WasCompilerGenerated = true
                };

                Debug.Assert(!diagnostics.HasAnyErrors());
                Debug.Assert(!body.HasErrors);

                bool sawLambdas;
                bool sawLocalFunctions;
                bool sawAwaitInExceptionHandler;
                ImmutableArray <SourceSpan> dynamicAnalysisSpans = ImmutableArray <SourceSpan> .Empty;
                body = LocalRewriter.Rewrite(
                    compilation: this.DeclaringCompilation,
                    method: this,
                    methodOrdinal: _methodOrdinal,
                    containingType: _container,
                    statement: body,
                    compilationState: compilationState,
                    previousSubmissionFields: null,
                    allowOmissionOfConditionalCalls: false,
                    instrumentForDynamicAnalysis: false,
                    debugDocumentProvider: null,
                    dynamicAnalysisSpans: ref dynamicAnalysisSpans,
                    diagnostics: diagnostics,
                    sawLambdas: out sawLambdas,
                    sawLocalFunctions: out sawLocalFunctions,
                    sawAwaitInExceptionHandler: out sawAwaitInExceptionHandler);

                Debug.Assert(!sawAwaitInExceptionHandler);
                Debug.Assert(dynamicAnalysisSpans.Length == 0);

                if (body.HasErrors)
                {
                    return;
                }

                // Variables may have been captured by lambdas in the original method
                // or in the expression, and we need to preserve the existing values of
                // those variables in the expression. This requires rewriting the variables
                // in the expression based on the closure classes from both the original
                // method and the expression, and generating a preamble that copies
                // values into the expression closure classes.
                //
                // Consider the original method:
                // static void M()
                // {
                //     int x, y, z;
                //     ...
                //     F(() => x + y);
                // }
                // and the expression in the EE: "F(() => x + z)".
                //
                // The expression is first rewritten using the closure class and local <1>
                // from the original method: F(() => <1>.x + z)
                // Then lambda rewriting introduces a new closure class that includes
                // the locals <1> and z, and a corresponding local <2>: F(() => <2>.<1>.x + <2>.z)
                // And a preamble is added to initialize the fields of <2>:
                //     <2> = new <>c__DisplayClass0();
                //     <2>.<1> = <1>;
                //     <2>.z = z;

                // Rewrite "this" and "base" references to parameter in this method.
                // Rewrite variables within body to reference existing display classes.
                body = (BoundStatement)CapturedVariableRewriter.Rewrite(
                    this.SubstitutedSourceMethod.IsStatic ? null : _parameters[0],
                    compilation.Conversions,
                    _displayClassVariables,
                    body,
                    diagnostics);

                if (body.HasErrors)
                {
                    Debug.Assert(false, "Please add a test case capturing whatever caused this assert.");
                    return;
                }

                if (diagnostics.HasAnyErrors())
                {
                    return;
                }

                if (sawLambdas || sawLocalFunctions)
                {
                    var closureDebugInfoBuilder = ArrayBuilder <ClosureDebugInfo> .GetInstance();

                    var lambdaDebugInfoBuilder = ArrayBuilder <LambdaDebugInfo> .GetInstance();

                    body = LambdaRewriter.Rewrite(
                        loweredBody: body,
                        thisType: this.SubstitutedSourceMethod.ContainingType,
                        thisParameter: _thisParameter,
                        method: this,
                        methodOrdinal: _methodOrdinal,
                        substitutedSourceMethod: this.SubstitutedSourceMethod.OriginalDefinition,
                        closureDebugInfoBuilder: closureDebugInfoBuilder,
                        lambdaDebugInfoBuilder: lambdaDebugInfoBuilder,
                        slotAllocatorOpt: null,
                        compilationState: compilationState,
                        diagnostics: diagnostics,
                        assignLocals: true);

                    // we don't need this information:
                    closureDebugInfoBuilder.Free();
                    lambdaDebugInfoBuilder.Free();
                }

                // Insert locals from the original method,
                // followed by any new locals.
                var block        = (BoundBlock)body;
                var localBuilder = ArrayBuilder <LocalSymbol> .GetInstance();

                foreach (var local in this.Locals)
                {
                    Debug.Assert(!(local is EELocalSymbol) || (((EELocalSymbol)local).Ordinal == localBuilder.Count));
                    localBuilder.Add(local);
                }
                foreach (var local in block.Locals)
                {
                    var oldLocal = local as EELocalSymbol;
                    if (oldLocal != null)
                    {
                        Debug.Assert(localBuilder[oldLocal.Ordinal] == oldLocal);
                        continue;
                    }
                    localBuilder.Add(local);
                }

                body = block.Update(localBuilder.ToImmutableAndFree(), block.LocalFunctions, block.Statements);
                TypeParameterChecker.Check(body, _allTypeParameters);
                compilationState.AddSynthesizedMethod(this, body);
            }
            catch (BoundTreeVisitor.CancelledByStackGuardException ex)
            {
                ex.AddAnError(diagnostics);
            }
        }