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:
Simon Nattress 2017-02-01 12:49:38 -08:00 коммит произвёл GitHub
Родитель ebb9695853
Коммит eac5c5cb2b
7 изменённых файлов: 69 добавлений и 1 удалений

Просмотреть файл

@ -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;
}
}
}