Beispiel #1
0
        /// <summary>
        /// Conceptually, converts a given <paramref name="workspace"/> into "evaluation AST" (which can next be evaluated/interpreted).
        ///
        /// In reality, this "evaluation AST" is so tightly coupled with the engine, so this method has no choice but to
        /// create a big hairball of hosts/controllers/resolvers/contexts/configurations/etc to make evaluation possible.
        ///
        /// This method tries to bypass as much of the front-end stuff as possible.  For example, it doesn't start evaluation from
        /// <see cref="FrontEndHost"/>, but instead it creates a single resolver (namely <see cref="DScriptSourceResolver"/>
        /// and uses that resolver directly to evaluate the AST.
        ///
        /// Any errors can be retrieved via the <see cref="CreateTestResult"/> method.
        /// </summary>
        public async Task <TestResult <Interpreter> > ConvertNoErrorCheckAsync(Workspace workspace, [CanBeNull] PipGraph oldPipGraph)
        {
            var nonPreludeModules = NonPreludeModules(workspace).ToArray();
            var moduleRegistry    = new ModuleRegistry(SymbolTable);

            var configStringPath = Path.Combine(SrcRoot.ToString(PathTable), Names.ConfigDsc);

            var configuration = new ConfigurationImpl()
            {
                FrontEnd =
                {
                    EnableIncrementalFrontEnd              = false,
                    ReloadPartialEngineStateWhenPossible   = false,
                    UseSpecPublicFacadeAndAstWhenAvailable = false,
                    ConstructAndSaveBindingFingerprint     = false,
                    UsePartialEvaluation                   = false,
                }
            };
            var frontEndHost = FrontEndHostController.CreateForTesting(FrontEndContext, Engine, moduleRegistry, configStringPath, FrontEndLogger);
            var frontEnd     = new DScriptFrontEnd(FrontEndStatistics, AstLogger, null);

            frontEnd.InitializeFrontEnd(frontEndHost, FrontEndContext, configuration);

            var resolver = (DScriptSourceResolver)frontEnd.CreateResolver(KnownResolverKind.DScriptResolverKind);
            var packages = nonPreludeModules.Select(module => CreatePackageForModule(module)).ToList();

            resolver.InitResolverForTesting("Test", packages);

            frontEndHost.InitializeResolvers(new[] { resolver });

            // convert all modules and assert it succeeds
            var convertTasks = nonPreludeModules.Select(module => frontEndHost.ConvertWorkspaceToEvaluationAsync(workspace));
            await Task.WhenAll(convertTasks);

            // prepare for evaluation
            var graphBuilder = new PipGraph.Builder(
                new PipTable(PathTable, SymbolTable, initialBufferSize: 16, maxDegreeOfParallelism: Environment.ProcessorCount, debug: false),
                new EngineContext(CancellationToken.None, PathTable, SymbolTable, new QualifierTable(PathTable.StringTable), FrontEndContext.FileSystem, new TokenTextTable()),
                global::BuildXL.Pips.Tracing.Logger.Log,
                FrontEndContext.LoggingContext,
                // For tests, allow writes outside of mounts unles defined otherwise
                new ConfigurationImpl()
            {
                Engine = { UnsafeAllowOutOfMountWrites = true }
            },
                new MountPathExpander(PathTable));

            IMutablePipGraph pipGraph = oldPipGraph != null
                ? new PatchablePipGraph(oldPipGraph.DataflowGraph, oldPipGraph.PipTable, graphBuilder, maxDegreeOfParallelism: Environment.ProcessorCount)
                : (IMutablePipGraph)graphBuilder;

            frontEndHost.SetState(Engine, pipGraph, configuration);

            return(new TestResult <Interpreter>(frontEndHost, Diagnostics));
        }
Beispiel #2
0
        /// <summary>
        /// Evaluates an expression in the current debugger state context
        /// </summary>
        internal Possible <ObjectContext, EvaluateFailure> EvaluateExpression(FrameContext frameContext, string expressionString)
        {
            var evalState     = (EvaluationState)m_state.GetThreadState(frameContext.ThreadId);
            var context       = evalState.Context;
            var moduleLiteral = evalState.GetEnvForFrame(frameContext.FrameIndex);

            var frontEnd = new DScriptFrontEnd(new FrontEndStatistics());

            frontEnd.InitializeFrontEnd(context.FrontEndHost, context.FrontEndContext, s_configuration);

            // We clear the logger before using it.
            var isClear = m_logger.TryClearCapturedDiagnostics();

            // This logger should only be used in the context of one thread, so it should always be possible to clear it
            Contract.Assert(isClear);

            RuntimeModelContext runtimeModelContext = new RuntimeModelContext(
                context.FrontEndHost,
                context.FrontEndContext,
                m_logger,
                context.Package);

            // We recreate the local scope so the expression is parsed using the same local variables indexes
            // than the context where it is going to be evaluated
            var localScope = BuildLocalScopeForLocalVars(context, evalState.GetStackEntryForFrame(frameContext.FrameIndex));
            var expression = s_parser.ParseExpression(runtimeModelContext, context.Package.Path, expressionString, localScope, useSemanticNameResolution: false);

            // If parsing failed, we report it and return
            // VS code only displays the first error that is sent. So we only send the first one.
            // An alternative would be to concatenate all errors and send them as a single message, but new lines are not respected by VS Code,
            // so the concatenation is not very legible.
            // Anyway, the first error should be good enough for almost all cases
            if (expression == null)
            {
                Contract.Assert(runtimeModelContext.Logger.CapturedDiagnostics.Count > 0);
                var diagnostic = runtimeModelContext.Logger.CapturedDiagnostics[0];
                return(new EvaluateFailure(diagnostic));
            }

            // We clear the logger again since it may contain warnings that didn't prevent the parser from finishing successfully
            isClear = m_logger.TryClearCapturedDiagnostics();
            Contract.Assert(isClear);

            object expressionResult;

            // We temporary override the context logger so it doesn't affect the normal evaluation
            using (var expressionContext = new SnippetEvaluationContext(context, m_logger))
            {
                expressionResult = expression.Eval(expressionContext.GetContextForSnippetEvaluation(), moduleLiteral, evalState.GetArgsForFrame(frameContext.FrameIndex)).Value;

                // If evaluation failed, we report it and return
                if (expressionResult.IsErrorValue())
                {
                    Contract.Assert(context.Logger.CapturedDiagnostics.Count > 0);
                    var diagnostic = context.Logger.CapturedDiagnostics[0];
                    return(new EvaluateFailure(diagnostic));
                }
            }

            return(new ObjectContext(context, expressionResult));
        }