mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
438 lines
14 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|