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); } }
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); }
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); }
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); }
/// <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); }
/// <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); }
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 )); });
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)); } }
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); }
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); }
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); }
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); }
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); } } }
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); }
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); }
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); }
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 }); } }
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)); }
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); }
public ErrorSink(PhpSyntaxTree syntaxTree) { _syntaxTree = syntaxTree; }
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); }
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)); }