Don't add VTable for Finalize method in multi-module (#2621)
* Don't add VTable for Finalize method in multi-module Finalize is not added to the VTable with the CoreRT runtime; instead it is added after the VTable in conjunction with an EEType flag to save VTable space. In multi-module library mode with eagerly built VTables, skip the Finalize method (since it appears in the type system as a virtual on `System.Object`). Add a test to validate finalizers are run. * Throw exception if code callvirts `Object.Finalize` * Add `IsFinalize` to `MethodDesc`
This commit is contained in:
Родитель
ebb9695853
Коммит
eac5c5cb2b
|
@ -29,5 +29,6 @@ namespace Internal.TypeSystem
|
|||
// InvalidProgramException
|
||||
InvalidProgramSpecific,
|
||||
InvalidProgramVararg,
|
||||
InvalidProgramCallVirtFinalize,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -458,6 +458,14 @@ namespace Internal.TypeSystem
|
|||
}
|
||||
}
|
||||
|
||||
public bool IsFinalizer
|
||||
{
|
||||
get
|
||||
{
|
||||
return OwningType.GetFinalizer() == this || OwningType.IsObject && Name == "Finalize";
|
||||
}
|
||||
}
|
||||
|
||||
public virtual MethodDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation)
|
||||
{
|
||||
Instantiation instantiation = Instantiation;
|
||||
|
|
|
@ -77,6 +77,14 @@ namespace ILCompiler.DependencyAnalysis
|
|||
defType.ComputeStaticFieldLayout(StaticLayoutKind.StaticRegionSizesAndFields);
|
||||
}
|
||||
break;
|
||||
case ReadyToRunHelperId.VirtualCall:
|
||||
{
|
||||
// Make sure we aren't trying to callvirt Object.Finalize
|
||||
MethodDesc method = (MethodDesc)target;
|
||||
if (method.IsFinalizer)
|
||||
throw new TypeSystemException.InvalidProgramException(ExceptionStringID.InvalidProgramCallVirtFinalize, method);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
{
|
||||
var slots = new ArrayBuilder<MethodDesc>();
|
||||
|
||||
MethodDesc finalizerMethod = type.GetFinalizer();
|
||||
DefType defType = _type.GetClosestDefType();
|
||||
foreach (var method in defType.GetAllMethods())
|
||||
{
|
||||
|
@ -64,6 +65,10 @@ namespace ILCompiler.DependencyAnalysis
|
|||
if (method.HasInstantiation)
|
||||
continue;
|
||||
|
||||
// Finalizers are called via a field on the EEType, not through the VTable
|
||||
if (finalizerMethod == method || (type.IsObject && method.Name == "Finalize"))
|
||||
continue;
|
||||
|
||||
slots.Add(method);
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,8 @@ namespace Internal.Runtime
|
|||
return SR.InvalidProgram_Specific;
|
||||
case ExceptionStringID.InvalidProgramVararg:
|
||||
return SR.InvalidProgram_Vararg;
|
||||
case ExceptionStringID.InvalidProgramCallVirtFinalize:
|
||||
return SR.InvalidProgram_CallVirtFinalize;
|
||||
case ExceptionStringID.MissingField:
|
||||
return SR.EE_MissingField;
|
||||
case ExceptionStringID.MissingMethod:
|
||||
|
|
|
@ -1137,6 +1137,9 @@
|
|||
<data name="InvalidProgram_Vararg" xml:space="preserve">
|
||||
<value>Method '{0}' has a variable argument list. Variable argument lists are not supported in .NET Core.</value>
|
||||
</data>
|
||||
<data name="InvalidProgram_CallVirtFinalize" xml:space="preserve">
|
||||
<value>Object.Finalize() can not be called directly. It is only callable by the runtime.</value>
|
||||
</data>
|
||||
<data name="InvalidTimeZone_InvalidRegistryData" xml:space="preserve">
|
||||
<value>The time zone ID '{0}' was found on the local computer, but the registry information was corrupt.</value>
|
||||
</data>
|
||||
|
|
|
@ -3,15 +3,56 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
class Program
|
||||
{
|
||||
public const int Pass = 100;
|
||||
public const int Fail = -1;
|
||||
|
||||
static int Main()
|
||||
{
|
||||
SimpleReadWriteThreadStaticTest.Run(42, "SimpleReadWriteThreadStatic");
|
||||
ThreadStaticsTestWithTasks.Run();
|
||||
return 100;
|
||||
if (FinalizeTest.Run() != Pass)
|
||||
return Fail;
|
||||
|
||||
return Pass;
|
||||
}
|
||||
}
|
||||
|
||||
class FinalizeTest
|
||||
{
|
||||
public static bool visited = false;
|
||||
public class Dummy
|
||||
{
|
||||
~Dummy()
|
||||
{
|
||||
Console.WriteLine("In Finalize() of Dummy");
|
||||
FinalizeTest.visited = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Run()
|
||||
{
|
||||
int iterationCount = 0;
|
||||
while (!visited && iterationCount++ < 1000000)
|
||||
{
|
||||
GC.KeepAlive(new Dummy());
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
if (visited)
|
||||
{
|
||||
Console.WriteLine("Test for Finalize() & WaitForPendingFinalizers() passed!");
|
||||
return Program.Pass;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Test for Finalize() & WaitForPendingFinalizers() failed!");
|
||||
return Program.Fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче