summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Wrighton <davidwr@microsoft.com>2019-05-20 15:15:52 -0700
committerGitHub <noreply@github.com>2019-05-20 15:15:52 -0700
commitadecd858f558489d8f52c9187fca395ec669a715 (patch)
treec2b597b90f4eeace4f5d898462b99f609531e1d7
parentf4ae8f0312890a7bc14c28764adb609820d86662 (diff)
downloadcoreclr-adecd858f558489d8f52c9187fca395ec669a715.tar.gz
coreclr-adecd858f558489d8f52c9187fca395ec669a715.tar.bz2
coreclr-adecd858f558489d8f52c9187fca395ec669a715.zip
Cuckoo metadata (#24498)
* Basic infra for cuckoo filter of attributes - Implement cuckoo filter lookup logic - Implement new ready to run section - Add dumper to R2RDump - Parse section on load into data structure - Implement function to query filter - Add concept of enum of well known attributes - So that attribute name hashes themselves may be cached * Wrap all even vaguely perf critical uses of attribute by name parsing with use of R2R data * Update emmintrin.h in the PAL header to contain the needed SSE2 intrinsics for the feature - Disable the presence table for non Corelib cases. Current performance data does not warrant the size increase in other generated binaries
-rw-r--r--src/inc/readytorun.h5
-rw-r--r--src/md/enc/mdinternalrw.cpp4
-rw-r--r--src/md/runtime/mdinternalro.cpp4
-rw-r--r--src/pal/inc/rt/cpp/emmintrin.h124
-rw-r--r--src/pal/inc/rt/cpp/xmmintrin.h2
-rw-r--r--src/tools/r2rdump/NativeHashtable.cs57
-rw-r--r--src/tools/r2rdump/R2RSection.cs1
-rw-r--r--src/tools/r2rdump/README.md4
-rw-r--r--src/tools/r2rdump/TextDumper.cs7
-rw-r--r--src/vm/assembly.cpp3
-rw-r--r--src/vm/assembly.hpp14
-rw-r--r--src/vm/ceeload.cpp13
-rw-r--r--src/vm/ceeload.h17
-rw-r--r--src/vm/class.cpp5
-rw-r--r--src/vm/comcallablewrapper.cpp4
-rw-r--r--src/vm/comdelegate.cpp2
-rw-r--r--src/vm/commtmemberinfomap.cpp13
-rw-r--r--src/vm/comtoclrcall.cpp4
-rw-r--r--src/vm/dllimport.cpp13
-rw-r--r--src/vm/fieldmarshaler.cpp23
-rw-r--r--src/vm/fieldmarshaler.h3
-rw-r--r--src/vm/interoputil.cpp52
-rw-r--r--src/vm/interoputil.h4
-rw-r--r--src/vm/marshalnative.cpp28
-rw-r--r--src/vm/marshalnative.h6
-rw-r--r--src/vm/method.cpp6
-rw-r--r--src/vm/method.hpp10
-rw-r--r--src/vm/methodtable.h5
-rw-r--r--src/vm/methodtable.inl9
-rw-r--r--src/vm/methodtablebuilder.cpp48
-rw-r--r--src/vm/methodtablebuilder.h12
-rw-r--r--src/vm/mlinfo.cpp2
-rw-r--r--src/vm/nativeformatreader.h144
-rw-r--r--src/vm/readytoruninfo.cpp40
-rw-r--r--src/vm/readytoruninfo.h5
-rw-r--r--src/vm/siginfo.cpp13
-rw-r--r--src/vm/staticallocationhelpers.inl4
-rw-r--r--src/vm/typehashingalgorithms.h91
-rw-r--r--src/vm/wellknownattributes.h107
-rw-r--r--src/zap/zapimage.cpp2
-rw-r--r--src/zap/zapimage.h3
-rw-r--r--src/zap/zapreadytorun.cpp266
42 files changed, 1031 insertions, 148 deletions
diff --git a/src/inc/readytorun.h b/src/inc/readytorun.h
index 6307bc9937..d28d78c67d 100644
--- a/src/inc/readytorun.h
+++ b/src/inc/readytorun.h
@@ -16,7 +16,7 @@
#define READYTORUN_SIGNATURE 0x00525452 // 'RTR'
#define READYTORUN_MAJOR_VERSION 0x0003
-#define READYTORUN_MINOR_VERSION 0x0000
+#define READYTORUN_MINOR_VERSION 0x0001
#define MINIMUM_READYTORUN_MAJOR_VERSION 0x003
// R2R Version 2.1 adds the READYTORUN_SECTION_INLINING_INFO section
// R2R Version 2.2 adds the READYTORUN_SECTION_PROFILEDATA_INFO section
@@ -66,7 +66,8 @@ enum ReadyToRunSectionType
READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS = 109,
READYTORUN_SECTION_INLINING_INFO = 110, // Added in V2.1
READYTORUN_SECTION_PROFILEDATA_INFO = 111, // Added in V2.2
- READYTORUN_SECTION_MANIFEST_METADATA = 112 // Added in V2.3
+ READYTORUN_SECTION_MANIFEST_METADATA = 112, // Added in V2.3
+ READYTORUN_SECTION_ATTRIBUTEPRESENCE = 113, // Added in V3.1
// If you add a new section consider whether it is a breaking or non-breaking change.
// Usually it is non-breaking, but if it is preferable to have older runtimes fail
diff --git a/src/md/enc/mdinternalrw.cpp b/src/md/enc/mdinternalrw.cpp
index c8f844d625..c24c1b7cae 100644
--- a/src/md/enc/mdinternalrw.cpp
+++ b/src/md/enc/mdinternalrw.cpp
@@ -1482,6 +1482,10 @@ HRESULT MDInternalRW::EnumAllInit( // return S_FALSE if record not found
phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountFiles();
break;
+ case mdtCustomAttribute:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountCustomAttributes();
+ break;
+
default:
_ASSERTE(!"Bad token kind!");
break;
diff --git a/src/md/runtime/mdinternalro.cpp b/src/md/runtime/mdinternalro.cpp
index a4f59a37b2..99a1a0d9c8 100644
--- a/src/md/runtime/mdinternalro.cpp
+++ b/src/md/runtime/mdinternalro.cpp
@@ -609,6 +609,10 @@ HRESULT MDInternalRO::EnumAllInit( // return S_FALSE if record not found
phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountFiles();
break;
+ case mdtCustomAttribute:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountCustomAttributes();
+ break;
+
default:
_ASSERTE(!"Bad token kind!");
break;
diff --git a/src/pal/inc/rt/cpp/emmintrin.h b/src/pal/inc/rt/cpp/emmintrin.h
index 5401fabc13..14b72bec43 100644
--- a/src/pal/inc/rt/cpp/emmintrin.h
+++ b/src/pal/inc/rt/cpp/emmintrin.h
@@ -2,4 +2,128 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+// From llvm-3.9/clang-3.9.1 emmintrin.h:
+
+/*===---- emmintrin.h - SSE2 intrinsics ------------------------------------===
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
#include "palrt.h"
+#ifdef __GNUC__
+#ifndef __EMMINTRIN_H
+#define __IMMINTRIN_H
+
+typedef long long __m128i __attribute__((__vector_size__(16)));
+
+typedef unsigned long long __v2du __attribute__ ((__vector_size__ (16)));
+typedef short __v8hi __attribute__((__vector_size__(16)));
+typedef char __v16qi __attribute__((__vector_size__(16)));
+
+
+/* Define the default attribute for the functions in this file. */
+#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("sse2")))
+
+/// \brief Performs a bitwise OR of two 128-bit integer vectors.
+///
+/// \headerfile <x86intrin.h>
+///
+/// This intrinsic corresponds to the \c VPOR / POR instruction.
+///
+/// \param __a
+/// A 128-bit integer vector containing one of the source operands.
+/// \param __b
+/// A 128-bit integer vector containing one of the source operands.
+/// \returns A 128-bit integer vector containing the bitwise OR of the values
+/// in both operands.
+static __inline__ __m128i __DEFAULT_FN_ATTRS
+_mm_or_si128(__m128i __a, __m128i __b)
+{
+ return (__m128i)((__v2du)__a | (__v2du)__b);
+}
+
+/// \brief Compares each of the corresponding 16-bit values of the 128-bit
+/// integer vectors for equality. Each comparison yields 0h for false, FFFFh
+/// for true.
+///
+/// \headerfile <x86intrin.h>
+///
+/// This intrinsic corresponds to the \c VPCMPEQW / PCMPEQW instruction.
+///
+/// \param __a
+/// A 128-bit integer vector.
+/// \param __b
+/// A 128-bit integer vector.
+/// \returns A 128-bit integer vector containing the comparison results.
+static __inline__ __m128i __DEFAULT_FN_ATTRS
+_mm_cmpeq_epi16(__m128i __a, __m128i __b)
+{
+ return (__m128i)((__v8hi)__a == (__v8hi)__b);
+}
+
+/// \brief Moves packed integer values from an unaligned 128-bit memory location
+/// to elements in a 128-bit integer vector.
+///
+/// \headerfile <x86intrin.h>
+///
+/// This intrinsic corresponds to the \c VMOVDQU / MOVDQU instruction.
+///
+/// \param __p
+/// A pointer to a memory location containing integer values.
+/// \returns A 128-bit integer vector containing the moved values.
+static __inline__ __m128i __DEFAULT_FN_ATTRS
+_mm_loadu_si128(__m128i const *__p)
+{
+ struct __loadu_si128 {
+ __m128i __v;
+ } __attribute__((__packed__, __may_alias__));
+ return ((struct __loadu_si128*)__p)->__v;
+}
+
+/// \brief Initializes all values in a 128-bit vector of [8 x i16] with the
+/// specified 16-bit value.
+///
+/// \headerfile <x86intrin.h>
+///
+/// This intrinsic is a utility function and does not correspond to a specific
+/// instruction.
+///
+/// \param __w
+/// A 16-bit value used to initialize the elements of the destination integer
+/// vector.
+/// \returns An initialized 128-bit vector of [8 x i16] with all elements
+/// containing the value provided in the operand.
+static __inline__ __m128i __DEFAULT_FN_ATTRS
+_mm_set1_epi16(short __w)
+{
+ return (__m128i)(__v8hi){ __w, __w, __w, __w, __w, __w, __w, __w };
+}
+
+static __inline__ int __DEFAULT_FN_ATTRS
+_mm_movemask_epi8(__m128i __a)
+{
+ return __builtin_ia32_pmovmskb128((__v16qi)__a);
+}
+
+#undef __DEFAULT_FN_ATTRS
+
+#endif /* __EMMINTRIN_H */
+#endif // __GNUC__
diff --git a/src/pal/inc/rt/cpp/xmmintrin.h b/src/pal/inc/rt/cpp/xmmintrin.h
index 33bc8b4b92..ed2ff583b3 100644
--- a/src/pal/inc/rt/cpp/xmmintrin.h
+++ b/src/pal/inc/rt/cpp/xmmintrin.h
@@ -113,4 +113,6 @@ _mm_store_ps(float *__p, __m128 __a)
*(__m128*)__p = __a;
}
+#undef __DEFAULT_FN_ATTRS
+
#endif // __GNUC__
diff --git a/src/tools/r2rdump/NativeHashtable.cs b/src/tools/r2rdump/NativeHashtable.cs
index 830805eee5..44e7b3fd4f 100644
--- a/src/tools/r2rdump/NativeHashtable.cs
+++ b/src/tools/r2rdump/NativeHashtable.cs
@@ -238,4 +238,61 @@ namespace R2RDump
return new AllEntriesEnumerator(this);
}
}
+
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/nativeformatreader.h">NativeFormat::NativeHashtable</a>
+ /// </summary>
+ struct NativeCuckooFilter
+ {
+ private byte[] _image;
+ private int _filterStartOffset;
+ private int _filterEndOffset;
+
+ public NativeCuckooFilter(byte[] image, int filterStartOffset, int filterEndOffset)
+ {
+ _image = image;
+ _filterStartOffset = filterStartOffset;
+ _filterEndOffset = filterEndOffset;
+
+ if (((_filterStartOffset & 0xF) != 0) || ((_filterEndOffset & 0xF) != 0))
+ {
+ // Native cuckoo filters must be aligned at 16byte boundaries within the PE file
+ throw new System.BadImageFormatException();
+ }
+ }
+
+ private IEnumerable<ushort[]> GetBuckets()
+ {
+ int offset = _filterStartOffset;
+ while (offset < _filterEndOffset)
+ {
+ ushort[] bucket = new ushort[8];
+ for (int i = 0; i < bucket.Length; i++)
+ {
+ bucket[i] = NativeReader.ReadUInt16(_image, ref offset);
+ }
+ yield return bucket;
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.AppendLine($"NativeCuckooFilter Size: {(_filterEndOffset - _filterStartOffset) / 16}");
+ int bucket = 0;
+ foreach (ushort [] bucketContents in GetBuckets())
+ {
+ sb.Append($"Bucket: {bucket} [");
+ for (int i = 0; i < 8; i++)
+ {
+ sb.Append($"{bucketContents[i],4:X} ");
+ }
+ sb.AppendLine("]");
+ bucket++;
+ }
+
+ return sb.ToString();
+ }
+ }
}
diff --git a/src/tools/r2rdump/R2RSection.cs b/src/tools/r2rdump/R2RSection.cs
index 3ba922b609..0eca7e9a51 100644
--- a/src/tools/r2rdump/R2RSection.cs
+++ b/src/tools/r2rdump/R2RSection.cs
@@ -29,6 +29,7 @@ namespace R2RDump
READYTORUN_SECTION_INLINING_INFO = 110,
READYTORUN_SECTION_PROFILEDATA_INFO = 111,
READYTORUN_SECTION_MANIFEST_METADATA = 112, // Added in v2.3
+ READYTORUN_SECTION_ATTRIBUTEPRESENCE = 113, // Added in V3.1
}
/// <summary>
diff --git a/src/tools/r2rdump/README.md b/src/tools/r2rdump/README.md
index 0d7bdb9ad6..ee16e40573 100644
--- a/src/tools/r2rdump/README.md
+++ b/src/tools/r2rdump/README.md
@@ -81,6 +81,10 @@ A [NativeArray](NativeArray.cs) used for finding the index of the entrypoint Run
A [NativeHashtable](NativeHashtable.cs) mapping type hashcodes of types defined in the program to the rowIds. The hashcode is calculated with [ComputeNameHashCode](../../vm/typehashingalgorithms.h)(namespace) ^ [ComputeNameHashCode](../../vm/typehashingalgorithms.h)(name)
+### READYTORUN_SECTION_ATTRIBUTEPRESENCE
+
+A [NativeCuckooFilter](NativeHashtable.cs) to discover which tokens have which "System.Runtime." prefixed attributes. The System.Runtime.CompilerServices.NullableAttribute is not used in this calculation. The filter is composed of a name hash of the type name using [ComputeNameHashCode](../../vm/typehashingalgorithms.h)(namespace + name) hash combined with a hash of each token that produced it. In addition the upper 16 bits is used as the fingerprint in the filter.
+
### READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS
A [NativeHashtable](NativeHashtable.cs) mapping type hashcodes of generic instances to the (methodFlags, methodRowId, list of types, runtimeFunctionId). Each type in the list of types corresponds to a generic type in the method.
diff --git a/src/tools/r2rdump/TextDumper.cs b/src/tools/r2rdump/TextDumper.cs
index db4c5f3eeb..b1528df37a 100644
--- a/src/tools/r2rdump/TextDumper.cs
+++ b/src/tools/r2rdump/TextDumper.cs
@@ -374,6 +374,13 @@ namespace R2RDump
_writer.WriteLine($"[ID 0x{manifestAsmIndex + assemblyRefCount + 2:X2}]: {_r2r.ManifestReferenceAssemblies[manifestAsmIndex]}");
}
break;
+ case R2RSection.SectionType.READYTORUN_SECTION_ATTRIBUTEPRESENCE:
+ int attributesStartOffset = _r2r.GetOffset(section.RelativeVirtualAddress);
+ int attributesEndOffset = attributesStartOffset + section.Size;
+ NativeCuckooFilter attributes = new NativeCuckooFilter(_r2r.Image, attributesStartOffset, attributesEndOffset);
+ _writer.WriteLine("Attribute presence filter");
+ _writer.WriteLine(attributes.ToString());
+ break;
}
}
diff --git a/src/vm/assembly.cpp b/src/vm/assembly.cpp
index 119f9224a9..4573f7cd42 100644
--- a/src/vm/assembly.cpp
+++ b/src/vm/assembly.cpp
@@ -229,8 +229,7 @@ BOOL Assembly::IsDisabledPrivateReflection()
if (m_isDisabledPrivateReflection == UNINITIALIZED)
{
- IMDInternalImport *pImport = GetManifestImport();
- HRESULT hr = pImport->GetCustomAttributeByName(GetManifestToken(), DISABLED_PRIVATE_REFLECTION_TYPE, NULL, 0);
+ HRESULT hr = GetManifestModule()->GetCustomAttribute(GetManifestToken(), WellKnownAttribute::DisablePrivateReflectionType, NULL, 0);
IfFailThrow(hr);
if (hr == S_OK)
diff --git a/src/vm/assembly.hpp b/src/vm/assembly.hpp
index 89793974e5..e4b0795a78 100644
--- a/src/vm/assembly.hpp
+++ b/src/vm/assembly.hpp
@@ -304,6 +304,16 @@ public:
return m_pManifestFile->GetPersistentMDImport();
}
+ HRESULT GetCustomAttribute(mdToken parentToken,
+ WellKnownAttribute attribute,
+ const void **ppData,
+ ULONG *pcbData)
+ {
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ return GetManifestModule()->GetCustomAttribute(parentToken, attribute, ppData, pcbData);
+ }
+
#ifndef DACCESS_COMPILE
IMetaDataAssemblyImport* GetManifestAssemblyImporter()
{
@@ -549,9 +559,9 @@ protected:
if (!IsWinMD()) // ignore classic COM interop CAs in .winmd
{
- if (this->GetManifestImport()->GetCustomAttributeByName(TokenFromRid(1, mdtAssembly), INTEROP_IMPORTEDFROMTYPELIB_TYPE, 0, 0) == S_OK)
+ if (GetManifestModule()->GetCustomAttribute(TokenFromRid(1, mdtAssembly), WellKnownAttribute::ImportedFromTypeLib, NULL, 0) == S_OK)
mask |= INTEROP_ATTRIBUTE_IMPORTED_FROM_TYPELIB;
- if (this->GetManifestImport()->GetCustomAttributeByName(TokenFromRid(1, mdtAssembly), INTEROP_PRIMARYINTEROPASSEMBLY_TYPE, 0, 0) == S_OK)
+ if (GetManifestModule()->GetCustomAttribute(TokenFromRid(1, mdtAssembly), WellKnownAttribute::PrimaryInteropAssembly, NULL, 0) == S_OK)
mask |= INTEROP_ATTRIBUTE_PRIMARY_INTEROP_ASSEMBLY;
}
diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp
index e389df97e3..40b73a03ef 100644
--- a/src/vm/ceeload.cpp
+++ b/src/vm/ceeload.cpp
@@ -219,6 +219,7 @@ void Module::UpdateNewlyAddedTypes()
DWORD countTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
DWORD countExportedTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
+ DWORD countCustomAttributeCount = GetMDImport()->GetCountWithTokenKind(mdtCustomAttribute);
// typeDefs rids 0 and 1 aren't included in the count, thus X typeDefs before means rid X+1 was valid and our incremental addition should start at X+2
for (DWORD typeDefRid = m_dwTypeCount + 2; typeDefRid < countTypesAfterProfilerUpdate + 2; typeDefRid++)
@@ -232,8 +233,15 @@ void Module::UpdateNewlyAddedTypes()
GetAssembly()->AddExportedType(TokenFromRid(exportedTypeDef, mdtExportedType));
}
+ if ((countCustomAttributeCount != m_dwCustomAttributeCount) && IsReadyToRun())
+ {
+ // Set of custom attributes has changed. Disable the cuckoo filter from ready to run, and do normal custom attribute parsing
+ GetReadyToRunInfo()->DisableCustomAttributeFilter();
+ }
+
m_dwTypeCount = countTypesAfterProfilerUpdate;
m_dwExportedTypeCount = countExportedTypesAfterProfilerUpdate;
+ m_dwCustomAttributeCount = countCustomAttributeCount;
}
void Module::NotifyProfilerLoadFinished(HRESULT hr)
@@ -257,6 +265,7 @@ void Module::NotifyProfilerLoadFinished(HRESULT hr)
{
m_dwTypeCount = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
m_dwExportedTypeCount = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
+ m_dwCustomAttributeCount = GetMDImport()->GetCountWithTokenKind(mdtCustomAttribute);
}
// Notify the profiler, this may cause metadata to be updated
@@ -622,6 +631,7 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName)
// a safe initial value now.
m_dwTypeCount = 0;
m_dwExportedTypeCount = 0;
+ m_dwCustomAttributeCount = 0;
// Prepare statics that are known at module load time
AllocateStatics(pamTracker);
@@ -2535,10 +2545,9 @@ BOOL Module::HasDefaultDllImportSearchPathsAttribute()
{
return (m_dwPersistedFlags & DEFAULT_DLL_IMPORT_SEARCH_PATHS_STATUS) != 0 ;
}
- IMDInternalImport *mdImport = GetAssembly()->GetManifestImport();
BOOL attributeIsFound = FALSE;
- attributeIsFound = GetDefaultDllImportSearchPathsAttributeValue(mdImport, TokenFromRid(1, mdtAssembly),&m_DefaultDllImportSearchPathsAttributeValue);
+ attributeIsFound = GetDefaultDllImportSearchPathsAttributeValue(this, TokenFromRid(1, mdtAssembly),&m_DefaultDllImportSearchPathsAttributeValue);
if(attributeIsFound)
{
FastInterlockOr(&m_dwPersistedFlags, DEFAULT_DLL_IMPORT_SEARCH_PATHS_IS_CACHED | DEFAULT_DLL_IMPORT_SEARCH_PATHS_STATUS);
diff --git a/src/vm/ceeload.h b/src/vm/ceeload.h
index 602a69c31a..3aab11dfeb 100644
--- a/src/vm/ceeload.h
+++ b/src/vm/ceeload.h
@@ -36,6 +36,8 @@
#include "corcompile.h"
#include <gcinfodecoder.h>
+#include "wellknownattributes.h"
+
#ifdef FEATURE_PREJIT
#include "dataimage.h"
#endif // FEATURE_PREJIT
@@ -1627,6 +1629,7 @@ private:
#if PROFILING_SUPPORTED_DATA
DWORD m_dwTypeCount;
DWORD m_dwExportedTypeCount;
+ DWORD m_dwCustomAttributeCount;
#endif // PROFILING_SUPPORTED_DATA
#ifdef FEATURE_PREJIT
@@ -1889,6 +1892,20 @@ protected:
CHECK CheckActivated();
+ HRESULT GetCustomAttribute(mdToken parentToken,
+ WellKnownAttribute attribute,
+ const void **ppData,
+ ULONG *pcbData)
+ {
+ if (IsReadyToRun())
+ {
+ if (!GetReadyToRunInfo()->MayHaveCustomAttribute(attribute, parentToken))
+ return S_FALSE;
+ }
+
+ return GetMDImport()->GetCustomAttributeByName(parentToken, GetWellKnownAttributeName(attribute), ppData, pcbData);
+ }
+
IMDInternalImport *GetMDImport() const
{
WRAPPER_NO_CONTRACT;
diff --git a/src/vm/class.cpp b/src/vm/class.cpp
index abc1230e76..10271c95be 100644
--- a/src/vm/class.cpp
+++ b/src/vm/class.cpp
@@ -1649,7 +1649,7 @@ TypeHandle MethodTable::SetupCoClassForInterface()
if (!IsProjectedFromWinRT()) // ignore classic COM interop CA on WinRT types
{
- HRESULT hr = GetMDImport()->GetCustomAttributeByName(GetCl(), INTEROP_COCLASS_TYPE , (const void **)&pVal, &cbVal);
+ HRESULT hr = GetCustomAttribute(WellKnownAttribute::CoClass, (const void **)&pVal, &cbVal);
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
@@ -2026,7 +2026,7 @@ void EEClass::GetBestFitMapping(MethodTable * pMT, BOOL *pfBestFitMapping, BOOL
*pfBestFitMapping = FALSE;
*pfThrowOnUnmappableChar = FALSE;
- ReadBestFitCustomAttribute(pMT->GetMDImport(), pMT->GetCl(), pfBestFitMapping, pfThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(pMT->GetModule(), pMT->GetCl(), pfBestFitMapping, pfThrowOnUnmappableChar);
DWORD flags = VMFLAG_BESTFITMAPPING_INITED;
if (*pfBestFitMapping) flags |= VMFLAG_BESTFITMAPPING;
@@ -3954,7 +3954,6 @@ void EEClassLayoutInfo::ParseFieldNativeTypes(
pFieldInfoArrayOut,
pNativeType,
cbNativeType,
- pInternalImport,
cl,
pTypeContext,
fDisqualifyFromManagedSequential
diff --git a/src/vm/comcallablewrapper.cpp b/src/vm/comcallablewrapper.cpp
index f38f180d04..d271a6dd22 100644
--- a/src/vm/comcallablewrapper.cpp
+++ b/src/vm/comcallablewrapper.cpp
@@ -349,7 +349,7 @@ bool IsOleAutDispImplRequiredForClass(MethodTable *pClass)
}
// First check for the IDispatchImplType custom attribute first.
- hr = pClass->GetMDImport()->GetCustomAttributeByName(pClass->GetCl(), INTEROP_IDISPATCHIMPL_TYPE, (const void**)&pVal, &cbVal);
+ hr = pClass->GetCustomAttribute(WellKnownAttribute::IDispatchImpl, (const void**)&pVal, &cbVal);
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
@@ -367,7 +367,7 @@ bool IsOleAutDispImplRequiredForClass(MethodTable *pClass)
return (bool) (DispImplType == CompatibleImpl);
// Check to see if the assembly has the IDispatchImplType attribute set.
- hr = pAssembly->GetManifestImport()->GetCustomAttributeByName(pAssembly->GetManifestToken(), INTEROP_IDISPATCHIMPL_TYPE, (const void**)&pVal, &cbVal);
+ hr = pAssembly->GetCustomAttribute(pAssembly->GetManifestToken(), WellKnownAttribute::IDispatchImpl, (const void**)&pVal, &cbVal);
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
diff --git a/src/vm/comdelegate.cpp b/src/vm/comdelegate.cpp
index 58cbdb0e11..8125f89d25 100644
--- a/src/vm/comdelegate.cpp
+++ b/src/vm/comdelegate.cpp
@@ -1071,7 +1071,7 @@ PCODE COMDelegate::ConvertToCallback(MethodDesc* pMD)
LONG cData = 0;
CorPinvokeMap callConv = (CorPinvokeMap)0;
- HRESULT hr = pMD->GetMDImport()->GetCustomAttributeByName(pMD->GetMemberDef(), g_NativeCallableAttribute, (const VOID **)(&pData), (ULONG *)&cData);
+ HRESULT hr = pMD->GetCustomAttribute(WellKnownAttribute::NativeCallable, (const VOID **)(&pData), (ULONG *)&cData);
IfFailThrow(hr);
if (cData > 0)
diff --git a/src/vm/commtmemberinfomap.cpp b/src/vm/commtmemberinfomap.cpp
index e53cd2f525..f2a8e33a14 100644
--- a/src/vm/commtmemberinfomap.cpp
+++ b/src/vm/commtmemberinfomap.cpp
@@ -214,24 +214,15 @@ void ComMTMemberInfoMap::Init(size_t sizeOfPtr)
CONTRACTL_END;
HRESULT hr = S_OK;
- mdTypeDef td; // Token for the class.
BYTE const *pData; // Pointer to a custom attribute blob.
ULONG cbData; // Size of a custom attribute blob.
- // Get the TypeDef and some info about it.
- td = m_pMT->GetCl();
-
m_bHadDuplicateDispIds = FALSE;
// See if there is a default property.
m_DefaultProp[0] = 0; // init to 'none'.
- hr = m_pMT->GetMDImport()->GetCustomAttributeByName(
- td, INTEROP_DEFAULTMEMBER_TYPE, reinterpret_cast<const void**>(&pData), &cbData);
- if (hr == S_FALSE)
- {
- hr = m_pMT->GetMDImport()->GetCustomAttributeByName(
- td, "System.Reflection.DefaultMemberAttribute", reinterpret_cast<const void**>(&pData), &cbData);
- }
+ hr = m_pMT->GetCustomAttribute(
+ WellKnownAttribute::DefaultMember, reinterpret_cast<const void**>(&pData), &cbData);
if (hr == S_OK && cbData > 5 && pData[0] == 1 && pData[1] == 0)
{
diff --git a/src/vm/comtoclrcall.cpp b/src/vm/comtoclrcall.cpp
index a527c6e290..362759a000 100644
--- a/src/vm/comtoclrcall.cpp
+++ b/src/vm/comtoclrcall.cpp
@@ -1119,7 +1119,7 @@ void ComCallMethodDesc::InitNativeInfo()
// Look up the best fit mapping info via Assembly & Interface level attributes
BOOL BestFit = TRUE;
BOOL ThrowOnUnmappableChar = FALSE;
- ReadBestFitCustomAttribute(fsig.GetModule()->GetMDImport(), pFD->GetEnclosingMethodTable()->GetCl(), &BestFit, &ThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(fsig.GetModule(), pFD->GetEnclosingMethodTable()->GetCl(), &BestFit, &ThrowOnUnmappableChar);
MarshalInfo info(fsig.GetModule(), fsig.GetArgProps(), fsig.GetSigTypeContext(), pFD->GetMemberDef(), MarshalInfo::MARSHAL_SCENARIO_COMINTEROP,
(CorNativeLinkType)0, (CorNativeLinkFlags)0,
@@ -1423,7 +1423,7 @@ void ComCall::PopulateComCallMethodDesc(ComCallMethodDesc *pCMD, DWORD *pdwStubF
_ASSERTE(IsMemberVisibleFromCom(pFD->GetApproxEnclosingMethodTable(), pFD->GetMemberDef(), mdTokenNil) && "Calls are not permitted on this member since it isn't visible from COM. The only way you can have reached this code path is if your native interface doesn't match the managed interface.");
MethodTable *pMT = pFD->GetEnclosingMethodTable();
- ReadBestFitCustomAttribute(pMT->GetMDImport(), pMT->GetCl(), &BestFit, &ThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(pMT->GetModule(), pMT->GetCl(), &BestFit, &ThrowOnUnmappableChar);
}
else
{
diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp
index 56f5c93bea..8f3458d800 100644
--- a/src/vm/dllimport.cpp
+++ b/src/vm/dllimport.cpp
@@ -2716,7 +2716,7 @@ void PInvokeStaticSigInfo::PreInit(Module* pModule, MethodTable * pMT)
}
else
{
- ReadBestFitCustomAttribute(m_pModule->GetMDImport(), mdTypeDefNil, &bBestFit, &bThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(m_pModule, mdTypeDefNil, &bBestFit, &bThrowOnUnmappableChar);
}
SetBestFitMapping (bBestFit);
@@ -2790,8 +2790,8 @@ PInvokeStaticSigInfo::PInvokeStaticSigInfo(MethodDesc* pMD, ThrowOnError throwOn
LONG cData = 0;
CorPinvokeMap callConv = (CorPinvokeMap)0;
- HRESULT hRESULT = pMT->GetMDImport()->GetCustomAttributeByName(
- pMT->GetCl(), g_UnmanagedFunctionPointerAttribute, (const VOID **)(&pData), (ULONG *)&cData);
+ HRESULT hRESULT = pMT->GetCustomAttribute(
+ WellKnownAttribute::UnmanagedFunctionPointer, (const VOID **)(&pData), (ULONG *)&cData);
IfFailThrow(hRESULT);
if (cData != 0)
{
@@ -4657,15 +4657,14 @@ HRESULT FindPredefinedILStubMethod(MethodDesc *pTargetMD, DWORD dwStubFlags, Met
if (pTargetMD->IsInterface())
{
_ASSERTE(!pTargetMD->GetAssembly()->IsWinMD());
- hr = pTargetMD->GetMDImport()->GetCustomAttributeByName(
- pTargetMD->GetMemberDef(),
- FORWARD_INTEROP_STUB_METHOD_TYPE,
+ hr = pTargetMD->GetCustomAttribute(
+ WellKnownAttribute::ManagedToNativeComInteropStub,
&pBytes,
&cbBytes);
if (FAILED(hr))
RETURN hr;
- // GetCustomAttributeByName returns S_FALSE when it cannot find the attribute but nothing fails...
+ // GetCustomAttribute returns S_FALSE when it cannot find the attribute but nothing fails...
// Translate that to E_FAIL
else if (hr == S_FALSE)
RETURN E_FAIL;
diff --git a/src/vm/fieldmarshaler.cpp b/src/vm/fieldmarshaler.cpp
index 0d994c14e7..8691f2b036 100644
--- a/src/vm/fieldmarshaler.cpp
+++ b/src/vm/fieldmarshaler.cpp
@@ -67,7 +67,6 @@ VOID ParseNativeType(Module* pModule,
LayoutRawFieldInfo* pfwalk,
PCCOR_SIGNATURE pNativeType,
ULONG cbNativeType,
- IMDInternalImport* pInternalImport,
mdTypeDef cl,
const SigTypeContext * pTypeContext,
BOOL *pfDisqualifyFromManagedSequential // set to TRUE if needed (never set to FALSE, it may come in as TRUE!)
@@ -217,7 +216,7 @@ do \
{
if (fAnsi)
{
- ReadBestFitCustomAttribute(pInternalImport, cl, &BestFit, &ThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(pModule, cl, &BestFit, &ThrowOnUnmappableChar);
INITFIELDMARSHALER(NFT_ANSICHAR, FieldMarshaler_Ansi, (BestFit, ThrowOnUnmappableChar));
}
else
@@ -227,7 +226,7 @@ do \
}
else if (ntype == NATIVE_TYPE_I1 || ntype == NATIVE_TYPE_U1)
{
- ReadBestFitCustomAttribute(pInternalImport, cl, &BestFit, &ThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(pModule, cl, &BestFit, &ThrowOnUnmappableChar);
INITFIELDMARSHALER(NFT_ANSICHAR, FieldMarshaler_Ansi, (BestFit, ThrowOnUnmappableChar));
}
else if (ntype == NATIVE_TYPE_I2 || ntype == NATIVE_TYPE_U2)
@@ -518,7 +517,7 @@ do \
if (IsStructMarshalable(thNestedType))
{
#ifdef _DEBUG
- INITFIELDMARSHALER(NFT_NESTEDVALUECLASS, FieldMarshaler_NestedValueClass, (thNestedType.GetMethodTable(), IsFixedBuffer(pfwalk->m_MD, pInternalImport)));
+ INITFIELDMARSHALER(NFT_NESTEDVALUECLASS, FieldMarshaler_NestedValueClass, (thNestedType.GetMethodTable(), IsFixedBuffer(pfwalk->m_MD, pModule->GetMDImport())));
#else
INITFIELDMARSHALER(NFT_NESTEDVALUECLASS, FieldMarshaler_NestedValueClass, (thNestedType.GetMethodTable()));
#endif
@@ -624,7 +623,7 @@ do \
#endif // FEATURE_COMINTEROP
if (fAnsi)
{
- ReadBestFitCustomAttribute(pInternalImport, cl, &BestFit, &ThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(pModule, cl, &BestFit, &ThrowOnUnmappableChar);
INITFIELDMARSHALER(NFT_STRINGANSI, FieldMarshaler_StringAnsi, (BestFit, ThrowOnUnmappableChar));
}
else
@@ -637,7 +636,7 @@ do \
switch (ntype)
{
case NATIVE_TYPE_LPSTR:
- ReadBestFitCustomAttribute(pInternalImport, cl, &BestFit, &ThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(pModule, cl, &BestFit, &ThrowOnUnmappableChar);
INITFIELDMARSHALER(NFT_STRINGANSI, FieldMarshaler_StringAnsi, (BestFit, ThrowOnUnmappableChar));
break;
@@ -682,7 +681,7 @@ do \
if (fAnsi)
{
- ReadBestFitCustomAttribute(pInternalImport, cl, &BestFit, &ThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(pModule, cl, &BestFit, &ThrowOnUnmappableChar);
INITFIELDMARSHALER(NFT_FIXEDSTRINGANSI, FieldMarshaler_FixedStringAnsi, (nchars, BestFit, ThrowOnUnmappableChar));
}
else
@@ -786,7 +785,7 @@ do \
// Since these always export to arrays of BSTRs, we don't need to fetch the native type.
// Compat: FixedArrays of System.Arrays map to fixed arrays of BSTRs.
- INITFIELDMARSHALER(NFT_FIXEDARRAY, FieldMarshaler_FixedArray, (pInternalImport, cl, numElements, VT_BSTR, g_pStringClass));
+ INITFIELDMARSHALER(NFT_FIXEDARRAY, FieldMarshaler_FixedArray, (pModule, cl, numElements, VT_BSTR, g_pStringClass));
}
}
#endif // FEATURE_CLASSIC_COMINTEROP
@@ -929,7 +928,7 @@ do \
// We need to special case fixed sized arrays of ANSI chars since the OleVariant code
// that is used by the generic fixed size array marshaller doesn't support them
// properly.
- ReadBestFitCustomAttribute(pInternalImport, cl, &BestFit, &ThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(pModule, cl, &BestFit, &ThrowOnUnmappableChar);
INITFIELDMARSHALER(NFT_FIXEDCHARARRAYANSI, FieldMarshaler_FixedCharArrayAnsi, (numElements, BestFit, ThrowOnUnmappableChar));
break;
}
@@ -937,7 +936,7 @@ do \
{
VARTYPE elementVT = arrayMarshalInfo.GetElementVT();
- INITFIELDMARSHALER(NFT_FIXEDARRAY, FieldMarshaler_FixedArray, (pInternalImport, cl, numElements, elementVT, arrayMarshalInfo.GetElementTypeHandle().GetMethodTable()));
+ INITFIELDMARSHALER(NFT_FIXEDARRAY, FieldMarshaler_FixedArray, (pModule, cl, numElements, elementVT, arrayMarshalInfo.GetElementTypeHandle().GetMethodTable()));
break;
}
}
@@ -2728,7 +2727,7 @@ VOID FieldMarshaler_FixedCharArrayAnsi::UpdateCLRImpl(const VOID *pNativeValue,
// Embedded array
// See FieldMarshaler for details.
//=======================================================================
-FieldMarshaler_FixedArray::FieldMarshaler_FixedArray(IMDInternalImport *pMDImport, mdTypeDef cl, UINT32 numElems, VARTYPE vt, MethodTable* pElementMT)
+FieldMarshaler_FixedArray::FieldMarshaler_FixedArray(Module *pModule, mdTypeDef cl, UINT32 numElems, VARTYPE vt, MethodTable* pElementMT)
: m_numElems(numElems)
, m_vt(vt)
, m_BestFitMap(FALSE)
@@ -2750,7 +2749,7 @@ FieldMarshaler_FixedArray::FieldMarshaler_FixedArray(IMDInternalImport *pMDImpor
{
BOOL BestFitMap = FALSE;
BOOL ThrowOnUnmappableChar = FALSE;
- ReadBestFitCustomAttribute(pMDImport, cl, &BestFitMap, &ThrowOnUnmappableChar);
+ ReadBestFitCustomAttribute(pModule, cl, &BestFitMap, &ThrowOnUnmappableChar);
m_BestFitMap = !!BestFitMap;
m_ThrowOnUnmappableChar = !!ThrowOnUnmappableChar;
}
diff --git a/src/vm/fieldmarshaler.h b/src/vm/fieldmarshaler.h
index e3e05095a6..9e393b7755 100644
--- a/src/vm/fieldmarshaler.h
+++ b/src/vm/fieldmarshaler.h
@@ -103,7 +103,6 @@ VOID ParseNativeType(Module* pModule,
LayoutRawFieldInfo* pfwalk,
PCCOR_SIGNATURE pNativeType,
ULONG cbNativeType,
- IMDInternalImport* pInternalImport,
mdTypeDef cl,
const SigTypeContext * pTypeContext,
BOOL *pfDisqualifyFromManagedSequential
@@ -1024,7 +1023,7 @@ private:
class FieldMarshaler_FixedArray : public FieldMarshaler
{
public:
- FieldMarshaler_FixedArray(IMDInternalImport *pMDImport, mdTypeDef cl, UINT32 numElems, VARTYPE vt, MethodTable* pElementMT);
+ FieldMarshaler_FixedArray(Module *pModule, mdTypeDef cl, UINT32 numElems, VARTYPE vt, MethodTable* pElementMT);
VOID UpdateNativeImpl(OBJECTREF* pCLRValue, LPVOID pNativeValue, OBJECTREF *ppCleanupWorkListOnStack) const;
VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const;
diff --git a/src/vm/interoputil.cpp b/src/vm/interoputil.cpp
index 7b24f9c80c..044cd2a427 100644
--- a/src/vm/interoputil.cpp
+++ b/src/vm/interoputil.cpp
@@ -898,24 +898,24 @@ void FillExceptionData(
//---------------------------------------------------------------------------
// If pImport has the DefaultDllImportSearchPathsAttribute,
// set the value of the attribute in pDlImportSearchPathFlags and return true.
-BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, mdToken token, DWORD * pDllImportSearchPathFlags)
+BOOL GetDefaultDllImportSearchPathsAttributeValue(Module *pModule, mdToken token, DWORD * pDllImportSearchPathFlags)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
- PRECONDITION(CheckPointer(pImport));
+ PRECONDITION(CheckPointer(pModule));
}
CONTRACTL_END;
BYTE* pData = NULL;
LONG cData = 0;
- HRESULT hr = pImport->GetCustomAttributeByName(token,
- g_DefaultDllImportSearchPathsAttribute,
- (const VOID **)(&pData),
- (ULONG *)&cData);
+ HRESULT hr = pModule->GetCustomAttribute(token,
+ WellKnownAttribute::DefaultDllImportSearchPaths,
+ (const VOID **)(&pData),
+ (ULONG *)&cData);
IfFailThrow(hr);
if(cData == 0 )
@@ -954,7 +954,7 @@ int GetLCIDParameterIndex(MethodDesc *pMD)
if (!pMD->GetMethodTable()->IsProjectedFromWinRT()) // ignore LCIDConversionAttribute on WinRT methods
{
// Check to see if the method has the LCIDConversionAttribute.
- hr = pMD->GetMDImport()->GetCustomAttributeByName(pMD->GetMemberDef(), INTEROP_LCIDCONVERSION_TYPE, (const void**)&pVal, &cbVal);
+ hr = pMD->GetCustomAttribute(WellKnownAttribute::LCIDConversion, (const void**)&pVal, &cbVal);
if (hr == S_OK)
{
CustomAttributeParser caLCID(pVal, cbVal);
@@ -1030,6 +1030,7 @@ BOOL IsMemberVisibleFromCom(MethodTable *pDeclaringMT, mdToken tk, mdMethodDef m
DWORD dwFlags;
IMDInternalImport *pInternalImport = pDeclaringMT->GetMDImport();
+ Module *pModule = pDeclaringMT->GetModule();
// Check to see if the member is public.
switch (TypeFromToken(tk))
@@ -1077,7 +1078,7 @@ BOOL IsMemberVisibleFromCom(MethodTable *pDeclaringMT, mdToken tk, mdMethodDef m
if (!pDeclaringMT->IsProjectedFromWinRT() && !pDeclaringMT->IsExportedToWinRT() && !pDeclaringMT->IsWinRTObjectType())
{
// Check to see if the associate has the ComVisible attribute set (non-WinRT members only).
- hr = pInternalImport->GetCustomAttributeByName(mdAssociate, INTEROP_COMVISIBLE_TYPE, (const void**)&pVal, &cbVal);
+ hr = pModule->GetCustomAttribute(mdAssociate, WellKnownAttribute::ComVisible, (const void**)&pVal, &cbVal);
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
@@ -1101,7 +1102,7 @@ BOOL IsMemberVisibleFromCom(MethodTable *pDeclaringMT, mdToken tk, mdMethodDef m
if (!pDeclaringMT->IsProjectedFromWinRT() && !pDeclaringMT->IsExportedToWinRT() && !pDeclaringMT->IsWinRTObjectType())
{
// Check to see if the member has the ComVisible attribute set (non-WinRT members only).
- hr = pInternalImport->GetCustomAttributeByName(tk, INTEROP_COMVISIBLE_TYPE, (const void**)&pVal, &cbVal);
+ hr = pModule->GetCustomAttribute(tk, WellKnownAttribute::ComVisible, (const void**)&pVal, &cbVal);
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
@@ -1321,7 +1322,7 @@ HRESULT GetStringizedTypeLibGuidForAssembly(Assembly *pAssembly, CQuickArray<BYT
{
// If the ComCompatibleVersionAttribute is set, then use the version
// number in the attribute when generating the GUID.
- IfFailGo(pAssembly->GetManifestImport()->GetCustomAttributeByName(TokenFromRid(1, mdtAssembly), INTEROP_COMCOMPATIBLEVERSION_TYPE, (const void**)&pbData, &cbData));
+ IfFailGo(pAssembly->GetCustomAttribute(TokenFromRid(1, mdtAssembly), WellKnownAttribute::ComCompatibleVersion, (const void**)&pbData, &cbData));
}
if (hr == S_OK && cbData >= (2 + 4 * sizeof(INT32)))
@@ -1635,13 +1636,13 @@ ReadBestFitCustomAttribute(MethodDesc* pMD, BOOL* BestFit, BOOL* ThrowOnUnmappab
}
CONTRACTL_END;
- ReadBestFitCustomAttribute(pMD->GetMDImport(),
+ ReadBestFitCustomAttribute(pMD->GetModule(),
pMD->GetMethodTable()->GetCl(),
BestFit, ThrowOnUnmappableChar);
}
VOID
-ReadBestFitCustomAttribute(IMDInternalImport* pInternalImport, mdTypeDef cl, BOOL* BestFit, BOOL* ThrowOnUnmappableChar)
+ReadBestFitCustomAttribute(Module* pModule, mdTypeDef cl, BOOL* BestFit, BOOL* ThrowOnUnmappableChar)
{
// Set the attributes to their defaults, just to be safe.
*BestFit = TRUE;
@@ -1652,7 +1653,7 @@ ReadBestFitCustomAttribute(IMDInternalImport* pInternalImport, mdTypeDef cl, BOO
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
- PRECONDITION(CheckPointer(pInternalImport));
+ PRECONDITION(CheckPointer(pModule));
}
CONTRACTL_END;
@@ -1668,7 +1669,7 @@ ReadBestFitCustomAttribute(IMDInternalImport* pInternalImport, mdTypeDef cl, BOO
// 30 for the ThrowOnUnmappableChar bool
// Try the assembly first
- hr = pInternalImport->GetCustomAttributeByName(TokenFromRid(1, mdtAssembly), INTEROP_BESTFITMAPPING_TYPE, (const VOID**)(&pData), &cbCount);
+ hr = pModule->GetCustomAttribute(TokenFromRid(1, mdtAssembly), WellKnownAttribute::BestFitMapping, (const VOID**)(&pData), &cbCount);
if ((hr == S_OK) && (pData) && (cbCount > 4) && (pData[0] == 1) && (pData[1] == 0))
{
_ASSERTE((cbCount == 5) || (cbCount == 30));
@@ -1685,7 +1686,7 @@ ReadBestFitCustomAttribute(IMDInternalImport* pInternalImport, mdTypeDef cl, BOO
// Now try the interface/class/struct
if (IsNilToken(cl))
return;
- hr = pInternalImport->GetCustomAttributeByName(cl, INTEROP_BESTFITMAPPING_TYPE, (const VOID**)(&pData), &cbCount);
+ hr = pModule->GetCustomAttribute(cl, WellKnownAttribute::BestFitMapping, (const VOID**)(&pData), &cbCount);
if ((hr == S_OK) && (pData) && (cbCount > 4) && (pData[0] == 1) && (pData[1] == 0))
{
_ASSERTE((cbCount == 5) || (cbCount == 30));
@@ -1781,7 +1782,7 @@ int InternalWideToAnsi(__in_ecount(iNumWideChars) LPCWSTR szWideString, int iNum
namespace
{
HRESULT TryParseClassInterfaceAttribute(
- _In_ IMDInternalImport *import,
+ _In_ Module *pModule,
_In_ mdToken tkObj,
_Out_ CorClassIfaceAttr *val)
{
@@ -1790,14 +1791,14 @@ namespace
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
- PRECONDITION(CheckPointer(import));
+ PRECONDITION(CheckPointer(pModule));
PRECONDITION(CheckPointer(val));
}
CONTRACTL_END
const BYTE *pVal = nullptr;
ULONG cbVal = 0;
- HRESULT hr = import->GetCustomAttributeByName(tkObj, INTEROP_CLASSINTERFACE_TYPE, (const void**)&pVal, &cbVal);
+ HRESULT hr = pModule->GetCustomAttribute(tkObj, WellKnownAttribute::ClassInterface, (const void**)&pVal, &cbVal);
if (hr != S_OK)
{
*val = clsIfNone;
@@ -1840,7 +1841,7 @@ CorClassIfaceAttr ReadClassInterfaceTypeCustomAttribute(TypeHandle type)
CorClassIfaceAttr attrValueMaybe;
// First look for the class interface attribute at the class level.
- HRESULT hr = TryParseClassInterfaceAttribute(type.GetMethodTable()->GetMDImport(), type.GetCl(), &attrValueMaybe);
+ HRESULT hr = TryParseClassInterfaceAttribute(type.GetModule(), type.GetCl(), &attrValueMaybe);
if (FAILED(hr))
ThrowHR(hr, BFA_BAD_CLASS_INT_CA_FORMAT);
@@ -1848,7 +1849,7 @@ CorClassIfaceAttr ReadClassInterfaceTypeCustomAttribute(TypeHandle type)
{
// Check the class interface attribute at the assembly level.
Assembly *pAssembly = type.GetAssembly();
- hr = TryParseClassInterfaceAttribute(pAssembly->GetManifestImport(), pAssembly->GetManifestToken(), &attrValueMaybe);
+ hr = TryParseClassInterfaceAttribute(pAssembly->GetManifestModule(), pAssembly->GetManifestToken(), &attrValueMaybe);
if (FAILED(hr))
ThrowHR(hr, BFA_BAD_CLASS_INT_CA_FORMAT);
}
@@ -2604,7 +2605,7 @@ BOOL ClassSupportsIClassX(MethodTable *pMT)
}
// If the class is decorated with an explicit ClassInterfaceAttribute, we're going to say yes.
- if (S_OK == pMT->GetMDImport()->GetCustomAttributeByName(pMT->GetCl(), INTEROP_CLASSINTERFACE_TYPE, NULL, NULL))
+ if (S_OK == pMT->GetCustomAttribute(WellKnownAttribute::ClassInterface, NULL, NULL))
return TRUE;
MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap();
@@ -2769,7 +2770,7 @@ DefaultInterfaceType GetDefaultInterfaceForClassInternal(TypeHandle hndClass, Ty
return DefaultInterfaceType_IUnknown;
// Start by checking for the ComDefaultInterface attribute.
- hr = pClassMT->GetMDImport()->GetCustomAttributeByName(pClassMT->GetCl(), INTEROP_COMDEFAULTINTERFACE_TYPE, &pvData, &cbData);
+ hr = pClassMT->GetCustomAttribute(WellKnownAttribute::ComDefaultInterface, &pvData, &cbData);
IfFailThrow(hr);
if (hr == S_OK && cbData > 2)
{
@@ -3043,7 +3044,7 @@ void GetComSourceInterfacesForClass(MethodTable *pMT, CQuickArray<MethodTable *>
for (; pMT != NULL; pMT = pMT->GetParentMethodTable())
{
// See if there is any [source] interface at this level of the hierarchy.
- hr = pMT->GetMDImport()->GetCustomAttributeByName(pMT->GetCl(), INTEROP_COMSOURCEINTERFACES_TYPE, &pvData, &cbData);
+ hr = pMT->GetCustomAttribute(WellKnownAttribute::ComSourceInterfaces, &pvData, &cbData);
IfFailThrow(hr);
if (hr == S_OK && cbData > 2)
{
@@ -3565,6 +3566,7 @@ static BOOL SpecialIsGenericTypeVisibleFromCom(TypeHandle hndType)
mdTypeDef mdType = pMT->GetCl();
IMDInternalImport * pInternalImport = pMT->GetMDImport();
Assembly * pAssembly = pMT->GetAssembly();
+ Module * pModule = pMT->GetModule();
// If the type is a COM imported interface then it is visible from COM.
if (pMT->IsInterface() && pMT->IsComImport())
@@ -3606,7 +3608,7 @@ static BOOL SpecialIsGenericTypeVisibleFromCom(TypeHandle hndType)
return FALSE;
// Check to see if the type has the ComVisible attribute set.
- hr = pInternalImport->GetCustomAttributeByName(mdType, INTEROP_COMVISIBLE_TYPE, (const void**)&pVal, &cbVal);
+ hr = pModule->GetCustomAttribute(mdType, WellKnownAttribute::ComVisible, (const void**)&pVal, &cbVal);
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
@@ -3621,7 +3623,7 @@ static BOOL SpecialIsGenericTypeVisibleFromCom(TypeHandle hndType)
}
// Check to see if the assembly has the ComVisible attribute set.
- hr = pAssembly->GetManifestImport()->GetCustomAttributeByName(pAssembly->GetManifestToken(), INTEROP_COMVISIBLE_TYPE, (const void**)&pVal, &cbVal);
+ hr = pModule->GetCustomAttribute(pAssembly->GetManifestToken(), WellKnownAttribute::ComVisible, (const void**)&pVal, &cbVal);
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
diff --git a/src/vm/interoputil.h b/src/vm/interoputil.h
index 311cedd102..bcb0a97f12 100644
--- a/src/vm/interoputil.h
+++ b/src/vm/interoputil.h
@@ -118,7 +118,7 @@ BOOL IsComObjectClass(TypeHandle type);
// both assembly level and interface level
//---------------------------------------------------------
VOID ReadBestFitCustomAttribute(MethodDesc* pMD, BOOL* BestFit, BOOL* ThrowOnUnmappableChar);
-VOID ReadBestFitCustomAttribute(IMDInternalImport* pInternalImport, mdTypeDef cl, BOOL* BestFit, BOOL* ThrowOnUnmappableChar);
+VOID ReadBestFitCustomAttribute(Module* pModule, mdTypeDef cl, BOOL* BestFit, BOOL* ThrowOnUnmappableChar);
int InternalWideToAnsi(__in_ecount(iNumWideChars) LPCWSTR szWideString, int iNumWideChars, __out_ecount_opt(cbAnsiBufferSize) LPSTR szAnsiString, int cbAnsiBufferSize, BOOL fBestFit, BOOL fThrowOnUnmappableChar);
//---------------------------------------------------------
@@ -138,7 +138,7 @@ void FillExceptionData(
//---------------------------------------------------------------------------
// If pImport has the DefaultDllImportSearchPathsAttribute,
// set the value of the attribute in pDlImportSearchPathFlags and return true.
-BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, mdToken token, DWORD * pDlImportSearchPathFlags);
+BOOL GetDefaultDllImportSearchPathsAttributeValue(Module *pModule, mdToken token, DWORD * pDlImportSearchPathFlags);
//---------------------------------------------------------------------------
// Returns the index of the LCID parameter if one exists and -1 otherwise.
diff --git a/src/vm/marshalnative.cpp b/src/vm/marshalnative.cpp
index bd4ad09731..2f4a8d9403 100644
--- a/src/vm/marshalnative.cpp
+++ b/src/vm/marshalnative.cpp
@@ -1755,34 +1755,6 @@ FCIMPL2(void, MarshalNative::DoGetTypeLibGuidForAssembly, GUID * result, Assembl
}
FCIMPLEND
-FCIMPL3(void, MarshalNative::GetTypeLibVersionForAssembly, AssemblyBaseObject* refAsmUNSAFE, INT32 *pMajorVersion, INT32 *pMinorVersion)
-{
- FCALL_CONTRACT;
-
- // Validate the arguments.
- _ASSERTE(refAsmUNSAFE != NULL);
-
- ASSEMBLYREF refAsm = (ASSEMBLYREF) refAsmUNSAFE;
- HELPER_METHOD_FRAME_BEGIN_1(refAsm);
-
- HRESULT hr = S_OK;
-
- // Retrieve the assembly from the ASSEMBLYREF.
- Assembly *pAssembly = refAsm->GetAssembly();
- _ASSERTE(pAssembly);
-
- // Retrieve the version for the assembly.
- USHORT usMaj, usMin;
- IfFailThrow(::GetTypeLibVersionForAssembly(pAssembly, &usMaj, &usMin));
-
- // Set the out parameters.
- *pMajorVersion = usMaj;
- *pMinorVersion = usMin;
-
- HELPER_METHOD_FRAME_END();
-}
-FCIMPLEND
-
FCIMPL1(int, MarshalNative::GetStartComSlot, ReflectClassBaseObject* tUNSAFE)
{
FCALL_CONTRACT;
diff --git a/src/vm/marshalnative.h b/src/vm/marshalnative.h
index bac0c2475d..089ed6c4a0 100644
--- a/src/vm/marshalnative.h
+++ b/src/vm/marshalnative.h
@@ -205,12 +205,6 @@ public:
static FCDECL2(void, DoGetTypeLibGuidForAssembly, GUID * result, AssemblyBaseObject* refAsmUNSAFE);
//====================================================================
- // Given a assembly, return the version number of the type library
- // that would be exported from the assembly.
- //====================================================================
- static FCDECL3(void, GetTypeLibVersionForAssembly, AssemblyBaseObject* refAsmUNSAFE, INT32 *pMajorVersion, INT32 *pMinorVersion);
-
- //====================================================================
// These methods are used to map COM slots to method info's.
//====================================================================
static FCDECL1(int, GetStartComSlot, ReflectClassBaseObject* tUNSAFE);
diff --git a/src/vm/method.cpp b/src/vm/method.cpp
index 2f6a9b4997..31c49748bc 100644
--- a/src/vm/method.cpp
+++ b/src/vm/method.cpp
@@ -157,7 +157,7 @@ BOOL NDirectMethodDesc::HasDefaultDllImportSearchPathsAttribute()
_ASSERTE(!IsZapped());
- BOOL attributeIsFound = GetDefaultDllImportSearchPathsAttributeValue(GetMDImport(),GetMemberDef(),&ndirect.m_DefaultDllImportSearchPathsAttributeValue);
+ BOOL attributeIsFound = GetDefaultDllImportSearchPathsAttributeValue(GetModule(),GetMemberDef(),&ndirect.m_DefaultDllImportSearchPathsAttributeValue);
if(attributeIsFound )
{
@@ -5281,8 +5281,8 @@ BOOL MethodDesc::HasNativeCallableAttribute()
}
CONTRACTL_END;
- HRESULT hr = GetMDImport()->GetCustomAttributeByName(GetMemberDef(),
- g_NativeCallableAttribute,
+ HRESULT hr = GetModule()->GetCustomAttribute(GetMemberDef(),
+ WellKnownAttribute::NativeCallable,
NULL,
NULL);
if (hr == S_OK)
diff --git a/src/vm/method.hpp b/src/vm/method.hpp
index cf532f8102..7722097113 100644
--- a/src/vm/method.hpp
+++ b/src/vm/method.hpp
@@ -866,6 +866,16 @@ public:
return pModule->GetMDImport();
}
+ HRESULT GetCustomAttribute(WellKnownAttribute attribute,
+ const void **ppData,
+ ULONG *pcbData) const
+ {
+ WRAPPER_NO_CONTRACT;
+ Module *pModule = GetModule();
+ PREFIX_ASSUME(pModule != NULL);
+ return pModule->GetCustomAttribute(GetMemberDef(), attribute, ppData, pcbData);
+ }
+
#ifndef DACCESS_COMPILE
IMetaDataEmit* GetEmitter()
{
diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h
index 3fe016f69d..2b7c516ab3 100644
--- a/src/vm/methodtable.h
+++ b/src/vm/methodtable.h
@@ -63,6 +63,7 @@ class ComCallWrapperTemplate;
class ClassFactoryBase;
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
class ArgDestination;
+enum class WellKnownAttribute : DWORD;
//============================================================================
// This is the in-memory structure of a class and it will evolve.
@@ -2868,6 +2869,10 @@ public:
// Get the MD Import for the metadata for the corresponding type declaration
IMDInternalImport* GetMDImport();
+
+ HRESULT GetCustomAttribute(WellKnownAttribute attribute,
+ const void **ppData,
+ ULONG *pcbData);
mdTypeDef GetEnclosingCl();
diff --git a/src/vm/methodtable.inl b/src/vm/methodtable.inl
index 31e373c6ae..de3e982afa 100644
--- a/src/vm/methodtable.inl
+++ b/src/vm/methodtable.inl
@@ -1194,6 +1194,15 @@ inline IMDInternalImport* MethodTable::GetMDImport()
}
//==========================================================================================
+inline HRESULT MethodTable::GetCustomAttribute(
+ WellKnownAttribute attribute,
+ const void **ppData,
+ ULONG *pcbData)
+{
+ return GetModule()->GetCustomAttribute(GetCl(), attribute, ppData, pcbData);
+}
+
+//==========================================================================================
inline BOOL MethodTable::IsSealed()
{
LIMITED_METHOD_CONTRACT;
diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp
index 91a888af1a..0bafe59f8a 100644
--- a/src/vm/methodtablebuilder.cpp
+++ b/src/vm/methodtablebuilder.cpp
@@ -1420,17 +1420,17 @@ MethodTableBuilder::BuildMethodTableThrowing(
{
bmtProp->fIsValueClass = true;
- HRESULT hr = GetMDImport()->GetCustomAttributeByName(bmtInternal->pType->GetTypeDefToken(),
- g_CompilerServicesUnsafeValueTypeAttribute,
- NULL, NULL);
+ HRESULT hr = GetCustomAttribute(bmtInternal->pType->GetTypeDefToken(),
+ WellKnownAttribute::UnsafeValueType,
+ NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
{
SetUnsafeValueClass();
}
- hr = GetMDImport()->GetCustomAttributeByName(bmtInternal->pType->GetTypeDefToken(),
- g_CompilerServicesIsByRefLikeAttribute,
+ hr = GetCustomAttribute(bmtInternal->pType->GetTypeDefToken(),
+ WellKnownAttribute::IsByRefLike,
NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
@@ -1486,8 +1486,8 @@ MethodTableBuilder::BuildMethodTableThrowing(
// We check this here fairly early to ensure other downstream checks on these types can be slightly more efficient.
if (GetModule()->IsSystem() || GetAssembly()->IsSIMDVectorAssembly())
{
- HRESULT hr = GetMDImport()->GetCustomAttributeByName(bmtInternal->pType->GetTypeDefToken(),
- g_CompilerServicesIntrinsicAttribute,
+ HRESULT hr = GetCustomAttribute(bmtInternal->pType->GetTypeDefToken(),
+ WellKnownAttribute::Intrinsic,
NULL,
NULL);
@@ -3389,9 +3389,9 @@ MethodTableBuilder::EnumerateClassFields()
// If this static field is thread static, then we need
// to increment bmtEnumFields->dwNumThreadStaticFields
- hr = pMDInternalImport->GetCustomAttributeByName(tok,
- g_ThreadStaticAttributeClassName,
- NULL, NULL);
+ hr = GetCustomAttribute(tok,
+ WellKnownAttribute::ThreadStatic,
+ NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
{
@@ -3794,9 +3794,9 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList,
HRESULT hr;
- hr = pInternalImport->GetCustomAttributeByName(bmtMetaData->pFields[i],
- g_ThreadStaticAttributeClassName,
- NULL, NULL);
+ hr = GetCustomAttribute(bmtMetaData->pFields[i],
+ WellKnownAttribute::ThreadStatic,
+ NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
{
@@ -3806,9 +3806,9 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList,
if (ElementType == ELEMENT_TYPE_VALUETYPE)
{
- hr = pInternalImport->GetCustomAttributeByName(bmtMetaData->pFields[i],
- g_CompilerServicesFixedAddressValueTypeAttribute,
- NULL, NULL);
+ hr = GetCustomAttribute(bmtMetaData->pFields[i],
+ WellKnownAttribute::FixedAddressValueType,
+ NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
{
@@ -5124,12 +5124,10 @@ MethodTableBuilder::InitNewMethodDesc(
// Check for methods marked as [Intrinsic]
if (GetModule()->IsSystem() || GetAssembly()->IsSIMDVectorAssembly())
{
- HRESULT hr = GetMDImport()->GetCustomAttributeByName(pMethod->GetMethodSignature().GetToken(),
- g_CompilerServicesIntrinsicAttribute,
- NULL,
- NULL);
-
- if (hr == S_OK || bmtProp->fIsHardwareIntrinsic)
+ if (bmtProp->fIsHardwareIntrinsic || (S_OK == GetCustomAttribute(pMethod->GetMethodSignature().GetToken(),
+ WellKnownAttribute::Intrinsic,
+ NULL,
+ NULL)))
{
pNewMD->SetIsJitIntrinsic();
}
@@ -10358,7 +10356,7 @@ MethodTableBuilder::SetupMethodTable2(
{
const BYTE * pVal;
ULONG cbVal;
- HRESULT hr = GetMDImport()->GetCustomAttributeByName(GetCl(), g_WindowsFoundationMarshalingBehaviorAttributeClassName, (const void **) &pVal, &cbVal);
+ HRESULT hr = GetCustomAttribute(GetCl(), WellKnownAttribute::WinRTMarshalingBehaviorAttribute, (const void **) &pVal, &cbVal);
if (hr == S_OK)
{
CustomAttributeParser cap(pVal, cbVal);
@@ -11240,7 +11238,7 @@ VOID MethodTableBuilder::CheckForSpecialTypes()
// Check to see if the type is a COM event interface (classic COM interop only).
if (IsInterface() && !GetHalfBakedClass()->IsProjectedFromWinRT())
{
- HRESULT hr = pMDImport->GetCustomAttributeByName(GetCl(), INTEROP_COMEVENTINTERFACE_TYPE, NULL, NULL);
+ HRESULT hr = GetCustomAttribute(GetCl(), WellKnownAttribute::ComEventInterface, NULL, NULL);
if (hr == S_OK)
{
bmtProp->fComEventItfType = true;
@@ -11608,7 +11606,7 @@ void MethodTableBuilder::GetCoClassAttribInfo()
if (!GetHalfBakedClass()->IsProjectedFromWinRT()) // ignore classic COM interop CA on WinRT interfaces
{
// Retrieve the CoClassAttribute CA.
- HRESULT hr = GetMDImport()->GetCustomAttributeByName(GetCl(), INTEROP_COCLASS_TYPE, NULL, NULL);
+ HRESULT hr = GetCustomAttribute(GetCl(), WellKnownAttribute::CoClass, NULL, NULL);
if (hr == S_OK)
{
// COM class interfaces may lazily populate the m_pCoClassForIntf field of EEClass. This field is
diff --git a/src/vm/methodtablebuilder.h b/src/vm/methodtablebuilder.h
index 82f1c4f630..c309fd5e7c 100644
--- a/src/vm/methodtablebuilder.h
+++ b/src/vm/methodtablebuilder.h
@@ -185,6 +185,18 @@ private:
PTR_EEClass GetHalfBakedClass() { LIMITED_METHOD_CONTRACT; return m_pHalfBakedClass; }
PTR_MethodTable GetHalfBakedMethodTable() { LIMITED_METHOD_CONTRACT; return m_pHalfBakedMT; }
+ HRESULT GetCustomAttribute(mdToken parentToken, WellKnownAttribute attribute, const void **ppData, ULONG *pcbData)
+ {
+ WRAPPER_NO_CONTRACT;
+ if (GetModule()->IsReadyToRun())
+ {
+ if (!GetModule()->GetReadyToRunInfo()->MayHaveCustomAttribute(attribute, parentToken))
+ return S_FALSE;
+ }
+
+ return GetMDImport()->GetCustomAttributeByName(parentToken, GetWellKnownAttributeName(attribute), ppData, pcbData);
+ }
+
// <NOTE> The following functions are used during MethodTable construction to access/set information about the type being constructed.
// Beware that some of the fields of the underlying EEClass/MethodTable being constructed may not
// be initialized. Because of this, ideally the code will gradually be cleaned up so that
diff --git a/src/vm/mlinfo.cpp b/src/vm/mlinfo.cpp
index c4696bc1f3..8a1eda073c 100644
--- a/src/vm/mlinfo.cpp
+++ b/src/vm/mlinfo.cpp
@@ -2961,7 +2961,7 @@ lExit:
if (SUCCEEDED(pInternalImport->GetDefaultValue(token, &defaultValue)) && defaultValue.m_bType == ELEMENT_TYPE_VOID)
{
// check if it has params attribute
- if (pInternalImport->GetCustomAttributeByName(token, INTEROP_PARAMARRAY_TYPE, 0,0) == S_OK)
+ if (pModule->GetCustomAttribute(token, WellKnownAttribute::ParamArray, 0,0) == S_OK)
m_fOleVarArgCandidate = TRUE;
}
}
diff --git a/src/vm/nativeformatreader.h b/src/vm/nativeformatreader.h
index 4182b032c4..c97f324b59 100644
--- a/src/vm/nativeformatreader.h
+++ b/src/vm/nativeformatreader.h
@@ -10,6 +10,27 @@
#pragma once
+#ifndef DACCESS_COMPILE
+
+#if defined(_AMD64_) || defined(_X86_)
+#include "emmintrin.h"
+#define USE_INTEL_INTRINSICS_FOR_CUCKOO_FILTER
+#elif defined(_ARM_) || defined(_ARM64_)
+
+#ifndef FEATURE_PAL // The Mac and Linux build environments are not setup for NEON simd.
+#define USE_ARM_INTRINSICS_FOR_CUCKOO_FILTER
+
+#if defined(_ARM_)
+#include "arm_neon.h"
+#else
+#include "arm64_neon.h"
+#endif
+#endif // FEATURE_PAL
+
+#endif // _ARM_ || _ARM64_
+
+#endif // DACCESS_COMPILE
+
// To reduce differences between C# and C++ versions
#define byte uint8_t
#define uint uint32_t
@@ -45,7 +66,7 @@ namespace NativeFormat
{
_ASSERTE(false);
-#ifndef DACCESS_COMPILE
+#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
// Failfast instead of throwing, to avoid violating NOTHROW contracts of callers
EEPOLICY_HANDLE_FATAL_ERROR(COR_E_BADIMAGEFORMAT);
#endif
@@ -523,4 +544,125 @@ namespace NativeFormat
return Enumerator(parser, endOffset, (byte)hashcode);
}
};
+
+ class NativeCuckooFilter;
+ typedef DPTR(NativeCuckooFilter) PTR_NativeCuckooFilter;
+
+ class NativeCuckooFilter
+ {
+ PTR_BYTE _base;
+ UInt32 _size;
+ LONG _disableFilter;
+
+ bool IsPowerOfTwo(UInt32 number)
+ {
+ return (number & (number - 1)) == 0;
+ }
+
+ public:
+ static UInt32 ComputeFingerprintHash(UInt16 fingerprint)
+ {
+ // As the number of buckets is not reasonably greater than 65536, just use fingerprint as its own hash
+ // This implies that the hash of the entrypoint should be an independent hash function as compared
+ // to the fingerprint
+ return fingerprint;
+ }
+
+ NativeCuckooFilter()
+ {
+ _base = NULL;
+ _size = 0;
+ _disableFilter = 0;
+ }
+
+ NativeCuckooFilter(PTR_BYTE base_, UInt32 size, UInt32 rvaOfTable, UInt32 filterSize)
+ {
+ if (((rvaOfTable & 0xF) != 0) || ((filterSize & 0xF) != 0))
+ {
+ // Native cuckoo filters must be aligned at 16byte boundaries within the PE file
+ NativeReader exceptionReader;
+ exceptionReader.ThrowBadImageFormatException();
+ }
+ if ((filterSize != 0) && !IsPowerOfTwo(filterSize))
+ {
+ // Native cuckoo filters must be power of two in size
+ NativeReader exceptionReader;
+ exceptionReader.ThrowBadImageFormatException();
+ }
+ _base = base_ + rvaOfTable;
+ _size = filterSize;
+ _disableFilter = 0;
+ }
+
+ void DisableFilter()
+ {
+ // Set disable filter flag using interlocked to ensure that future
+ // attempts to read the filter will capture the change.
+ InterlockedExchange(&_disableFilter, 1);
+ }
+
+ bool HashComputationImmaterial()
+ {
+ if ((_base == NULL) || (_size == 0))
+ return true;
+ return false;
+ }
+
+ bool MayExist(UInt32 hashcode, UInt16 fingerprint)
+ {
+ if ((_base == NULL) || (_disableFilter))
+ return true;
+
+ if (_size == 0)
+ return false; // Empty table means none of the attributes exist
+
+ // Fingerprints of 0 don't actually exist. Just use 1, and lose some entropy
+ if (fingerprint == 0)
+ fingerprint = 1;
+
+ UInt32 bucketCount = _size / 16;
+ UInt32 bucketMask = bucketCount - 1; // filters are power of 2 in size
+
+ UInt32 bucketAIndex = hashcode & bucketMask;
+ UInt32 bucketBIndex = bucketAIndex ^ (ComputeFingerprintHash(fingerprint) & bucketMask);
+
+#if defined(USE_INTEL_INTRINSICS_FOR_CUCKOO_FILTER)
+ __m128i bucketA = _mm_loadu_si128(&((__m128i*)_base)[bucketAIndex]);
+ __m128i bucketB = _mm_loadu_si128(&((__m128i*)_base)[bucketBIndex]);
+ __m128i fingerprintSIMD = _mm_set1_epi16(fingerprint);
+ __m128i bucketACompare = _mm_cmpeq_epi16(bucketA, fingerprintSIMD);
+ __m128i bucketBCompare = _mm_cmpeq_epi16(bucketB, fingerprintSIMD);
+ __m128i bothCompare = _mm_or_si128(bucketACompare, bucketBCompare);
+ return !!_mm_movemask_epi8(bothCompare);
+#elif defined(USE_ARM_INTRINSICS_FOR_CUCKOO_FILTER)
+ uint16x8_t bucketA = vld1q_u16((uint16_t*)&((uint16x8_t*)_base)[bucketAIndex]);
+ uint16x8_t bucketB = vld1q_u16((uint16_t*)&((uint16x8_t*)_base)[bucketBIndex]);
+ uint16x8_t fingerprintSIMD = vdupq_n_u16(fingerprint);
+ uint16x8_t bucketACompare = vceqq_u16(bucketA, fingerprintSIMD);
+ uint16x8_t bucketBCompare = vceqq_u16(bucketB, fingerprintSIMD);
+ uint16x8_t bothCompare = vorrq_u16(bucketACompare, bucketBCompare);
+ uint64_t bits0Lane = vgetq_lane_u64(bothCompare, 0);
+ uint64_t bits1Lane = vgetq_lane_u64(bothCompare, 1);
+ return !!(bits0Lane | bits1Lane);
+#else // Non-intrinsic implementation supporting NativeReader to cross DAC boundary
+ NativeReader reader(_base, _size);
+
+ // Check for existence in bucketA
+ for (int i = 0; i < 8; i++)
+ {
+ if (reader.ReadUInt16(bucketAIndex * 16 + i * sizeof(UInt16)) == fingerprint)
+ return true;
+ }
+
+ // Check for existence in bucketB
+ for (int i = 0; i < 8; i++)
+ {
+ if (reader.ReadUInt16(bucketBIndex * 16 + i * sizeof(UInt16)) == fingerprint)
+ return true;
+ }
+
+ return false;
+#endif
+ }
+ };
}
diff --git a/src/vm/readytoruninfo.cpp b/src/vm/readytoruninfo.cpp
index 3307fe21f3..86eb37201b 100644
--- a/src/vm/readytoruninfo.cpp
+++ b/src/vm/readytoruninfo.cpp
@@ -16,6 +16,7 @@
#include "versionresilienthashcode.h"
#include "typehashingalgorithms.h"
#include "method.hpp"
+#include "wellknownattributes.h"
using namespace NativeFormat;
@@ -614,6 +615,18 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYT
}
}
+ // For format version 3.1 and later, there is an optional attributes section
+ IMAGE_DATA_DIRECTORY *attributesPresenceDataInfoDir = FindSection(READYTORUN_SECTION_ATTRIBUTEPRESENCE);
+ if (attributesPresenceDataInfoDir != NULL)
+ {
+ NativeCuckooFilter newFilter(
+ (byte *)pLayout->GetBase(),
+ pLayout->GetVirtualSize(),
+ attributesPresenceDataInfoDir->VirtualAddress,
+ attributesPresenceDataInfoDir->Size);
+
+ m_attributesPresence = newFilter;
+ }
}
static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, Module * pModule)
@@ -907,4 +920,31 @@ BOOL ReadyToRunInfo::IsImageVersionAtLeast(int majorVersion, int minorVersion)
}
+static DWORD s_wellKnownAttributeHashes[(DWORD)WellKnownAttribute::CountOfWellKnownAttributes];
+
+bool ReadyToRunInfo::MayHaveCustomAttribute(WellKnownAttribute attribute, mdToken token)
+{
+ UINT32 hash = 0;
+ UINT16 fingerprint = 0;
+ if (!m_attributesPresence.HashComputationImmaterial())
+ {
+ DWORD wellKnownHash = s_wellKnownAttributeHashes[(DWORD)attribute];
+ if (wellKnownHash == 0)
+ {
+ // TODO, investigate using constexpr to compute string hashes at compile time initially
+ s_wellKnownAttributeHashes[(DWORD)attribute] = wellKnownHash = ComputeNameHashCode(GetWellKnownAttributeName(attribute));
+ }
+
+ hash = CombineTwoValuesIntoHash(wellKnownHash, token);
+ fingerprint = hash >> 16;
+ }
+
+ return m_attributesPresence.MayExist(hash, fingerprint);
+}
+
+void ReadyToRunInfo::DisableCustomAttributeFilter()
+{
+ m_attributesPresence.DisableFilter();
+}
+
#endif // DACCESS_COMPILE
diff --git a/src/vm/readytoruninfo.h b/src/vm/readytoruninfo.h
index 9205ae1d66..65b49a6174 100644
--- a/src/vm/readytoruninfo.h
+++ b/src/vm/readytoruninfo.h
@@ -14,6 +14,7 @@
#include "nativeformatreader.h"
#include "inlinetracking.h"
+#include "wellknownattributes.h"
typedef DPTR(struct READYTORUN_SECTION) PTR_READYTORUN_SECTION;
@@ -40,6 +41,7 @@ class ReadyToRunInfo
NativeFormat::NativeHashtable m_instMethodEntryPoints;
NativeFormat::NativeHashtable m_availableTypesHashtable;
NativeFormat::NativeHashtable m_pMetaDataHashtable;
+ NativeFormat::NativeCuckooFilter m_attributesPresence;
Crst m_Crst;
PtrHashMap m_entryPointToMethodDescMap;
@@ -135,6 +137,9 @@ public:
return m_pPersistentInlineTrackingMap;
}
+ bool MayHaveCustomAttribute(WellKnownAttribute attribute, mdToken token);
+ void DisableCustomAttributeFilter();
+
private:
BOOL GetTypeNameFromToken(IMDInternalImport * pImport, mdToken mdType, LPCUTF8 * ppszName, LPCUTF8 * ppszNameSpace);
BOOL GetEnclosingToken(IMDInternalImport * pImport, mdToken mdType, mdToken * pEnclosingToken);
diff --git a/src/vm/siginfo.cpp b/src/vm/siginfo.cpp
index 0397b5aa8b..8dfb1cdc02 100644
--- a/src/vm/siginfo.cpp
+++ b/src/vm/siginfo.cpp
@@ -2745,9 +2745,9 @@ HRESULT TypeIdentifierData::Init(Module *pModule, mdToken tk)
ULONG cbData;
const BYTE *pData;
- IfFailRet(pInternalImport->GetCustomAttributeByName(
+ IfFailRet(pModule->GetCustomAttribute(
tk,
- g_TypeIdentifierAttributeClassName,
+ WellKnownAttribute::TypeIdentifier,
(const void **)&pData,
&cbData));
@@ -2797,13 +2797,12 @@ HRESULT TypeIdentifierData::Init(Module *pModule, mdToken tk)
if (IsTdInterface(dwAttrType) && IsTdImport(dwAttrType))
{
// ComImport interfaces get scope from their GUID
- hr = pInternalImport->GetCustomAttributeByName(tk, INTEROP_GUID_TYPE, (const void **)&pData, &cbData);
+ hr = pModule->GetCustomAttribute(tk, WellKnownAttribute::Guid, (const void **)&pData, &cbData);
}
else
{
// other equivalent types get it from the declaring assembly
- IMDInternalImport *pAssemblyImport = pModule->GetAssembly()->GetManifestImport();
- hr = pAssemblyImport->GetCustomAttributeByName(TokenFromRid(1, mdtAssembly), INTEROP_GUID_TYPE, (const void **)&pData, &cbData);
+ hr = pModule->GetCustomAttribute(TokenFromRid(1, mdtAssembly), WellKnownAttribute::Guid, (const void **)&pData, &cbData);
}
if (hr != S_OK)
@@ -3131,7 +3130,7 @@ BOOL IsTypeDefEquivalent(mdToken tk, Module *pModule)
}
// Check for the TypeIdentifierAttribute and auto opt-in
- HRESULT hr = pInternalImport->GetCustomAttributeByName(tk, g_TypeIdentifierAttributeClassName, NULL, NULL);
+ HRESULT hr = pModule->GetCustomAttribute(tk, WellKnownAttribute::TypeIdentifier, NULL, NULL);
IfFailThrow(hr);
// 1. Type is within assembly marked with ImportedFromTypeLibAttribute or PrimaryInteropAssemblyAttribute
@@ -3172,7 +3171,7 @@ BOOL IsTypeDefEquivalent(mdToken tk, Module *pModule)
else
{
// COMEvent
- hr = pInternalImport->GetCustomAttributeByName(tk, INTEROP_COMEVENTINTERFACE_TYPE, NULL, NULL);
+ hr = pModule->GetCustomAttribute(tk, WellKnownAttribute::ComEventInterface, NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
diff --git a/src/vm/staticallocationhelpers.inl b/src/vm/staticallocationhelpers.inl
index 26c2573762..dfdeba67e8 100644
--- a/src/vm/staticallocationhelpers.inl
+++ b/src/vm/staticallocationhelpers.inl
@@ -119,8 +119,8 @@ static BOOL GetStaticFieldElementTypeForFieldDef(Module * pModule, IMDInternalIm
return TRUE;
// We need to do an extra check to see if this field is ThreadStatic
- HRESULT hr = pImport->GetCustomAttributeByName((mdToken)field,
- g_ThreadStaticAttributeClassName,
+ HRESULT hr = pModule->GetCustomAttribute((mdToken)field,
+ WellKnownAttribute::ThreadStatic,
NULL, NULL);
IfFailThrow(hr);
diff --git a/src/vm/typehashingalgorithms.h b/src/vm/typehashingalgorithms.h
index c661451eff..c05b523032 100644
--- a/src/vm/typehashingalgorithms.h
+++ b/src/vm/typehashingalgorithms.h
@@ -97,3 +97,94 @@ inline static int ComputeGenericInstanceHashCode(int definitionHashcode, int ari
}
return (hashcode + _rotl(hashcode, 15));
}
+
+/*
+
+The below hash combining function is based on the xxHash32 logic implemented
+in System.HashCode. In particular it is a port of the 2 element hash
+combining routines, which are in turn based on xxHash32 logic.
+
+The xxHash32 implementation is based on the code published by Yann Collet:
+https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c
+
+ xxHash - Fast Hash algorithm
+ Copyright (C) 2012-2016, Yann Collet
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You can contact the author at :
+ - xxHash homepage: http://www.xxhash.com
+ - xxHash source repository : https://github.com/Cyan4973/xxHash
+
+*/
+
+inline static UINT32 HashMDToken(mdToken token)
+{
+ // Hash function to generate a value useable for reasonable hashes from a single 32bit value
+ // This function was taken from http://burtleburtle.net/bob/hash/integer.html
+ UINT32 a = token;
+ a -= (a<<6);
+ a ^= (a>>17);
+ a -= (a<<9);
+ a ^= (a<<4);
+ a -= (a<<3);
+ a ^= (a<<10);
+ a ^= (a>>15);
+ return a;
+}
+
+inline static UINT32 XXHash32_MixEmptyState()
+{
+ // Unlike System.HashCode, these hash values are required to be stable, so don't
+ // mixin a random process specific value
+ return 374761393U; // Prime5
+}
+
+inline static UINT32 XXHash32_QueueRound(UINT32 hash, UINT32 queuedValue)
+{
+ return ((UINT32)_rotl((int)(hash + queuedValue * 3266489917U/*Prime3*/), 17)) * 668265263U/*Prime4*/;
+}
+
+inline static UINT32 XXHash32_MixFinal(UINT32 hash)
+{
+ hash ^= hash >> 15;
+ hash *= 2246822519U/*Prime2*/;
+ hash ^= hash >> 13;
+ hash *= 3266489917U/*Prime3*/;
+ hash ^= hash >> 16;
+ return hash;
+}
+
+inline static UINT32 CombineTwoValuesIntoHash(UINT32 value1, UINT32 value2)
+{
+ // This matches the behavior of System.HashCode.Combine(value1, value2) as of the time of authoring
+ DWORD hash = XXHash32_MixEmptyState();
+ hash += 8;
+ hash = XXHash32_QueueRound(hash, value1);
+ hash = XXHash32_QueueRound(hash, value2);
+ hash = XXHash32_MixFinal(hash);
+ return hash;
+}
diff --git a/src/vm/wellknownattributes.h b/src/vm/wellknownattributes.h
new file mode 100644
index 0000000000..297bf9a15f
--- /dev/null
+++ b/src/vm/wellknownattributes.h
@@ -0,0 +1,107 @@
+// 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.
+
+#ifndef __WELLKNOWNATTRIBUTES_H_
+#define __WELLKNOWNATTRIBUTES_H_
+
+enum class WellKnownAttribute : DWORD
+{
+ ParamArray,
+ DefaultMember,
+ DisablePrivateReflectionType,
+ FixedAddressValueType,
+ UnsafeValueType,
+ BestFitMapping,
+ ClassInterface,
+ CoClass,
+ ComCompatibleVersion,
+ ComDefaultInterface,
+ ComEventInterface,
+ ComSourceInterfaces,
+ ComVisible,
+ DefaultDllImportSearchPaths,
+ Guid,
+ LCIDConversion,
+ IDispatchImpl,
+ ImportedFromTypeLib,
+ Intrinsic,
+ IsByRefLike,
+ PrimaryInteropAssembly,
+ ManagedToNativeComInteropStub,
+ NativeCallable,
+ TypeIdentifier,
+ UnmanagedFunctionPointer,
+ ThreadStatic,
+ WinRTMarshalingBehaviorAttribute,
+
+ CountOfWellKnownAttributes
+};
+
+inline const char *GetWellKnownAttributeName(WellKnownAttribute attribute)
+{
+ switch (attribute)
+ {
+ case WellKnownAttribute::ParamArray:
+ return "System.ParamArrayAttribute";
+ case WellKnownAttribute::DefaultMember:
+ return "System.Reflection.DefaultMemberAttribute";
+ case WellKnownAttribute::DisablePrivateReflectionType:
+ return "System.Runtime.CompilerServices.DisablePrivateReflectionAttribute";
+ case WellKnownAttribute::FixedAddressValueType:
+ return "System.Runtime.CompilerServices.FixedAddressValueTypeAttribute";
+ case WellKnownAttribute::UnsafeValueType:
+ return "System.Runtime.CompilerServices.UnsafeValueTypeAttribute";
+ case WellKnownAttribute::BestFitMapping:
+ return "System.Runtime.InteropServices.BestFitMappingAttribute";
+ case WellKnownAttribute::ClassInterface:
+ return "System.Runtime.InteropServices.ClassInterfaceAttribute";
+ case WellKnownAttribute::CoClass:
+ return "System.Runtime.InteropServices.CoClassAttribute";
+ case WellKnownAttribute::ComCompatibleVersion:
+ return "System.Runtime.InteropServices.ComCompatibleVersionAttribute";
+ case WellKnownAttribute::ComDefaultInterface:
+ return "System.Runtime.InteropServices.ComDefaultInterfaceAttribute";
+ case WellKnownAttribute::ComEventInterface:
+ return "System.Runtime.InteropServices.ComEventInterfaceAttribute";
+ case WellKnownAttribute::ComSourceInterfaces:
+ return "System.Runtime.InteropServices.ComSourceInterfacesAttribute";
+ case WellKnownAttribute::ComVisible:
+ return "System.Runtime.InteropServices.ComVisibleAttribute";
+ case WellKnownAttribute::DefaultDllImportSearchPaths:
+ return "System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute";
+ case WellKnownAttribute::Guid:
+ return "System.Runtime.InteropServices.GuidAttribute";
+ case WellKnownAttribute::LCIDConversion:
+ return "System.Runtime.InteropServices.LCIDConversionAttribute";
+ case WellKnownAttribute::IDispatchImpl:
+ return "System.Runtime.InteropServices.IDispatchImplAttribute";
+ case WellKnownAttribute::ImportedFromTypeLib:
+ return "System.Runtime.InteropServices.ImportedFromTypeLibAttribute";
+ case WellKnownAttribute::Intrinsic:
+ return "System.Runtime.CompilerServices.IntrinsicAttribute";
+ case WellKnownAttribute::IsByRefLike:
+ return "System.Runtime.CompilerServices.IsByRefLikeAttribute";
+ case WellKnownAttribute::PrimaryInteropAssembly:
+ return "System.Runtime.InteropServices.PrimaryInteropAssemblyAttribute";
+ case WellKnownAttribute::ManagedToNativeComInteropStub:
+ return "System.Runtime.InteropServices.ManagedToNativeComInteropStubAttribute";
+ case WellKnownAttribute::NativeCallable:
+ return "System.Runtime.InteropServices.NativeCallableAttribute";
+ case WellKnownAttribute::TypeIdentifier:
+ return "System.Runtime.InteropServices.TypeIdentifierAttribute";
+ case WellKnownAttribute::UnmanagedFunctionPointer:
+ return "System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute";
+ case WellKnownAttribute::ThreadStatic:
+ return "System.ThreadStaticAttribute";
+ case WellKnownAttribute::WinRTMarshalingBehaviorAttribute:
+ return "Windows.Foundation.Metadata.MarshalingBehaviorAttribute";
+ case WellKnownAttribute::CountOfWellKnownAttributes:
+ default:
+ break; // Silence compiler warnings
+ }
+ _ASSERTE(false); // Should not be possible
+ return nullptr;
+}
+
+#endif // __WELLKNOWNATTRIBUTES_H_
diff --git a/src/zap/zapimage.cpp b/src/zap/zapimage.cpp
index e64c0b67d8..c498719bf7 100644
--- a/src/zap/zapimage.cpp
+++ b/src/zap/zapimage.cpp
@@ -562,6 +562,7 @@ void ZapImage::AllocateVirtualSections()
if (IsReadyToRunCompilation())
{
m_pAvailableTypesSection = NewVirtualSection(pTextSection, IBCUnProfiledSection | WarmRange | ReadonlySection);
+ m_pAttributePresenceSection = NewVirtualSection(pTextSection, IBCUnProfiledSection | WarmRange | ReadonlyDataSection, 16/* Must be 16 byte aligned */);
}
#endif
@@ -1833,6 +1834,7 @@ void ZapImage::Compile()
OutputEntrypointsTableForReadyToRun();
OutputDebugInfoForReadyToRun();
OutputTypesTableForReadyToRun(m_pMDImport);
+ OutputAttributePresenceFilter(m_pMDImport);
OutputInliningTableForReadyToRun();
OutputProfileDataForReadyToRun();
if (IsLargeVersionBubbleEnabled())
diff --git a/src/zap/zapimage.h b/src/zap/zapimage.h
index 4e9d233e75..745178924a 100644
--- a/src/zap/zapimage.h
+++ b/src/zap/zapimage.h
@@ -222,6 +222,7 @@ public:
#ifdef FEATURE_READYTORUN_COMPILER
ZapVirtualSection * m_pAvailableTypesSection;
+ ZapVirtualSection * m_pAttributePresenceSection;
#endif
// Preloader sections
@@ -597,6 +598,8 @@ private:
void OutputInliningTableForReadyToRun();
void OutputProfileDataForReadyToRun();
void OutputManifestMetadataForReadyToRun();
+ HRESULT ComputeAttributePresenceTable(IMDInternalImport * pMDImport, SArray<UINT16> *table);
+ void OutputAttributePresenceFilter(IMDInternalImport * pMDImport);
void CopyDebugDirEntry();
void CopyWin32Resources();
diff --git a/src/zap/zapreadytorun.cpp b/src/zap/zapreadytorun.cpp
index 8d83ff7400..cb41a1a4d7 100644
--- a/src/zap/zapreadytorun.cpp
+++ b/src/zap/zapreadytorun.cpp
@@ -20,6 +20,9 @@
#include "nibblestream.h"
+#include "../vm/typehashingalgorithms.h"
+#include "../vm/nativeformatreader.h"
+
using namespace NativeFormat;
void ZapReadyToRunHeader::Save(ZapWriter * pZapWriter)
@@ -467,6 +470,269 @@ void ZapImage::OutputTypesTableForReadyToRun(IMDInternalImport * pMDImport)
GetReadyToRunHeader()->RegisterSection(READYTORUN_SECTION_AVAILABLE_TYPES, pBlob);
}
+template<class Tlambda>
+HRESULT EnumerateAllCustomAttributes(IMDInternalImport *pMDImport, Tlambda lambda)
+{
+ HENUMInternalHolder hEnum(pMDImport);
+ hEnum.EnumAllInit(mdtCustomAttribute);
+
+ HRESULT hr = S_OK;
+
+ mdCustomAttribute tkCustomAttribute;
+ while (pMDImport->EnumNext(&hEnum, &tkCustomAttribute))
+ {
+ LPCUTF8 szNamespace;
+ LPCUTF8 szName;
+
+ hr = pMDImport->GetNameOfCustomAttribute(tkCustomAttribute, &szNamespace, &szName);
+ if (FAILED(hr))
+ return hr;
+
+ if (szNamespace == NULL)
+ continue;
+
+ if (szName == NULL)
+ continue;
+
+ // System.Runtime.CompilerServices.NullableAttribute is NEVER added to the table (There are *many* of these, and they provide no useful value to the runtime)
+ if ((strcmp(szNamespace, "System.Runtime.CompilerServices") == 0) && (strcmp(szName, "NullableAttribute") == 0))
+ continue;
+
+ bool addToTable = false;
+ // Other than Nullable attribute, all attributes under System.Runtime are added to the table
+ if (strncmp(szNamespace, "System.Runtime.", strlen("System.Runtime.")) == 0)
+ {
+ addToTable = true;
+ }
+ else if (strcmp(szNamespace, "Windows.Foundation.Metadata") == 0)
+ {
+ // Windows.Foundation.Metadata attributes are a similar construct to compilerservices attributes. Add them to the table
+ addToTable = true;
+ }
+ else if (strcmp(szNamespace, "System") == 0)
+ {
+ // Some historical well known attributes were placed in the System namespace. Special case them
+ if (strcmp(szName, "ParamArrayAttribute") == 0)
+ addToTable = true;
+ else if (strcmp(szName, "ThreadStaticAttribute") == 0)
+ addToTable = true;
+ }
+ else if (strcmp(szNamespace, "System.Reflection") == 0)
+ {
+ // Historical attribute in the System.Reflection namespace
+ if (strcmp(szName, "DefaultMemberAttribute") == 0)
+ addToTable = true;
+ }
+
+ if (!addToTable)
+ continue;
+
+ mdToken tkParent;
+ hr = pMDImport->GetParentToken(tkCustomAttribute, &tkParent);
+ if (FAILED(hr))
+ return hr;
+
+ hr = lambda(szNamespace, szName, tkParent);
+ if (FAILED(hr))
+ return hr;
+ }
+
+ return hr;
+}
+
+uint32_t xorshift128(uint32_t state[4])
+{
+ /* Algorithm "xor128" from p. 5 of Marsaglia, "Xorshift RNGs" */
+ uint32_t s, t = state[3];
+ state[3] = state[2];
+ state[2] = state[1];
+ state[1] = s = state[0];
+ t ^= t << 11;
+ t ^= t >> 8;
+ return state[0] = t ^ s ^ (s >> 19);
+}
+
+HRESULT ZapImage::ComputeAttributePresenceTable(IMDInternalImport * pMDImport, SArray<UINT16> *table)
+{
+ int countOfEntries = 0;
+ HRESULT hr = EnumerateAllCustomAttributes(pMDImport, [&countOfEntries](LPCUTF8 szNamespace, LPCUTF8 szName, mdToken tkParent)
+ {
+ countOfEntries++;
+ return S_OK;
+ });
+ if (FAILED(hr))
+ return hr;
+
+ if (countOfEntries == 0)
+ {
+ table->Clear();
+ _ASSERTE(table->IsEmpty());
+ return S_OK;
+ }
+
+ // Buckets have 8 entries
+ UINT minTableBucketCount = (countOfEntries / 8) + 1;
+ UINT bucketCount = 1;
+
+ // Bucket count must be power of two
+ while (bucketCount < minTableBucketCount)
+ bucketCount *= 2;
+
+ // Resize the array.
+ bool tryAgainWithBiggerTable = false;
+ int countOfRetries = 0;
+ do
+ {
+ tryAgainWithBiggerTable = false;
+ UINT actualSizeOfTable = bucketCount * 8; // Buckets have 8 entries in them
+ UINT16* pTable = table->OpenRawBuffer(actualSizeOfTable);
+ memset(pTable, 0, sizeof(UINT16) * actualSizeOfTable);
+ table->CloseRawBuffer();
+
+ uint32_t state[4] = {729055690, 833774698, 218408041, 493449127}; // 4 randomly generated numbers to initialize random number state
+
+ // Attempt to fill table
+
+ hr = EnumerateAllCustomAttributes(pMDImport, [&](LPCUTF8 szNamespace, LPCUTF8 szName, mdToken tkParent)
+ {
+ StackSString name(SString::Utf8);
+ name.AppendUTF8(szNamespace);
+ name.AppendUTF8(NAMESPACE_SEPARATOR_STR);
+ name.AppendUTF8(szName);
+
+ StackScratchBuffer buff;
+ const char* pDebugNameUTF8 = name.GetUTF8(buff);
+ size_t len = strlen(pDebugNameUTF8);
+
+ // This hashing algorithm MUST match exactly the logic in NativeCuckooFilter
+ DWORD hashOfAttribute = ComputeNameHashCode(pDebugNameUTF8);
+ UINT32 hash = CombineTwoValuesIntoHash(hashOfAttribute, tkParent);
+ UINT16 fingerprint = (UINT16)(hash >> 16);
+ if (fingerprint == 0)
+ fingerprint = 1;
+
+ UINT bucketAIndex = hash % bucketCount;
+ UINT bucketBIndex = (bucketAIndex ^ (NativeFormat::NativeCuckooFilter::ComputeFingerprintHash(fingerprint) % bucketCount));
+
+ _ASSERTE(bucketAIndex == (bucketBIndex ^ (NativeFormat::NativeCuckooFilter::ComputeFingerprintHash(fingerprint) % bucketCount)));
+
+ if (xorshift128(state) & 1) // Randomly choose which bucket to attempt to fill first
+ {
+ UINT temp = bucketAIndex;
+ bucketAIndex = bucketBIndex;
+ bucketBIndex = temp;
+ }
+
+ auto hasEntryInBucket = [&table](UINT bucketIndex, UINT16 fprint)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ if ((*table)[(bucketIndex * 8) + i] == fprint)
+ return true;
+ }
+ return false;
+ };
+
+ auto isEmptyEntryInBucket = [&table](UINT bucketIndex)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ if ((*table)[(bucketIndex * 8) + i] == 0)
+ return true;
+ }
+ return false;
+ };
+
+ auto fillEmptyEntryInBucket = [&table](UINT bucketIndex, UINT16 fprint)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ if ((*table)[(bucketIndex * 8) + i] == 0)
+ {
+ (*table)[(bucketIndex * 8) + i] = fprint;
+ return;
+ }
+ }
+ _ASSERTE(!"Not possible to reach here");
+ return;
+ };
+
+ // Scan for pre-existing fingerprint entry in buckets
+ if (hasEntryInBucket(bucketAIndex, fingerprint) || hasEntryInBucket(bucketBIndex, fingerprint))
+ return S_OK;
+
+ // Determine if there is space in a bucket to add a new entry
+ if (isEmptyEntryInBucket(bucketAIndex))
+ {
+ fillEmptyEntryInBucket(bucketAIndex, fingerprint);
+ return S_OK;
+ }
+ if (isEmptyEntryInBucket(bucketBIndex))
+ {
+ fillEmptyEntryInBucket(bucketBIndex, fingerprint);
+ return S_OK;
+ }
+
+ int MaxNumKicks = 256;
+ // Note, that bucketAIndex itself was chosen randomly above.
+ for (int n = 0; n < MaxNumKicks; n++)
+ {
+ // Randomly swap an entry in bucket bucketAIndex with fingerprint
+ UINT entryIndexInBucket = xorshift128(state) & 0x7;
+ UINT16 temp = fingerprint;
+ fingerprint = (*table)[(bucketAIndex * 8) + entryIndexInBucket];
+ (*table)[(bucketAIndex * 8) + entryIndexInBucket] = temp;
+
+ // Find other bucket
+ bucketAIndex = bucketAIndex ^ (NativeFormat::NativeCuckooFilter::ComputeFingerprintHash(fingerprint) % bucketCount);
+ if (isEmptyEntryInBucket(bucketAIndex))
+ {
+ fillEmptyEntryInBucket(bucketAIndex, fingerprint);
+ return S_OK;
+ }
+ }
+
+ tryAgainWithBiggerTable = true;
+ return E_FAIL;
+ });
+
+ if (tryAgainWithBiggerTable)
+ {
+ // bucket entry kicking path requires bucket counts to be power of two in size due to use of xor to retrieve second hash
+ bucketCount *= 2;
+ }
+ } while(tryAgainWithBiggerTable && ((countOfRetries++) < 2));
+
+ if (tryAgainWithBiggerTable)
+ {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+void ZapImage::OutputAttributePresenceFilter(IMDInternalImport * pMDImport)
+{
+ // Core library attributes are checked FAR more often than other dlls
+ // attributes, so produce a highly efficient table for determining if they are
+ // present. Other assemblies *MAY* benefit from this feature, but it doesn't show
+ // as useful at this time.
+
+ if (m_hModule != m_zapper->m_pEECompileInfo->GetLoaderModuleForMscorlib())
+ return;
+
+ SArray<UINT16> table;
+ if (SUCCEEDED(ComputeAttributePresenceTable(pMDImport, &table)))
+ {
+ UINT16* pRawTable = table.OpenRawBuffer(table.GetCount());
+ ZapNode * pBlob = ZapBlob::NewBlob(this, pRawTable, table.GetCount() * sizeof(UINT16));
+ table.CloseRawBuffer();
+
+ _ASSERTE(m_pAttributePresenceSection);
+ m_pAttributePresenceSection->Place(pBlob);
+ GetReadyToRunHeader()->RegisterSection(READYTORUN_SECTION_ATTRIBUTEPRESENCE, pBlob);
+ }
+}
//
// Verify that data structures and flags shared between NGen and ReadyToRun are in sync