/*
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.Linq;
using System.Text;
using System.Windows.Input;
using dnlib.DotNet;
using dnSpy.AsmEditor.Properties;
using dnSpy.Contracts.Decompiler;
using dnSpy.Contracts.MVVM;
namespace dnSpy.AsmEditor.DnlibDialogs {
enum GPVariance {
NonVariant = (int)GenericParamAttributes.NonVariant >> 0,
Covariant = (int)GenericParamAttributes.Covariant >> 0,
Contravariant = (int)GenericParamAttributes.Contravariant >> 0,
}
sealed class GenericParamVM : ViewModelBase {
readonly GenericParamOptions origOptions;
public ICommand ReinitializeCommand => new RelayCommand(a => Reinitialize());
public string FullName {
get {
var sb = new StringBuilder();
if (Number.HasError)
sb.Append("???");
else
sb.Append(string.Format(dnSpy_AsmEditor_Resources.GenericParameterNumber, Number.Value));
sb.Append(' ');
sb.Append(string.IsNullOrEmpty(Name) ? dnSpy_AsmEditor_Resources.NoName : Name);
return sb.ToString();
}
}
public GenericParamAttributes Attributes {
get {
var mask = GenericParamAttributes.VarianceMask;
return (attributes & ~mask) |
((GenericParamAttributes)((int)(GPVariance)GPVarianceVM.SelectedItem! << 0) & GenericParamAttributes.VarianceMask);
}
set {
if (attributes != value) {
attributes = value;
OnPropertyChanged(nameof(Attributes));
OnPropertyChanged(nameof(ReferenceTypeConstraint));
OnPropertyChanged(nameof(NotNullableValueTypeConstraint));
OnPropertyChanged(nameof(DefaultConstructorConstraint));
}
}
}
GenericParamAttributes attributes;
public EnumListVM GPVarianceVM { get; }
public bool ReferenceTypeConstraint {
get => GetFlagValue(GenericParamAttributes.ReferenceTypeConstraint);
set => SetFlagValue(GenericParamAttributes.ReferenceTypeConstraint, value);
}
public bool NotNullableValueTypeConstraint {
get => GetFlagValue(GenericParamAttributes.NotNullableValueTypeConstraint);
set => SetFlagValue(GenericParamAttributes.NotNullableValueTypeConstraint, value);
}
public bool DefaultConstructorConstraint {
get => GetFlagValue(GenericParamAttributes.DefaultConstructorConstraint);
set => SetFlagValue(GenericParamAttributes.DefaultConstructorConstraint, value);
}
bool GetFlagValue(GenericParamAttributes flag) => (Attributes & flag) != 0;
void SetFlagValue(GenericParamAttributes flag, bool value) {
if (value)
Attributes |= flag;
else
Attributes &= ~flag;
}
public string? Name {
get => name;
set {
if (name != value) {
name = value;
OnPropertyChanged(nameof(Name));
OnPropertyChanged(nameof(FullName));
}
}
}
UTF8String? name;
public UInt16VM Number { get; }
public TypeDefOrRefAndCAsVM TypeDefOrRefAndCAsVM { get; }
public CustomAttributesVM CustomAttributesVM { get; }
public TypeSigCreatorVM TypeSigCreator { get; }
readonly ModuleDef ownerModule;
public GenericParamVM(GenericParamOptions options, ModuleDef ownerModule, IDecompilerService decompilerService, TypeDef? ownerType, MethodDef? ownerMethod) {
this.ownerModule = ownerModule;
origOptions = options;
Number = new UInt16VM(a => { OnPropertyChanged(nameof(FullName)); HasErrorUpdated(); });
TypeDefOrRefAndCAsVM = new TypeDefOrRefAndCAsVM(dnSpy_AsmEditor_Resources.EditGenericParameterConstraint, dnSpy_AsmEditor_Resources.CreateGenericParameterConstraint, ownerModule, decompilerService, ownerType, ownerMethod);
CustomAttributesVM = new CustomAttributesVM(ownerModule, decompilerService);
GPVarianceVM = new EnumListVM(EnumVM.Create(typeof(GPVariance)));
var typeSigCreatorOptions = new TypeSigCreatorOptions(ownerModule, decompilerService) {
IsLocal = false,
CanAddGenericTypeVar = true,
CanAddGenericMethodVar = false,
OwnerType = ownerType,
OwnerMethod = ownerMethod,
};
if (ownerType is not null && ownerType.GenericParameters.Count == 0)
typeSigCreatorOptions.CanAddGenericTypeVar = false;
if (ownerMethod is not null && ownerMethod.GenericParameters.Count > 0)
typeSigCreatorOptions.CanAddGenericMethodVar = true;
TypeSigCreator = new TypeSigCreatorVM(typeSigCreatorOptions);
Reinitialize();
}
void Reinitialize() => InitializeFrom(origOptions);
public GenericParamOptions CreateGenericParamOptions() => CopyTo(new GenericParamOptions());
void InitializeFrom(GenericParamOptions options) {
Number.Value = options.Number;
Attributes = options.Flags;
Name = options.Name;
GPVarianceVM.SelectedItem = (GPVariance)((int)(options.Flags & GenericParamAttributes.VarianceMask) >> 0);
TypeSigCreator.TypeSig = options.Kind.ToTypeSig();
TypeDefOrRefAndCAsVM.InitializeFrom(options.GenericParamConstraints);
CustomAttributesVM.InitializeFrom(options.CustomAttributes);
}
GenericParamOptions CopyTo(GenericParamOptions options) {
options.Number = Number.Value;
options.Flags = Attributes;
options.Name = Name;
options.Kind = TypeSigCreator.TypeSig.ToTypeDefOrRef();
options.GenericParamConstraints.Clear();
options.GenericParamConstraints.AddRange(TypeDefOrRefAndCAsVM.Collection.Select(a => a.CreateTypeDefOrRefAndCAOptions().CreateGenericParamConstraint(ownerModule)));
options.CustomAttributes.Clear();
options.CustomAttributes.AddRange(CustomAttributesVM.Collection.Select(a => a.CreateCustomAttributeOptions().Create()));
return options;
}
public override bool HasError => Number.HasError;
}
}