Example #1
0
        public void UpdateFile(string path, string text)
        {
            _parserDiagnostics.TryGetValue(path, out var previousDiagnostics);

            var syntaxTree = PhpSyntaxTree.ParseCode(SourceText.From(text, SourceEncoding), PhpParseOptions.Default, PhpParseOptions.Default, path);

            if (syntaxTree.Diagnostics.Length != 0)
            {
                _parserDiagnostics[path] = syntaxTree.Diagnostics;
            }
            else if (previousDiagnostics != null)
            {
                _parserDiagnostics.TryRemove(path, out _);
            }

            if (syntaxTree.Diagnostics.Length != 0 || previousDiagnostics != null)
            {
                // If there were any errors previously, send an empty set to remove them
                OnDocumentDiagnosticsChanged(path, syntaxTree.Diagnostics);
            }

            // Update the compilation
            if (syntaxTree.Diagnostics.All(d => d.Severity != DiagnosticSeverity.Error) && _diagnosticBroker.Compilation != null)
            {
                var currentTree = _diagnosticBroker.Compilation.SyntaxTrees.FirstOrDefault(tree => tree.FilePath == path);

                var updatedCompilation = currentTree == null
                    ? (PhpCompilation)_diagnosticBroker.Compilation.AddSyntaxTrees(syntaxTree)
                    : (PhpCompilation)_diagnosticBroker.Compilation.ReplaceSyntaxTree(currentTree, syntaxTree);

                _diagnosticBroker.UpdateCompilation(updatedCompilation);
            }
        }
Example #2
0
        private PhpSyntaxTree ParseFile(
            TextWriter consoleOutput,
            PhpParseOptions parseOptions,
            PhpParseOptions scriptParseOptions,
            ref bool hadErrors,
            CommandLineSourceFile file,
            ErrorLogger errorLogger)
        {
            var diagnosticInfos = new List <DiagnosticInfo>();
            var content         = ReadFileContent(file, diagnosticInfos);

            if (diagnosticInfos.Count != 0)
            {
                ReportErrors(diagnosticInfos, consoleOutput, errorLogger);
                hadErrors = true;
            }

            PhpSyntaxTree result = null;

            if (content != null)
            {
                result = PhpSyntaxTree.ParseCode(content.ToString(), parseOptions, scriptParseOptions, file.Path);
            }

            if (result != null && result.Diagnostics.Length != 0)
            {
                ReportErrors(result.Diagnostics, consoleOutput, errorLogger);
                hadErrors = true;
            }

            return(result);
        }
Example #3
0
        private bool CheckParameterProperties(PhpSyntaxTree syntaxTree, IEnumerable <SymbolsSelector.SymbolStat> symbolStats, MatchCollection expectedParamProps)
        {
            var positionParamMap = new Dictionary <int, SourceParameterSymbol>(
                from symbolStat in symbolStats
                let symbol = symbolStat.Symbol
                             where symbol is SourceParameterSymbol
                             select new KeyValuePair <int, SourceParameterSymbol>(symbolStat.Span.End, (SourceParameterSymbol)symbol));

            bool isCorrect = true;

            foreach (Match match in expectedParamProps)
            {
                int expectedPos = match.Index;
                if (!positionParamMap.TryGetValue(expectedPos, out var param))
                {
                    var linePos = GetLinePosition(syntaxTree.GetLineSpan(match.GetTextSpan()));
                    _output.WriteLine($"Cannot get parameter information for properties on {linePos.Line},{linePos.Character}");
                    isCorrect = false;
                    continue;
                }

                bool expectedSkipPass = (int.Parse(match.Groups[1].Value) != 0);
                bool actualSkipPass   = !param.CopyOnPass;
                if (expectedSkipPass != actualSkipPass)
                {
                    var linePos = GetLinePosition(syntaxTree.GetLineSpan(match.GetTextSpan()));
                    _output.WriteLine(
                        $"Wrong value of SkipPass {actualSkipPass} instead of {expectedSkipPass} of the parameter {param.Name} in {param.Routine.Name} on {linePos.Line},{linePos.Character}");
                    isCorrect = false;
                }
            }

            return(isCorrect);
        }
Example #4
0
        private bool CheckRoutineProperties(PhpSyntaxTree syntaxTree, IEnumerable <SourceRoutineSymbol> userDeclaredRoutines, MatchCollection expectedRoutineProps)
        {
            var positionRoutineMap = new Dictionary <int, SourceRoutineSymbol>(
                from routine in userDeclaredRoutines
                let position = (routine.Syntax as FunctionDecl)?.ParametersSpan.End ?? (routine.Syntax as MethodDecl)?.ParametersSpan.End
                               where position != null
                               select new KeyValuePair <int, SourceRoutineSymbol>(position.Value, routine));

            // Routine properties are voluntary; therefore, check only the specified ones
            bool isCorrect = true;

            foreach (Match match in expectedRoutineProps)
            {
                int expectedPos = match.Index;
                if (!positionRoutineMap.TryGetValue(expectedPos, out var routine))
                {
                    var linePos = GetLinePosition(syntaxTree.GetLineSpan(match.GetTextSpan()));
                    _output.WriteLine($"Cannot get routine information for properties on {linePos.Line},{linePos.Character}");
                    isCorrect = false;
                    continue;
                }

                int expectedVersion = int.Parse(match.Groups[1].Value);
                int actualVersion   = routine.ControlFlowGraph.FlowContext.Version;
                if (expectedVersion != actualVersion)
                {
                    var linePos = GetLinePosition(syntaxTree.GetLineSpan(match.GetTextSpan()));
                    _output.WriteLine(
                        $"Wrong final flow analysis version {actualVersion} instead of {expectedVersion} of the routine {routine.Name} on {linePos.Line},{linePos.Character}");
                    isCorrect = false;
                }
            }

            return(isCorrect);
        }
