82 changed files with 2476 additions and 1338 deletions
@ -0,0 +1,249 @@
@@ -0,0 +1,249 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Text.RegularExpressions; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
|
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.Decompiler; |
||||
using ICSharpCode.Decompiler.Ast; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.SharpDevelop; |
||||
using ICSharpCode.SharpDevelop.Parser; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.ILSpyAddIn |
||||
{ |
||||
/// <summary>
|
||||
/// Description of DecompilerService.
|
||||
/// </summary>
|
||||
public static class ILSpyDecompilerService |
||||
{ |
||||
class ModuleCacheInfo |
||||
{ |
||||
public readonly DateTime LastUpdateTime; |
||||
public readonly WeakReference<ModuleDefinition> Module; |
||||
|
||||
public ModuleCacheInfo(DateTime lastUpdateTime, ModuleDefinition assembly) |
||||
{ |
||||
if (assembly == null) |
||||
throw new ArgumentNullException("assembly"); |
||||
this.LastUpdateTime = lastUpdateTime; |
||||
this.Module = new WeakReference<ModuleDefinition>(assembly); |
||||
} |
||||
} |
||||
|
||||
static readonly Dictionary<FileName, ModuleCacheInfo> moduleCache = new Dictionary<FileName, ModuleCacheInfo>(); |
||||
|
||||
static ModuleDefinition GetModuleDefinitionFromCache(FileName file) |
||||
{ |
||||
if (file == null) return null; |
||||
ReaderParameters parameters = new ReaderParameters(); |
||||
var resolver = new ILSpyAssemblyResolver(file); |
||||
var lastUpdateTime = File.GetLastWriteTimeUtc(file); |
||||
lock (moduleCache) { |
||||
ModuleCacheInfo info; |
||||
ModuleDefinition module; |
||||
if (!moduleCache.TryGetValue(file, out info)) { |
||||
module = ModuleDefinition.ReadModule(file, parameters); |
||||
moduleCache.Add(file, new ModuleCacheInfo(lastUpdateTime, module)); |
||||
return module; |
||||
} else if (info.LastUpdateTime < lastUpdateTime) { |
||||
moduleCache.Remove(file); |
||||
module = ModuleDefinition.ReadModule(file, parameters); |
||||
moduleCache.Add(file, new ModuleCacheInfo(lastUpdateTime, module)); |
||||
return module; |
||||
} else { |
||||
if (info.Module.TryGetTarget(out module)) |
||||
return module; |
||||
module = ModuleDefinition.ReadModule(file, parameters); |
||||
info.Module.SetTarget(module); |
||||
return module; |
||||
} |
||||
} |
||||
} |
||||
|
||||
class ILSpyAssemblyResolver : DefaultAssemblySearcher, IAssemblyResolver |
||||
{ |
||||
public ISet<AssemblyDefinition> ResolvedAssemblies { |
||||
get { return resolvedAssemblies; } |
||||
} |
||||
|
||||
public ILSpyAssemblyResolver(FileName fileName) |
||||
: base(fileName) |
||||
{ |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Used to remember the referenced assemblies for the WeakReference cache policy.
|
||||
/// </summary>
|
||||
readonly ISet<AssemblyDefinition> resolvedAssemblies = new HashSet<AssemblyDefinition>(); |
||||
|
||||
AssemblyDefinition Resolve(DomAssemblyName name, ReaderParameters parameters) |
||||
{ |
||||
var moduleDefinition = GetModuleDefinitionFromCache(FindAssembly(name)); |
||||
if (moduleDefinition != null) { |
||||
resolvedAssemblies.Add(moduleDefinition.Assembly); |
||||
return moduleDefinition.Assembly; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public AssemblyDefinition Resolve(AssemblyNameReference name) |
||||
{ |
||||
return Resolve(name, new ReaderParameters()); |
||||
} |
||||
|
||||
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) |
||||
{ |
||||
return Resolve(new DomAssemblyName(name.FullName), parameters); |
||||
} |
||||
|
||||
public AssemblyDefinition Resolve(string fullName) |
||||
{ |
||||
return Resolve(fullName, new ReaderParameters()); |
||||
} |
||||
|
||||
public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters) |
||||
{ |
||||
return Resolve(new DomAssemblyName(fullName), parameters); |
||||
} |
||||
} |
||||
|
||||
public static ILSpyFullParseInformation DecompileType(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) |
||||
{ |
||||
if (name == null) |
||||
throw new ArgumentNullException("name"); |
||||
var astBuilder = CreateAstBuilder(name, cancellationToken); |
||||
return new ILSpyFullParseInformation(ILSpyUnresolvedFile.Create(name, astBuilder), null, astBuilder.SyntaxTree); |
||||
} |
||||
|
||||
static AstBuilder CreateAstBuilder(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) |
||||
{ |
||||
ReaderParameters readerParameters = new ReaderParameters(); |
||||
// Use new assembly resolver instance so that the AssemblyDefinitions
|
||||
// can be garbage-collected once the code is decompiled.
|
||||
var resolver = new ILSpyAssemblyResolver(name.AssemblyFile); |
||||
readerParameters.AssemblyResolver = resolver; |
||||
ModuleDefinition module = GetModuleDefinitionFromCache(name.AssemblyFile); |
||||
if (module == null) |
||||
throw new InvalidOperationException("Could not find assembly file"); |
||||
TypeDefinition typeDefinition = module.GetType(name.Type.ReflectionName); |
||||
if (typeDefinition == null) |
||||
throw new InvalidOperationException("Could not find type"); |
||||
DecompilerContext context = new DecompilerContext(module); |
||||
context.CancellationToken = cancellationToken; |
||||
AstBuilder astBuilder = new AstBuilder(context); |
||||
astBuilder.AddType(typeDefinition); |
||||
return astBuilder; |
||||
} |
||||
|
||||
static ILSpyUnresolvedFile DoDecompile(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) |
||||
{ |
||||
return ILSpyUnresolvedFile.Create(name, CreateAstBuilder(name, cancellationToken)); |
||||
} |
||||
} |
||||
|
||||
public class DecompiledTypeReference : IEquatable<DecompiledTypeReference> |
||||
{ |
||||
public FileName AssemblyFile { get; private set; } |
||||
public TopLevelTypeName Type { get; private set; } |
||||
|
||||
public DecompiledTypeReference(FileName assemblyFile, TopLevelTypeName type) |
||||
{ |
||||
this.AssemblyFile = assemblyFile; |
||||
this.Type = type; |
||||
} |
||||
|
||||
public FileName ToFileName() |
||||
{ |
||||
return FileName.Create("ilspy://" + AssemblyFile + "/" + EscapeTypeName(Type.ReflectionName) + ".cs"); |
||||
} |
||||
|
||||
static readonly Regex nameRegex = new Regex(@"^ilspy\://(.+)/(.+)\.cs$", RegexOptions.Compiled | RegexOptions.IgnoreCase); |
||||
|
||||
public static DecompiledTypeReference FromFileName(string filename) |
||||
{ |
||||
var match = nameRegex.Match(filename); |
||||
if (!match.Success) return null; |
||||
|
||||
string asm, typeName; |
||||
asm = match.Groups[1].Value; |
||||
typeName = UnescapeTypeName(match.Groups[2].Value); |
||||
|
||||
return new DecompiledTypeReference(new FileName(asm), new TopLevelTypeName(typeName)); |
||||
} |
||||
|
||||
public static DecompiledTypeReference FromTypeDefinition(ITypeDefinition definition) |
||||
{ |
||||
FileName assemblyLocation = definition.ParentAssembly.GetRuntimeAssemblyLocation(); |
||||
if (assemblyLocation != null && SD.FileSystem.FileExists(assemblyLocation)) { |
||||
return new DecompiledTypeReference(assemblyLocation, definition.FullTypeName.TopLevelTypeName); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static string EscapeTypeName(string typeName) |
||||
{ |
||||
if (typeName == null) |
||||
throw new ArgumentNullException("typeName"); |
||||
foreach (var ch in new[] { '_' }.Concat(Path.GetInvalidFileNameChars())) { |
||||
typeName = typeName.Replace(ch.ToString(), string.Format("_{0:X4}", (int)ch)); |
||||
} |
||||
return typeName; |
||||
} |
||||
|
||||
static readonly Regex unescapeRegex = new Regex(@"_([0-9A-F]{4})", RegexOptions.Compiled | RegexOptions.IgnoreCase); |
||||
|
||||
public static string UnescapeTypeName(string typeName) |
||||
{ |
||||
if (typeName == null) |
||||
throw new ArgumentNullException("typeName"); |
||||
typeName = unescapeRegex.Replace(typeName, m => ((char)int.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber)).ToString()); |
||||
return typeName; |
||||
} |
||||
|
||||
#region Equals and GetHashCode implementation
|
||||
public override bool Equals(object obj) |
||||
{ |
||||
DecompiledTypeReference other = (DecompiledTypeReference)obj; |
||||
if (other == null) |
||||
return false; |
||||
return Equals(other); |
||||
} |
||||
|
||||
public bool Equals(DecompiledTypeReference other) |
||||
{ |
||||
return object.Equals(this.AssemblyFile, other.AssemblyFile) && this.Type == other.Type; |
||||
} |
||||
|
||||
public override int GetHashCode() |
||||
{ |
||||
int hashCode = 0; |
||||
unchecked { |
||||
if (AssemblyFile != null) |
||||
hashCode += 1000000007 * AssemblyFile.GetHashCode(); |
||||
hashCode += 1000000009 * Type.GetHashCode(); |
||||
} |
||||
return hashCode; |
||||
} |
||||
|
||||
public static bool operator ==(DecompiledTypeReference lhs, DecompiledTypeReference rhs) { |
||||
if (ReferenceEquals(lhs, rhs)) |
||||
return true; |
||||
if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null)) |
||||
return false; |
||||
return lhs.Equals(rhs); |
||||
} |
||||
|
||||
public static bool operator !=(DecompiledTypeReference lhs, DecompiledTypeReference rhs) { |
||||
return !(lhs == rhs); |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using ICSharpCode.SharpDevelop.Parser; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.ILSpyAddIn |
||||
{ |
||||
/// <summary>
|
||||
/// Description of ILSpyFullParseInformation.
|
||||
/// </summary>
|
||||
public class ILSpyFullParseInformation : ParseInformation |
||||
{ |
||||
SyntaxTree syntaxTree; |
||||
|
||||
public ILSpyFullParseInformation(ILSpyUnresolvedFile unresolvedFile, ITextSourceVersion parsedVersion, SyntaxTree syntaxTree) |
||||
: base(unresolvedFile, parsedVersion, true) |
||||
{ |
||||
this.syntaxTree = syntaxTree; |
||||
} |
||||
|
||||
public SyntaxTree SyntaxTree { get { return syntaxTree; } } |
||||
} |
||||
} |
@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Reflection; |
||||
using System.Threading; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.NRefactory; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using ICSharpCode.NRefactory.CSharp.Resolver; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using ICSharpCode.NRefactory.Semantics; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.SharpDevelop; |
||||
using ICSharpCode.SharpDevelop.Dom.ClassBrowser; |
||||
using ICSharpCode.SharpDevelop.Editor.Search; |
||||
using ICSharpCode.SharpDevelop.Parser; |
||||
using ICSharpCode.SharpDevelop.Project; |
||||
|
||||
namespace ICSharpCode.ILSpyAddIn |
||||
{ |
||||
/// <summary>
|
||||
/// This class "parses" a decompiled type to provide the information required
|
||||
/// by the ParserService.
|
||||
/// </summary>
|
||||
public class ILSpyParser : IParser |
||||
{ |
||||
public bool CanParse(string fileName) |
||||
{ |
||||
return fileName != null && fileName.StartsWith("ilspy://", StringComparison.OrdinalIgnoreCase); |
||||
} |
||||
|
||||
public ParseInformation Parse(FileName fileName, ITextSource fileContent, bool fullParseInformationRequested, IProject parentProject, CancellationToken cancellationToken) |
||||
{ |
||||
return ILSpyDecompilerService.DecompileType(DecompiledTypeReference.FromFileName(fileName), cancellationToken); |
||||
} |
||||
|
||||
public ResolveResult Resolve(ParseInformation parseInfo, TextLocation location, ICompilation compilation, CancellationToken cancellationToken) |
||||
{ |
||||
var decompiledParseInfo = parseInfo as ILSpyFullParseInformation; |
||||
if (decompiledParseInfo == null) |
||||
throw new ArgumentException("Parse info does not have SyntaxTree"); |
||||
return ResolveAtLocation.Resolve(compilation, null, decompiledParseInfo.SyntaxTree, location, cancellationToken); |
||||
} |
||||
|
||||
public ResolveResult ResolveSnippet(ParseInformation parseInfo, TextLocation location, string codeSnippet, ICompilation compilation, CancellationToken cancellationToken) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public void FindLocalReferences(ParseInformation parseInfo, ITextSource fileContent, IVariable variable, ICompilation compilation, Action<SearchResultMatch> callback, CancellationToken cancellationToken) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public ICompilation CreateCompilationForSingleFile(FileName fileName, IUnresolvedFile unresolvedFile) |
||||
{ |
||||
DecompiledTypeReference reference = DecompiledTypeReference.FromFileName(fileName); |
||||
if (reference != null) { |
||||
var model = SD.GetService<IClassBrowser>().FindAssemblyModel(reference.AssemblyFile); |
||||
if (model == null) |
||||
model = SD.AssemblyParserService.GetAssemblyModelSafe(reference.AssemblyFile, true); |
||||
if (model != null) |
||||
return model.Context.GetCompilation(); |
||||
} |
||||
return new CSharpProjectContent() |
||||
.AddOrUpdateFiles(unresolvedFile) |
||||
.CreateCompilation(); |
||||
} |
||||
|
||||
public IReadOnlyList<string> TaskListTokens { get; set; } |
||||
} |
||||
} |
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.Decompiler; |
||||
using ICSharpCode.Decompiler.Ast; |
||||
using ICSharpCode.NRefactory; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using ICSharpCode.NRefactory.CSharp.TypeSystem; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
|
||||
namespace ICSharpCode.ILSpyAddIn |
||||
{ |
||||
/// <summary>
|
||||
/// Description of ILSpyUnresolvedFile.
|
||||
/// </summary>
|
||||
public class ILSpyUnresolvedFile : CSharpUnresolvedFile |
||||
{ |
||||
DecompiledTypeReference name; |
||||
string output; |
||||
|
||||
public static ILSpyUnresolvedFile Create(DecompiledTypeReference name, AstBuilder builder) |
||||
{ |
||||
var writer = new StringWriter(); |
||||
var target = new TextWriterTokenWriter(writer) { IndentationString = "\t" }; |
||||
var output = new DebugInfoTokenWriterDecorator(TokenWriter.WrapInWriterThatSetsLocationsInAST(target)); |
||||
builder.RunTransformations(); |
||||
var syntaxTree = builder.SyntaxTree; |
||||
|
||||
syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); |
||||
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(output, FormattingOptionsFactory.CreateSharpDevelop())); |
||||
ILSpyUnresolvedFile file = new ILSpyUnresolvedFile(name); |
||||
var v = new TypeSystemConvertVisitor(file); |
||||
syntaxTree.AcceptVisitor(v); |
||||
|
||||
file.MemberLocations = output.MemberLocations; |
||||
file.DebugSymbols = output.DebugSymbols; |
||||
file.output = writer.ToString(); |
||||
|
||||
return file; |
||||
} |
||||
|
||||
ILSpyUnresolvedFile(DecompiledTypeReference name) |
||||
{ |
||||
this.name = name; |
||||
FileName = name.ToFileName(); |
||||
} |
||||
|
||||
public Dictionary<string, TextLocation> MemberLocations { get; private set; } |
||||
|
||||
public Dictionary<string, MethodDebugSymbols> DebugSymbols { get; private set; } |
||||
|
||||
public string Output { |
||||
get { return output; } |
||||
} |
||||
|
||||
public FileName AssemblyFile { |
||||
get { return name.AssemblyFile; } |
||||
} |
||||
} |
||||
} |
@ -1,107 +0,0 @@
@@ -1,107 +0,0 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.SharpDevelop; |
||||
using ICSharpCode.SharpDevelop.Parser; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.ILSpyAddIn.LaunchILSpy |
||||
{ |
||||
class ILSpyAssemblyResolver : IAssemblyResolver |
||||
{ |
||||
readonly DirectoryInfo directoryInfo; |
||||
readonly IDictionary<string, AssemblyDefinition> cache; |
||||
readonly IDictionary<string, AssemblyDefinition> localAssembliesCache; |
||||
|
||||
public ILSpyAssemblyResolver(string decompiledAssemblyFolder) |
||||
{ |
||||
if (string.IsNullOrEmpty(decompiledAssemblyFolder)) |
||||
throw new ArgumentException("Invalid working folder"); |
||||
|
||||
FolderPath = decompiledAssemblyFolder; |
||||
this.directoryInfo = new DirectoryInfo(decompiledAssemblyFolder); |
||||
this.cache = new Dictionary<string, AssemblyDefinition> (); |
||||
this.localAssembliesCache = new Dictionary<string, AssemblyDefinition>(); |
||||
|
||||
ReadLocalAssemblies(); |
||||
} |
||||
|
||||
public string FolderPath { |
||||
get; private set; |
||||
} |
||||
|
||||
public AssemblyDefinition Resolve(AssemblyNameReference name) |
||||
{ |
||||
return this.Resolve(name, new ReaderParameters()); |
||||
} |
||||
|
||||
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) |
||||
{ |
||||
if (name == null) |
||||
throw new ArgumentNullException("name"); |
||||
|
||||
if (parameters == null) |
||||
throw new ArgumentNullException("parameters"); |
||||
|
||||
try { |
||||
AssemblyDefinition assembly = null; |
||||
if (cache.TryGetValue(name.FullName, out assembly)) |
||||
return assembly; |
||||
|
||||
// search into assemblyDecompiledFolder
|
||||
if (localAssembliesCache.ContainsKey(name.FullName)) { |
||||
assembly = localAssembliesCache[name.FullName]; |
||||
} |
||||
|
||||
if (assembly == null) { |
||||
// search using ILSpy's GacInterop.FindAssemblyInNetGac()
|
||||
string fileInGac = SD.GlobalAssemblyCache.FindAssemblyInNetGac(new DomAssemblyName(name.FullName)); |
||||
if (!string.IsNullOrEmpty(fileInGac)) { |
||||
assembly = AssemblyDefinition.ReadAssembly(fileInGac, parameters); |
||||
} |
||||
} |
||||
|
||||
// update caches
|
||||
if (assembly != null) { |
||||
this.cache.Add(assembly.FullName, assembly); |
||||
} |
||||
return assembly; |
||||
} catch (Exception ex) { |
||||
LoggingService.Error("Exception (ILSpyAssemblyResolver): " + ex.Message); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public AssemblyDefinition Resolve(string fullName) |
||||
{ |
||||
return this.Resolve(fullName, new ReaderParameters()); |
||||
} |
||||
|
||||
public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters) |
||||
{ |
||||
if (string.IsNullOrEmpty(fullName)) |
||||
throw new ArgumentException("fullName is null or empty"); |
||||
|
||||
return Resolve(AssemblyNameReference.Parse(fullName), parameters); |
||||
} |
||||
|
||||
void ReadLocalAssemblies() |
||||
{ |
||||
// read local assemblies
|
||||
foreach (var file in this.directoryInfo.GetFiles()) { |
||||
try { |
||||
var localAssembly = AssemblyDefinition.ReadAssembly(file.FullName); |
||||
localAssembliesCache.Add(localAssembly.FullName, localAssembly); |
||||
} catch { |
||||
// unable to read assembly file
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,60 +0,0 @@
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp |
||||
{ |
||||
/// <summary>
|
||||
/// Output formatter for the Output visitor.
|
||||
/// </summary>
|
||||
public interface IOutputFormatter |
||||
{ |
||||
void StartNode(AstNode node); |
||||
void EndNode(AstNode node); |
||||
|
||||
/// <summary>
|
||||
/// Writes an identifier.
|
||||
/// If the identifier conflicts with a keyword, the output visitor will
|
||||
/// call <c>WriteToken("@")</c> before calling WriteIdentifier().
|
||||
/// </summary>
|
||||
void WriteIdentifier(string identifier); |
||||
|
||||
/// <summary>
|
||||
/// Writes a keyword to the output.
|
||||
/// </summary>
|
||||
void WriteKeyword(string keyword); |
||||
|
||||
/// <summary>
|
||||
/// Writes a token to the output.
|
||||
/// </summary>
|
||||
void WriteToken(string token); |
||||
void Space(); |
||||
|
||||
void OpenBrace(BraceStyle style); |
||||
void CloseBrace(BraceStyle style); |
||||
|
||||
void Indent(); |
||||
void Unindent(); |
||||
|
||||
void NewLine(); |
||||
|
||||
void WriteComment(CommentType commentType, string content); |
||||
void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument); |
||||
} |
||||
} |
@ -0,0 +1,161 @@
@@ -0,0 +1,161 @@
|
||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.IO; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp |
||||
{ |
||||
public abstract class TokenWriter |
||||
{ |
||||
public abstract void StartNode(AstNode node); |
||||
public abstract void EndNode(AstNode node); |
||||
|
||||
/// <summary>
|
||||
/// Writes an identifier.
|
||||
/// </summary>
|
||||
public abstract void WriteIdentifier(Identifier identifier); |
||||
|
||||
/// <summary>
|
||||
/// Writes a keyword to the output.
|
||||
/// </summary>
|
||||
public abstract void WriteKeyword(Role role, string keyword); |
||||
|
||||
/// <summary>
|
||||
/// Writes a token to the output.
|
||||
/// </summary>
|
||||
public abstract void WriteToken(Role role, string token); |
||||
|
||||
/// <summary>
|
||||
/// Writes a primitive/literal value
|
||||
/// </summary>
|
||||
public abstract void WritePrimitiveValue(object value, string literalValue = null); |
||||
|
||||
public abstract void WritePrimitiveType(string type); |
||||
|
||||
public abstract void Space(); |
||||
public abstract void Indent(); |
||||
public abstract void Unindent(); |
||||
public abstract void NewLine(); |
||||
|
||||
public abstract void WriteComment(CommentType commentType, string content); |
||||
public abstract void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument); |
||||
|
||||
public static TokenWriter Create(TextWriter writer, string indentation = "\t") |
||||
{ |
||||
return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new TextWriterTokenWriter(writer) { IndentationString = indentation })); |
||||
} |
||||
|
||||
public static TokenWriter CreateWriterThatSetsLocationsInAST(TextWriter writer, string indentation = "\t") |
||||
{ |
||||
var target = new TextWriterTokenWriter(writer) { IndentationString = indentation }; |
||||
return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(target, target))); |
||||
} |
||||
|
||||
public static TokenWriter WrapInWriterThatSetsLocationsInAST(TokenWriter writer) |
||||
{ |
||||
if (!(writer is ILocatable)) |
||||
throw new InvalidOperationException("writer does not provide locations!"); |
||||
return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(writer, (ILocatable)writer))); |
||||
} |
||||
} |
||||
|
||||
public interface ILocatable |
||||
{ |
||||
TextLocation Location { get; } |
||||
} |
||||
|
||||
public abstract class DecoratingTokenWriter : TokenWriter |
||||
{ |
||||
TokenWriter decoratedWriter; |
||||
|
||||
protected DecoratingTokenWriter(TokenWriter decoratedWriter) |
||||
{ |
||||
if (decoratedWriter == null) |
||||
throw new ArgumentNullException("decoratedWriter"); |
||||
this.decoratedWriter = decoratedWriter; |
||||
} |
||||
|
||||
public override void StartNode(AstNode node) |
||||
{ |
||||
decoratedWriter.StartNode(node); |
||||
} |
||||
|
||||
public override void EndNode(AstNode node) |
||||
{ |
||||
decoratedWriter.EndNode(node); |
||||
} |
||||
|
||||
public override void WriteIdentifier(Identifier identifier) |
||||
{ |
||||
decoratedWriter.WriteIdentifier(identifier); |
||||
} |
||||
|
||||
public override void WriteKeyword(Role role, string keyword) |
||||
{ |
||||
decoratedWriter.WriteKeyword(role, keyword); |
||||
} |
||||
|
||||
public override void WriteToken(Role role, string token) |
||||
{ |
||||
decoratedWriter.WriteToken(role, token); |
||||
} |
||||
|
||||
public override void WritePrimitiveValue(object value, string literalValue = null) |
||||
{ |
||||
decoratedWriter.WritePrimitiveValue(value, literalValue); |
||||
} |
||||
|
||||
public override void WritePrimitiveType(string type) |
||||
{ |
||||
decoratedWriter.WritePrimitiveType(type); |
||||
} |
||||
|
||||
public override void Space() |
||||
{ |
||||
decoratedWriter.Space(); |
||||
} |
||||
|
||||
public override void Indent() |
||||
{ |
||||
decoratedWriter.Indent(); |
||||
} |
||||
|
||||
public override void Unindent() |
||||
{ |
||||
decoratedWriter.Unindent(); |
||||
} |
||||
|
||||
public override void NewLine() |
||||
{ |
||||
decoratedWriter.NewLine(); |
||||
} |
||||
|
||||
public override void WriteComment(CommentType commentType, string content) |
||||
{ |
||||
decoratedWriter.WriteComment(commentType, content); |
||||
} |
||||
|
||||
public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) |
||||
{ |
||||
decoratedWriter.WritePreProcessorDirective(type, argument); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
@ -0,0 +1,122 @@
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp |
||||
{ |
||||
class InsertMissingTokensDecorator : DecoratingTokenWriter |
||||
{ |
||||
readonly Stack<List<AstNode>> nodes = new Stack<List<AstNode>>(); |
||||
List<AstNode> currentList; |
||||
readonly ILocatable locationProvider; |
||||
|
||||
public InsertMissingTokensDecorator(TokenWriter writer, ILocatable locationProvider) |
||||
: base(writer) |
||||
{ |
||||
this.locationProvider = locationProvider; |
||||
currentList = new List<AstNode>(); |
||||
} |
||||
|
||||
public override void StartNode(AstNode node) |
||||
{ |
||||
currentList.Add(node); |
||||
nodes.Push(currentList); |
||||
currentList = new List<AstNode>(); |
||||
base.StartNode(node); |
||||
} |
||||
|
||||
public override void EndNode(AstNode node) |
||||
{ |
||||
System.Diagnostics.Debug.Assert(currentList != null); |
||||
foreach (var removable in node.Children.Where(n => n is CSharpTokenNode)) { |
||||
removable.Remove(); |
||||
} |
||||
foreach (var child in currentList) { |
||||
System.Diagnostics.Debug.Assert(child.Parent == null || node == child.Parent); |
||||
child.Remove(); |
||||
node.AddChildWithExistingRole(child); |
||||
} |
||||
currentList = nodes.Pop(); |
||||
base.EndNode(node); |
||||
} |
||||
|
||||
public override void WriteToken(Role role, string token) |
||||
{ |
||||
CSharpTokenNode t = new CSharpTokenNode(locationProvider.Location, (TokenRole)role); |
||||
EmptyStatement node = nodes.Peek().LastOrDefault() as EmptyStatement; |
||||
if (node == null) |
||||
currentList.Add(t); |
||||
else { |
||||
node.Location = locationProvider.Location; |
||||
} |
||||
base.WriteToken(role, token); |
||||
} |
||||
|
||||
public override void WriteKeyword(Role role, string keyword) |
||||
{ |
||||
TextLocation start = locationProvider.Location; |
||||
CSharpTokenNode t = null; |
||||
if (role is TokenRole) |
||||
t = new CSharpTokenNode(start, (TokenRole)role); |
||||
else if (role == EntityDeclaration.ModifierRole) |
||||
t = new CSharpModifierToken(start, CSharpModifierToken.GetModifierValue(keyword)); |
||||
else if (keyword == "this") { |
||||
ThisReferenceExpression node = nodes.Peek().LastOrDefault() as ThisReferenceExpression; |
||||
if (node != null) |
||||
node.Location = start; |
||||
} else if (keyword == "base") { |
||||
BaseReferenceExpression node = nodes.Peek().LastOrDefault() as BaseReferenceExpression; |
||||
if (node != null) |
||||
node.Location = start; |
||||
} |
||||
if (t != null) currentList.Add(t); |
||||
base.WriteKeyword(role, keyword); |
||||
} |
||||
|
||||
public override void WriteIdentifier(Identifier identifier) |
||||
{ |
||||
if (!identifier.IsNull) |
||||
identifier.SetStartLocation(locationProvider.Location); |
||||
currentList.Add(identifier); |
||||
base.WriteIdentifier(identifier); |
||||
} |
||||
|
||||
public override void WritePrimitiveValue(object value, string literalValue = null) |
||||
{ |
||||
Expression node = nodes.Peek().LastOrDefault() as Expression; |
||||
if (node is PrimitiveExpression) { |
||||
((PrimitiveExpression)node).SetStartLocation(locationProvider.Location); |
||||
} |
||||
if (node is NullReferenceExpression) { |
||||
((NullReferenceExpression)node).SetStartLocation(locationProvider.Location); |
||||
} |
||||
base.WritePrimitiveValue(value, literalValue); |
||||
} |
||||
|
||||
public override void WritePrimitiveType(string type) |
||||
{ |
||||
PrimitiveType node = nodes.Peek().LastOrDefault() as PrimitiveType; |
||||
if (node != null) |
||||
node.SetStartLocation(locationProvider.Location); |
||||
base.WritePrimitiveType(type); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,184 @@
@@ -0,0 +1,184 @@
|
||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Globalization; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading.Tasks; |
||||
using ICSharpCode.NRefactory.PatternMatching; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp |
||||
{ |
||||
class InsertRequiredSpacesDecorator : DecoratingTokenWriter |
||||
{ |
||||
/// <summary>
|
||||
/// Used to insert the minimal amount of spaces so that the lexer recognizes the tokens that were written.
|
||||
/// </summary>
|
||||
LastWritten lastWritten; |
||||
|
||||
enum LastWritten |
||||
{ |
||||
Whitespace, |
||||
Other, |
||||
KeywordOrIdentifier, |
||||
Plus, |
||||
Minus, |
||||
Ampersand, |
||||
QuestionMark, |
||||
Division |
||||
} |
||||
|
||||
public InsertRequiredSpacesDecorator(TokenWriter writer) |
||||
: base(writer) |
||||
{ |
||||
} |
||||
|
||||
public override void WriteIdentifier(Identifier identifier) |
||||
{ |
||||
if (identifier.IsVerbatim) { |
||||
if (lastWritten == LastWritten.KeywordOrIdentifier) { |
||||
// this space is not strictly required, so we call Space()
|
||||
Space(); |
||||
} |
||||
} else if (lastWritten == LastWritten.KeywordOrIdentifier) { |
||||
// this space is strictly required, so we directly call the formatter
|
||||
base.Space(); |
||||
} |
||||
base.WriteIdentifier(identifier); |
||||
lastWritten = LastWritten.KeywordOrIdentifier; |
||||
} |
||||
|
||||
public override void WriteKeyword(Role role, string keyword) |
||||
{ |
||||
if (lastWritten == LastWritten.KeywordOrIdentifier) { |
||||
Space(); |
||||
} |
||||
base.WriteKeyword(role, keyword); |
||||
lastWritten = LastWritten.KeywordOrIdentifier; |
||||
} |
||||
|
||||
public override void WriteToken(Role role, string token) |
||||
{ |
||||
// Avoid that two +, - or ? tokens are combined into a ++, -- or ?? token.
|
||||
// Note that we don't need to handle tokens like = because there's no valid
|
||||
// C# program that contains the single token twice in a row.
|
||||
// (for +, - and &, this can happen with unary operators;
|
||||
// for ?, this can happen in "a is int? ? b : c" or "a as int? ?? 0";
|
||||
// and for /, this can happen with "1/ *ptr" or "1/ //comment".)
|
||||
if (lastWritten == LastWritten.Plus && token[0] == '+' || |
||||
lastWritten == LastWritten.Minus && token[0] == '-' || |
||||
lastWritten == LastWritten.Ampersand && token[0] == '&' || |
||||
lastWritten == LastWritten.QuestionMark && token[0] == '?' || |
||||
lastWritten == LastWritten.Division && token[0] == '*') { |
||||
base.Space(); |
||||
} |
||||
base.WriteToken(role, token); |
||||
if (token == "+") { |
||||
lastWritten = LastWritten.Plus; |
||||
} else if (token == "-") { |
||||
lastWritten = LastWritten.Minus; |
||||
} else if (token == "&") { |
||||
lastWritten = LastWritten.Ampersand; |
||||
} else if (token == "?") { |
||||
lastWritten = LastWritten.QuestionMark; |
||||
} else if (token == "/") { |
||||
lastWritten = LastWritten.Division; |
||||
} else { |
||||
lastWritten = LastWritten.Other; |
||||
} |
||||
} |
||||
|
||||
public override void Space() |
||||
{ |
||||
base.Space(); |
||||
lastWritten = LastWritten.Whitespace; |
||||
} |
||||
|
||||
public override void NewLine() |
||||
{ |
||||
base.NewLine(); |
||||
lastWritten = LastWritten.Whitespace; |
||||
} |
||||
|
||||
public override void WriteComment(CommentType commentType, string content) |
||||
{ |
||||
if (lastWritten == LastWritten.Division) { |
||||
// When there's a comment starting after a division operator
|
||||
// "1.0 / /*comment*/a", then we need to insert a space in front of the comment.
|
||||
base.Space(); |
||||
} |
||||
base.WriteComment(commentType, content); |
||||
lastWritten = LastWritten.Whitespace; |
||||
} |
||||
|
||||
public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) |
||||
{ |
||||
base.WritePreProcessorDirective(type, argument); |
||||
lastWritten = LastWritten.Whitespace; |
||||
} |
||||
|
||||
public override void WritePrimitiveValue(object value, string literalValue = null) |
||||
{ |
||||
base.WritePrimitiveValue(value, literalValue); |
||||
if (value == null || value is bool) |
||||
return; |
||||
if (value is string) { |
||||
lastWritten = LastWritten.Other; |
||||
} else if (value is char) { |
||||
lastWritten = LastWritten.Other; |
||||
} else if (value is decimal) { |
||||
lastWritten = LastWritten.Other; |
||||
} else if (value is float) { |
||||
float f = (float)value; |
||||
if (float.IsInfinity(f) || float.IsNaN(f)) return; |
||||
lastWritten = LastWritten.Other; |
||||
} else if (value is double) { |
||||
double f = (double)value; |
||||
if (double.IsInfinity(f) || double.IsNaN(f)) return; |
||||
// needs space if identifier follows number;
|
||||
// this avoids mistaking the following identifier as type suffix
|
||||
lastWritten = LastWritten.KeywordOrIdentifier; |
||||
} else if (value is IFormattable) { |
||||
// needs space if identifier follows number;
|
||||
// this avoids mistaking the following identifier as type suffix
|
||||
lastWritten = LastWritten.KeywordOrIdentifier; |
||||
} else { |
||||
lastWritten = LastWritten.Other; |
||||
} |
||||
} |
||||
|
||||
public override void WritePrimitiveType(string type) |
||||
{ |
||||
if (lastWritten == LastWritten.KeywordOrIdentifier) { |
||||
Space(); |
||||
} |
||||
base.WritePrimitiveType(type); |
||||
if (type == "new") { |
||||
lastWritten = LastWritten.Other; |
||||
} else { |
||||
lastWritten = LastWritten.KeywordOrIdentifier; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,157 @@
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp |
||||
{ |
||||
class InsertSpecialsDecorator : DecoratingTokenWriter |
||||
{ |
||||
readonly Stack<AstNode> positionStack = new Stack<AstNode>(); |
||||
int visitorWroteNewLine = 0; |
||||
|
||||
public InsertSpecialsDecorator(TokenWriter writer) : base(writer) |
||||
{ |
||||
} |
||||
|
||||
public override void StartNode(AstNode node) |
||||
{ |
||||
if (positionStack.Count > 0) { |
||||
WriteSpecialsUpToNode(node); |
||||
} |
||||
positionStack.Push(node.FirstChild); |
||||
base.StartNode(node); |
||||
} |
||||
|
||||
public override void EndNode(AstNode node) |
||||
{ |
||||
base.EndNode(node); |
||||
AstNode pos = positionStack.Pop(); |
||||
Debug.Assert(pos == null || pos.Parent == node); |
||||
WriteSpecials(pos, null); |
||||
} |
||||
|
||||
public override void WriteKeyword(Role role, string keyword) |
||||
{ |
||||
if (role != null) { |
||||
WriteSpecialsUpToRole(role); |
||||
} |
||||
base.WriteKeyword(role, keyword); |
||||
} |
||||
|
||||
public override void WriteIdentifier(Identifier identifier) |
||||
{ |
||||
WriteSpecialsUpToRole(identifier.Role ?? Roles.Identifier); |
||||
base.WriteIdentifier(identifier); |
||||
} |
||||
|
||||
public override void WriteToken(Role role, string token) |
||||
{ |
||||
WriteSpecialsUpToRole(role); |
||||
base.WriteToken(role, token); |
||||
} |
||||
|
||||
public override void NewLine() |
||||
{ |
||||
if (visitorWroteNewLine >= 0) |
||||
base.NewLine(); |
||||
visitorWroteNewLine++; |
||||
} |
||||
|
||||
#region WriteSpecials
|
||||
/// <summary>
|
||||
/// Writes all specials from start to end (exclusive). Does not touch the positionStack.
|
||||
/// </summary>
|
||||
void WriteSpecials(AstNode start, AstNode end) |
||||
{ |
||||
for (AstNode pos = start; pos != end; pos = pos.NextSibling) { |
||||
if (pos.Role == Roles.Comment) { |
||||
var node = (Comment)pos; |
||||
base.WriteComment(node.CommentType, node.Content); |
||||
} |
||||
// see CSharpOutputVisitor.VisitNewLine()
|
||||
// if (pos.Role == Roles.NewLine) {
|
||||
// if (visitorWroteNewLine <= 0)
|
||||
// base.NewLine();
|
||||
// visitorWroteNewLine--;
|
||||
// }
|
||||
if (pos.Role == Roles.PreProcessorDirective) { |
||||
var node = (PreProcessorDirective)pos; |
||||
base.WritePreProcessorDirective(node.Type, node.Argument); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Writes all specials between the current position (in the positionStack) and the next
|
||||
/// node with the specified role. Advances the current position.
|
||||
/// </summary>
|
||||
void WriteSpecialsUpToRole(Role role) |
||||
{ |
||||
WriteSpecialsUpToRole(role, null); |
||||
} |
||||
|
||||
void WriteSpecialsUpToRole(Role role, AstNode nextNode) |
||||
{ |
||||
if (positionStack.Count == 0) { |
||||
return; |
||||
} |
||||
// Look for the role between the current position and the nextNode.
|
||||
for (AstNode pos = positionStack.Peek(); pos != null && pos != nextNode; pos = pos.NextSibling) { |
||||
if (pos.Role == role) { |
||||
WriteSpecials(positionStack.Pop(), pos); |
||||
// Push the next sibling because the node matching the role is not a special,
|
||||
// and should be considered to be already handled.
|
||||
positionStack.Push(pos.NextSibling); |
||||
// This is necessary for OptionalComma() to work correctly.
|
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Writes all specials between the current position (in the positionStack) and the specified node.
|
||||
/// Advances the current position.
|
||||
/// </summary>
|
||||
void WriteSpecialsUpToNode(AstNode node) |
||||
{ |
||||
if (positionStack.Count == 0) { |
||||
return; |
||||
} |
||||
for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) { |
||||
if (pos == node) { |
||||
WriteSpecials(positionStack.Pop(), pos); |
||||
// Push the next sibling because the node itself is not a special,
|
||||
// and should be considered to be already handled.
|
||||
positionStack.Push(pos.NextSibling); |
||||
// This is necessary for OptionalComma() to work correctly.
|
||||
break; |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using NUnit.Framework; |
||||
using ICSharpCode.NRefactory.CSharp.Parser; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp |
||||
{ |
||||
/// <summary>
|
||||
/// Description of InsertMissingTokensDecoratorTests.
|
||||
/// </summary>
|
||||
[TestFixture] |
||||
public class InsertMissingTokensDecoratorTests |
||||
{ |
||||
string[] fileNames; |
||||
|
||||
[TestFixtureSetUp] |
||||
public void SetUp() |
||||
{ |
||||
string path = Path.GetFullPath (Path.Combine ("..", "..")); |
||||
if (!File.Exists(Path.Combine(path, "NRefactory.sln"))) |
||||
throw new InvalidOperationException("Test cannot find the NRefactory source code in " + path); |
||||
fileNames = Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories); |
||||
} |
||||
|
||||
static void RemoveTokens(AstNode node) |
||||
{ |
||||
foreach (var child in node.Descendants) { |
||||
if (child is CSharpTokenNode && !(child is CSharpModifierToken)) |
||||
child.Remove(); |
||||
else if (child.NodeType == NodeType.Whitespace) |
||||
child.Remove(); |
||||
} |
||||
} |
||||
|
||||
void AssertOutput(AstNode node) |
||||
{ |
||||
RemoveTokens(node); |
||||
StringWriter w = new StringWriter(); |
||||
w.NewLine = "\n"; |
||||
node.AcceptVisitor(new CSharpOutputVisitor(TokenWriter.CreateWriterThatSetsLocationsInAST(w), FormattingOptionsFactory.CreateSharpDevelop())); |
||||
var doc = new ReadOnlyDocument(w.ToString()); |
||||
ConsistencyChecker.CheckMissingTokens(node, "test.cs", doc); |
||||
ConsistencyChecker.CheckPositionConsistency(node, "test.cs", doc); |
||||
} |
||||
|
||||
[Test] |
||||
public void SimpleClass() |
||||
{ |
||||
var code = @"class Test
|
||||
{ |
||||
} |
||||
";
|
||||
var unit = SyntaxTree.Parse(code); |
||||
AssertOutput(unit); |
||||
} |
||||
|
||||
[Test] |
||||
public void SimpleMethod() |
||||
{ |
||||
var code = @"class Test
|
||||
{ |
||||
void A () |
||||
{ |
||||
} |
||||
} |
||||
";
|
||||
var unit = SyntaxTree.Parse(code); |
||||
AssertOutput(unit); |
||||
} |
||||
|
||||
[Test, Ignore] |
||||
public void SelfTest() |
||||
{ |
||||
foreach (var file in fileNames) { |
||||
Console.WriteLine("processing {0}...", file); |
||||
var node = SyntaxTree.Parse(File.ReadAllText(file), file); |
||||
AssertOutput(node); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,101 @@
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.IO; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using NUnit.Framework; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.Parser |
||||
{ |
||||
/// <summary>
|
||||
/// Provides utilities to check whether positions and/or tokens in an AST are valid.
|
||||
/// </summary>
|
||||
public static class ConsistencyChecker |
||||
{ |
||||
static void PrintNode (AstNode node) |
||||
{ |
||||
Console.WriteLine ("Parent:" + node.GetType ()); |
||||
Console.WriteLine ("Children:"); |
||||
foreach (var c in node.Children) |
||||
Console.WriteLine (c.GetType () +" at:"+ c.StartLocation +"-"+ c.EndLocation + " Role: "+ c.Role); |
||||
Console.WriteLine ("----"); |
||||
} |
||||
|
||||
public static void CheckPositionConsistency (AstNode node, string currentFileName, IDocument currentDocument = null) |
||||
{ |
||||
if (currentDocument == null) |
||||
currentDocument = new ReadOnlyDocument(File.ReadAllText(currentFileName)); |
||||
string comment = "(" + node.GetType ().Name + " at " + node.StartLocation + " in " + currentFileName + ")"; |
||||
var pred = node.StartLocation <= node.EndLocation; |
||||
if (!pred) |
||||
PrintNode (node); |
||||
Assert.IsTrue(pred, "StartLocation must be before EndLocation " + comment); |
||||
var prevNodeEnd = node.StartLocation; |
||||
var prevNode = node; |
||||
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
||||
bool assertion = child.StartLocation >= prevNodeEnd; |
||||
if (!assertion) { |
||||
PrintNode (prevNode); |
||||
PrintNode (node); |
||||
} |
||||
Assert.IsTrue(assertion, currentFileName + ": Child " + child.GetType () +" (" + child.StartLocation + ")" +" must start after previous sibling " + prevNode.GetType () + "(" + prevNode.StartLocation + ")"); |
||||
CheckPositionConsistency(child, currentFileName, currentDocument); |
||||
prevNodeEnd = child.EndLocation; |
||||
prevNode = child; |
||||
} |
||||
Assert.IsTrue(prevNodeEnd <= node.EndLocation, "Last child must end before parent node ends " + comment); |
||||
} |
||||
|
||||
public static void CheckMissingTokens(AstNode node, string currentFileName, IDocument currentDocument = null) |
||||
{ |
||||
if (currentDocument == null) |
||||
currentDocument = new ReadOnlyDocument(File.ReadAllText(currentFileName)); |
||||
if (IsLeafNode(node)) { |
||||
Assert.IsNull(node.FirstChild, "Token nodes should not have children"); |
||||
} else { |
||||
var prevNodeEnd = node.StartLocation; |
||||
var prevNode = node; |
||||
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
||||
CheckWhitespace(prevNode, prevNodeEnd, child, child.StartLocation, currentFileName, currentDocument); |
||||
CheckMissingTokens(child, currentFileName, currentDocument); |
||||
prevNode = child; |
||||
prevNodeEnd = child.EndLocation; |
||||
} |
||||
CheckWhitespace(prevNode, prevNodeEnd, node, node.EndLocation, currentFileName, currentDocument); |
||||
} |
||||
} |
||||
|
||||
static bool IsLeafNode(AstNode node) |
||||
{ |
||||
|
||||
if (node.NodeType == NodeType.Token) |
||||
return true; |
||||
if (node.NodeType == NodeType.Whitespace) |
||||
return !(node is PragmaWarningPreprocessorDirective); |
||||
return node is PrimitiveType || node is PrimitiveExpression || node is NullReferenceExpression; |
||||
} |
||||
|
||||
static void CheckWhitespace(AstNode startNode, TextLocation whitespaceStart, AstNode endNode, TextLocation whitespaceEnd, string currentFileName, IDocument currentDocument) |
||||
{ |
||||
if (whitespaceStart == whitespaceEnd || startNode == endNode) |
||||
return; |
||||
Assert.Greater(whitespaceStart.Line, 0); |
||||
Assert.Greater(whitespaceStart.Column, 0); |
||||
Assert.Greater(whitespaceEnd.Line, 0); |
||||
Assert.Greater(whitespaceEnd.Column, 0); |
||||
Assert.IsTrue(whitespaceEnd >= whitespaceStart, endNode.GetType().Name + ".StartLocation < " + startNode.GetType().Name + ".EndLocation: " + whitespaceEnd + " < " + whitespaceStart); |
||||
int start = currentDocument.GetOffset(whitespaceStart.Line, whitespaceStart.Column); |
||||
int end = currentDocument.GetOffset(whitespaceEnd.Line, whitespaceEnd.Column); |
||||
string text = currentDocument.GetText(start, end - start); |
||||
bool assertion = string.IsNullOrWhiteSpace(text); |
||||
if (!assertion) { |
||||
if (startNode.Parent != endNode.Parent) |
||||
PrintNode (startNode.Parent); |
||||
PrintNode (endNode.Parent); |
||||
} |
||||
Assert.IsTrue(assertion, "Expected whitespace between " + startNode.GetType () +":" + whitespaceStart + " and " + endNode.GetType () + ":" + whitespaceEnd |
||||
+ ", but got '" + text + "' (in " + currentFileName + " parent:" + startNode.Parent.GetType () +")"); |
||||
} |
||||
} |
||||
} |
@ -1,59 +0,0 @@
@@ -1,59 +0,0 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Linq; |
||||
using System.IO; |
||||
using ICSharpCode.NRefactory; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Dom |
||||
{ |
||||
public static class ModelFactoryExtensions |
||||
{ |
||||
/// <summary>
|
||||
/// Creates an <see cref="ICSharpCode.SharpDevelop.Dom.IAssemblyModel"/> from a file name.
|
||||
/// </summary>
|
||||
/// <param name="modelFactory">Model factory.</param>
|
||||
/// <param name="fileName">Assembly file name.</param>
|
||||
/// <returns>Created <see cref="ICSharpCode.SharpDevelop.Dom.IAssemblyModel"/>.</returns>
|
||||
public static IAssemblyModel CreateAssemblyModelFromFile(this IModelFactory modelFactory, string fileName) |
||||
{ |
||||
var loader = new CecilLoader(); |
||||
loader.IncludeInternalMembers = true; |
||||
loader.LazyLoad = true; |
||||
var assembly = loader.LoadAssemblyFile(fileName); |
||||
|
||||
IEntityModelContext context = new AssemblyEntityModelContext(assembly); |
||||
IAssemblyModel model = modelFactory.CreateAssemblyModel(context); |
||||
if (model is IUpdateableAssemblyModel) { |
||||
((IUpdateableAssemblyModel)model).Update(EmptyList<IUnresolvedTypeDefinition>.Instance, assembly.TopLevelTypeDefinitions.ToList()); |
||||
((IUpdateableAssemblyModel) model).AssemblyName = assembly.AssemblyName; |
||||
} |
||||
return model; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ICSharpCode.SharpDevelop.Dom.IAssemblyModel"/> from a file name and catches
|
||||
/// errors by showing messages to user.
|
||||
/// </summary>
|
||||
/// <param name="modelFactory">Model factory.</param>
|
||||
/// <param name="fileName">Assembly file name.</param>
|
||||
/// <returns>
|
||||
/// Created <see cref="ICSharpCode.SharpDevelop.Dom.IAssemblyModel"/> or <b>null</b>,
|
||||
/// if model couldn't be created.
|
||||
/// </returns>
|
||||
public static IAssemblyModel SafelyCreateAssemblyModelFromFile(this IModelFactory modelFactory, string fileName) |
||||
{ |
||||
try { |
||||
return modelFactory.CreateAssemblyModelFromFile(fileName); |
||||
} catch (BadImageFormatException) { |
||||
SD.MessageService.ShowWarningFormatted("${res:ICSharpCode.SharpDevelop.Dom.AssemblyInvalid}", Path.GetFileName(fileName)); |
||||
} catch (FileNotFoundException) { |
||||
SD.MessageService.ShowWarningFormatted("${res:ICSharpCode.SharpDevelop.Dom.AssemblyNotAccessible}", fileName); |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,58 @@
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.SharpDevelop.Dom; |
||||
using ICSharpCode.SharpDevelop.Dom.ClassBrowser; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Parser |
||||
{ |
||||
public class DefaultAssemblySearcher : IAssemblySearcher |
||||
{ |
||||
FileName mainAssemblyFileName; |
||||
DirectoryName baseDirectory; |
||||
|
||||
public DefaultAssemblySearcher(FileName mainAssemblyFileName) |
||||
{ |
||||
if (mainAssemblyFileName == null) |
||||
throw new ArgumentNullException("mainAssemblyFileName"); |
||||
this.mainAssemblyFileName = mainAssemblyFileName; |
||||
this.baseDirectory = mainAssemblyFileName.GetParentDirectory(); |
||||
} |
||||
|
||||
public FileName FindAssembly(DomAssemblyName fullName) |
||||
{ |
||||
// look for the assembly in the current loaded assembly lists
|
||||
var classBrowser = SD.GetRequiredService<IClassBrowser>(); |
||||
var relevantClassBrowserAssemblies = classBrowser.AssemblyLists |
||||
.Where(l => l.Assemblies.Any(a => a.Location == mainAssemblyFileName)) |
||||
.SelectMany(l => l.Assemblies); |
||||
foreach (var asm in relevantClassBrowserAssemblies) { |
||||
// TODO I am pretty sure we need the full name here as well...
|
||||
if (asm.AssemblyName == fullName.ShortName) |
||||
return asm.Location; |
||||
} |
||||
|
||||
// look in GAC
|
||||
var gacFileName = SD.GlobalAssemblyCache.FindAssemblyInNetGac(fullName); |
||||
if (gacFileName != null) |
||||
return gacFileName; |
||||
|
||||
// scan current directory
|
||||
var fileName = baseDirectory.CombineFile(fullName.ShortName + ".dll"); |
||||
if (File.Exists(fileName)) |
||||
return fileName; |
||||
|
||||
fileName = baseDirectory.CombineFile(fullName.ShortName + ".exe"); |
||||
if (File.Exists(fileName)) |
||||
return fileName; |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
Loading…
Reference in new issue