/* 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.Diagnostics; using System.Windows.Input; using dnlib.DotNet; using dnlib.DotNet.Emit; using dnSpy.AsmEditor.Commands; using dnSpy.AsmEditor.DnlibDialogs; using dnSpy.AsmEditor.Properties; using dnSpy.AsmEditor.ViewHelpers; using dnSpy.Contracts.MVVM; namespace dnSpy.AsmEditor.MethodBody { sealed class ExceptionHandlerVM : ViewModelBase, IIndexedItem { readonly ExceptionHandlerOptions origOptions; public ITypeSigCreator TypeSigCreator { set => typeSigCreator = value; } ITypeSigCreator? typeSigCreator; public ICommand ReinitializeCommand => new RelayCommand(a => Reinitialize()); public ICommand EditCatchTypeCommand => new RelayCommand(a => EditCatchType()); public int Index { get => index; set { if (index != value) { index = value; OnPropertyChanged(nameof(Index)); } } } int index; public ListVM TryStartVM { get; } public ListVM TryEndVM { get; } public ListVM FilterStartVM { get; } public ListVM HandlerStartVM { get; } public ListVM HandlerEndVM { get; } public ITypeDefOrRef? CatchType { get => catchType; set { if (catchType != value) { catchType = value; OnPropertyChanged(nameof(CatchType)); } } } ITypeDefOrRef? catchType; internal static readonly EnumVM[] exceptionHandlerTypeList = new EnumVM[] { new EnumVM(ExceptionHandlerType.Catch), new EnumVM(ExceptionHandlerType.Filter), new EnumVM(ExceptionHandlerType.Finally), new EnumVM(ExceptionHandlerType.Fault), }; public EnumListVM HandlerTypeVM { get; } readonly TypeSigCreatorOptions typeSigCreatorOptions; public ExceptionHandlerVM(TypeSigCreatorOptions typeSigCreatorOptions, ExceptionHandlerOptions options) { this.typeSigCreatorOptions = typeSigCreatorOptions.Clone(dnSpy_AsmEditor_Resources.CreateAnExceptionCatchType); this.typeSigCreatorOptions.IsLocal = false; this.typeSigCreatorOptions.NullTypeSigAllowed = true; origOptions = options; HandlerTypeVM = new EnumListVM(exceptionHandlerTypeList); TryStartVM = new ListVM((a, b) => OnSelectionChanged()) { DataErrorInfoDelegate = VerifyInstruction }; TryEndVM = new ListVM((a, b) => OnSelectionChanged()) { DataErrorInfoDelegate = VerifyInstruction }; FilterStartVM = new ListVM((a, b) => OnSelectionChanged()) { DataErrorInfoDelegate = VerifyInstruction }; HandlerStartVM = new ListVM((a, b) => OnSelectionChanged()) { DataErrorInfoDelegate = VerifyInstruction }; HandlerEndVM = new ListVM((a, b) => OnSelectionChanged()) { DataErrorInfoDelegate = VerifyInstruction }; Reinitialize(); } void OnSelectionChanged() => HasErrorUpdated(); string VerifyInstruction(ListVM list) { var item = list.SelectedItem; var instr = item as InstructionVM; if (item is not null && instr is null) return dnSpy_AsmEditor_Resources.Error_OnlyInstrsCanBeSelected; if (instr is not null && instr.Index == -1) return dnSpy_AsmEditor_Resources.Error_InstrHasBeenRemoved; return string.Empty; } bool HasListError(ListVM list) => !string.IsNullOrEmpty(VerifyInstruction(list)); public void InstructionChanged(IEnumerable instrs) { TryStartVM.InvalidateSelected(instrs, true, InstructionVM.Null); TryEndVM.InvalidateSelected(instrs, true, InstructionVM.Null); FilterStartVM.InvalidateSelected(instrs, true, InstructionVM.Null); HandlerStartVM.InvalidateSelected(instrs, true, InstructionVM.Null); HandlerEndVM.InvalidateSelected(instrs, true, InstructionVM.Null); } void EditCatchType() { if (typeSigCreator is null) throw new InvalidOperationException(); var newType = typeSigCreator.Create(typeSigCreatorOptions, CatchType.ToTypeSig(), out bool canceled); if (canceled) return; CatchType = newType.ToTypeDefOrRef(); } void Reinitialize() => InitializeFrom(origOptions); public ExceptionHandlerOptions CreateExceptionHandlerOptions() => CopyTo(new ExceptionHandlerOptions()); public void InitializeFrom(ExceptionHandlerOptions options) { TryStartVM.SelectedItem = options.TryStart ?? InstructionVM.Null; TryEndVM.SelectedItem = options.TryEnd ?? InstructionVM.Null; FilterStartVM.SelectedItem = options.FilterStart ?? InstructionVM.Null; HandlerStartVM.SelectedItem = options.HandlerStart ?? InstructionVM.Null; HandlerEndVM.SelectedItem = options.HandlerEnd ?? InstructionVM.Null; CatchType = options.CatchType; HandlerTypeVM.SelectedItem = options.HandlerType; } static InstructionVM? RemoveNullInstance(InstructionVM? vm) { Debug2.Assert(vm is not null); if (vm is null || vm == InstructionVM.Null) return null; return vm; } public ExceptionHandlerOptions CopyTo(ExceptionHandlerOptions options) { options.TryStart = RemoveNullInstance(TryStartVM.SelectedItem); options.TryEnd = RemoveNullInstance(TryEndVM.SelectedItem); options.FilterStart = RemoveNullInstance(FilterStartVM.SelectedItem); options.HandlerStart = RemoveNullInstance(HandlerStartVM.SelectedItem); options.HandlerEnd = RemoveNullInstance(HandlerEndVM.SelectedItem); options.CatchType = CatchType; options.HandlerType = (ExceptionHandlerType)HandlerTypeVM.SelectedItem!; return options; } public override bool HasError { get { return HasListError(TryStartVM) || HasListError(TryEndVM) || HasListError(FilterStartVM) || HasListError(HandlerStartVM) || HasListError(HandlerEndVM); } } public IIndexedItem Clone() => new ExceptionHandlerVM(typeSigCreatorOptions, CreateExceptionHandlerOptions()); } }