Example #5
0
        /// <summary>
        /// Compiles <paramref name="code"/> and creates script.
        /// </summary>
        /// <param name="options">Compilation options.</param>
        /// <param name="code">Code to be compiled.</param>
        /// <param name="builder">Assembly builder.</param>
        /// <param name="previousSubmissions">Enumeration of scripts that were evaluated within current context. New submission may reference them.</param>
        /// <returns>New script reepresenting the compiled code.</returns>
        public static Script Create(Context.ScriptOptions options, string code, PhpCompilationFactory builder, IEnumerable <Script> previousSubmissions)
        {
            var tree = PhpSyntaxTree.ParseCode(code,
                                               new PhpParseOptions(kind: options.IsSubmission ? SourceCodeKind.Script : SourceCodeKind.Regular),
                                               PhpParseOptions.Default,
                                               options.Location.Path);


            var diagnostics = tree.Diagnostics;

            if (!HasErrors(diagnostics))
            {
                // unique in-memory assembly name
                var name = builder.GetNewSubmissionName();

                // list of scripts that were eval'ed in the context already,
                // our compilation may depend on them
                var dependingSubmissions = previousSubmissions.Where(s => !s.Image.IsDefaultOrEmpty);
                IEnumerable <MetadataReference> metadatareferences
                    = dependingSubmissions.Select(s => MetadataReference.CreateFromImage(s.Image));

                if (options.AdditionalReferences != null)
                {
                    // add additional script references
                    metadatareferences
                        = metadatareferences.Concat(options.AdditionalReferences.Select(r => MetadataReference.CreateFromFile(r)));
                }

                // create the compilation object
                // TODO: add conditionally declared types into the compilation tables
                var compilation = (PhpCompilation)builder.CoreCompilation
                                  .WithAssemblyName(name.Name)
                                  .AddSyntaxTrees(tree)
                                  .AddReferences(metadatareferences);

                if (options.EmitDebugInformation)
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Debug).WithDebugPlusMode(true));
                }

                diagnostics = compilation.GetDeclarationDiagnostics();
                if (!HasErrors(diagnostics))
                {
                    var peStream  = new MemoryStream();
                    var pdbStream = options.EmitDebugInformation ? new MemoryStream() : null;
                    var result    = compilation.Emit(peStream, pdbStream);
                    if (result.Success)
                    {
                        return(new Script(name, peStream, pdbStream, builder, previousSubmissions));
                    }
                    else
                    {
                        diagnostics = result.Diagnostics;
                    }
                }
            }

            //
            return(CreateInvalid(diagnostics));
        }
        private void ReportMissingDiagnostic(PhpSyntaxTree syntaxTree, Match expectedMatch)
        {
            string idExpected = expectedMatch.Groups[1].Value;
            var    span       = new TextSpan(expectedMatch.Index, 0);
            var    position   = GetLinePosition(syntaxTree.GetLineSpan(span));

            _output.WriteLine($"Missing diagnostic {idExpected} on {position.Line},{position.Character}");
        }
        public void AddSyntaxTree(PhpSyntaxTree tree)
        {
            Contract.ThrowIfNull(tree);

            new BinderVisitor(_compilation, this, tree).VisitGlobalCode(tree.Source.Ast);

            _version++;
        }
        protected SourceFileSymbol(PhpCompilation compilation, PhpSyntaxTree syntaxTree)
        {
            Contract.ThrowIfNull(compilation);
            Contract.ThrowIfNull(syntaxTree);

            _compilation = compilation;
            _syntaxTree  = syntaxTree;
            _mainMethod  = new SourceGlobalMethodSymbol(this);
        }
Example #9
0
        /// <summary>
        /// Returns the offset of the location specified by (zero-based) line and character from the start of the file.
        /// In the case of invalid line, -1 is returned.
        /// </summary>
        public static int GetOffset(this PhpSyntaxTree tree, LinePosition linePosition)
        {
            if (linePosition.Line < 0 || linePosition.Line > tree.Source.LineBreaks.Count)
            {
                return(-1);
            }

            int lineStart = (linePosition.Line == 0) ? 0 : tree.Source.LineBreaks.EndOfLineBreak(linePosition.Line - 1);

            return(lineStart + linePosition.Character);
        }
Example #10
0
        public PhpSyntaxTree GetOrAddFile(string filename) => _parsedtrees.GetOrAdd(filename, _fname =>
        {
            using var fstream = File.OpenRead(filename);

            return(PhpSyntaxTree.ParseCode(
                       SourceText.From(fstream),
                       new PhpParseOptions(
                           kind: SourceCodeKind.Regular,
                           //languageVersion: CoreCompilation.Options.LanguageVersion,
                           shortOpenTags: false),
                       PhpParseOptions.Default,
                       filename
                       ));
        });
Example #11
0
        public PhpSession(string text, MirrorSharpPhpOptions options)
        {
            _text    = text;
            _options = options;

            var syntaxTree = PhpSyntaxTree.ParseCode(text, PhpParseOptions.Default, PhpParseOptions.Default, ScriptFileName);

            Compilation = (PhpCompilation)CoreCompilation.AddSyntaxTrees(syntaxTree);

            if (options.Debug == false)
            {
                Compilation = Compilation.WithPhpOptions(Compilation.Options.WithOptimizationLevel(PeachpieRoslyn.OptimizationLevel.Release));
            }
        }
