/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using dnSpy.Contracts.Debugger.DotNet.Evaluation.Engine; using dnSpy.Contracts.Debugger.DotNet.Evaluation.ExpressionCompiler; using dnSpy.Contracts.Debugger.DotNet.Evaluation.Formatters; using dnSpy.Contracts.Debugger.Engine.Evaluation; using dnSpy.Contracts.Debugger.Engine.Evaluation.Internal; using dnSpy.Contracts.Decompiler; using dnSpy.Contracts.Resources; using dnSpy.Debugger.DotNet.Code; using dnSpy.Debugger.DotNet.Evaluation.Engine.Interpreter; namespace dnSpy.Debugger.DotNet.Evaluation.Engine { abstract class DbgDotNetLanguageService2 : DbgDotNetLanguageService { public abstract IEnumerable CreateLanguages(); } [Export(typeof(DbgDotNetLanguageService))] [Export(typeof(DbgDotNetLanguageService2))] sealed class DbgDotNetLanguageServiceImpl : DbgDotNetLanguageService2 { readonly Lazy dbgMethodDebugInfoProvider; readonly Lazy dbgModuleReferenceProvider; readonly Lazy dbgDotNetEngineValueNodeFactoryService; readonly Lazy dnILInterpreter; readonly Lazy dbgAliasProvider; readonly Lazy[] dbgDotNetExpressionCompilers; readonly IDecompilerService decompilerService; readonly IPredefinedEvaluationErrorMessagesHelper predefinedEvaluationErrorMessagesHelper; readonly Dictionary> formattersDict; static readonly Guid csharpDecompilerGuid = new Guid(PredefinedDecompilerGuids.CSharp); static readonly Guid visualBasicDecompilerGuid = new Guid(PredefinedDecompilerGuids.VisualBasic); [ImportingConstructor] DbgDotNetLanguageServiceImpl(Lazy dbgMethodDebugInfoProvider, Lazy dbgModuleReferenceProvider, Lazy dbgDotNetEngineValueNodeFactoryService, Lazy dnILInterpreter, Lazy dbgAliasProvider, [ImportMany] IEnumerable> dbgDotNetExpressionCompilers, IDecompilerService decompilerService, IPredefinedEvaluationErrorMessagesHelper predefinedEvaluationErrorMessagesHelper, [ImportMany] IEnumerable> dbgDotNetFormatters) { this.dbgMethodDebugInfoProvider = dbgMethodDebugInfoProvider; this.dbgModuleReferenceProvider = dbgModuleReferenceProvider; this.dbgDotNetEngineValueNodeFactoryService = dbgDotNetEngineValueNodeFactoryService; this.dnILInterpreter = dnILInterpreter; this.dbgAliasProvider = dbgAliasProvider; this.decompilerService = decompilerService; this.predefinedEvaluationErrorMessagesHelper = predefinedEvaluationErrorMessagesHelper; var eeList = new List>(); var langGuids = new HashSet(); foreach (var lz in dbgDotNetExpressionCompilers.OrderBy(a => a.Metadata.Order)) { if (!Guid.TryParse(lz.Metadata.LanguageGuid, out var languageGuid)) { Debug.Fail($"Couldn't parse language GUID: '{lz.Metadata.LanguageGuid}'"); continue; } if (!Guid.TryParse(lz.Metadata.DecompilerGuid, out _)) { Debug.Fail($"Couldn't parse decompiler GUID: '{lz.Metadata.DecompilerGuid}'"); continue; } if (langGuids.Add(languageGuid)) eeList.Add(lz); } this.dbgDotNetExpressionCompilers = eeList.ToArray(); formattersDict = new Dictionary>(); foreach (var lz in dbgDotNetFormatters.OrderBy(a => a.Metadata.Order)) { if (!Guid.TryParse(lz.Metadata.LanguageGuid, out var languageGuid)) { Debug.Fail($"Couldn't parse language GUID: '{lz.Metadata.LanguageGuid}'"); continue; } if (!formattersDict.ContainsKey(languageGuid)) formattersDict.Add(languageGuid, lz); } } public override IEnumerable CreateLanguages() { foreach (var lz in dbgDotNetExpressionCompilers) { bool b = Guid.TryParse(lz.Metadata.DecompilerGuid, out var decompilerGuid); Debug.Assert(b); if (!b) continue; if (!TryGetFormatter(lz.Metadata.LanguageGuid, out var formatter)) continue; if (decompilerGuid == csharpDecompilerGuid) decompilerGuid = DecompilerConstants.LANGUAGE_CSHARP; else if (decompilerGuid == visualBasicDecompilerGuid) decompilerGuid = DecompilerConstants.LANGUAGE_VISUALBASIC; var decompiler = decompilerService.Find(decompilerGuid); Debug2.Assert(decompiler is not null); if (decompiler is null) continue; var valueNodeFactory = dbgDotNetEngineValueNodeFactoryService.Value.Create(lz.Metadata.LanguageGuid, formatter.Value); if (valueNodeFactory is null) continue; var languageDisplayName = ResourceHelper.GetString(lz.Value, lz.Metadata.LanguageDisplayName); yield return new DbgEngineLanguageImpl(dbgModuleReferenceProvider.Value, lz.Metadata.LanguageName, languageDisplayName, lz.Value, dbgMethodDebugInfoProvider.Value, decompiler, formatter.Value, valueNodeFactory, dnILInterpreter.Value, dbgAliasProvider.Value, predefinedEvaluationErrorMessagesHelper); } } bool TryGetFormatter(string guidString, [NotNullWhen(true)] out Lazy? formatter) { formatter = null; bool b = Guid.TryParse(guidString, out var languageGuid); Debug.Assert(b); if (!b) return false; if (formattersDict.TryGetValue(languageGuid, out formatter)) return true; if (formattersDict.TryGetValue(LanguageConstants.DefaultLanguageGuid, out formatter)) return true; Debug.Fail($"Default formatter ({LanguageConstants.DefaultLanguageGuid.ToString()}) wasn't exported"); formatter = formattersDict.Values.FirstOrDefault(); return formatter is not null; } public override DbgEngineObjectIdFactory GetEngineObjectIdFactory(Guid runtimeGuid) => new DbgEngineObjectIdFactoryImpl(runtimeGuid); } }