Files
RobustToolbox/Robust.Shared/ContentPack/AssemblyTypeChecker.Types.cs

438 lines
14 KiB
C#

using System.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
// ReSharper disable MemberCanBePrivate.Global
namespace Robust.Shared.ContentPack
{
internal sealed partial class AssemblyTypeChecker
{
internal abstract record MType
{
public virtual bool WhitelistEquals(MType other)
{
return false;
}
public virtual bool IsCoreTypeDefined()
{
return false;
}
/// <summary>
/// Outputs this type in a format re-parseable for the sandbox config whitelist.
/// </summary>
public virtual string? WhitelistToString()
{
return ToString();
}
}
internal abstract class MMemberRef
{
public readonly MType ParentType;
public readonly string Name;
protected MMemberRef(MType parentType, string name)
{
ParentType = parentType;
Name = name;
}
}
internal sealed class MMemberRefMethod : MMemberRef
{
public readonly MType ReturnType;
public readonly int GenericParameterCount;
public readonly ImmutableArray<MType> ParameterTypes;
public MMemberRefMethod(MType parentType, string name, MType returnType,
int genericParameterCount, ImmutableArray<MType> parameterTypes) : base(parentType, name)
{
ReturnType = returnType;
GenericParameterCount = genericParameterCount;
ParameterTypes = parameterTypes;
}
public override string ToString()
{
return $"{ReturnType} {ParentType}.{Name}({string.Join(", ", ParameterTypes)})";
}
}
internal sealed class MMemberRefField : MMemberRef
{
public readonly MType FieldType;
public MMemberRefField(MType parentType, string name, MType fieldType) : base(parentType, name)
{
FieldType = fieldType;
}
public override string ToString()
{
return $"{FieldType} {ParentType}.{Name}";
}
}
internal sealed record MTypeParsed(string FullName, MTypeParsed? NestedParent = null) : MType
{
public override string ToString()
{
return NestedParent != null ? $"{NestedParent}/{FullName}" : FullName;
}
public override bool WhitelistEquals(MType other)
{
switch (other)
{
case MTypeParsed parsed:
if (NestedParent != null)
{
if (parsed.NestedParent == null || !NestedParent.WhitelistEquals(parsed.NestedParent))
{
return false;
}
}
return parsed.FullName == FullName;
case MTypeReferenced referenced:
if (NestedParent != null)
{
if (referenced.ResolutionScope is not MResScopeType parentRes ||
!NestedParent.WhitelistEquals(parentRes.Type))
{
return false;
}
}
var refFullName = referenced.Namespace == null
? referenced.Name
: $"{referenced.Namespace}.{referenced.Name}";
return FullName == refFullName;
default:
return false;
}
}
}
internal sealed record MTypePrimitive(PrimitiveTypeCode TypeCode) : MType
{
public override string ToString()
{
return TypeCode switch
{
PrimitiveTypeCode.Void => "void",
PrimitiveTypeCode.Boolean => "bool",
PrimitiveTypeCode.Char => "char",
PrimitiveTypeCode.SByte => "int8",
PrimitiveTypeCode.Byte => "unsigned int8",
PrimitiveTypeCode.Int16 => "int16",
PrimitiveTypeCode.UInt16 => "unsigned int16",
PrimitiveTypeCode.Int32 => "int32",
PrimitiveTypeCode.UInt32 => "unsigned int32",
PrimitiveTypeCode.Int64 => "int64",
PrimitiveTypeCode.UInt64 => "unsigned int64",
PrimitiveTypeCode.Single => "float32",
PrimitiveTypeCode.Double => "float64",
PrimitiveTypeCode.String => "string",
// ReSharper disable once StringLiteralTypo
PrimitiveTypeCode.TypedReference => "typedref",
PrimitiveTypeCode.IntPtr => "native int",
PrimitiveTypeCode.UIntPtr => "unsigned native int",
PrimitiveTypeCode.Object => "object",
_ => "???"
};
}
public override string WhitelistToString()
{
return TypeCode switch
{
PrimitiveTypeCode.Void => "void",
PrimitiveTypeCode.Boolean => "bool",
PrimitiveTypeCode.Char => "char",
PrimitiveTypeCode.SByte => "sbyte",
PrimitiveTypeCode.Byte => "byte",
PrimitiveTypeCode.Int16 => "short",
PrimitiveTypeCode.UInt16 => "ushort",
PrimitiveTypeCode.Int32 => "int",
PrimitiveTypeCode.UInt32 => "uint",
PrimitiveTypeCode.Int64 => "long",
PrimitiveTypeCode.UInt64 => "ulong",
PrimitiveTypeCode.Single => "float",
PrimitiveTypeCode.Double => "double",
PrimitiveTypeCode.String => "string",
// ReSharper disable once StringLiteralTypo
PrimitiveTypeCode.TypedReference => "typedref",
// ReSharper disable once StringLiteralTypo
PrimitiveTypeCode.IntPtr => "nint",
// ReSharper disable once StringLiteralTypo
PrimitiveTypeCode.UIntPtr => "unint",
PrimitiveTypeCode.Object => "object",
_ => "???"
};
}
public override bool WhitelistEquals(MType other)
{
return Equals(other);
}
}
internal sealed record MTypeSZArray(MType ElementType) : MType
{
public override string ToString()
{
return $"{ElementType}[]";
}
public override string WhitelistToString()
{
return $"{ElementType.WhitelistToString()}[]";
}
public override bool WhitelistEquals(MType other)
{
return other is MTypeSZArray arr && ElementType.WhitelistEquals(arr.ElementType);
}
}
internal sealed record MTypeArray(MType ElementType, ArrayShape Shape) : MType
{
public override string ToString()
{
return $"{ElementType}[TODO]";
}
public override string WhitelistToString()
{
return $"{ElementType.WhitelistToString()}[TODO]";
}
public override bool WhitelistEquals(MType other)
{
return other is MTypeArray arr && ShapesEqual(Shape, arr.Shape) && ElementType.WhitelistEquals(arr);
}
private static bool ShapesEqual(in ArrayShape a, in ArrayShape b)
{
return a.Rank == b.Rank && a.LowerBounds.SequenceEqual(b.LowerBounds) && a.Sizes.SequenceEqual(b.Sizes);
}
public override bool IsCoreTypeDefined()
{
return ElementType.IsCoreTypeDefined();
}
}
internal sealed record MTypeByRef(MType ElementType) : MType
{
public override string ToString()
{
return $"{ElementType}&";
}
public override string WhitelistToString()
{
return $"ref {ElementType.WhitelistToString()}";
}
public override bool WhitelistEquals(MType other)
{
return other is MTypeByRef byRef && ElementType.WhitelistEquals(byRef.ElementType);
}
}
internal sealed record MTypePointer(MType ElementType) : MType
{
public override string ToString()
{
return $"{ElementType}*";
}
public override string WhitelistToString()
{
return $"{ElementType.WhitelistToString()}*";
}
public override bool WhitelistEquals(MType other)
{
return other is MTypePointer ptr && ElementType.WhitelistEquals(ptr.ElementType);
}
}
internal sealed record MTypeGeneric(MType GenericType, ImmutableArray<MType> TypeArguments) : MType
{
public override string ToString()
{
return $"{GenericType}<{string.Join(", ", TypeArguments)}>";
}
public override string WhitelistToString()
{
return $"{GenericType.WhitelistToString()}<{string.Join(", ", TypeArguments.Select(t => t.WhitelistToString()))}>";
}
public override bool WhitelistEquals(MType other)
{
if (!(other is MTypeGeneric generic))
{
return false;
}
if (TypeArguments.Length != generic.TypeArguments.Length)
{
return false;
}
for (var i = 0; i < TypeArguments.Length; i++)
{
var argA = TypeArguments[i];
var argB = generic.TypeArguments[i];
if (!argA.WhitelistEquals(argB))
{
return false;
}
}
return GenericType.WhitelistEquals(generic.GenericType);
}
public bool Equals(MTypeGeneric? otherGeneric)
{
return otherGeneric != null && GenericType.Equals(otherGeneric.GenericType) &&
TypeArguments.SequenceEqual(otherGeneric.TypeArguments);
}
public override bool IsCoreTypeDefined()
{
return GenericType.IsCoreTypeDefined();
}
}
internal sealed record MTypeDefined(string Name, string? Namespace, MTypeDefined? Enclosing) : MType
{
public override string ToString()
{
var name = Namespace != null ? $"{Namespace}.{Name}" : Name;
if (Enclosing != null)
{
return $"{Enclosing}/{name}";
}
return name;
}
public override bool IsCoreTypeDefined()
{
return true;
}
}
internal sealed record MTypeReferenced(MResScope ResolutionScope, string Name, string? Namespace) : MType
{
public override string ToString()
{
if (Namespace == null)
{
return $"{ResolutionScope}{Name}";
}
return $"{ResolutionScope}{Namespace}.{Name}";
}
public override string WhitelistToString()
{
if (Namespace == null)
{
return Name;
}
return $"{Namespace}.{Name}";
}
public override bool WhitelistEquals(MType other)
{
return other switch
{
MTypeParsed p => p.WhitelistEquals(this),
// TODO: ResolutionScope doesn't actually implement equals
// This is fine since we're not comparing these anywhere
MTypeReferenced r => r.Namespace == Namespace && r.Name == Name &&
r.ResolutionScope.Equals(ResolutionScope),
_ => false
};
}
}
internal abstract record MResScope
{
}
internal sealed record MResScopeType(MType Type) : MResScope
{
public override string ToString()
{
return $"{Type}/";
}
}
internal sealed record MResScopeAssembly(string Name) : MResScope
{
public override string ToString()
{
return $"[{Name}]";
}
}
internal sealed record MTypeGenericTypePlaceHolder(int Index) : MType
{
public override string ToString()
{
return $"!{Index}";
}
public override bool WhitelistEquals(MType other)
{
return Equals(other);
}
}
internal sealed record MTypeGenericMethodPlaceHolder(int Index) : MType
{
public override string ToString()
{
return $"!!{Index}";
}
public override bool WhitelistEquals(MType other)
{
return Equals(other);
}
}
internal sealed record MTypeModified(MType UnmodifiedType, MType ModifierType, bool Required) : MType
{
public override string ToString()
{
var modName = Required ? "modreq" : "modopt";
return $"{UnmodifiedType} {modName}({ModifierType})";
}
public override string? WhitelistToString()
{
return UnmodifiedType.WhitelistToString();
}
public override bool WhitelistEquals(MType other)
{
// TODO: This is asymmetric shit.
return UnmodifiedType.WhitelistEquals(other);
}
}
}
}