Example #12
0
        private bool CheckTypes(PhpSyntaxTree syntaxTree, IEnumerable <SymbolsSelector.SymbolStat> symbolStats, MatchCollection expectedTypes)
        {
            var positionSymbolMap = new Dictionary <int, SymbolsSelector.SymbolStat>();

            foreach (var stat in symbolStats)
            {
                positionSymbolMap.TryAdd(stat.Span.Start, stat);
            }

            // Type annotation is voluntary; therefore, check only the specified types
            bool isCorrect = true;

            foreach (Match match in expectedTypes)
            {
                // The text of symbol should start where the annotation ends
                int expectedPos = match.Index + match.Length;
                if (!positionSymbolMap.TryGetValue(expectedPos, out var symbolStat))
                {
                    var linePos = GetLinePosition(syntaxTree.GetLineSpan(match.GetTextSpan()));
                    _output.WriteLine($"Cannot get type information for type annotation on {linePos.Line},{linePos.Character}");
                    isCorrect = false;
                    continue;
                }

                // Obtain the type of the given symbol or expression
                string actualType = GetTypeString(symbolStat);
                if (string.IsNullOrEmpty(actualType))
                {
                    var linePos = GetLinePosition(syntaxTree.GetLineSpan(symbolStat.Span.ToTextSpan()));
                    _output.WriteLine($"Unable to get the type of the symbol on {linePos.Line},{linePos.Character}");
                    isCorrect = false;
                    continue;
                }

                // Reorder expected types alphabetically
                string expectedType = string.Join("|", match.Groups[1].Value.Split('|').OrderBy(s => s));

                // Report any problem
                if (actualType != expectedType)
                {
                    var linePos = GetLinePosition(syntaxTree.GetLineSpan(symbolStat.Span.ToTextSpan()));
                    _output.WriteLine(
                        $"Wrong type {actualType} instead of {expectedType} of the expression on {linePos.Line},{linePos.Character}");
                    isCorrect = false;
                }
            }

            return(isCorrect);
        }
Example #13
0
        public void ReplaceText(string newText, int start = 0, int?length = null)
        {
            if (length > 0)
            {
                _text = _text.Remove(start, length.Value);
            }
            if (newText?.Length > 0)
            {
                _text = _text.Insert(start, newText);
            }

            var syntaxTree = PhpSyntaxTree.ParseCode(_text, PhpParseOptions.Default, PhpParseOptions.Default, ScriptFileName);

            Compilation = (PhpCompilation)Compilation.ReplaceSyntaxTree(Compilation.SyntaxTrees.Single(), syntaxTree);
        }
Example #14
0
        public void DiagnosticRunTest(string dir, string fname)
        {
            var path = Path.Combine(dir, fname);

            _output.WriteLine("Analysing {0} ...", path);

            string code       = File.ReadAllText(path);
            var    syntaxTree = PhpSyntaxTree.ParseCode(code, PhpParseOptions.Default, PhpParseOptions.Default, path);

            var compilation = _emptyCompilation.AddSyntaxTrees(syntaxTree);
            var actual      = compilation.GetDiagnostics()
                              .OrderBy(diag => diag.Location.SourceSpan.Start)
                              .ToArray();

            var expected = DiagnosticAnnotationRegex.Matches(code);

            CheckDiagnostics(syntaxTree, actual, expected);
        }
Example #15
0
        public void DiagnosticRunTest(string dir, string fname)
        {
            var path = Path.Combine(dir, fname);

            _output.WriteLine("Analysing {0} ...", path);

            var code        = File.ReadAllText(path);
            var syntaxTree  = PhpSyntaxTree.ParseCode(SourceText.From(code, Encoding.UTF8), PhpParseOptions.Default, PhpParseOptions.Default, path);
            var compilation = (PhpCompilation)EmptyCompilation.AddSyntaxTrees(syntaxTree);

            bool isCorrect = true;

            // Gather and check diagnostics
            var expectedDiags = DiagnosticAnnotationRegex.Matches(code);
            var actualDiags   = compilation.GetDiagnostics()
                                .OrderBy(diag => diag.Location.SourceSpan.Start)
                                .ToArray();

            isCorrect &= CheckDiagnostics(syntaxTree, actualDiags, expectedDiags);

            // Gather and check types if there are any annotations
            var expectedTypes = TypeAnnotationRegex.Matches(code);

            if (expectedTypes.Count > 0)
            {
                var symbolsInfo = compilation.UserDeclaredRoutines
                                  .Where(routine => routine.ControlFlowGraph != null)
                                  .Select(routine => SymbolsSelector.Select(routine.ControlFlowGraph))
                                  .Concat(compilation.UserDeclaredRoutines.Select(routine => SymbolsSelector.Select(routine))) // routine declarations
                                  .Concat(compilation.UserDeclaredTypes.Select(type => SymbolsSelector.Select(type)))          // type declarations
                                  .SelectMany(enumerators => enumerators);                                                     // IEnumerable<IEnumerable<T>> => IEnumerable<T>
                isCorrect &= CheckTypes(syntaxTree, symbolsInfo, expectedTypes);
            }

            // Gather and check routine properties if there are any annotations
            var expectedRoutineProps = RoutinePropertiesRegex.Matches(code);

            if (expectedRoutineProps.Count > 0)
            {
                isCorrect &= CheckRoutineProperties(syntaxTree, compilation.SourceSymbolCollection.AllRoutines, expectedRoutineProps);
            }

            Assert.True(isCorrect);
        }
