/*
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());
}
}