Define type initialization semantics for interface instance methods (#16340)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Tue, 13 Feb 2018 04:08:29 +0000 (05:08 +0100)
committerJan Kotas <jkotas@microsoft.com>
Tue, 13 Feb 2018 04:08:29 +0000 (20:08 -0800)
* Define type initialization semantics for interface instance methods

Accessing an instance method on an interface should trigger non-beforefieldinit class constructors (same as it does for valuetypes).

Fixes #15650.

src/vm/jitinterface.cpp
tests/src/Regressions/coreclr/15650/interfacecctor.il [new file with mode: 0644]
tests/src/Regressions/coreclr/15650/interfacecctor.ilproj [new file with mode: 0644]

index 819198b..168927e 100644 (file)
@@ -4055,7 +4055,8 @@ CorInfoInitClassResult CEEInfo::initClass(
         else
         // According to the spec, we should be able to do this optimization for both reference and valuetypes.
         // To maintain backward compatibility, we are doing it for reference types only.
-        if (!pMD->IsCtor() && !pTypeToInitMT->IsValueType())
+        // We don't do this for interfaces though, as those don't have instance constructors.
+        if (!pMD->IsCtor() && !pTypeToInitMT->IsValueType() && !pTypeToInitMT->IsInterface())
         {
             // For instance methods of types with precise-initialization
             // semantics, we can assume that the .ctor triggerred the
@@ -4099,7 +4100,7 @@ CorInfoInitClassResult CEEInfo::initClass(
         // This optimization may cause static fields in reference types to be accessed without cctor being triggered
         // for NULL "this" object. It does not conform with what the spec says. However, we have been historically 
         // doing it for perf reasons.
-        if (!pTypeToInitMT->IsValueType() && !pTypeToInitMT->GetClass()->IsBeforeFieldInit())
+        if (!pTypeToInitMT->IsValueType() && !pTypeToInitMT->IsInterface() && !pTypeToInitMT->GetClass()->IsBeforeFieldInit())
         {
             if (pTypeToInitMT == GetTypeFromContext(context).AsMethodTable() || pTypeToInitMT == methodBeingCompiled->GetMethodTable())
             {
diff --git a/tests/src/Regressions/coreclr/15650/interfacecctor.il b/tests/src/Regressions/coreclr/15650/interfacecctor.il
new file mode 100644 (file)
index 0000000..8568240
--- /dev/null
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+.assembly extern mscorlib { }
+.assembly interfacecctor { }
+
+.class interface private abstract auto ansi IFoo
+{
+  .method private hidebysig specialname rtspecialname static 
+          void  .cctor() cil managed
+  {
+    .maxstack  8
+    ldc.i4.s   100
+    stsfld int32 s_result
+    ret
+  }
+
+  .method public hidebysig newslot virtual 
+          instance void Frob() cil managed
+  {
+    .maxstack  8
+    ret
+  }
+}
+
+.class private auto ansi beforefieldinit Fooer
+       extends [mscorlib]System.Object
+       implements IFoo
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    .maxstack  8
+    ldarg.0
+    call       instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
+
+.field public static int32 s_result
+
+.method private hidebysig static int32 
+        Main() cil managed
+{
+  .entrypoint
+  .maxstack  8
+  newobj     instance void Fooer::.ctor()
+  callvirt   instance void IFoo::Frob()
+  ldsfld int32 s_result
+  ret
+}
\ No newline at end of file
diff --git a/tests/src/Regressions/coreclr/15650/interfacecctor.ilproj b/tests/src/Regressions/coreclr/15650/interfacecctor.ilproj
new file mode 100644 (file)
index 0000000..4b6d609
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <AssemblyName>interfacecctor</AssemblyName>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{85DFC527-4DB1-595E-A7D7-E94EE1F8140D}</ProjectGuid>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <ReferenceLocalMscorlib>true</ReferenceLocalMscorlib>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <CLRTestPriority>0</CLRTestPriority>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Include="interfacecctor.il" />
+  </ItemGroup>
+
+
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>