Example #16
0
        internal bool ParseFiles()
        {
            using (Operation engineOp = L.Begin("Parsing {0} PHP file(s).", FileCount))
            {
                SyntaxTrees = new PhpSyntaxTree[FileCount];
                bool hasErrors = false;
                ExecuteConcurrentOperation((i) =>
                {
                    PhpSyntaxTree result = PhpSyntaxTree.ParseCode(FileText[i], PhpParseOptions.Default.WithDocumentationMode(DocumentationMode.Diagnose), PhpParseOptions.Default.WithKind(SourceCodeKind.Script), Files[i].Path);
                    if (result == null)
                    {
                        L.Error("Unknown error parsing file {0}.", Files[i]);
                        hasErrors = true;
                        return(false);
                    }
                    else if (result != null && result.Diagnostics.HasAnyErrors())
                    {
                        L.Error("Error(s) reported parsing file {0}: {1}", Files[i].Path, result.Diagnostics);
                        hasErrors = true;
                        return(false);
                    }
                    else
                    {
                        SyntaxTrees[i] = result;
                        return(true);
                    }
                }, FileCount);

                if (hasErrors)
                {
                    return(false);
                }
                else
                {
                    engineOp.Complete();
                    return(true);
                }
            }
        }
Example #17
0
        private bool CheckOperationProperties(PhpSyntaxTree syntaxTree, IEnumerable <IPhpOperation> interestingOps, MatchCollection expectedOpProps)
        {
            var copyPositionSet = new HashSet <int>(
                interestingOps
                .OfType <BoundCopyValue>()
                .Select(c => c.Expression.PhpSyntax.Span.End));

            bool isCorrect = true;

            foreach (Match match in expectedOpProps)
            {
                bool expectedSkipCopy = (int.Parse(match.Groups[1].Value) != 0);
                bool actualSkipCopy   = !copyPositionSet.Contains(match.Index);
                if (expectedSkipCopy != actualSkipCopy)
                {
                    var linePos = GetLinePosition(syntaxTree.GetLineSpan(match.GetTextSpan()));
                    _output.WriteLine(
                        $"Wrong value of copy skipping {actualSkipCopy} instead of {expectedSkipCopy} of the expression on {linePos.Line},{linePos.Character}");
                    isCorrect = false;
                }
            }

            return(isCorrect);
        }
Example #18
0
        private static PhpSyntaxTree[] ParseSourceFiles(ProjectInstance projectInstance, Encoding encoding)
        {
            var sourceFiles = projectInstance
                              .GetItems("Compile")
                              .Select(x => Path.Combine(projectInstance.Directory, x.EvaluatedInclude)).ToArray();

            var syntaxTrees = new PhpSyntaxTree[sourceFiles.Length];

            Parallel.For(0, sourceFiles.Length, i =>
            {
                var path = PathUtils.NormalizePath(sourceFiles[i]);
                if (path.EndsWith(".phar"))
                {
                    // TODO: process phar archives
                    syntaxTrees[i] = PhpSyntaxTree.ParseCode(SourceText.From(string.Empty), PhpParseOptions.Default, PhpParseOptions.Default, path);
                }
                else
                {
                    syntaxTrees[i] = PhpSyntaxTree.ParseCode(SourceText.From(File.OpenRead(path), encoding), PhpParseOptions.Default, PhpParseOptions.Default, path);
                }
            });

            return(syntaxTrees);
        }
Example #19
0
        public override Compilation CreateCompilation(TextWriter consoleOutput, TouchedFileLogger touchedFilesLogger, ErrorLogger errorLogger)
        {
            bool hadErrors   = false;
            var  sourceFiles = Arguments.SourceFiles;

            IEnumerable <PhpSyntaxTree> sourceTrees;
            var resources = Enumerable.Empty <ResourceDescription>();

            using (Arguments.CompilationOptions.EventSources.StartMetric("parse"))
            {
                // PARSE

                var parseOptions = Arguments.ParseOptions;

                // NOTE: order of trees is important!!
                var trees     = new PhpSyntaxTree[sourceFiles.Length];
                var pharFiles = new List <(int index, ParsedSource phar)>();

                void ProcessParsedSource(int index, ParsedSource parsed)
                {
                    if (parsed.Manifest == null)
                    {
                        trees[index] = parsed.SyntaxTree;
                    }
                    else
                    {
                        pharFiles.Add((index, parsed));
                    }
                }

                // We compute script parse options once so we don't have to do it repeatedly in
                // case there are many script files.
                var scriptParseOptions = parseOptions.WithKind(SourceCodeKind.Script);

                if (Arguments.CompilationOptions.ConcurrentBuild)
                {
                    Parallel.For(0, sourceFiles.Length, new Action <int>(i =>
                    {
                        ProcessParsedSource(i, ParseFile(consoleOutput, parseOptions, scriptParseOptions, ref hadErrors, sourceFiles[i], errorLogger));
                    }));
                }
                else
                {
                    for (int i = 0; i < sourceFiles.Length; i++)
                    {
                        ProcessParsedSource(i, ParseFile(consoleOutput, parseOptions, scriptParseOptions, ref hadErrors, sourceFiles[i], errorLogger));
                    }
                }

                // flattern trees and pharFiles

                if (pharFiles == null || pharFiles.Count == 0)
                {
                    sourceTrees = trees;
                }
                else
                {
                    var treesList = new List <PhpSyntaxTree>(trees);

                    // enlist phars from the end (index)
                    foreach (var f in pharFiles.OrderByDescending(x => x.index))
                    {
                        treesList[f.index] = f.phar.SyntaxTree; // phar stub, may be null
                        treesList.InsertRange(f.index + 1, f.phar.Trees);

                        // add content files
                        if (f.phar.Resources != null)
                        {
                            resources = resources.Concat(f.phar.Resources);
                        }
                    }

                    sourceTrees = treesList;
                }

                // END PARSE
            }

            // If errors had been reported in ParseFile, while trying to read files, then we should simply exit.
            if (hadErrors)
            {
                return(null);
            }

            var diagnostics = new List <DiagnosticInfo>();

            var assemblyIdentityComparer = DesktopAssemblyIdentityComparer.Default;

            var xmlFileResolver    = new LoggingXmlFileResolver(Arguments.BaseDirectory, touchedFilesLogger);
            var sourceFileResolver = new LoggingSourceFileResolver(ImmutableArray <string> .Empty, Arguments.BaseDirectory, Arguments.PathMap, touchedFilesLogger);

            MetadataReferenceResolver referenceDirectiveResolver;
            var resolvedReferences = ResolveMetadataReferences(diagnostics, touchedFilesLogger, out referenceDirectiveResolver);

            if (ReportErrors(diagnostics, consoleOutput, errorLogger))
            {
                return(null);
            }

            //
            var referenceResolver  = GetCommandLineMetadataReferenceResolver(touchedFilesLogger);
            var loggingFileSystem  = new LoggingStrongNameFileSystem(touchedFilesLogger);
            var strongNameProvider = Arguments.GetStrongNameProvider(loggingFileSystem, _tempDirectory);

            var compilation = PhpCompilation.Create(
                Arguments.CompilationName,
                sourceTrees.WhereNotNull(),
                resolvedReferences,
                resources: resources,
                options: Arguments.CompilationOptions.
                WithMetadataReferenceResolver(referenceResolver).
                WithAssemblyIdentityComparer(assemblyIdentityComparer).
                WithStrongNameProvider(strongNameProvider).
                WithXmlReferenceResolver(xmlFileResolver).
                WithSourceReferenceResolver(sourceFileResolver)
                );

            return(compilation);
        }
