/*
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.Windows.Input;
using dnlib.DotNet;
using dnSpy.AsmEditor.Properties;
using dnSpy.AsmEditor.ViewHelpers;
using dnSpy.Contracts.MVVM;
namespace dnSpy.AsmEditor.DnlibDialogs {
enum MethodCallingConv {
Default = CallingConvention.Default,
C = CallingConvention.C,
StdCall = CallingConvention.StdCall,
ThisCall = CallingConvention.ThisCall,
FastCall = CallingConvention.FastCall,
VarArg = CallingConvention.VarArg,
NativeVarArg = CallingConvention.NativeVarArg,
}
sealed class MethodSigCreatorVM : ViewModelBase {
public ITypeSigCreator TypeSigCreator {
set => typeSigCreator = value;
}
ITypeSigCreator? typeSigCreator;
public ICommand AddReturnTypeCommand => new RelayCommand(a => AddReturnType());
public PropertySig? PropertySig {
get => CreateSig(new PropertySig());
set => WriteSignature(value);
}
public MethodSig? MethodSig {
get => CreateSig(new MethodSig());
set => WriteSignature(value);
}
public MethodBaseSig? MethodBaseSig {
get => IsPropertySig ? (MethodBaseSig?)PropertySig : MethodSig;
set => WriteSignature(value);
}
public bool ShowSignatureFullName => !options.DontShowSignatureFullName;
public bool IsPropertySig => options.IsPropertySig;
public bool IsMethodSig => !IsPropertySig;
public bool CanHaveSentinel => options.CanHaveSentinel;
public string SignatureFullName {
get {
var sig = MethodBaseSig!;
if (sig.GenParamCount > 100)
sig.GenParamCount = 100;
return FullNameFactory.MethodBaseSigFullName(null, null, sig, options.TypeSigCreatorOptions.OwnerMethod, null);
}
}
public CallingConvention CallingConvention {
get => callingConvention;
set {
if (callingConvention != value) {
callingConvention = value;
OnPropertyChanged(nameof(CallingConvention));
OnPropertyChanged(nameof(SignatureFullName));
OnPropertyChanged(nameof(IsGeneric));
OnPropertyChanged(nameof(HasThis));
OnPropertyChanged(nameof(ExplicitThis));
}
}
}
CallingConvention callingConvention;
public bool IsGeneric {
get => GetFlags(dnlib.DotNet.CallingConvention.Generic);
set => SetFlags(dnlib.DotNet.CallingConvention.Generic, value);
}
public bool HasThis {
get => GetFlags(dnlib.DotNet.CallingConvention.HasThis);
set => SetFlags(dnlib.DotNet.CallingConvention.HasThis, value);
}
public bool ExplicitThis {
get => GetFlags(dnlib.DotNet.CallingConvention.ExplicitThis);
set => SetFlags(dnlib.DotNet.CallingConvention.ExplicitThis, value);
}
bool GetFlags(dnlib.DotNet.CallingConvention flag) => (CallingConvention & flag) != 0;
void SetFlags(dnlib.DotNet.CallingConvention flag, bool value) {
if (value)
CallingConvention |= flag;
else
CallingConvention &= ~flag;
}
public EnumListVM MethodCallingConv { get; }
internal static readonly EnumVM[] methodCallingConvList = EnumVM.Create(typeof(MethodCallingConv));
public TypeSig? ReturnType {
get => retType;
set {
if (retType != value) {
retType = value;
OnPropertyChanged(nameof(ReturnType));
OnPropertyChanged(nameof(SignatureFullName));
OnPropertyChanged(nameof(ErrorText));
HasErrorUpdated();
}
}
}
TypeSig? retType;
public UInt32VM GenericParameterCount { get; }
public string? Title {
get {
if (!string.IsNullOrEmpty(title))
return title;
return IsPropertySig ? dnSpy_AsmEditor_Resources.CreatePropertySignature : dnSpy_AsmEditor_Resources.CreateMethodSignature;
}
}
string? title;
public CreateTypeSigArrayVM ParametersCreateTypeSigArray { get; }
public CreateTypeSigArrayVM SentinelCreateTypeSigArray { get; }
readonly MethodSigCreatorOptions options;
public MethodSigCreatorVM(MethodSigCreatorOptions options) {
this.options = options.Clone();
title = options.TypeSigCreatorOptions.Title;
ParametersCreateTypeSigArray = new CreateTypeSigArrayVM(options.TypeSigCreatorOptions.Clone(null), null);
ParametersCreateTypeSigArray.TypeSigCollection.CollectionChanged += (s, e) => OnPropertyChanged(nameof(SignatureFullName));
SentinelCreateTypeSigArray = new CreateTypeSigArrayVM(options.TypeSigCreatorOptions.Clone(null), null);
SentinelCreateTypeSigArray.TypeSigCollection.CollectionChanged += (s, e) => OnPropertyChanged(nameof(SignatureFullName));
SentinelCreateTypeSigArray.IsEnabled = CanHaveSentinel;
GenericParameterCount = new UInt32VM(0, a => {
HasErrorUpdated();
OnPropertyChanged(nameof(SignatureFullName));
if (GenericParameterCount is not null && !GenericParameterCount.HasError)
IsGeneric = GenericParameterCount.Value != 0;
}) {
Min = ModelUtils.COMPRESSED_UINT32_MIN,
Max = ModelUtils.COMPRESSED_UINT32_MAX,
};
MethodCallingConv = new EnumListVM(methodCallingConvList, (a, b) => {
if (!IsMethodSig)
throw new InvalidOperationException();
CallingConvention = (CallingConvention & ~dnlib.DotNet.CallingConvention.Mask) |
(dnlib.DotNet.CallingConvention)(MethodCallingConv)MethodCallingConv!.SelectedItem!;
});
if (!CanHaveSentinel) {
MethodCallingConv.Items.RemoveAt(MethodCallingConv.GetIndex(DnlibDialogs.MethodCallingConv.VarArg));
MethodCallingConv.Items.RemoveAt(MethodCallingConv.GetIndex(DnlibDialogs.MethodCallingConv.NativeVarArg));
}
if (IsMethodSig)
MethodCallingConv.SelectedItem = DnlibDialogs.MethodCallingConv.Default;
else
CallingConvention = (CallingConvention & ~dnlib.DotNet.CallingConvention.Mask) | dnlib.DotNet.CallingConvention.Property;
ReturnType = options.TypeSigCreatorOptions.OwnerModule.CorLibTypes.Void;
}
T CreateSig(T sig) where T : MethodBaseSig {
sig.CallingConvention = CallingConvention;
sig.RetType = ReturnType;
sig.Params.AddRange(ParametersCreateTypeSigArray.TypeSigArray);
sig.GenParamCount = GenericParameterCount.HasError ? 0 : GenericParameterCount.Value;
var sentAry = SentinelCreateTypeSigArray.TypeSigArray;
sig.ParamsAfterSentinel = sentAry.Length == 0 ? null : new List(sentAry);
return sig;
}
void WriteSignature(MethodBaseSig? sig) {
if (sig is null) {
CallingConvention = 0;
ReturnType = null;
ParametersCreateTypeSigArray.TypeSigCollection.Clear();
GenericParameterCount.Value = 0;
SentinelCreateTypeSigArray.TypeSigCollection.Clear();
}
else {
CallingConvention = sig.CallingConvention;
ReturnType = sig.RetType;
ParametersCreateTypeSigArray.TypeSigCollection.Clear();
ParametersCreateTypeSigArray.TypeSigCollection.AddRange(sig.Params);
GenericParameterCount.Value = sig.GenParamCount;
SentinelCreateTypeSigArray.TypeSigCollection.Clear();
if (sig.ParamsAfterSentinel is not null)
SentinelCreateTypeSigArray.TypeSigCollection.AddRange(sig.ParamsAfterSentinel);
}
}
void AddReturnType() {
if (typeSigCreator is null)
throw new InvalidOperationException();
var newTypeSig = typeSigCreator.Create(options.TypeSigCreatorOptions.Clone(dnSpy_AsmEditor_Resources.CreateReturnType), ReturnType, out bool canceled);
if (newTypeSig is not null)
ReturnType = newTypeSig;
}
protected override string? Verify(string columnName) {
if (columnName == nameof(ReturnType))
return ReturnType is not null ? string.Empty : dnSpy_AsmEditor_Resources.ReturnTypeRequired;
return string.Empty;
}
public override bool HasError => GenericParameterCount.HasError || ReturnType is null;
public string ErrorText {
get {
string? err;
if (!string2.IsNullOrEmpty(err = Verify(nameof(ReturnType))))
return err;
return string.Empty;
}
}
}
}