Allow constructed byref-like types
We were not allowing these because we couldn't properly fill out the vtable (can't really make unboxing thunks to put in the vtable). We actually need the vtable, but only because the generic dictionary lives there and reflection might touch it. [tfs-changeset: 1705047]
This commit is contained in:
Родитель
5821d7e3cd
Коммит
2bcf67a5f1
|
@ -290,7 +290,17 @@ namespace ILCompiler
|
|||
|
||||
Module = owningModule;
|
||||
ValueTypeRepresented = valuetype;
|
||||
BoxedValue = new BoxedValueField(this);
|
||||
|
||||
// Unboxing thunks for byref-like types don't make sense. Byref-like types cannot be boxed.
|
||||
// We still allow these to exist in the system, because it's easier than trying to prevent
|
||||
// their creation. We create them as if they existed (in lieu of e.g. pointing all of them
|
||||
// to the same __unreachable method body) so that the various places that store pointers to
|
||||
// them because they want to be able to extract the target instance method can use the same
|
||||
// mechanism they use for everything else at runtime.
|
||||
// The main difference is that the "Boxed_ValueType" version has no fields. Reference types
|
||||
// cannot have byref-like fields.
|
||||
if (!valuetype.IsByRefLike)
|
||||
BoxedValue = new BoxedValueField(this);
|
||||
}
|
||||
|
||||
public override ClassLayoutMetadata GetClassLayout() => default(ClassLayoutMetadata);
|
||||
|
@ -332,7 +342,7 @@ namespace ILCompiler
|
|||
|
||||
public override FieldDesc GetField(string name)
|
||||
{
|
||||
if (name == BoxedValueFieldName)
|
||||
if (name == BoxedValueFieldName && BoxedValue != null)
|
||||
return BoxedValue;
|
||||
|
||||
return null;
|
||||
|
@ -340,7 +350,10 @@ namespace ILCompiler
|
|||
|
||||
public override IEnumerable<FieldDesc> GetFields()
|
||||
{
|
||||
yield return BoxedValue;
|
||||
if (BoxedValue != null)
|
||||
return new FieldDesc[] { BoxedValue };
|
||||
|
||||
return Array.Empty<FieldDesc>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -439,6 +452,15 @@ namespace ILCompiler
|
|||
|
||||
public override MethodIL EmitIL()
|
||||
{
|
||||
if (_owningType.BoxedValue == null)
|
||||
{
|
||||
// If this is the fake unboxing thunk for ByRef-like types, just make a method that throws.
|
||||
return new ILStubMethodIL(this,
|
||||
new byte[] { (byte)ILOpcode.ldnull, (byte)ILOpcode.throw_ },
|
||||
Array.Empty<LocalVariableDefinition>(),
|
||||
Array.Empty<object>());
|
||||
}
|
||||
|
||||
// Generate the unboxing stub. This loosely corresponds to following C#:
|
||||
// return BoxedValue.InstanceMethod(this.m_pEEType, [rest of parameters])
|
||||
|
||||
|
@ -507,6 +529,15 @@ namespace ILCompiler
|
|||
|
||||
public override MethodIL EmitIL()
|
||||
{
|
||||
if (_owningType.BoxedValue == null)
|
||||
{
|
||||
// If this is the fake unboxing thunk for ByRef-like types, just make a method that throws.
|
||||
return new ILStubMethodIL(this,
|
||||
new byte[] { (byte)ILOpcode.ldnull, (byte)ILOpcode.throw_ },
|
||||
Array.Empty<LocalVariableDefinition>(),
|
||||
Array.Empty<object>());
|
||||
}
|
||||
|
||||
// Generate the unboxing stub. This loosely corresponds to following C#:
|
||||
// return BoxedValue.InstanceMethod([rest of parameters])
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ namespace ILCompiler.DependencyAnalysis
|
|||
Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any));
|
||||
Debug.Assert(type == type.ConvertToCanonForm(CanonicalFormKind.Specific));
|
||||
Debug.Assert(!type.IsMdArray);
|
||||
Debug.Assert(!type.IsByRefLike);
|
||||
}
|
||||
|
||||
public override bool StaticDependenciesAreComputed => true;
|
||||
|
|
|
@ -204,10 +204,6 @@ namespace ILCompiler.DependencyAnalysis
|
|||
if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any))
|
||||
return false;
|
||||
|
||||
// Byref-like types have interior pointers and cannot be heap allocated.
|
||||
if (type.IsByRefLike)
|
||||
return false;
|
||||
|
||||
// The global "<Module>" type can never be allocated.
|
||||
if (((MetadataType)type).IsModuleType)
|
||||
return false;
|
||||
|
|
|
@ -437,7 +437,23 @@ internal class ReflectionTest
|
|||
}
|
||||
}
|
||||
|
||||
ref struct ByRefLike<T>
|
||||
{
|
||||
public readonly T Value;
|
||||
|
||||
public ByRefLike(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString() + " " + typeof(T).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
delegate string ToStringDelegate(ref ByRefLike thisObj);
|
||||
delegate string ToStringDelegate<T>(ref ByRefLike<T> thisObj);
|
||||
|
||||
public static void Run()
|
||||
{
|
||||
|
@ -449,15 +465,40 @@ internal class ReflectionTest
|
|||
default(ByRefLike).ToString();
|
||||
ToStringDelegate s = null;
|
||||
s = s.Invoke;
|
||||
default(ByRefLike<object>).ToString();
|
||||
ToStringDelegate<object> s2 = null;
|
||||
s2 = s2.Invoke;
|
||||
}
|
||||
|
||||
Type byRefLikeType = GetTestType(nameof(TestByRefLikeTypeMethod), nameof(ByRefLike));
|
||||
MethodInfo toStringMethod = byRefLikeType.GetMethod("ToString");
|
||||
var toString = (ToStringDelegate)toStringMethod.CreateDelegate(typeof(ToStringDelegate));
|
||||
{
|
||||
Type byRefLikeType = GetTestType(nameof(TestByRefLikeTypeMethod), nameof(ByRefLike));
|
||||
MethodInfo toStringMethod = byRefLikeType.GetMethod("ToString");
|
||||
var toString = (ToStringDelegate)toStringMethod.CreateDelegate(typeof(ToStringDelegate));
|
||||
|
||||
ByRefLike foo = new ByRefLike(123);
|
||||
if (toString(ref foo) != "123")
|
||||
throw new Exception();
|
||||
ByRefLike foo = new ByRefLike(123);
|
||||
if (toString(ref foo) != "123")
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
{
|
||||
Type byRefLikeGenericType = typeof(ByRefLike<string>);
|
||||
MethodInfo toStringGenericMethod = byRefLikeGenericType.GetMethod("ToString");
|
||||
var toStringGeneric = (ToStringDelegate<string>)toStringGenericMethod.CreateDelegate(typeof(ToStringDelegate<string>));
|
||||
|
||||
ByRefLike<string> fooGeneric = new ByRefLike<string>("Hello");
|
||||
if (toStringGeneric(ref fooGeneric) != "Hello System.String")
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
{
|
||||
Type byRefLikeGenericType = typeof(ByRefLike<object>);
|
||||
MethodInfo toStringGenericMethod = byRefLikeGenericType.GetMethod("ToString");
|
||||
var toStringGeneric = (ToStringDelegate<object>)toStringGenericMethod.CreateDelegate(typeof(ToStringDelegate<object>));
|
||||
|
||||
ByRefLike<object> fooGeneric = new ByRefLike<object>("Hello");
|
||||
if (toStringGeneric(ref fooGeneric) != "Hello System.Object")
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -591,4 +632,4 @@ internal class ReflectionTest
|
|||
}
|
||||
|
||||
class TestAssemblyAttribute : Attribute { }
|
||||
class TestModuleAttribute : Attribute { }
|
||||
class TestModuleAttribute : Attribute { }
|
Загрузка…
Ссылка в новой задаче