Example #20
0
        private ParsedSource ParseFile(
            TextWriter consoleOutput,
            PhpParseOptions parseOptions,
            PhpParseOptions scriptParseOptions,
            ref bool hadErrors,
            CommandLineSourceFile file,
            ErrorLogger errorLogger)
        {
            if (file.Path.IsPharFile())
            {
                // phar file archive
                var phar = Devsense.PHP.Phar.PharFile.OpenPharFile(file.Path); // TODO: report exception

                // treat the stub as a regular source code:
                var stub = PhpSyntaxTree.ParseCode(SourceText.From(GetPharStub(phar), Encoding.UTF8), parseOptions, scriptParseOptions, file.Path);

                // TODO: ConcurrentBuild -> Parallel

                var prefix  = PhpFileUtilities.NormalizeSlashes(PhpFileUtilities.GetRelativePath(file.Path, Arguments.BaseDirectory));
                var trees   = new List <PhpSyntaxTree>();
                var content = new List <Devsense.PHP.Phar.Entry>();

                foreach (var entry in phar.Manifest.Entries.Values)
                {
                    var entryName = PhpFileUtilities.NormalizeSlashes(entry.Name);

                    if (entry.IsCompileEntry())
                    {
                        var tree = PhpSyntaxTree.ParseCode(SourceText.From(entry.Code, Encoding.UTF8), parseOptions, scriptParseOptions, prefix + "/" + entryName);
                        tree.PharStubFile = stub;
                        trees.Add(tree);
                    }
                    else
                    {
                        content.Add(entry);
                    }
                }

                // create resource file
                var resources = new ResourceDescription($"phar://{prefix}.resources", () =>
                {
                    var stream = new MemoryStream();
                    var writer = new System.Resources.ResourceWriter(stream);

                    foreach (var entry in content)
                    {
                        var entryName = PhpFileUtilities.NormalizeSlashes(entry.Name);
                        writer.AddResource(entryName, entry.Code);
                    }

                    //
                    writer.Generate();
                    stream.Position = 0;
                    return(stream);
                }, isPublic: true);

                // TODO: report errors if any

                return(new ParsedSource {
                    SyntaxTree = stub, Manifest = phar.Manifest, Trees = trees, Resources = resources,
                });
            }
            else
            {
                // single source file

                var diagnosticInfos = new List <DiagnosticInfo>();
                var content         = TryReadFileContent(file, diagnosticInfos);

                if (diagnosticInfos.Count != 0)
                {
                    ReportErrors(diagnosticInfos, consoleOutput, errorLogger);
                    hadErrors = true;
                }

                PhpSyntaxTree result = null;

                if (content != null)
                {
                    result = PhpSyntaxTree.ParseCode(content, parseOptions, scriptParseOptions, file.Path);
                }

                if (result != null && result.Diagnostics.HasAnyErrors())
                {
                    ReportErrors(result.Diagnostics, consoleOutput, errorLogger);
                    hadErrors = true;
                }

                return(new ParsedSource {
                    SyntaxTree = result
                });
            }
        }
Example #21
0
 internal PhpSyntaxReference(PhpSyntaxTree tree, LangElement node)
 {
     _tree = tree;
     _node = node;
 }
        /// <summary>
        /// Compiles <paramref name="code"/> and creates script.
        /// </summary>
        /// <param name="options">Compilation options.</param>
        /// <param name="code">Code to be compiled.</param>
        /// <param name="builder">Assembly builder.</param>
        /// <param name="previousSubmissions">Enumeration of scripts that were evaluated within current context. New submission may reference them.</param>
        /// <returns>New script reepresenting the compiled code.</returns>
        public static Script Create(Context.ScriptOptions options, string code, PhpCompilationFactory builder, IEnumerable <Script> previousSubmissions)
        {
            // use the language version of the requesting context
            Version languageVersion = null;
            bool    shortOpenTags   = false;

            var language = options.Context.TargetPhpLanguage;

            if (language != null)
            {
                shortOpenTags = language.ShortOpenTag;
                Version.TryParse(language.LanguageVersion, out languageVersion);
            }

            // parse the source code
            var tree = PhpSyntaxTree.ParseCode(
                SourceText.From(code, Encoding.UTF8),
                new PhpParseOptions(
                    kind: options.IsSubmission ? SourceCodeKind.Script : SourceCodeKind.Regular,
                    languageVersion: languageVersion,
                    shortOpenTags: shortOpenTags),
                PhpParseOptions.Default,
                options.Location.Path);

            var diagnostics = tree.Diagnostics;

            if (!HasErrors(diagnostics))
            {
                // TODO: collect required types from {tree}, remember as a script dependencies
                // TODO: perform class autoload (now before compilation, and then always before invocation)

                // unique in-memory assembly name
                var name = builder.GetNewSubmissionName();

                // list of scripts that were eval'ed in the context already,
                // our compilation may depend on them
                var dependingSubmissions = previousSubmissions.Where(s => !s.Image.IsDefaultOrEmpty);
                IEnumerable <MetadataReference> metadatareferences
                    = dependingSubmissions.Select(s => MetadataReference.CreateFromImage(s.Image));

                if (options.AdditionalReferences != null)
                {
                    // add additional script references
                    metadatareferences
                        = metadatareferences.Concat(options.AdditionalReferences.Select(r => MetadataReference.CreateFromFile(r)));
                }

                // create the compilation object
                // TODO: add conditionally declared types into the compilation tables
                var compilation = (PhpCompilation)builder.CoreCompilation
                                  .WithAssemblyName(name.Name)
                                  .AddSyntaxTrees(tree)
                                  .AddReferences(metadatareferences);

                var emitOptions   = new EmitOptions();
                var embeddedTexts = default(IEnumerable <EmbeddedText>);

                if (options.EmitDebugInformation)
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Debug).WithDebugPlusMode(true));
                    //emitOptions = emitOptions.WithDebugInformationFormat(DebugInformationFormat.PortablePdb);
                    //embeddedTexts = new[] { EmbeddedText.FromSource(tree.FilePath, tree.GetText()) };
                }
                else
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Release));
                }

                diagnostics = compilation.GetDeclarationDiagnostics();
                if (!HasErrors(diagnostics))
                {
                    var peStream  = new MemoryStream();
                    var pdbStream = options.EmitDebugInformation ? new MemoryStream() : null;

                    var result = compilation.Emit(peStream,
                                                  pdbStream: pdbStream,
                                                  options: emitOptions,
                                                  embeddedTexts: embeddedTexts
                                                  );

                    if (result.Success)
                    {
                        return(new Script(name, peStream, pdbStream, builder, previousSubmissions));
                    }
                    else
                    {
                        diagnostics = result.Diagnostics;
                    }
                }
            }

            //
            return(CreateInvalid(diagnostics));
        }
        public void AddSyntaxTree(PhpSyntaxTree tree)
        {
            Contract.ThrowIfNull(tree);

            Debug.Assert(tree.Root != null);

            // create file symbol (~ php script containing type)
            var fsymbol = new SourceFileSymbol(_compilation, tree);

            if (FirstScript == null)
            {
                FirstScript = fsymbol;
            }

            // collect type declarations
            foreach (var t in tree.Types)
            {
                var typesymbol = (t is AnonymousTypeDecl)
                    ? new SourceAnonymousTypeSymbol(fsymbol, (AnonymousTypeDecl)t)
                    : new SourceTypeSymbol(fsymbol, t);

                t.SetProperty(typesymbol);    // remember bound type symbol
                fsymbol.ContainedTypes.Add(typesymbol);
            }

            // annotate routines that contain yield
            if (!tree.YieldNodes.IsDefaultOrEmpty)
            {
                foreach (var y in tree.YieldNodes)
                {
                    var containingroutine = y.GetContainingRoutine();
                    Debug.Assert(containingroutine != null);
                    // TODO: annotate routine as generator ! so it can be bound as generator already
                }
            }

            //
            foreach (var f in tree.Functions)
            {
                var routine = new SourceFunctionSymbol(fsymbol, f);

                f.SetProperty(routine); // remember bound function symbol
                fsymbol.AddFunction(routine);
            }

            //
            foreach (var l in tree.Lambdas)
            {
                var containingtype = l.ContainingType;
                var container      = (containingtype != null) ? (NamedTypeSymbol)containingtype.GetProperty <SourceTypeSymbol>() : fsymbol;

                var lambdasymbol = new SourceLambdaSymbol(l, container, !l.IsStatic);
                Debug.Assert(container is ILambdaContainerSymbol);
                ((ILambdaContainerSymbol)container).AddLambda(lambdasymbol);
            }

            //
            _files.Add(fsymbol.RelativeFilePath, fsymbol);
            _ordinalMap.Add(tree, _ordinalMap.Count);
            _version++;
        }
        /// <summary>
        /// Compiles <paramref name="code"/> and creates script.
        /// </summary>
        /// <param name="options">Compilation options.</param>
        /// <param name="code">Code to be compiled.</param>
        /// <param name="builder">Assembly builder.</param>
        /// <param name="previousSubmissions">Enumeration of scripts that were evaluated within current context. New submission may reference them.</param>
        /// <returns>New script reepresenting the compiled code.</returns>
        public static Script Create(Context.ScriptOptions options, string code, PhpCompilationFactory builder, IEnumerable <Script> previousSubmissions)
        {
            // use the language version of the requesting context
            var  languageVersion = options.LanguageVersion;
            bool shortOpenTags   = false;

            var language = options.Context.TargetPhpLanguage;

            if (language != null)
            {
                shortOpenTags = language.ShortOpenTag;
                languageVersion ??= language.LanguageVersion;
            }

            // unique in-memory assembly name
            var name = builder.GetNewSubmissionName();

            // submission do not have the opening "<?php" script tag:
            var kind = options.IsSubmission ? SourceCodeKind.Script : SourceCodeKind.Regular;

            if (kind == SourceCodeKind.Script && options.EmitDebugInformation)
            {
                // since submission do not have the opening "<?php" tag,
                // add a comment with the opening tag, so source code editors don't get confused and colorize the code properly:
                code = $"#<?php\n{code}";
            }

            // parse the source code
            var tree = PhpSyntaxTree.ParseCode(
                SourceText.From(code, Encoding.UTF8, SourceHashAlgorithm.Sha256),
                new PhpParseOptions(
                    kind: kind,
                    languageVersion: languageVersion,
                    shortOpenTags: shortOpenTags),
                PhpParseOptions.Default,
                options.IsSubmission ? BuildSubmissionFileName(options.Location.Path, name.Name) : options.Location.Path
                );

            var diagnostics = tree.Diagnostics;

            if (!HasErrors(diagnostics))
            {
                // TODO: collect required types from {tree}, remember as a script dependencies
                // TODO: perform class autoload (now before compilation, and then always before invocation)

                // list of scripts that were eval'ed in the context already,
                // our compilation may depend on them
                var dependingSubmissions = previousSubmissions.Where(s => !s.Image.IsDefaultOrEmpty);
                IEnumerable <MetadataReference> metadatareferences
                    = dependingSubmissions.Select(s => MetadataReference.CreateFromImage(s.Image));

                if (options.AdditionalReferences != null)
                {
                    // add additional script references
                    metadatareferences
                        = metadatareferences.Concat(options.AdditionalReferences.Select(r => MetadataReference.CreateFromFile(r)));
                }

                // create the compilation object
                // TODO: add conditionally declared types into the compilation tables
                var compilation = (PhpCompilation)builder.CoreCompilation
                                  .WithLangVersion(languageVersion)
                                  .WithAssemblyName(name.Name)
                                  .AddSyntaxTrees(tree)
                                  .AddReferences(metadatareferences);

                var emitOptions   = new EmitOptions();
                var embeddedTexts = default(IEnumerable <EmbeddedText>);

                if (options.EmitDebugInformation)
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Debug).WithDebugPlusMode(true));

                    if (options.IsSubmission)
                    {
                        emitOptions   = emitOptions.WithDebugInformationFormat(DebugInformationFormat.PortablePdb);
                        embeddedTexts = new[] { EmbeddedText.FromSource(tree.FilePath, tree.GetText()) };
                    }
                }
                else
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Release));
                }

                diagnostics = compilation.GetDeclarationDiagnostics();
                if (!HasErrors(diagnostics))
                {
                    var peStream  = new MemoryStream();
                    var pdbStream = options.EmitDebugInformation ? new MemoryStream() : null;

                    var result = compilation.Emit(peStream,
                                                  pdbStream: pdbStream,
                                                  options: emitOptions,
                                                  embeddedTexts: embeddedTexts
                                                  );

                    if (result.Success)
                    {
                        //if (pdbStream != null)
                        //{
                        //    // DEBUG DUMP
                        //    var fname = @"C:\Users\jmise\OneDrive\Desktop\" + Path.GetFileNameWithoutExtension(tree.FilePath);
                        //    File.WriteAllBytes(fname + ".dll", peStream.ToArray());
                        //    File.WriteAllBytes(fname + ".pdb", pdbStream.ToArray());
                        //}

                        return(new Script(name, peStream, pdbStream, builder, previousSubmissions));
                    }
                    else
                    {
                        diagnostics = result.Diagnostics;
                    }
                }
            }

            //
            return(CreateInvalid(diagnostics));
        }
Example #25
0
        private void CheckDiagnostics(PhpSyntaxTree syntaxTree, Diagnostic[] actual, MatchCollection expected)
        {
            // Compare in ascending order to systematically find all discrepancies
            bool isCorrect = true;
            int  iActual   = 0;
            int  iExpected = 0;

            while (iActual < actual.Length && iExpected < expected.Count)
            {
                // The comment is right after the expected diagnostic
                int posActual   = actual[iActual].Location.SourceSpan.End;
                int posExpected = expected[iExpected].Index;

                if (posActual < posExpected)
                {
                    isCorrect = false;
                    ReportUnexpectedDiagnostic(actual[iActual]);

                    iActual++;
                }
                else if (posActual > posExpected)
                {
                    isCorrect = false;
                    ReportMissingDiagnostic(syntaxTree, expected[iExpected]);

                    iExpected++;
                }
                else
                {
                    string idActual   = actual[iActual].Id;
                    string idExpected = expected[iExpected].Groups[1].Value;

                    if (idActual != idExpected)
                    {
                        isCorrect = false;
                        ReportWrongDiagnosticId(actual[iActual], idActual, idExpected);
                    }

                    iActual++;
                    iExpected++;
                }
            }

            // Process the remainder if present
            if (iActual < actual.Length)
            {
                isCorrect = false;

                for (; iActual < actual.Length; iActual++)
                {
                    ReportUnexpectedDiagnostic(actual[iActual]);
                }
            }
            else if (iExpected < expected.Count)
            {
                isCorrect = false;

                for (; iExpected < expected.Count; iExpected++)
                {
                    ReportMissingDiagnostic(syntaxTree, expected[iExpected]);
                }
            }

            Assert.True(isCorrect);
        }
Example #26
0
 public ErrorSink(PhpSyntaxTree syntaxTree)
 {
     _syntaxTree = syntaxTree;
 }
Example #27
0
        public void AddSyntaxTree(PhpSyntaxTree tree)
        {
            Contract.ThrowIfNull(tree);

            Debug.Assert(tree.Root != null);

            // create file symbol (~ php script containing type)
            var fsymbol = new SourceFileSymbol(_compilation, tree);

            if (FirstScript == null)
            {
                FirstScript = fsymbol;
            }

            // collect type declarations
            foreach (var t in tree.Types)
            {
                var typesymbol = (t is AnonymousTypeDecl)
                    ? new SourceAnonymousTypeSymbol(fsymbol, (AnonymousTypeDecl)t)
                    : new SourceTypeSymbol(fsymbol, t);

                t.SetProperty(typesymbol);    // remember bound type symbol
                fsymbol.ContainedTypes.Add(typesymbol);
            }

            // annotate routines that contain yield
            if (!tree.YieldNodes.IsDefaultOrEmpty)
            {
                var yieldsInRoutines = new Dictionary <LangElement, List <IYieldLikeEx> >();
                foreach (var y in tree.YieldNodes)
                {
                    Debug.Assert(y is IYieldLikeEx);
                    var yield = y as IYieldLikeEx;

                    var containingRoutine = y.GetContainingRoutine();
                    Debug.Assert(containingRoutine != null);

                    if (!yieldsInRoutines.ContainsKey(containingRoutine))
                    {
                        yieldsInRoutines.Add(containingRoutine, new List <IYieldLikeEx>());
                    }
                    yieldsInRoutines[containingRoutine].Add(yield);
                }

                foreach (var yieldsInRoutine in yieldsInRoutines)
                {
                    var routine = yieldsInRoutine.Key;
                    var yields  = yieldsInRoutine.Value;

                    routine.Properties.SetProperty(typeof(ImmutableArray <IYieldLikeEx>), yields.ToImmutableArray());
                }
            }

            //
            foreach (var f in tree.Functions)
            {
                var routine = new SourceFunctionSymbol(fsymbol, f);

                f.SetProperty(routine); // remember bound function symbol
                fsymbol.AddFunction(routine);
            }

            //
            foreach (var l in tree.Lambdas)
            {
                var containingtype = l.ContainingType;
                var container      = (containingtype != null) ? (NamedTypeSymbol)containingtype.GetProperty <SourceTypeSymbol>() : fsymbol;

                var lambdasymbol = new SourceLambdaSymbol(l, container, !l.IsStatic);
                Debug.Assert(container is ILambdaContainerSymbol);
                ((ILambdaContainerSymbol)container).AddLambda(lambdasymbol);
            }

            //
            _files.Add(fsymbol.RelativeFilePath, fsymbol);
            _ordinalMap.Add(tree, _ordinalMap.Count);
            _version++;
        }
 public SourcePharEntrySymbol(PhpCompilation compilation, PhpSyntaxTree syntaxTree)
     : base(compilation, syntaxTree)
 {
     Debug.Assert(syntaxTree.PharStubFile != null);
 }
Example #29
0
        public override Compilation CreateCompilation(TextWriter consoleOutput, TouchedFileLogger touchedFilesLogger, ErrorLogger errorLogger)
        {
            var parseOptions = Arguments.ParseOptions;

            // We compute script parse options once so we don't have to do it repeatedly in
            // case there are many script files.
            var scriptParseOptions = parseOptions.WithKind(SourceCodeKind.Script);

            bool hadErrors = false;

            var sourceFiles = Arguments.SourceFiles;
            var trees       = new PhpSyntaxTree[sourceFiles.Length];

            if (Arguments.CompilationOptions.ConcurrentBuild)
            {
                Parallel.For(0, sourceFiles.Length, new Action <int>(i =>
                {
                    //NOTE: order of trees is important!!
                    trees[i] = ParseFile(consoleOutput, parseOptions, scriptParseOptions, ref hadErrors, sourceFiles[i], errorLogger);
                }));
            }
            else
            {
                for (int i = 0; i < sourceFiles.Length; i++)
                {
                    //NOTE: order of trees is important!!
                    trees[i] = ParseFile(consoleOutput, parseOptions, scriptParseOptions, ref hadErrors, sourceFiles[i], errorLogger);
                }
            }

            // If errors had been reported in ParseFile, while trying to read files, then we should simply exit.
            if (hadErrors)
            {
                return(null);
            }

            var diagnostics = new List <DiagnosticInfo>();

            var assemblyIdentityComparer = DesktopAssemblyIdentityComparer.Default;

            var xmlFileResolver    = new LoggingXmlFileResolver(Arguments.BaseDirectory, touchedFilesLogger);
            var sourceFileResolver = new LoggingSourceFileResolver(ImmutableArray <string> .Empty, Arguments.BaseDirectory, Arguments.PathMap, touchedFilesLogger);

            MetadataReferenceResolver referenceDirectiveResolver;
            var resolvedReferences = ResolveMetadataReferences(diagnostics, touchedFilesLogger, out referenceDirectiveResolver);

            if (ReportErrors(diagnostics, consoleOutput, errorLogger))
            {
                return(null);
            }

            var referenceResolver  = GetCommandLineMetadataReferenceResolver(touchedFilesLogger);
            var strongNameProvider = new LoggingStrongNameProvider(Arguments.KeyFileSearchPaths, touchedFilesLogger);

            var compilation = PhpCompilation.Create(
                Arguments.CompilationName,
                trees.WhereNotNull(),
                resolvedReferences,
                Arguments.CompilationOptions.
                WithMetadataReferenceResolver(referenceResolver).
                WithAssemblyIdentityComparer(assemblyIdentityComparer).
                WithStrongNameProvider(strongNameProvider).
                WithXmlReferenceResolver(xmlFileResolver).
                WithSourceReferenceResolver(sourceFileResolver)
                );

            return(compilation);
        }
 public static SourceFileSymbol Create(PhpCompilation compilation, PhpSyntaxTree syntaxTree)
 {
     return(syntaxTree.IsPharEntry
         ? new SourcePharEntrySymbol(compilation, syntaxTree)
         : new SourceFileSymbol(compilation, syntaxTree));
 }