summaryrefslogtreecommitdiff
path: root/src/md/enc
diff options
context:
space:
mode:
Diffstat (limited to 'src/md/enc')
-rw-r--r--src/md/enc/.gitmirror1
-rw-r--r--src/md/enc/CMakeLists.txt25
-rw-r--r--src/md/enc/crossgen/.gitmirror1
-rw-r--r--src/md/enc/crossgen/MDRuntimeRW_crossgen.nativeproj15
-rw-r--r--src/md/enc/dac/.gitmirror1
-rw-r--r--src/md/enc/dac/CMakeLists.txt5
-rw-r--r--src/md/enc/dac/dirs.proj19
-rw-r--r--src/md/enc/dbi/.gitmirror1
-rw-r--r--src/md/enc/dbi/CMakeLists.txt2
-rw-r--r--src/md/enc/dbi/MDRuntimeRW-dbi.props10
-rw-r--r--src/md/enc/dbi/dirs.proj19
-rw-r--r--src/md/enc/dirs.proj23
-rw-r--r--src/md/enc/enc.settings.targets45
-rw-r--r--src/md/enc/imptlb.cpp8058
-rw-r--r--src/md/enc/liteweightstgdbrw.cpp1273
-rw-r--r--src/md/enc/mdinternalrw.cpp4356
-rw-r--r--src/md/enc/metamodelenc.cpp468
-rw-r--r--src/md/enc/metamodelrw.cpp8894
-rw-r--r--src/md/enc/peparse.cpp149
-rw-r--r--src/md/enc/rwutil.cpp1413
-rw-r--r--src/md/enc/stdafx.cpp13
-rw-r--r--src/md/enc/stdafx.h27
-rw-r--r--src/md/enc/stgio.cpp1443
-rw-r--r--src/md/enc/stgtiggerstorage.cpp1026
-rw-r--r--src/md/enc/stgtiggerstream.cpp138
-rw-r--r--src/md/enc/wks/.gitmirror1
-rw-r--r--src/md/enc/wks/CMakeLists.txt2
-rw-r--r--src/md/enc/wks/MDRuntimeRW.nativeproj19
28 files changed, 27447 insertions, 0 deletions
diff --git a/src/md/enc/.gitmirror b/src/md/enc/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/enc/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/md/enc/CMakeLists.txt b/src/md/enc/CMakeLists.txt
new file mode 100644
index 0000000000..0c34b7bbc5
--- /dev/null
+++ b/src/md/enc/CMakeLists.txt
@@ -0,0 +1,25 @@
+add_definitions(-DNO_CRT)
+
+set(MDRUNTIMERW_SOURCES
+ liteweightstgdbrw.cpp
+ metamodelenc.cpp
+ metamodelrw.cpp
+ peparse.cpp
+ rwutil.cpp
+ stgio.cpp
+ stgtiggerstorage.cpp
+ stgtiggerstream.cpp
+ mdinternalrw.cpp
+)
+
+convert_to_absolute_path(MDRUNTIMERW_SOURCES ${MDRUNTIMERW_SOURCES})
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+add_subdirectory(dac)
+add_subdirectory(wks)
+if(WIN32)
+ add_subdirectory(dbi)
+endif(WIN32) \ No newline at end of file
diff --git a/src/md/enc/crossgen/.gitmirror b/src/md/enc/crossgen/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/enc/crossgen/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/md/enc/crossgen/MDRuntimeRW_crossgen.nativeproj b/src/md/enc/crossgen/MDRuntimeRW_crossgen.nativeproj
new file mode 100644
index 0000000000..d47053fac6
--- /dev/null
+++ b/src/md/enc/crossgen/MDRuntimeRW_crossgen.nativeproj
@@ -0,0 +1,15 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <PropertyGroup>
+ <!-- All features are set in file:..\..\MD.props -->
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <MetadataFlavor>wks</MetadataFlavor>
+ <OutputName>mdruntimerw_crossgen</OutputName>
+ </PropertyGroup>
+
+ <!--Leaf project Properties-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\enc\enc.settings.targets" />
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/enc/dac/.gitmirror b/src/md/enc/dac/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/enc/dac/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/md/enc/dac/CMakeLists.txt b/src/md/enc/dac/CMakeLists.txt
new file mode 100644
index 0000000000..b651fc28ac
--- /dev/null
+++ b/src/md/enc/dac/CMakeLists.txt
@@ -0,0 +1,5 @@
+
+include(${CLR_DIR}/dac.cmake)
+include(../../md_dac.cmake)
+
+add_library(mdruntimerw_dac ${MDRUNTIMERW_SOURCES})
diff --git a/src/md/enc/dac/dirs.proj b/src/md/enc/dac/dirs.proj
new file mode 100644
index 0000000000..eb38212025
--- /dev/null
+++ b/src/md/enc/dac/dirs.proj
@@ -0,0 +1,19 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="HostLocal\mdruntimerw_dac.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/enc/dbi/.gitmirror b/src/md/enc/dbi/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/enc/dbi/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/md/enc/dbi/CMakeLists.txt b/src/md/enc/dbi/CMakeLists.txt
new file mode 100644
index 0000000000..7898bfafdd
--- /dev/null
+++ b/src/md/enc/dbi/CMakeLists.txt
@@ -0,0 +1,2 @@
+include(../../md_dbi.cmake)
+add_library(mdruntimerw-dbi ${MDRUNTIMERW_SOURCES}) \ No newline at end of file
diff --git a/src/md/enc/dbi/MDRuntimeRW-dbi.props b/src/md/enc/dbi/MDRuntimeRW-dbi.props
new file mode 100644
index 0000000000..eb5a7d44b9
--- /dev/null
+++ b/src/md/enc/dbi/MDRuntimeRW-dbi.props
@@ -0,0 +1,10 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <PropertyGroup>
+ <!-- All features are set in file:..\..\MD.props -->
+ <MetadataFlavor>mscordbi</MetadataFlavor>
+ </PropertyGroup>
+
+ <!--Leaf project Properties-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\enc\enc.settings.targets" />
+
+</Project>
diff --git a/src/md/enc/dbi/dirs.proj b/src/md/enc/dbi/dirs.proj
new file mode 100644
index 0000000000..6a153f09a0
--- /dev/null
+++ b/src/md/enc/dbi/dirs.proj
@@ -0,0 +1,19 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="HostLocal\mdruntimerw-dbi.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/enc/dirs.proj b/src/md/enc/dirs.proj
new file mode 100644
index 0000000000..32ac18cb68
--- /dev/null
+++ b/src/md/enc/dirs.proj
@@ -0,0 +1,23 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="wks\mdruntimerw.nativeproj" />
+ <ProjectFile Include="dbi\dirs.proj" />
+ <ProjectFile Include="dac\dirs.proj" />
+ <ProjectFile Include="winrt-ro\mdruntimerw-winrt-ro.nativeproj" />
+ <ProjectFile Include="winrt-rw\mdruntimerw-winrt-rw.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/enc/enc.settings.targets b/src/md/enc/enc.settings.targets
new file mode 100644
index 0000000000..dcbe410196
--- /dev/null
+++ b/src/md/enc/enc.settings.targets
@@ -0,0 +1,45 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\MD.props" />
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <MDEncSrcDirectory>$(ClrSrcDirectory)\MD\enc\</MDEncSrcDirectory>
+ <UserIncludes>$(UserIncludes);
+ $(ClrSrcDirectory)\MD\inc;
+ $(ClrSrcDirectory)\vm;
+ $(ClrSrcDirectory)\strongname\inc
+ </UserIncludes>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE -DNO_CRT</ClAdditionalOptions>
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <PCHHeader>stdafx.h</PCHHeader>
+ <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders>
+ <!--PCH: Both precompiled header and cpp are on the same ..\ path this is likely to be wrong.-->
+ <PCHCompile>$(MDEncSrcDirectory)\stdafx.cpp</PCHCompile>
+ <PCHObject>stdafx_mdruntimerw.obj</PCHObject>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="$(ClrSrcDirectory)inc\corguids.nativeproj">
+ <Comment>clrinternal.h</Comment>
+ </ProjectReference>
+ </ItemGroup>
+
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <CppCompile Include="$(MDEncSrcDirectory)\LiteWeightStgdbRW.cpp" />
+ <CppCompile Include="$(MDEncSrcDirectory)\MetaModelENC.cpp" />
+ <CppCompile Include="$(MDEncSrcDirectory)\MetaModelRW.cpp" />
+ <CppCompile Include="$(MDEncSrcDirectory)\peparse.cpp" />
+ <CppCompile Include="$(MDEncSrcDirectory)\RWUtil.cpp" />
+ <CppCompile Include="$(MDEncSrcDirectory)\StgIO.cpp" />
+ <CppCompile Include="$(MDEncSrcDirectory)\StgTiggerStorage.cpp" />
+ <CppCompile Include="$(MDEncSrcDirectory)\StgTiggerStream.cpp" />
+
+ <CppCompile Include="$(MDEncSrcDirectory)\ImpTlb.cpp" Condition="'$(FeatureCominteropTlbSupport)' == 'true'" />
+
+ <!-- Content is under #ifdef FEATURE_METADATA_INTERNAL_APIS -->
+ <CppCompile Include="$(MDEncSrcDirectory)\MDInternalRW.cpp" />
+ </ItemGroup>
+</Project>
diff --git a/src/md/enc/imptlb.cpp b/src/md/enc/imptlb.cpp
new file mode 100644
index 0000000000..62eba2c523
--- /dev/null
+++ b/src/md/enc/imptlb.cpp
@@ -0,0 +1,8058 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ===========================================================================
+// File: ImpTlb.CPP
+//
+
+//
+
+// ---------------------------------------------------------------
+// Who When What
+// ---------------------------------------------------------------
+// WGE 970906 Created
+//
+// ===========================================================================
+#include "stdafx.h"
+
+#include "imptlb.h"
+#include <posterror.h>
+#include <strongname.h>
+#include <nsutilpriv.h>
+
+#include "..\compiler\regmeta.h"
+#include "..\compiler\importhelper.h"
+#include "tlbutils.h" // For GenerateMangledTypeName().
+#include <tlbimpexp.h>
+#include "sstring.h"
+#include "strsafe.h"
+
+#include <metahost.h>
+
+// Pointer to the activated CLR interface provided by the shim.
+extern ICLRRuntimeInfo *g_pCLRRuntime;
+
+#ifdef wcsncmp
+ #undef wcsncmp
+#endif
+#ifdef wcsncpy
+ #undef wcsncpy
+#endif
+
+// deprecated: use the secureCrt replacements
+// _CRTIMP int __cdecl wcsncmp(const wchar_t *, const wchar_t *, size_t);
+// _CRTIMP wchar_t * __cdecl wcsncpy(wchar_t *, const wchar_t *, size_t);
+
+#define S_CONVERSION_LOSS _HRESULT_TYPEDEF_(3) // Non-error code meaning a conversion lost information.
+
+#define ADD_ITF_MEMBERS_TO_CLASS // Define to add interface members to the CoClass.
+#define ITF_MEMBER_RESOLUTION_NAMEONLY // Define to ignore signatures when looking for collisions (ie, when defined
+ // void Foo(int) and void Foo(String) collide).
+
+// defines controlling ctor of non-creatable objects.
+#define NONCREATABLE_CTOR_VISIBILITY mdAssem // Define to a visibility flag.
+
+#define MAX_CLASSNAME_SIZE 1024
+
+#ifndef lengthof
+#define lengthof(rg) (sizeof(rg)/sizeof(rg[0]))
+#endif
+
+#ifndef IfNullGo
+#define IfNullGo(x) do {if (!(x)) IfFailGo(E_OUTOFMEMORY);} while (0)
+#endif
+
+#define BUILD_CUSTOM_ATTRIBUTE(type,bytes) {*reinterpret_cast<UNALIGNED type*>(__pca) = bytes; __pca += sizeof(type); _ASSERTE(__pca-__ca <= sizeof(__ca));}
+#define INIT_CUSTOM_ATTRIBUTE(n) {_ASSERTE((n) <= (sizeof(__ca)-sizeof(SHORT)));__pca = __ca; BUILD_CUSTOM_ATTRIBUTE(USHORT,1);}
+#define SIZEOF_CUSTOM_ATTRIBUTE() ((ULONG) (__pca - __ca))
+#define PTROF_CUSTOM_ATTRIBUTE() (&__ca[0])
+#define DECLARE_CUSTOM_ATTRIBUTE(n) BYTE __ca[(n)+sizeof(SHORT)*2], *__pca;__pca=__ca; INIT_CUSTOM_ATTRIBUTE(n);
+#define APPEND_STRING_TO_CUSTOM_ATTRIBUTE(str) {int l = (int)strlen(str); __pca=(BYTE*)CPackedLen::PutLength(__pca,l);memcpy(__pca,str,l);__pca+=l;}
+#define FINISH_CUSTOM_ATTRIBUTE() {BUILD_CUSTOM_ATTRIBUTE(short,0);}
+
+#define DECLARE_DYNLEN_CUSTOM_ATTRIBUTE(n) CQuickArray<BYTE> __tmpCAArray; IfFailGo(__tmpCAArray.ReSizeNoThrow(n + sizeof(SHORT)*2)); BYTE *__ca, *__pca; __ca = __tmpCAArray.Ptr(); __pca=__ca; BUILD_CUSTOM_ATTRIBUTE(USHORT,1);
+#define BUILD_DYNLEN_CUSTOM_ATTRIBUTE(type,bytes) {*reinterpret_cast<UNALIGNED type*>(__pca) = bytes; __pca += sizeof(type); _ASSERTE(__pca-__ca <= (int)__tmpCAArray.Size());}
+#define FINISH_DYNLEN_CUSTOM_ATTRIBUTE() {BUILD_DYNLEN_CUSTOM_ATTRIBUTE(short,0);}
+
+#define APPEND_WIDE_STRING_TO_CUSTOM_ATTRIBUTE(str) \
+{ \
+ CQuickArray<char> __tmpStr; \
+ int __cStr = WszWideCharToMultiByte(CP_ACP, 0, str, -1, 0, 0, NULL, NULL); \
+ IfFailGo(__tmpStr.ReSizeNoThrow(__cStr)); \
+ __cStr = WszWideCharToMultiByte(CP_ACP, 0, str, -1, __tmpStr.Ptr(), __cStr, NULL, NULL); \
+ __pca=(BYTE*)CPackedLen::PutLength(__pca,__cStr); \
+ memcpy(__pca,__tmpStr.Ptr(),__cStr); \
+ __pca+=__cStr; \
+}
+
+// The maximum number of bytes the encoding of a DWORD can take.
+#define DWORD_MAX_CB 4
+
+// The maximum number of bytes the encoding of a DWORD can take.
+#define STRING_OVERHEAD_MAX_CB 4
+
+// Use the unused variant types m_knowntypes for common types.
+#define VT_SLOT_FOR_GUID VT_EMPTY
+#define VT_SLOT_FOR_IENUMERABLE VT_NULL
+#define VT_SLOT_FOR_MULTICASTDEL VT_I2
+#define VT_SLOT_FOR_TYPE VT_I4
+#define VT_SLOT_FOR_STRINGBUF VT_I8
+
+static LPCWSTR szObject = W("System.Object");
+static LPCWSTR szValueType = W("System.ValueType");
+static LPCWSTR szEnum = W("System.Enum");
+
+static LPCWSTR TLB_CLASSLIB_ARRAY = {W("System.Array")};
+static LPCWSTR TLB_CLASSLIB_DATE = {W("System.DateTime")};
+static LPCWSTR TLB_CLASSLIB_DECIMAL = {W("System.Decimal")};
+static LPCWSTR TLB_CLASSLIB_VARIANT = {W("System.Variant")};
+static LPCWSTR TLB_CLASSLIB_GUID = {W("System.Guid")};
+static LPCWSTR TLB_CLASSLIB_IENUMERABLE = {W("System.Collections.IEnumerable")};
+static LPCWSTR TLB_CLASSLIB_MULTICASTDELEGATE = {W("System.MulticastDelegate")};
+static LPCWSTR TLB_CLASSLIB_TYPE = {W("System.Type")};
+static LPCWSTR TLB_CLASSLIB_STRINGBUFFER = {W("System.Text.StringBuilder")};
+
+static LPCWSTR COM_STDOLE2 = {W("StdOle")};
+static LPCWSTR COM_GUID = {W("GUID")};
+
+static const LPCWSTR PROP_DECORATION_GET = {W("get_")};
+static const LPCWSTR PROP_DECORATION_SET = {W("set_")};
+static const LPCWSTR PROP_DECORATION_LET = {W("let_")};
+static const int PROP_DECORATION_LEN = 4;
+
+static const LPCWSTR DLL_EXTENSION = {W(".dll")};
+static const int DLL_EXTENSION_LEN = 4;
+static const LPCWSTR EXE_EXTENSION = {W(".exe")};
+static const int EXE_EXTENSION_LEN = 4;
+
+static LPCWSTR const OBJECT_INITIALIZER_NAME = {W(".ctor")};
+static const int OBJECT_INITIALIZER_FLAGS = mdPublic | mdSpecialName;
+static const int OBJECT_INITIALIZER_IMPL_FLAGS = miNative | miRuntime | miInternalCall;
+static const int NONCREATABLE_OBJECT_INITIALIZER_FLAGS = NONCREATABLE_CTOR_VISIBILITY | mdSpecialName;
+
+static const COR_SIGNATURE OBJECT_INITIALIZER_SIG[3] = {
+ (IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS), 0, ELEMENT_TYPE_VOID };
+
+static const int DEFAULT_INTERFACE_FUNC_FLAGS = mdPublic | mdVirtual | mdAbstract | mdHideBySig | mdNewSlot;
+static const int DEFAULT_PROPERTY_FUNC_FLAGS = mdPublic | mdVirtual | mdAbstract | mdHideBySig | mdSpecialName | mdNewSlot;
+static const int DEFAULT_CONST_FIELD_FLAGS = fdPublic | fdStatic | fdLiteral;
+static const int DEFAULT_RECORD_FIELD_FLAGS = fdPublic;
+static const int DELEGATE_INVOKE_FUNC_FLAGS = mdPublic | mdVirtual;
+
+static const int DEFAULT_ITF_FUNC_IMPL_FLAGS = miNative | miRuntime | miInternalCall;
+
+static const WCHAR VTBL_GAP_FUNCTION[] = {W("_VtblGap")};
+static const int VTBL_GAP_FUNCTION_FLAGS = mdPublic | mdSpecialName;
+static const int VTBL_GAP_FUNC_IMPL_FLAGS = miRuntime;
+static const COR_SIGNATURE VTBL_GAP_SIGNATURE[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT, 0, ELEMENT_TYPE_VOID};
+static const LPCWSTR VTBL_GAP_FORMAT_1 = {W("%ls%d")};
+static const LPCWSTR VTBL_GAP_FORMAT_N = {W("%ls%d_%d")};
+
+static const LPCWSTR ENUM_TYPE_NAME = {COR_ENUM_FIELD_NAME_W};
+static const DWORD ENUM_TYPE_FLAGS = fdPublic;
+static const COR_SIGNATURE ENUM_TYPE_SIGNATURE[] = {IMAGE_CEE_CS_CALLCONV_FIELD, ELEMENT_TYPE_I4};
+static const DWORD ENUM_TYPE_SIGNATURE_SIZE = lengthof(ENUM_TYPE_SIGNATURE);
+
+static const LPCWSTR DYNAMIC_NAMESPACE_NAME = {W("DynamicModule")};
+
+static const LPCWSTR UNSAFE_ITF_PREFIX = {W("Unsafe.")};
+
+static const LPCWSTR GET_ENUMERATOR_MEMBER_NAME = {W("GetEnumerator")};
+
+static const WCHAR CLASS_SUFFIX[] = {W("Class")};
+static const DWORD CLASS_SUFFIX_LENGTH = lengthof(CLASS_SUFFIX);
+static const WCHAR EVENT_ITF_SUFFIX[] = {W("_Event")};
+static const DWORD EVENT_ITF_SUFFIX_LENGTH = lengthof(EVENT_ITF_SUFFIX);
+static const WCHAR EVENT_PROVIDER_SUFFIX[] = {W("_EventProvider")};
+static const DWORD EVENT_PROVIDER_SUFFIX_LENGTH = lengthof(EVENT_ITF_SUFFIX);
+static const WCHAR EVENT_HANDLER_SUFFIX[] = {W("EventHandler")};
+static const DWORD EVENT_HANDLER_SUFFIX_LENGTH = lengthof(EVENT_HANDLER_SUFFIX);
+
+static const WCHAR EVENT_ADD_METH_PREFIX[] = {W("add_")};
+static const DWORD EVENT_ADD_METH_PREFIX_LENGTH = lengthof(EVENT_ADD_METH_PREFIX);
+static const WCHAR EVENT_REM_METH_PREFIX[] = {W("remove_")};
+static const DWORD EVENT_REM_METH_PREFIX_LENGTH = lengthof(EVENT_REM_METH_PREFIX);
+
+static const WCHAR DELEGATE_INVOKE_METH_NAME[] = {W("Invoke")};
+static const DWORD DELEGATE_INVOKE_METH_NAME_LENGTH = lengthof(EVENT_ADD_METH_PREFIX);
+
+// {C013B386-CC3E-4b6d-9B67-A3AE97274BBE}
+static const GUID FREE_STATUS_GUID =
+{ 0xc013b386, 0xcc3e, 0x4b6d, { 0x9b, 0x67, 0xa3, 0xae, 0x97, 0x27, 0x4b, 0xbe } };
+
+// {C013B387-CC3E-4b6d-9B67-A3AE97274BBE}
+static const GUID DELETED_STATUS_GUID =
+{ 0xc013b387, 0xcc3e, 0x4b6d, { 0x9b, 0x67, 0xa3, 0xae, 0x97, 0x27, 0x4b, 0xbe } };
+
+// {C013B388-CC3E-4b6d-9B67-A3AE97274BBE}
+static const GUID USED_STATUS_GUID =
+{ 0xc013b388, 0xcc3e, 0x4b6d, { 0x9b, 0x67, 0xa3, 0xae, 0x97, 0x27, 0x4b, 0xbe } };
+
+static const GUID IID_IEnumerable =
+{ 0x496b0abe, 0xcdee, 0x11d3, { 0x88, 0xe8, 0x00, 0x90, 0x27, 0x54, 0xc4, 0x3a } };
+
+
+ #define STRUCTLAYOUT tdSequentialLayout
+// ULONG_MAX is a flag meaning "don't convert".
+static const ULONG rdwTypeFlags[] = {
+ tdPublic | tdSealed, // TKIND_ENUM = 0,
+ tdPublic | tdSealed | tdBeforeFieldInit | STRUCTLAYOUT, // TKIND_RECORD = TKIND_ENUM + 1,
+ tdPublic | tdAbstract, // TKIND_MODULE = TKIND_RECORD + 1,
+ tdPublic | tdInterface | tdAbstract | tdImport, // TKIND_INTERFACE = TKIND_MODULE + 1,
+ tdPublic | tdInterface | tdAbstract | tdImport, // TKIND_DISPATCH = TKIND_INTERFACE + 1,
+ tdPublic | tdImport, // TKIND_COCLASS = TKIND_DISPATCH + 1,
+ tdPublic | tdImport, // TKIND_ALIAS = TKIND_COCLASS + 1,
+ tdPublic | tdSealed | tdExplicitLayout, // TKIND_UNION = TKIND_ALIAS + 1,
+ ULONG_MAX, // TKIND_MAX = TKIND_UNION + 1
+};
+static const LPCWSTR g_szTypekind[] = {
+ W("Enum "),
+ W("Record "),
+ W("Module "),
+ W("Interface "),
+ W("Dispinterface"),
+ W("Coclass "),
+ W("Alias "),
+ W("Union "),
+};
+
+#define NATIVE_TYPE_NONE ((CorNativeType)(NATIVE_TYPE_MAX+1))
+
+#define NON_CONVERTED_PARAMS_FLAGS (PARAMFLAG_FRETVAL|PARAMFLAG_FLCID)
+
+
+//*****************************************************************************
+// External declarations.
+//*****************************************************************************
+extern mdAssemblyRef DefineAssemblyRefForImportedTypeLib(
+ void *pAssembly, // Assembly importing the typelib.
+ void *pvModule, // Module importing the typelib.
+ IUnknown *pIMeta, // IMetaData* from import module.
+ IUnknown *pIUnk, // IUnknown to referenced Assembly.
+ BSTR *pwzNamespace, // The namespace of the resolved assembly.
+ BSTR *pwzAsmName, // The name of the resolved assembly.
+ Assembly **AssemblyRef); // The resolved assembly.
+
+extern mdAssemblyRef DefineAssemblyRefForExportedAssembly(
+ LPCWSTR szFullName, // Assembly full name.
+ IUnknown *pIMeta); // Metadata emit interface.
+
+static HRESULT _UnpackVariantToConstantBlob(VARIANT *pvar, BYTE *pcvType, void **pvValue, __int64 *pd);
+static INT64 _DoubleDateToTicks(const double d);
+static HRESULT TryGetFuncDesc(ITypeInfo *pITI, int i, FUNCDESC **ppFunc);
+
+//*****************************************************************************
+// Class factory.
+//*****************************************************************************
+CImportTlb* CImportTlb::CreateImporter(
+ LPCWSTR szLibrary,
+ ITypeLib *pitlb,
+ BOOL bGenerateTCEAdapters,
+ BOOL bUnsafeInterfaces,
+ BOOL bSafeArrayAsSystemArray,
+ BOOL bTransformDispRetVals,
+ BOOL bPreventClassMembers,
+ BOOL bSerializableValueClasses)
+{
+ return new (nothrow) CImportTlb(szLibrary, pitlb, bGenerateTCEAdapters, bUnsafeInterfaces, bSafeArrayAsSystemArray, bTransformDispRetVals, bPreventClassMembers, bSerializableValueClasses);
+} // CImportTlb* CImportTlb::CreateImporter()
+
+//*****************************************************************************
+// Default constructor.
+//*****************************************************************************
+CImportTlb::CImportTlb()
+ : m_szLibrary(NULL),
+ m_pITLB(NULL),
+ m_bGenerateTCEAdapters(false),
+ m_bSafeArrayAsSystemArray(false),
+ m_bTransformDispRetVals(false),
+ m_bPreventClassMembers(false),
+ m_bSerializableValueClasses(false),
+ m_pEmit(NULL),
+ m_pImport(NULL),
+ m_pITI(NULL),
+ m_pOrigITI(NULL),
+ m_psAttr(NULL),
+ m_arSystem(mdAssemblyRefNil),
+ m_Notify(NULL),
+ m_trValueType(0),
+ m_trEnum(0),
+ m_bUnsafeInterfaces(FALSE),
+ m_tkSuppressCheckAttr(mdTokenNil),
+ m_tdHasDefault(0),
+ m_szName(NULL),
+ m_szMember(NULL),
+ m_wzNamespace(NULL),
+ m_tkInterface(0),
+ m_szInterface(NULL),
+ m_pMemberNames(NULL),
+ m_cMemberProps(0),
+ m_ImplIface(eImplIfaceNone)
+{
+ // Clear the known types array. The values will be lazily initialized.
+ memset(m_tkKnownTypes, 0, sizeof(m_tkKnownTypes));
+ memset(m_tkAttr, 0, sizeof(m_tkAttr));
+} // CImportTlb::CImportTlb()
+
+//*****************************************************************************
+// Complex constructor.
+//*****************************************************************************
+CImportTlb::CImportTlb(
+ LPCWSTR szLibrary, // Name of library being imported.
+ ITypeLib *pitlb, // The type library to import from.
+ BOOL bGenerateTCEAdapters, // A flag indicating if the TCE adapters are being generated.
+ BOOL bUnsafeInterfaces, // A flag indicating that runtime security checks should be disabled
+ BOOL bSafeArrayAsSystemArray,// A flag indicating whether to import SAFEARRAY's as System.Array's.
+ BOOL bTransformDispRetVals, // A flag indicating if we should do [out,retval] transformation on disp only itfs.
+ BOOL bPreventClassMembers, // A flag indicating if we should add members to CoClasses.
+ BOOL bSerializableValueClasses) // A flag indicating if we should mark value classes serializable.
+ : m_szLibrary(szLibrary),
+ m_pITLB(pitlb),
+ m_bGenerateTCEAdapters(bGenerateTCEAdapters),
+ m_bUnsafeInterfaces(bUnsafeInterfaces),
+ m_bSafeArrayAsSystemArray(bSafeArrayAsSystemArray),
+ m_bTransformDispRetVals(bTransformDispRetVals),
+ m_bPreventClassMembers(bPreventClassMembers),
+ m_bSerializableValueClasses(bSerializableValueClasses),
+ m_pEmit(0),
+ m_pImport(0),
+ m_pITI(0),
+ m_pOrigITI(0),
+ m_psAttr(0),
+ m_arSystem(mdAssemblyRefNil),
+ m_Notify(0),
+ m_trValueType(0),
+ m_trEnum(0),
+ m_tkSuppressCheckAttr(mdTokenNil),
+ m_tdHasDefault(0),
+ m_szName(0),
+ m_szMember(0),
+ m_wzNamespace(0),
+ m_tkInterface(0),
+ m_szInterface(0),
+ m_pMemberNames(0),
+ m_cMemberProps(0),
+ m_ImplIface(eImplIfaceNone)
+{
+ if (pitlb)
+ pitlb->AddRef();
+
+ // Clear the known types array. The values will be lazily initialized.
+ memset(m_tkKnownTypes, 0, sizeof(m_tkKnownTypes));
+ memset(m_tkAttr, 0, sizeof(m_tkAttr));
+
+#if defined(TLB_STATS)
+ m_bStats = QueryPerformanceFrequency(&m_freqVal);
+#endif
+} // CImportTlb::CImportTlb()
+
+//*****************************************************************************
+// Destructor.
+//*****************************************************************************
+CImportTlb::~CImportTlb()
+{
+ if (m_pEmit)
+ m_pEmit->Release();
+ if (m_pImport)
+ m_pImport->Release();
+ if (m_pITLB)
+ m_pITLB->Release();
+ if (m_Notify)
+ m_Notify->Release();
+
+ if (m_wzNamespace)
+ ::SysFreeString(m_wzNamespace);
+} // CImportTlb::~CImportTlb()
+
+
+//*****************************************************************************
+// Allow the user to specify a namespace to be used in the conversion.
+//*****************************************************************************
+HRESULT CImportTlb::SetNamespace(
+ WCHAR const *pNamespace)
+{
+ HRESULT hr=S_OK; // A result.
+
+ IfNullGo(m_wzNamespace=::SysAllocString(pNamespace));
+
+ErrExit:
+
+ return hr;
+} // HRESULT CImportTlb::SetNamespace()
+
+//*****************************************************************************
+// Allow the user to specify a notification object to be used in the conversion.
+//*****************************************************************************
+HRESULT CImportTlb::SetNotification(
+ ITypeLibImporterNotifySink *pNotify)
+{
+ _ASSERTE(m_Notify == 0);
+ m_Notify = pNotify;
+ pNotify->AddRef();
+
+ return S_OK;
+} // HRESULT CImportTlb::SetNotification()
+
+//*****************************************************************************
+// Allow the user to specify the MetaData scope to be used in the conversion.
+//*****************************************************************************
+HRESULT CImportTlb::SetMetaData(
+ IUnknown *pIUnk)
+{
+ HRESULT hr;
+ _ASSERTE(m_pEmit == 0);
+ IfFailGo(pIUnk->QueryInterface(IID_IMetaDataEmit2, (void**)&m_pEmit));
+ErrExit:
+ return hr;
+} // HRESULT CImportTlb::SetMetaData()
+
+//*****************************************************************************
+// Import a TypeLibrary into a CompLib.
+//*****************************************************************************
+HRESULT CImportTlb::Import()
+{
+#ifndef DACCESS_COMPILE
+ HRESULT hr; // A result.
+ mdModule md; // Module token.
+ VARIANT vt = {0}; // For setting options.
+ ITypeLib2 *pITLB2 = 0; // To get custom attributes.
+ IMetaDataDispenserEx *pDisp = 0; // To create export scope.
+ TLIBATTR *psAttr=0; // The library's attributes.
+ BSTR szLibraryName = 0; // The library's name.
+ LPCWSTR wzFile; // The filename of the typelib (no path).
+ LPCWSTR wzSource; // Source of the typelib, for CA.
+
+ _ASSERTE(m_Notify);
+
+ // Quick sanity check.
+ if (!m_pITLB)
+ return (E_INVALIDARG);
+
+ // Check to see if the type library implements ITypeLib2.
+ if (m_pITLB->QueryInterface(IID_ITypeLib2, (void **)&pITLB2) != S_OK)
+ pITLB2 = 0;
+
+ // If custom attribute for namespace exists, use it.
+ if (pITLB2)
+ {
+ VARIANT vt;
+ VariantInit(&vt);
+ if (pITLB2->GetCustData(GUID_ManagedName, &vt) == S_OK)
+ {
+ if (V_VT(&vt) == VT_BSTR)
+ {
+ // If there already was a namespace set, release it.
+ if (m_wzNamespace)
+ SysFreeString(m_wzNamespace);
+
+ // If the namespace ends with .dll then remove the extension.
+ LPWSTR pDest = wcsstr(vt.bstrVal, DLL_EXTENSION);
+ if (pDest && (pDest[DLL_EXTENSION_LEN] == 0 || pDest[DLL_EXTENSION_LEN] == ' '))
+ *pDest = 0;
+
+ if (!pDest)
+ {
+ // If the namespace ends with .exe then remove the extension.
+ pDest = wcsstr(vt.bstrVal, EXE_EXTENSION);
+ if (pDest && (pDest[EXE_EXTENSION_LEN] == 0 || pDest[EXE_EXTENSION_LEN] == ' '))
+ *pDest = 0;
+ }
+
+ if (pDest)
+ {
+ // We removed the extension so re-allocate a string of the new length.
+ m_wzNamespace = SysAllocString(vt.bstrVal);
+ SysFreeString(vt.bstrVal);
+ IfNullGo(m_wzNamespace);
+ }
+ else
+ {
+ // There was no extension to remove so we can use the string returned
+ // by GetCustData().
+ m_wzNamespace = vt.bstrVal;
+ }
+ }
+ else
+ {
+ VariantClear(&vt);
+ }
+ }
+ }
+
+ // Use the namespace name if we don't know the filename.
+ if (!m_szLibrary)
+ m_szLibrary = m_wzNamespace;
+
+ // If the typelib was exported from COM+ to begin with, don't import it.
+ if (pITLB2)
+ {
+ ::VariantInit(&vt);
+ hr = pITLB2->GetCustData(GUID_ExportedFromComPlus, &vt);
+ if (vt.vt != VT_EMPTY)
+ {
+ if (0)
+ {
+ // com emulates option is ON
+ }
+ else
+ {
+ IfFailGo(PostError(TLBX_E_CIRCULAR_IMPORT, m_szLibrary));
+ }
+ }
+ }
+
+ _ASSERTE(m_pEmit);
+ IfFailGo(m_pEmit->QueryInterface(IID_IMetaDataImport2, (void **)&m_pImport));
+
+ // Initialize the reserved names map.
+ IfFailGo(m_ReservedNames.Init());
+
+ // Initialize the default interface to class interface map for the TLB being imported.
+ IfFailGo(m_DefItfToClassItfMap.Init(m_pITLB, m_wzNamespace));
+
+ // Create the Object classref record and AssemblyRef for mscorlib.dll.
+ IfFailGo(_DefineSysRefs());
+
+ // Create the library record.
+ IfFailGo(_NewLibraryObject());
+
+ // Note that this was imported.
+ IfFailGo(m_pITLB->GetLibAttr(&psAttr));
+ if (SUCCEEDED(::QueryPathOfRegTypeLib(psAttr->guid, psAttr->wMajorVerNum, psAttr->wMinorVerNum, psAttr->lcid, &szLibraryName)))
+ wzSource = szLibraryName;
+ else
+ wzSource = m_szLibrary;
+
+ // We can't base the decision on SYSKIND. For example, we can have a SYS_WIN64 tlb loaded as 32-bit with 4-byte aligned pointers.
+ m_cbVtableSlot = 0;
+
+ IfFailGo(m_pImport->GetModuleFromScope(&md));
+ // Skip the path or drive info
+ wzFile = wcsrchr(wzSource, W('\\'));
+ if (wzFile == 0)
+ { // That's odd, should have been a fully qualified path. Just use an empty string.
+ wzFile = W("");
+ }
+ else
+ { // skip leading backslash
+ wzFile++;
+ }
+
+ // Convert the typelib.
+ IfFailGo(ConvertTypeLib());
+
+ErrExit:
+ if (psAttr)
+ m_pITLB->ReleaseTLibAttr(psAttr);
+ if (szLibraryName)
+ ::SysFreeString(szLibraryName);
+ if (pITLB2)
+ pITLB2->Release();
+ if (pDisp)
+ pDisp->Release();
+
+ return (hr);
+#else
+ DacNotImpl();
+ return E_NOTIMPL;
+#endif // #ifndef DACCESS_COMPILE
+} // HRESULT CImportTlb::Import()
+
+//*****************************************************************************
+// Create the Complib to represent the TypeLib.
+//*****************************************************************************
+HRESULT CImportTlb::_NewLibraryObject()
+{
+ HRESULT hr; // A result.
+ TLIBATTR * psAttr=0; // The library's attributes.
+ BSTR szLibraryName=0; // The library's name.
+ CQuickArray<WCHAR> rScopeName; // The name of the scope.
+
+ // Information about the library.
+ IfFailGo(m_pITLB->GetLibAttr(&psAttr));
+ IfFailGo(m_pITLB->GetDocumentation(MEMBERID_NIL, &szLibraryName, 0, 0, 0));
+
+ // Create the scope name by using the typelib name and adding .dll.
+ IfFailGo(rScopeName.ReSizeNoThrow(SysStringLen(szLibraryName) + 5 * sizeof(WCHAR)));
+ StringCchPrintf(rScopeName.Ptr(), rScopeName.Size(), W("%s.dll"), szLibraryName);
+
+ IfFailGo(m_pEmit->SetModuleProps(rScopeName.Ptr()));
+
+ErrExit:
+ if (psAttr)
+ m_pITLB->ReleaseTLibAttr(psAttr);
+
+ if (szLibraryName)
+ ::SysFreeString(szLibraryName);
+
+ return (hr);
+} // HRESULT CImportTlb::_NewLibraryObject()
+
+//*****************************************************************************
+// Define an assembly ref for mscorlib, typeref for Object.
+//*****************************************************************************
+HRESULT CImportTlb::_DefineSysRefs()
+{
+ HRESULT hr; // A result.
+ WCHAR szPath[_MAX_PATH];
+ WCHAR szDrive[_MAX_DRIVE];
+ WCHAR szDir[_MAX_PATH];
+ DWORD dwLen; // Length of system directory name.
+ IMetaDataDispenserEx *pDisp = 0; // To import mscorlib.
+ IMetaDataAssemblyImport *pAImp = 0; // To read mscorlib assembly.
+ IMetaDataAssemblyEmit *pAEmit = 0; // To create mscorlib assembly ref.
+ ASSEMBLYMETADATA amd = {0}; // Assembly metadata.
+ mdToken tk; // A token.
+ const void *pvPublicKey; // Public key.
+ ULONG cbPublicKey; // Length of public key.
+ BYTE *pbToken=0; // Compressed token for public key.
+ ULONG cbToken; // Length of token.
+ ULONG ulHashAlg; // Hash algorithm.
+ DWORD dwFlags; // Assembly flags.
+
+ // Get the dispenser.
+ IfFailGo(g_pCLRRuntime->GetInterface(
+ CLSID_CorMetaDataDispenser,
+ IID_IMetaDataDispenserEx,
+ (void **)&pDisp));
+
+ // Get the name of mscorlib.
+ //@todo: define, function, etc., instead of hard coded "mscorlib"
+ dwLen = lengthof(szPath) - 13; // allow space for "mscorlib" ".dll" "\0"
+ IfFailGo(pDisp->GetCORSystemDirectory(szPath, dwLen, &dwLen));
+ SplitPath(szPath, szDrive, _MAX_DRIVE, szDir, _MAX_PATH, 0, 0, 0, 0);
+ MakePath(szPath, szDrive, szDir, W("mscorlib"), W(".dll"));
+
+ // Open the scope, get the details.
+ IfFailGo(pDisp->OpenScope(szPath, 0, IID_IMetaDataAssemblyImport, (IUnknown**)&pAImp));
+ IfFailGo(pAImp->GetAssemblyFromScope(&tk));
+ IfFailGo(pAImp->GetAssemblyProps(tk, &pvPublicKey,&cbPublicKey, &ulHashAlg,
+ szPath,lengthof(szPath),&dwLen, &amd, &dwFlags));
+
+ if (!StrongNameTokenFromPublicKey((BYTE*)(pvPublicKey),cbPublicKey, &pbToken,&cbToken))
+ {
+ hr = StrongNameErrorInfo();
+ goto ErrExit;
+ }
+ dwFlags &= ~afPublicKey;
+
+ // Define the assembly ref.
+ IfFailGo(m_pEmit->QueryInterface(IID_IMetaDataAssemblyEmit, (void**)&pAEmit));
+ IfFailGo(pAEmit->DefineAssemblyRef(pbToken,cbToken, szPath, &amd,0,0,dwFlags, &m_arSystem));
+
+ IfFailGo(m_TRMap.DefineTypeRef(m_pEmit, m_arSystem, szObject, &m_trObject));
+
+ m_tkKnownTypes[VT_DISPATCH] = m_trObject;
+ m_tkKnownTypes[VT_UNKNOWN] = m_trObject;
+ m_tkKnownTypes[VT_VARIANT] = m_trObject;
+
+ErrExit:
+ if (pbToken)
+ StrongNameFreeBuffer(pbToken);
+ if (pDisp)
+ pDisp->Release();
+ if (pAEmit)
+ pAEmit->Release();
+ if (pAImp)
+ pAImp->Release();
+
+ return hr;
+} // HRESULT CImportTlb::_DefineSysRefs()
+
+//*****************************************************************************
+// Lazily get the token for a CustomAttribute.
+//*****************************************************************************
+HRESULT CImportTlb::GetAttrType(
+ int attr, // The attribute for which the type is desired.
+ mdToken *pTk) // Put the type here.
+{
+ HRESULT hr = S_OK; // A result.
+ mdTypeRef tr; // An intermediate typeref.
+ DWORD dwSigSize; // The size of the sig for special sigs.
+ DWORD dwMaxSigSize; // The max size of the special sig.
+ COR_SIGNATURE *pSig; // Pointer to the start of the sig,
+ COR_SIGNATURE *pCurr; // Current sig pointer.
+ mdTypeRef trType; // The typeref for System.Type.
+
+ _ASSERTE(attr >= 0);
+ _ASSERTE(attr < ATTR_COUNT);
+
+ //@todo: globally define these names.
+#define INTEROP_ATTRIBUTE(x) static COR_SIGNATURE x##_SIG[] = INTEROP_##x##_SIG;
+#define INTEROP_ATTRIBUTE_SPECIAL(x)
+ INTEROP_ATTRIBUTES();
+#undef INTEROP_ATTRIBUTE
+#undef INTEROP_ATTRIBUTE_SPECIAL
+#define INTEROP_ATTRIBUTE(x) \
+ case ATTR_##x: \
+ IfFailGo(m_pEmit->DefineTypeRefByName(m_arSystem, INTEROP_##x##_TYPE_W, &tr)); \
+ IfFailGo(m_pEmit->DefineMemberRef(tr, W(".ctor"), x##_SIG, lengthof(x##_SIG), &m_tkAttr[attr])); \
+ break;
+#define INTEROP_ATTRIBUTE_SPECIAL(x)
+
+ if (IsNilToken(m_tkAttr[attr]))
+ {
+ switch (attr)
+ {
+ INTEROP_ATTRIBUTES();
+
+ case ATTR_COMEVENTINTERFACE:
+ {
+ // Retrieve token for System.Type.
+ IfFailGo(GetKnownTypeToken(VT_SLOT_FOR_TYPE, &trType));
+
+ // Build the sig.
+ dwMaxSigSize = 5 + sizeof(mdTypeRef) * 2;
+ pSig = (COR_SIGNATURE*)_alloca(dwMaxSigSize);
+ pCurr = pSig;
+ *pCurr++ = IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS;
+ *pCurr++ = 2;
+ *pCurr++ = ELEMENT_TYPE_VOID;
+ *pCurr++ = ELEMENT_TYPE_CLASS;
+ pCurr += CorSigCompressToken(trType, pCurr);
+ *pCurr++ = ELEMENT_TYPE_CLASS;
+ pCurr += CorSigCompressToken(trType, pCurr);
+ dwSigSize = (DWORD)(pCurr - pSig);
+ _ASSERTE(dwSigSize <= dwMaxSigSize);
+
+ // Declare the typeref and the member ref for the CA.
+ IfFailGo(m_pEmit->DefineTypeRefByName(m_arSystem, INTEROP_COMEVENTINTERFACE_TYPE_W, &tr)); \
+ IfFailGo(m_pEmit->DefineMemberRef(tr, W(".ctor"), pSig, dwSigSize, &m_tkAttr[attr])); \
+ break;
+ }
+
+ case ATTR_COCLASS:
+ {
+ // Retrieve token for System.Type.
+ IfFailGo(GetKnownTypeToken(VT_SLOT_FOR_TYPE, &trType));
+
+ // Build the sig.
+ dwMaxSigSize = 4 + sizeof(mdTypeRef);
+ pSig = (COR_SIGNATURE*)_alloca(dwMaxSigSize);
+ pCurr = pSig;
+ *pCurr++ = IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS;
+ *pCurr++ = 1;
+ *pCurr++ = ELEMENT_TYPE_VOID;
+ *pCurr++ = ELEMENT_TYPE_CLASS;
+ pCurr += CorSigCompressToken(trType, pCurr);
+ dwSigSize = (DWORD)(pCurr - pSig);
+ _ASSERTE(dwSigSize <= dwMaxSigSize);
+
+ // Declare the typeref and the member ref for the CA.
+ IfFailGo(m_pEmit->DefineTypeRefByName(m_arSystem, INTEROP_COCLASS_TYPE_W, &tr)); \
+ IfFailGo(m_pEmit->DefineMemberRef(tr, W(".ctor"), pSig, dwSigSize, &m_tkAttr[attr])); \
+ break;
+ }
+ }
+ }
+#undef INTEROP_ATTRIBUTE
+#undef INTEROP_ATTRIBUTE_SPECIAL
+
+ *pTk = m_tkAttr[attr];
+ErrExit:
+ return hr;
+} // HRESULT CImportTlb::GetAttrType()
+
+//*****************************************************************************
+// Create the TypeDefs.
+//*****************************************************************************
+HRESULT
+CImportTlb::ConvertTypeLib()
+{
+ HRESULT hr;
+ int cTi; // Count of TypeInfos.
+ int i; // Loop control.
+
+ // How many TypeInfos?
+ IfFailGo(cTi = m_pITLB->GetTypeInfoCount());
+
+ // Iterate over them.
+ for (i = 0; i < cTi; ++i)
+ {
+ // Get the TypeInfo.
+ hr = m_pITLB->GetTypeInfo(i, &m_pITI);
+ if (SUCCEEDED(hr))
+ {
+ // Save up the original TypeInfo (may be later alias-resolved).
+ _ASSERTE(m_pOrigITI == NULL);
+ m_pOrigITI = m_pITI;
+ m_pOrigITI->AddRef();
+
+ // Retrieve the attributes of the type info.
+ IfFailGo(m_pITI->GetTypeAttr(&m_psAttr));
+
+ // Convert the TypeInfo.
+ hr = ConvertTypeInfo();
+ if (FAILED(hr))
+ {
+ if (hr == CEE_E_CVTRES_NOT_FOUND || hr == TLBX_I_RESOLVEREFFAILED)
+ { // Reflection emit is broken, no need to try to continue.
+ goto ErrExit;
+ }
+
+ BSTR szTypeInfoName = NULL;
+ hr = m_pITI->GetDocumentation(MEMBERID_NIL, &szTypeInfoName, 0, 0, 0);
+ if (SUCCEEDED(hr))
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_INVALID_TYPEINFO, szTypeInfoName);
+ }
+ else
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_INVALID_TYPEINFO_UNNAMED, i);
+ }
+ if (szTypeInfoName != NULL)
+ ::SysFreeString(szTypeInfoName);
+#if defined(_DEBUG)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_TlbImp_BreakOnErr))
+ _ASSERTE(!"Invalid type");
+#endif
+ }
+
+ // Release for next TypeInfo.
+ m_pOrigITI->Release();
+ m_pOrigITI = NULL;
+
+ m_pITI->ReleaseTypeAttr(m_psAttr);
+ m_psAttr = NULL;
+ m_pITI->Release();
+ m_pITI = NULL;
+ }
+ }
+
+ErrExit:
+ if (m_pOrigITI != NULL)
+ {
+ m_pOrigITI->Release();
+ m_pOrigITI = NULL;
+ }
+
+ if (m_psAttr != NULL)
+ {
+ m_pITI->ReleaseTypeAttr(m_psAttr);
+ m_psAttr = NULL;
+ }
+ if (m_pITI != NULL)
+ {
+ m_pITI->Release();
+ m_pITI = NULL;
+ }
+ return hr;
+} // CImportTlb::ConvertTypeLib
+
+//*****************************************************************************
+// Convert a single ITypeInfo into the scope.
+//*****************************************************************************
+HRESULT CImportTlb::ConvertTypeInfo() // S_OK or error.
+{
+ HRESULT hr; // A result.
+ BSTR bstrManagedName=0; // Managed name (or part thereof).
+ CQuickArray<WCHAR> qbClassName; // The name of the class.
+ ULONG ulFlags; // TypeDef flags.
+ WORD wTypeInfoFlags; // TypeInfo flags. Alias flags, if an alias.
+ mdToken tkAttr; // Attribute type for flags.
+ TYPEKIND tkindAlias; // TYPEKIND of an aliased TypeInfo.
+ GUID guid; // GUID of the typeinfo.
+ BOOL bConversionLoss=false; // If true, info was lost converting sigs.
+ mdToken tkParent; // Parent of the typedef.
+ mdToken td; // For looking up a TypeDef.
+ ITypeInfo2 *pITI2=0; // For getting custom value.
+
+#if defined(TLB_STATS)
+ WCHAR rcStats[16]; // Buffer for stats.
+ LARGE_INTEGER __startVal;
+ QueryPerformanceCounter(&__startVal);
+#endif
+
+ m_tdTypeDef = mdTypeDefNil;
+
+ // Get some information about the TypeInfo.
+ IfFailGo(m_pITI->GetDocumentation(MEMBERID_NIL, &m_szName, 0, 0, 0));
+
+#if defined(_DEBUG)
+ LPWSTR strShouldBreakOnTypeName = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_TlbImp_BreakOnTypeImport);
+ if ((NULL != strShouldBreakOnTypeName) && (wcsncmp(strShouldBreakOnTypeName, m_szName, MAX_CLASSNAME_LENGTH) == 0))
+ _ASSERTE(!"MD_TlbImp_BreakOnTypeImport");
+#endif
+
+ // Assume that we will be able to convert the typeinfo.
+ guid = m_psAttr->guid;
+ wTypeInfoFlags = m_psAttr->wTypeFlags;
+
+ // If this typeinfo is an alias, see what it is an alias for. If for a built-in
+ // type, we will just skip it. If for a user-defined type, we will duplicate
+ // that definition under this alias' name and guid.
+ if (m_psAttr->typekind == TKIND_ALIAS)
+ {
+ hr = _ResolveTypeDescAliasTypeKind(m_pITI, &m_psAttr->tdescAlias, &tkindAlias);
+ IfFailGo(hr);
+ if (hr == S_OK)
+ {
+ TYPEDESC tdesc = m_psAttr->tdescAlias;
+ m_pITI->ReleaseTypeAttr(m_psAttr);
+ m_pITI->Release();
+
+ IfFailGo(_ResolveTypeDescAlias(m_pOrigITI, &tdesc, &m_pITI, &m_psAttr, &guid));
+ // Now m_pOrigITI refers to the alias whereas m_pITI is the TypeInfo of the aliased type.
+
+ // We should no longer have an alias.
+ _ASSERTE(m_psAttr->typekind == tkindAlias);
+ _ASSERTE(tkindAlias != TKIND_ALIAS);
+
+ ulFlags = rdwTypeFlags[tkindAlias];
+ }
+ else
+ ulFlags = ULONG_MAX;
+ }
+ else
+ {
+ ulFlags = rdwTypeFlags[m_psAttr->typekind];
+ }
+
+ // Figure out the name.
+
+ // If the type info is for a CoClass, we need to decorate the name.
+ if (m_psAttr->typekind == TKIND_COCLASS)
+ {
+ // Generate a mangled name for the component.
+ IfFailGo(GetManagedNameForCoClass(m_pOrigITI, qbClassName));
+ m_szMngName = qbClassName.Ptr();
+ }
+ else
+ {
+ IfFailGo(GetManagedNameForTypeInfo(m_pOrigITI, m_wzNamespace, NULL, &bstrManagedName));
+ m_szMngName = bstrManagedName;
+ }
+
+ if (m_psAttr->typekind == TKIND_INTERFACE ||
+ (m_psAttr->typekind == TKIND_DISPATCH && m_psAttr->wTypeFlags & TYPEFLAG_FDUAL))
+ {
+ // If the interface is not derived from IUnknown, or not an interface, we can't convert it.
+ if (IsIUnknownDerived(m_pITI, m_psAttr) != S_OK)
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_NOTIUNKNOWN, m_szName);
+ ulFlags = ULONG_MAX;
+ }
+ // If the interface is not derived from IDispatch, but claims to be [dual], give a warning but convert it.
+ if ((m_psAttr->wTypeFlags & TYPEFLAG_FDUAL) && IsIDispatchDerived(m_pITI, m_psAttr) != S_OK)
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_W_DUAL_NOT_DISPATCH, m_szName);
+ }
+ }
+ else
+ if (m_psAttr->typekind == TKIND_MODULE)
+ { // If module has no vars, skip it. We currently don't import module functions.
+ if (m_psAttr->cVars == 0)
+ ulFlags = ULONG_MAX;
+ }
+
+ // If something we can convert...
+ if (ulFlags != ULONG_MAX)
+ {
+ // Interfaces derive from nil...
+ if (IsTdInterface(ulFlags))
+ tkParent = mdTypeDefNil;
+ else // ... enums from Enum, ...
+ if (m_psAttr->typekind == TKIND_ENUM)
+ {
+ if (IsNilToken(m_trEnum))
+ IfFailGo(m_TRMap.DefineTypeRef(m_pEmit, m_arSystem, szEnum, &m_trEnum));
+ tkParent = m_trEnum;
+ }
+ else // ... structs from ValueType, ...
+ if (m_psAttr->typekind == TKIND_RECORD || m_psAttr->typekind == TKIND_UNION)
+ {
+ if (IsNilToken(m_trValueType))
+ IfFailGo(m_TRMap.DefineTypeRef(m_pEmit, m_arSystem, szValueType, &m_trValueType));
+ tkParent = m_trValueType;
+ }
+ else // ... and classes derive from Object.
+ tkParent = m_trObject;
+
+ // The typelib importer generates metadata into an empty ReflectionEmit scope. Because
+ // RE manages type names itself, duplicate checking is turned off. Because of user-defined
+ // names (via CUSTOM), it is possible for the user to declare a duplicate. So,
+ // before adding the new type, check for duplicates.
+ hr = m_pImport->FindTypeDefByName(m_szMngName, mdTypeDefNil, &td);
+ if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_DUPLICATE_TYPE_NAME, m_szMngName);
+ IfFailGo(TLBX_E_DUPLICATE_TYPE_NAME);
+ }
+
+ // Create the typedef.
+ IfFailGo(m_pEmit->DefineTypeDef(m_szMngName, ulFlags, tkParent, 0, &m_tdTypeDef));
+ IfFailGo(_AddGuidCa(m_tdTypeDef, guid));
+
+ // Save the typeinfo flags.
+ if (wTypeInfoFlags)
+ {
+ IfFailGo(GetAttrType(ATTR_TYPELIBTYPE, &tkAttr));
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(WORD));
+ BUILD_CUSTOM_ATTRIBUTE(WORD, wTypeInfoFlags);
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+
+ // Mark unsafe interfaces (suppressed security runtime checks).
+ if (m_bUnsafeInterfaces)
+ {
+ if (m_tkSuppressCheckAttr == mdTokenNil)
+ {
+ mdTypeRef tr;
+ COR_SIGNATURE rSig[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 0, ELEMENT_TYPE_VOID};
+ IfFailGo(m_pEmit->DefineTypeRefByName(m_arSystem, COR_SUPPRESS_UNMANAGED_CODE_CHECK_ATTRIBUTE, &tr));
+ IfFailGo(m_pEmit->DefineMemberRef(tr, COR_CTOR_METHOD_NAME_W, rSig, lengthof(rSig), &m_tkSuppressCheckAttr));
+ }
+
+ DECLARE_CUSTOM_ATTRIBUTE(0);
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, m_tkSuppressCheckAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+ }
+
+ // Fill in the details depending on the type of the TypeInfo.
+ switch (m_psAttr->typekind)
+ {
+ case TKIND_ENUM:
+ hr = ConvEnum(m_pITI, m_psAttr);
+ break;
+
+ case TKIND_RECORD:
+ hr = ConvRecord(m_pITI, m_psAttr, FALSE);
+ break;
+
+ case TKIND_UNION:
+ hr = ConvRecord(m_pITI, m_psAttr, TRUE);
+ break;
+
+ case TKIND_MODULE:
+ hr = ConvModule(m_pITI, m_psAttr);
+ break;
+
+ case TKIND_INTERFACE:
+ hr = ConvIface(m_pITI, m_psAttr);
+ break;
+
+ case TKIND_DISPATCH:
+ hr = ConvDispatch(m_pITI, m_psAttr);
+ break;
+
+ case TKIND_COCLASS:
+ hr = ConvCoclass(m_pITI, m_psAttr);
+ break;
+
+ case TKIND_ALIAS:
+ _ASSERTE(!"Alias should have been resolved!");
+ break;
+
+ default:
+ _ASSERTE(!"Unexpected TYPEKIND");
+ break;
+ }
+ if (FAILED(hr))
+ goto ErrExit;
+
+ if (hr == S_CONVERSION_LOSS)
+ {
+ bConversionLoss = true;
+ IfFailGo(GetAttrType(ATTR_COMCONVERSIONLOSS, &tkAttr));
+ DECLARE_CUSTOM_ATTRIBUTE(0);
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, tkAttr, PTROF_CUSTOM_ATTRIBUTE(),SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+
+ }
+
+ if (bConversionLoss)
+ hr = S_CONVERSION_LOSS;
+ else
+ hr = S_OK;
+
+#if defined(TLB_STATS)
+ LARGE_INTEGER __stopVal;
+ QueryPerformanceCounter(&__stopVal);
+ DWORD __delta;
+ __delta = (DWORD)(__stopVal.QuadPart - __startVal.QuadPart);
+ StringCchPrintf(rcStats, COUNTOF(rcStats), W(" %.2f"),
+ ((float)__delta*1000)/(float)m_freqVal.QuadPart);
+#endif
+
+ // Report that this type has been converted.
+ ReportEvent(NOTIF_TYPECONVERTED, TLBX_I_TYPEINFO_IMPORTED, m_szName);
+
+ErrExit:
+ if (pITI2)
+ pITI2->Release();
+ if (m_szName)
+ ::SysFreeString(m_szName), m_szName = 0;
+ if (bstrManagedName)
+ ::SysFreeString(bstrManagedName);
+ return (hr);
+} // HRESULT CImportTlb::ConvertTypeInfo()
+
+
+//*****************************************************************************
+// Determine if the type explicitly implements IEnumerable.
+//*****************************************************************************
+HRESULT CImportTlb::ExplicitlyImplementsIEnumerable(
+ ITypeInfo *pITI, // ITypeInfo* to check for IEnumerable.
+ TYPEATTR *psAttr, // TYPEATTR of TypeInfo.
+ BOOL fLookupPartner) // Flag indicating if we should look at the partner itf.
+{
+ HREFTYPE href; // HREFTYPE of an implemented interface.
+ ITypeInfo *pItiIface=0; // ITypeInfo for an interface.
+ TYPEATTR *psAttrIface=0; // TYPEATTR for an interface.
+ BOOL fFoundImpl = FALSE;
+ int i = 0;
+ HRESULT hr = S_OK;
+ ITypeInfo* pITISelf2 = NULL;
+ TYPEATTR psAttrSelf2;
+ int ImplFlags = 0;
+
+ // Look through each of the implemented/inherited interfaces
+ for (i=0; i<psAttr->cImplTypes && !fFoundImpl; ++i)
+ {
+ // Get an interface
+ IfFailGo(pITI->GetRefTypeOfImplType(i, &href));
+ IfFailGo(pITI->GetRefTypeInfo(href, &pItiIface));
+ IfFailGo(pItiIface->GetTypeAttr(&psAttrIface));
+ IfFailGo(pITI->GetImplTypeFlags(i, &ImplFlags));
+
+ if (!(ImplFlags & IMPLTYPEFLAG_FSOURCE))
+ {
+ hr = ExplicitlyImplementsIEnumerable(pItiIface, psAttrIface, TRUE);
+ if (hr == S_OK)
+ fFoundImpl = TRUE;
+
+ // Check this interface for the IEnumerable.
+ if (psAttrIface->guid == IID_IEnumerable)
+ fFoundImpl = TRUE;
+ }
+
+ pItiIface->ReleaseTypeAttr(psAttrIface);
+ psAttrIface = 0;
+ pItiIface->Release();
+ pItiIface = 0;
+ }
+
+ if ( fLookupPartner && (pITI->GetRefTypeOfImplType(-1, &href) == S_OK) )
+ {
+ IfFailGo(pITI->GetRefTypeInfo(href, &pItiIface));
+ IfFailGo(pItiIface->GetTypeAttr(&psAttrIface));
+
+ hr = ExplicitlyImplementsIEnumerable(pItiIface, psAttrIface, FALSE);
+ if (hr == S_OK)
+ fFoundImpl = TRUE;
+
+ // Check this interface for the IEnumerable.
+ if (psAttrIface->guid == IID_IEnumerable)
+ fFoundImpl = TRUE;
+ }
+
+
+ErrExit:
+ if (psAttrIface)
+ pItiIface->ReleaseTypeAttr(psAttrIface);
+ if (pItiIface)
+ pItiIface->Release();
+
+ return (fFoundImpl) ? S_OK : S_FALSE;
+}
+
+
+//*****************************************************************************
+// Convert the details for a coclass.
+//*****************************************************************************
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+HRESULT CImportTlb::ConvCoclass( // S_OK or error.
+ ITypeInfo *pITI, // ITypeInfo* to convert.
+ TYPEATTR *psAttr) // TYPEATTR of TypeInfo.
+{
+ BOOL fHadDefaultItf = FALSE;
+ HRESULT hr; // A result.
+ int i; // Loop control.
+ HREFTYPE href; // HREFTYPE of an implemented interface.
+ ITypeInfo *pItiIface=0; // ITypeInfo for an interface.
+ TYPEATTR *psAttrIface=0; // TYPEATTR for an interface.
+ int ImplFlags; // ImplType flags.
+ mdToken tkIface; // Token for an interface.
+ CQuickArray<mdToken> rImpls; // Array of implemented interfaces.
+ CQuickArray<mdToken> rEvents; // Array of implemented event interfaces.
+ CQuickArray<mdToken> rTmpImpls; // Temporary array of impls.
+ CQuickArray<ITypeInfo*> rImplTypes; // Array of implemented ITypeInfo*s.
+ CQuickArray<ITypeInfo*> rSrcTypes; // Array of source ITypeInfo*s.
+ int ixSrc; // Index into rSrcTypes for source interfaces.
+ int ixImpl; // Index into rImpls for implemented interface.
+ int ixTmpImpl; // Index into rTmpImpls.
+ mdToken mdCtor; // Dummy token for the object initializer.
+ mdToken tkAttr; // Token for custom attribute type.
+ mdToken token; // Dummy token for typeref.
+ BOOL fInheritsIEnum = FALSE;
+
+#ifdef _DEBUG
+ int bImplIEnumerable=0; // If true, the class implements IEnumerable.
+#endif
+
+ // Size the rImpls and rSrcs arrays large enough for impls, events, the IEnumerable itf and two ending nulls.
+ IfFailGo(rImpls.ReSizeNoThrow(psAttr->cImplTypes+2));
+ memset(rImpls.Ptr(), 0, (psAttr->cImplTypes+2)*sizeof(mdToken));
+ IfFailGo(rEvents.ReSizeNoThrow(psAttr->cImplTypes+1));
+ memset(rEvents.Ptr(), 0, (psAttr->cImplTypes+1)*sizeof(mdToken));
+ IfFailGo(rTmpImpls.ReSizeNoThrow(psAttr->cImplTypes+3));
+ memset(rTmpImpls.Ptr(), 0, (psAttr->cImplTypes+3)*sizeof(mdToken));
+ IfFailGo(rImplTypes.ReSizeNoThrow(psAttr->cImplTypes+2));
+ memset(rImplTypes.Ptr(), 0, (psAttr->cImplTypes+2)*sizeof(ITypeInfo*));
+ IfFailGo(rSrcTypes.ReSizeNoThrow(psAttr->cImplTypes+1));
+ memset(rSrcTypes.Ptr(), 0, (psAttr->cImplTypes+1)*sizeof(ITypeInfo*));
+ ixImpl = -1;
+ ixSrc = -1;
+ ixTmpImpl = -1;
+
+ if (ExplicitlyImplementsIEnumerable(pITI, psAttr) == S_OK)
+ fInheritsIEnum = TRUE;
+
+ // Build the list of implemented and event interfaces.
+ // The EE cares about implemented interfaces, so we convert them to actual
+ // tokens and add them to the typedef. VB cares about event interfaces,
+ // but we are going to add a list of typeref names as a custom attribute.
+ // We can't build the list as we go along, because the default may not
+ // be the first event source. So, we store tokens for the implemented
+ // interfaces, but ITypeInfo*s for the event sources.
+ for (i=0; i<psAttr->cImplTypes; ++i)
+ {
+ IfFailGo(pITI->GetRefTypeOfImplType(i, &href));
+ IfFailGo(pITI->GetRefTypeInfo(href, &pItiIface));
+ IfFailGo(pItiIface->GetTypeAttr(&psAttrIface));
+ IfFailGo(pITI->GetImplTypeFlags(i, &ImplFlags));
+
+ // If the interface is derived from IUnknown, or not an interface, we can't use it as an interface.
+ // Don't add explicit IUnknown or IDispatch.
+ if ((IsIUnknownDerived(pItiIface, psAttrIface) != S_OK && psAttrIface->typekind != TKIND_DISPATCH) ||
+ psAttrIface->guid == IID_IDispatch ||
+ psAttrIface->guid == IID_IUnknown)
+ {
+ pItiIface->ReleaseTypeAttr(psAttrIface);
+ psAttrIface = 0;
+ pItiIface->Release();
+ pItiIface = 0;
+ continue;
+ }
+
+ // Add the event to the impls list or the events list.
+ if (ImplFlags & IMPLTYPEFLAG_FSOURCE)
+ {
+ // Get the token for the event interface.
+ IfFailGo(_GetTokenForEventItf(pItiIface, &tkIface));
+
+ // If we've already marked this CoClass as implementing this source interface, don't do so again.
+ for (int iCheck=0; iCheck <= ixSrc; iCheck++)
+ {
+ if (rEvents[iCheck] == tkIface)
+ goto LoopEnd;
+ }
+
+ // Add the source interface to the list of source interfaces.
+ ++ixSrc;
+
+ // If this is explicitly the default source interface...
+ if (ImplFlags & IMPLTYPEFLAG_FDEFAULT)
+ {
+ // Put the def source ITypeInfo at the head of the list of source
+ // ITypeInfo's.
+ for (int ix = ixSrc; ix > 0; --ix)
+ {
+ rSrcTypes[ix] = rSrcTypes[ix-1];
+ rEvents[ix] = rEvents[ix-1];
+ }
+ rEvents[0] = tkIface;
+ rSrcTypes[0] = pItiIface;
+ }
+ else
+ {
+ rEvents[ixSrc] = tkIface;
+ rSrcTypes[ixSrc] = pItiIface;
+ }
+ }
+ else
+ {
+ // Get the token for the interface.
+ IfFailGo(_GetTokenForTypeInfo(pItiIface, FALSE, &tkIface));
+
+ // If we've already marked this CoClass as implementing this interface, don't do so again.
+ for (int iCheck=0; iCheck <= ixImpl; iCheck++)
+ {
+ if (rImpls[iCheck] == tkIface)
+ goto LoopEnd;
+ }
+
+ // Add the implemented interface to the list of implemented interfaces.
+ ++ixImpl;
+
+ // If this is explicitly the default interface...
+ if (ImplFlags & IMPLTYPEFLAG_FDEFAULT)
+ {
+ fHadDefaultItf = TRUE;
+ // Put the new interface at the start of the list.
+ for (int ix=ixImpl; ix > 0; --ix)
+ {
+ rImpls[ix] = rImpls[ix-1];
+ rImplTypes[ix] = rImplTypes[ix-1];
+ }
+ rImpls[0] = tkIface;
+ rImplTypes[0] = pItiIface;
+ }
+ else
+ {
+ rImpls[ixImpl] = tkIface;
+ rImplTypes[ixImpl] = pItiIface;
+ }
+ }
+
+LoopEnd:
+ pItiIface->ReleaseTypeAttr(psAttrIface);
+ psAttrIface = 0;
+ pItiIface = 0; // Pointer now owned by array.
+ }
+
+ // Create an interface that will represent the class.
+ IfFailGo(_CreateClassInterface(pITI, rImplTypes[0], rImpls[0], rEvents[0], &tkIface));
+
+ // Create a temporary array of interface tokens.
+ if (fHadDefaultItf)
+ {
+ // default interface should be the first interface
+ rTmpImpls[++ixTmpImpl] = rImpls[0];
+ rTmpImpls[++ixTmpImpl] = tkIface;
+ }
+ else
+ {
+ rTmpImpls[++ixTmpImpl] = tkIface;
+ if (ixImpl >= 0)
+ rTmpImpls[++ixTmpImpl] = rImpls[0];
+ }
+ if (ixSrc >= 0)
+ rTmpImpls[++ixTmpImpl] = rEvents[0];
+ if (ixImpl >= 0)
+ {
+ memcpy(&rTmpImpls[ixTmpImpl + 1], &rImpls[1], ixImpl * sizeof(mdTypeRef));
+ ixTmpImpl += ixImpl;
+ }
+ if (ixSrc >= 0)
+ {
+ memcpy(&rTmpImpls[ixTmpImpl + 1], &rEvents[1], ixSrc * sizeof(mdTypeRef));
+ ixTmpImpl += ixSrc;
+ }
+
+ // Check to see if the default interface has a member with a DISPID of DISPID_NEWENUM.
+ BOOL fIEnumFound = FALSE;
+ if (ixImpl >= 0)
+ {
+ // The ITypeInfo for the default interface had better be set.
+ _ASSERTE(rImplTypes[0]);
+
+ if ( (!fInheritsIEnum) && (HasNewEnumMember(rImplTypes[0]) == S_OK) )
+ {
+ IfFailGo(GetKnownTypeToken(VT_SLOT_FOR_IENUMERABLE, &tkIface));
+ rTmpImpls[++ixTmpImpl] = tkIface;
+ fIEnumFound = TRUE;
+ }
+ }
+
+ // Else Check to see if the IEnumerable Custom Value exists on the CoClass.
+ if (!fIEnumFound)
+ {
+ BOOL CVExists = FALSE;
+ _ForceIEnumerableCVExists(pITI, &CVExists);
+ if (CVExists && !fInheritsIEnum)
+ {
+ IfFailGo(GetKnownTypeToken(VT_SLOT_FOR_IENUMERABLE, &tkIface));
+ rTmpImpls[++ixTmpImpl] = tkIface;
+ fIEnumFound = TRUE;
+ }
+ }
+
+ // Add the implemented interfaces and event interfaces to the TypeDef.
+ IfFailGo(m_pEmit->SetTypeDefProps(m_tdTypeDef, ULONG_MAX/*Classflags*/,
+ ULONG_MAX, (mdToken*)rTmpImpls.Ptr()));
+
+ // Create an initializer for the class.
+ ULONG ulFlags;
+ if (psAttr->wTypeFlags & TYPEFLAG_FCANCREATE)
+ ulFlags = OBJECT_INITIALIZER_FLAGS;
+ else
+ ulFlags = NONCREATABLE_OBJECT_INITIALIZER_FLAGS;
+ {
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, OBJECT_INITIALIZER_NAME, ulFlags,
+ OBJECT_INITIALIZER_SIG, sizeof(OBJECT_INITIALIZER_SIG), 0/*rva*/, OBJECT_INITIALIZER_IMPL_FLAGS/*flags*/, &mdCtor));
+ }
+
+ // Set ClassInterfaceType.None on the generated class.
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(short));
+ BUILD_CUSTOM_ATTRIBUTE(short, clsIfNone);
+ IfFailGo(GetAttrType(ATTR_CLASSINTERFACE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+
+
+ if (!m_bPreventClassMembers)
+ {
+ // Iterate over the implemented interfaces, and add the members to the coclass.
+ m_ImplIface = eImplIfaceDefault;
+ for (i=0; i<=ixImpl; ++i)
+ {
+ _ASSERTE(rImplTypes[i]);
+
+ // Interface info.
+ m_tkInterface = rImpls[i];
+ pItiIface = rImplTypes[i];
+ rImplTypes[i] = 0; // ownership transferred.
+
+ // Get interface name for decoration.
+ if (m_szInterface)
+ ::SysFreeString(m_szInterface), m_szInterface = 0;
+ IfFailGo(pItiIface->GetDocumentation(MEMBERID_NIL, &m_szInterface, 0,0,0));
+
+ // Add the interface members to the coclass.
+ IfFailGo(pItiIface->GetTypeAttr(&psAttrIface));
+ switch (psAttrIface->typekind)
+ {
+ case TKIND_DISPATCH:
+ hr = ConvDispatch(pItiIface, psAttrIface, false);
+ break;
+ case TKIND_INTERFACE:
+ hr = ConvIface(pItiIface, psAttrIface, false);
+ break;
+ default:
+ hr = S_OK;
+ _ASSERTE(!"Unexpected typekind for implemented interface");
+ }
+ pItiIface->ReleaseTypeAttr(psAttrIface);
+ psAttrIface = 0;
+ IfFailGo(hr);
+ m_ImplIface = eImplIface;
+ rImplTypes[i] = pItiIface;
+ pItiIface = 0; // ownership transferred back.
+ }
+
+ // Add the methods of the event interfaces to the class.
+ for (i=0; i<=ixSrc; ++i)
+ IfFailGo(_AddSrcItfMembersToClass(rEvents[i]));
+ }
+
+ // If there are source interfaces, add a custom value for that.
+ if (ixSrc >= 0)
+ {
+ CQuickArray<char> rEvents; // Output buffer.
+ int cbCur; // Current location in output buffer.
+ int cbReq; // Size of an individual piece.
+ CQuickArray<WCHAR> rEvent;
+
+ // Save 6 bytes at the beginning of the buffer for the custom attribute prolog and
+ // the string length. The string length may require 1, 2, or 4 bytes to express.
+ cbCur = 6;
+
+ // For each event interface...
+ for (int ix=0; ix <= ixSrc; ++ix)
+ {
+ pItiIface = rSrcTypes[ix];
+ rSrcTypes[ix] = 0;
+
+ // Get the typeref name for the interface.
+ for(;;)
+ {
+ int cchReq;
+ IfFailGo(_GetTokenForTypeInfo(pItiIface, FALSE, &token, rEvent.Ptr(), (int)rEvent.MaxSize(), &cchReq, TRUE));
+ if (cchReq <= (int)rEvent.MaxSize())
+ break;
+ IfFailGo(rEvent.ReSizeNoThrow(cchReq));
+ }
+
+ // Append to the buffer. See how much space is required, get it.
+ cbReq = WszWideCharToMultiByte(CP_UTF8,0, rEvent.Ptr(),-1, 0,0, 0,0);
+
+ // make sure we have enough space for the extra terminating 0 and for the 00 00 suffix
+ size_t cbNewSize;
+ if (!ClrSafeInt<size_t>::addition(cbCur, cbReq, cbNewSize) ||
+ !ClrSafeInt<size_t>::addition(cbNewSize, 3, cbNewSize))
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ if (cbNewSize > rEvents.MaxSize())
+ {
+ IfFailGo(rEvents.ReSizeNoThrow(cbNewSize));
+ }
+ // Do the conversion.
+ WszWideCharToMultiByte(CP_UTF8,0, rEvent.Ptr(),-1, rEvents.Ptr()+cbCur,cbReq, 0,0);
+ cbCur += cbReq;
+ pItiIface->Release();
+ }
+ pItiIface = 0;
+
+ // Add an extra terminating 0.
+ *(rEvents.Ptr()+cbCur) = 0;
+ ++cbCur;
+
+ // Now build the custom attribute.
+ int iLen = cbCur - 6;
+ char *pBytes = rEvents.Ptr();
+
+ // Length may be encoded with less the 4 bytes.
+ int lenPad = 4 - CPackedLen::Size(iLen);
+ _ASSERTE(lenPad >= 0);
+
+ pBytes += lenPad;
+ cbCur -= lenPad;
+
+ // Prologue.
+ pBytes[0] = 0x01;
+ pBytes[1] = 0x00;
+
+ CPackedLen::PutLength(pBytes + 2, iLen);
+
+ // Zero named properties/fields.
+ pBytes[cbCur + 0] = 0x00;
+ pBytes[cbCur + 1] = 0x00;
+ cbCur += 2;
+
+ // Finally, store it.
+ IfFailGo(GetAttrType(ATTR_COMSOURCEINTERFACES, &tkAttr));
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, tkAttr, pBytes, cbCur, 0));
+ }
+
+ErrExit:
+ if (psAttrIface)
+ pItiIface->ReleaseTypeAttr(psAttrIface);
+ if (pItiIface)
+ pItiIface->Release();
+ // Clean up any left-over ITypeInfo*.
+ for (ULONG ix=0; ix < rImplTypes.Size(); ++ix)
+ if (rImplTypes[ix])
+ (rImplTypes[ix])->Release();
+ for (ULONG ix=0; ix < rSrcTypes.Size(); ++ix)
+ if (rSrcTypes[ix])
+ (rSrcTypes[ix])->Release();
+ m_tkInterface = 0;
+ if (m_szInterface)
+ ::SysFreeString(m_szInterface), m_szInterface = 0;
+ m_ImplIface = eImplIfaceNone;
+ return (hr);
+} // HRESULT CImportTlb::ConvCoclass()
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+// Convert an enum to a class with fields that have default values.
+//*****************************************************************************
+HRESULT CImportTlb::ConvEnum( // S_OK or error.
+ ITypeInfo *pITI, // ITypeInfo* to convert.
+ TYPEATTR *psAttr) // TYPEATTR of TypeInfo.
+{
+ HRESULT hr; // A result.
+ int i; // Loop control.
+ VARDESC *psVar=0; // VARDESC for a member.
+ mdFieldDef mdField; // The FieldDef for the enum's type.
+
+ // Create the field definition for the enum type. Always import as an __int32.
+ IfFailGo(m_pEmit->DefineField(m_tdTypeDef, ENUM_TYPE_NAME, ENUM_TYPE_FLAGS, ENUM_TYPE_SIGNATURE,ENUM_TYPE_SIGNATURE_SIZE,
+ 0,0, -1, &mdField));
+
+ // Iterate over the vars.
+ for (i=0; i<psAttr->cVars; ++i)
+ {
+ // Get variable information.
+ IfFailGo(pITI->GetVarDesc(i, &psVar));
+ // Do the conversion.
+ IfFailGo(_ConvConstant(pITI, psVar, true/*enum member*/));
+ // Release for next var.
+ pITI->ReleaseVarDesc(psVar);
+ psVar = 0;
+ }
+
+ hr = S_OK;
+
+ErrExit:
+ if (psVar)
+ pITI->ReleaseVarDesc(psVar);
+ return (hr);
+} // HRESULT CImportTlb::ConvEnum()
+
+//*****************************************************************************
+// Convert a record to a class with fields.
+//*****************************************************************************
+HRESULT CImportTlb::ConvRecord( // S_OK or error.
+ ITypeInfo *pITI, // ITypeInfo* to convert.
+ TYPEATTR *psAttr, // TYPEATTR of TypeInfo.
+ BOOL bUnion) // Convert as a union?
+{
+ HRESULT hr=S_OK; // A result.
+ int i; // Loop control.
+ VARDESC *psVar=0; // VARDESC for a member.
+ mdFieldDef mdField; // Token for a given field.
+ CQuickArray<COR_FIELD_OFFSET> rLayout; // Array for layout information.
+ BOOL bConversionLoss=false; // If true, some attributes were lost on conversion.
+
+ // Unions with embedded Object Types can't really be converted. Just reserve correct size.
+ if (bUnion && (HasObjectFields(pITI, psAttr) == S_OK))
+ {
+ IfFailGo(m_pEmit->SetClassLayout(m_tdTypeDef, psAttr->cbAlignment, 0, psAttr->cbSizeInstance));
+ goto ErrExit;
+ }
+
+ // Prepare for layout info.
+ IfFailGo(rLayout.ReSizeNoThrow(psAttr->cVars+1));
+
+ // Iterate over the vars.
+ for (i=0; i<psAttr->cVars; ++i)
+ {
+ // Get variable information.
+ IfFailGo(pITI->GetVarDesc(i, &psVar));
+ // Do the conversion.
+ IfFailGo(_ConvField(pITI, psVar, &mdField, bUnion));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+ // Save the layout info.
+ rLayout[i].ridOfField = mdField;
+ rLayout[i].ulOffset = psVar->oInst;
+ // Release for next var.
+ pITI->ReleaseVarDesc(psVar);
+ psVar = 0;
+ }
+
+ // If it is a union, Save the layout information.
+ if (bUnion)
+ {
+ rLayout[psAttr->cVars].ridOfField = mdFieldDefNil;
+ IfFailGo(m_pEmit->SetClassLayout(m_tdTypeDef, psAttr->cbAlignment, rLayout.Ptr(), -1));
+ }
+ else // Not a union. Preserve the alignment.
+ IfFailGo(m_pEmit->SetClassLayout(m_tdTypeDef, psAttr->cbAlignment, 0, -1));
+
+ // If we are marking these as serializable - do so now.
+ if (m_bSerializableValueClasses)
+ {
+ mdToken tkAttr;
+ IfFailGo(GetAttrType(ATTR_SERIALIZABLE, &tkAttr));
+ DECLARE_CUSTOM_ATTRIBUTE(0);
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, tkAttr, PTROF_CUSTOM_ATTRIBUTE(),SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+
+ if (bConversionLoss)
+ hr = S_CONVERSION_LOSS;
+
+ErrExit:
+ if (psVar)
+ pITI->ReleaseVarDesc(psVar);
+ return (hr);
+} // HRESULT CImportTlb::ConvRecord()
+
+//*****************************************************************************
+// Convert an module to a class with fields that have default values.
+// @FUTURE: convert methods as PInvoke methods.
+//*****************************************************************************
+HRESULT CImportTlb::ConvModule( // S_OK or error.
+ ITypeInfo *pITI, // ITypeInfo* to convert.
+ TYPEATTR *psAttr) // TYPEATTR of TypeInfo.
+{
+ HRESULT hr; // A result.
+ int i; // Loop control.
+ VARDESC *psVar=0; // VARDESC for a member.
+
+ // Iterate over the vars.
+ for (i=0; i<psAttr->cVars; ++i)
+ {
+ // Get variable information.
+ IfFailGo(pITI->GetVarDesc(i, &psVar));
+ // Do the conversion.
+ IfFailGo(_ConvConstant(pITI, psVar));
+ // Release for next var.
+ pITI->ReleaseVarDesc(psVar);
+ psVar = 0;
+ }
+
+ hr = S_OK;
+
+ErrExit:
+ if (psVar)
+ pITI->ReleaseVarDesc(psVar);
+ return (hr);
+} // HRESULT CImportTlb::ConvModule()
+
+//*****************************************************************************
+// Convert metadata for an interface.
+//*****************************************************************************
+HRESULT CImportTlb::ConvIface( // S_OK or error.
+ ITypeInfo *pITI, // ITypeInfo* to convert.
+ TYPEATTR *psAttr, // TYPEATTR of TypeInfo.
+ BOOL bVtblGapFuncs) // Vtable gap functions?
+{
+ HRESULT hr; // A result.
+ ITypeInfo *pITIBase=0; // ITypeInfo* of base interface.
+ TYPEATTR *psAttrBase=0; // TYPEATTR of base interface.
+ ITypeInfo *pITISelf2=0; // ITypeInfo* of partner.
+ TYPEATTR *psAttrSelf2=0; // TYPEATTR of partner.
+ mdToken tkImpls[3]={0,0,0}; // Token of implemented interfaces.
+ int ixImpls = 0; // Index of current implemented interface.
+ HREFTYPE href; // href of base interface.
+ mdToken tkIface; // Token for an interface.
+ BOOL fInheritsIEnum = FALSE;
+
+ // If there is a partner interface, prefer it.
+ if (pITI->GetRefTypeOfImplType(-1, &href) == S_OK)
+ {
+ IfFailGo(pITI->GetRefTypeInfo(href, &pITISelf2));
+ IfFailGo(pITISelf2->GetTypeAttr(&psAttrSelf2));
+ }
+
+ // Base interface?
+ if (psAttr->cImplTypes == 1)
+ {
+ IfFailGo(pITI->GetRefTypeOfImplType(0, &href));
+ IfFailGo(pITI->GetRefTypeInfo(href, &pITIBase));
+ IfFailGo(pITIBase->GetTypeAttr(&psAttrBase));
+
+ // If this interface extends something other than IDispatch or IUnknown, record that
+ // fact as an "implemented interface".
+ if (psAttrBase->guid != IID_IDispatch && psAttrBase->guid != IID_IUnknown)
+ {
+ // Get Token of the base interface.
+ IfFailGo(_GetTokenForTypeInfo(pITIBase, FALSE, &tkImpls[ixImpls++]));
+ }
+ else
+ { // Maybe we're "funky"...
+ if (pITISelf2)
+ {
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ pITIBase->Release();
+ pITIBase = 0;
+ psAttrBase = 0;
+
+ if (psAttrSelf2->cImplTypes == 1)
+ {
+ IfFailGo(pITISelf2->GetRefTypeOfImplType(0, &href));
+ IfFailGo(pITISelf2->GetRefTypeInfo(href, &pITIBase));
+ IfFailGo(pITIBase->GetTypeAttr(&psAttrBase));
+
+ if (psAttrBase->guid != IID_IDispatch && psAttrBase->guid != IID_IUnknown)
+ {
+ // Get Token of the base interface.
+ IfFailGo(_GetTokenForTypeInfo(pITIBase, FALSE, &tkImpls[ixImpls++]));
+ }
+ }
+ else
+ {
+ BSTR szTypeInfoName;
+ pITISelf2->GetDocumentation(MEMBERID_NIL, &szTypeInfoName, 0, 0, 0);
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_INVALID_TYPEINFO, szTypeInfoName);
+ SysFreeString(szTypeInfoName);
+
+ IfFailGo(TLBX_E_INVALID_TYPEINFO);
+ }
+ }
+ }
+
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ psAttrBase = 0;
+ pITIBase->Release();
+ pITIBase = 0;
+ }
+
+ if (ExplicitlyImplementsIEnumerable(pITI, psAttr) == S_OK)
+ fInheritsIEnum = TRUE;
+
+ // If this interface has a NewEnum member then have it implement IEnumerable.
+ if ( (!fInheritsIEnum) && (HasNewEnumMember(pITI) == S_OK) )
+ {
+ IfFailGo(GetKnownTypeToken(VT_SLOT_FOR_IENUMERABLE, &tkIface));
+ tkImpls[ixImpls++] = tkIface;
+ }
+
+ // If not processing an implemented interface, add additional interface properties.
+ if (m_ImplIface == eImplIfaceNone)
+ {
+ // Set base interface as an implemented interface.
+ if (tkImpls[0])
+ IfFailGo(m_pEmit->SetTypeDefProps(m_tdTypeDef, ULONG_MAX/*flags*/, ULONG_MAX/*extends*/, tkImpls));
+
+ // If the interface is not derived from IDispatch mark it as IUnknown based.
+ if (IsIDispatchDerived(pITI, psAttr) == S_FALSE)
+ {
+ mdMemberRef mr;
+ // Note that this is a vtable, but not IDispatch derived.
+ // Custom attribute buffer.
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(short));
+ // Set up the attribute.
+ BUILD_CUSTOM_ATTRIBUTE(short, ifVtable);
+ // Store the attribute
+ IfFailGo(GetAttrType(ATTR_INTERFACETYPE, &mr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, mr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+ }
+ }
+
+ // Convert the members on the interface (and base interfaces).
+ // If this interface had a "funky partner", base the conversion on that.
+ if (pITISelf2)
+ IfFailGo(_ConvIfaceMembers(pITISelf2, psAttrSelf2, bVtblGapFuncs, psAttr->wTypeFlags & TYPEFLAG_FDUAL, fInheritsIEnum));
+ else
+ IfFailGo(_ConvIfaceMembers(pITI, psAttr, bVtblGapFuncs, psAttr->wTypeFlags & TYPEFLAG_FDUAL, fInheritsIEnum));
+
+ErrExit:
+ if (psAttrSelf2)
+ pITISelf2->ReleaseTypeAttr(psAttrSelf2);
+ if (pITISelf2)
+ pITISelf2->Release();
+ if (psAttrBase)
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ if (pITIBase)
+ pITIBase->Release();
+ return (hr);
+} // HRESULT CImportTlb::ConvIface()
+
+//*****************************************************************************
+// Convert the metadata for a dispinterface. Try to convert as a normal
+// interface.
+//*****************************************************************************
+HRESULT CImportTlb::ConvDispatch( // S_OK or error.
+ ITypeInfo *pITI, // ITypeInfo* to convert.
+ TYPEATTR *psAttr, // TYPEATTR of TypeInfo.
+ BOOL bVtblGapFuncs) // Vtable gap functions for interface implementations?
+{
+ HRESULT hr; // A result.
+ HREFTYPE href; // Base interface href.
+ ITypeInfo *pITIBase=0; // Base interface ITypeInfo.
+ TYPEATTR *psAttrBase=0; // TYPEATTR of base interface.
+ mdMemberRef mr; // MemberRef for custom value.
+ DWORD attr[2] = {0x00010001, 0x00000002};
+ BYTE bIface = ifDispatch; // Custom value means "dispinterface"
+ BOOL fInheritsIEnum = FALSE;
+
+ // If this is a dual interface, treat it like a normal interface.
+ if ((psAttr->wTypeFlags & TYPEFLAG_FDUAL))
+ {
+ hr = ConvIface(pITI, psAttr, bVtblGapFuncs);
+ goto ErrExit;
+ }
+
+ if (ExplicitlyImplementsIEnumerable(pITI, psAttr) == S_OK)
+ fInheritsIEnum = TRUE;
+
+ // If there is a vtable view of this interface (funky dispinterface).
+ // @FUTURE: what would be really nice here would be an alias mechanism, so that we could
+ // just point this dispinterface to that other interface, in those situations that it
+ // is dual. OTOH, that is probably pretty rare, because if that other interface
+ // were dual, why would the dispinterface even be needed?
+ if (pITI->GetRefTypeOfImplType(-1, &href) == S_OK)
+ {
+ IfFailGo(pITI->GetRefTypeInfo(href, &pITIBase));
+ IfFailGo(pITIBase->GetTypeAttr(&psAttrBase));
+ IfFailGo(_ConvIfaceMembers(pITIBase, psAttrBase, bVtblGapFuncs, TRUE, fInheritsIEnum));
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ psAttrBase = 0;
+ pITIBase->Release();
+ pITIBase = 0;
+ goto ErrExit;
+ }
+
+ // If not processing an implemented interface, mark the interface type.
+ if (m_ImplIface == eImplIfaceNone)
+ {
+ // If this interface has a NewEnum member then have it implement IEnumerable.
+ if ((S_OK == HasNewEnumMember(pITI)) && !fInheritsIEnum)
+ {
+ mdToken tkImpl[2] = {0,0};
+ IfFailGo(GetKnownTypeToken(VT_SLOT_FOR_IENUMERABLE, &tkImpl[0]));
+ IfFailGo(m_pEmit->SetTypeDefProps(m_tdTypeDef, ULONG_MAX, ULONG_MAX, tkImpl));
+ }
+
+ // Note that this is a dispinterface.
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(short));
+ // Set up the attribute.
+ BUILD_CUSTOM_ATTRIBUTE(short, ifDispatch);
+ // Store the attribute
+ IfFailGo(GetAttrType(ATTR_INTERFACETYPE, &mr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, mr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+ }
+
+ IfFailGo(_ConvDispatchMembers(pITI, psAttr, fInheritsIEnum));
+
+ErrExit:
+ if (psAttrBase)
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ if (pITIBase)
+ pITIBase->Release();
+ return (hr);
+} // HRESULT CImportTlb::ConvDispatch()
+
+//*****************************************************************************
+// Determine if an interface is derived from IUnknown.
+//*****************************************************************************
+HRESULT CImportTlb::IsIUnknownDerived(
+ ITypeInfo *pITI, // The containing ITypeInfo.
+ TYPEATTR *psAttr) // The ITypeInfo's TYPEATTR
+{
+ HRESULT hr=S_OK; // A result.
+
+ HREFTYPE href; // Base interface href.
+ ITypeInfo *pITIBase=0; // Base interface ITypeInfo.
+ TYPEATTR *psAttrBase=0; // TYPEATTR of base interface.
+
+ // This should never be called on CoClasses.
+ _ASSERTE(psAttr->typekind != TKIND_COCLASS);
+
+ // If IDispatch or IUnknown, we've recursed far enough.
+ if (IsEqualGUID(psAttr->guid, IID_IUnknown) || IsEqualGUID(psAttr->guid, IID_IDispatch))
+ {
+ goto ErrExit;
+ }
+
+ // Handle base interface.
+ if (psAttr->cImplTypes == 1)
+ {
+ IfFailGo(pITI->GetRefTypeOfImplType(0, &href));
+ IfFailGo(pITI->GetRefTypeInfo(href, &pITIBase));
+ IfFailGo(pITIBase->GetTypeAttr(&psAttrBase));
+
+ // IUnknow derived if base interface is.
+ hr = IsIUnknownDerived(pITIBase, psAttrBase);
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ psAttrBase = 0;
+ pITIBase->Release();
+ pITIBase = 0;
+ }
+ else
+ { // No base interface, not IUnknown, not IDispatch. Not very COM-ish, so don't try to handle.
+ hr = S_FALSE;
+ }
+
+ErrExit:
+ if (psAttrBase)
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ if (pITIBase)
+ pITIBase->Release();
+ return (hr);
+} // HRESULT CImportTlb::IsIUnknownDerived()
+
+//*****************************************************************************
+// Determine if an interface is derived from IDispatch. Note that a pure
+// dispinterface doesn't derive from IDispatch.
+//*****************************************************************************
+HRESULT CImportTlb::IsIDispatchDerived(
+ ITypeInfo *pITI, // The containing ITypeInfo.
+ TYPEATTR *psAttr) // The ITypeInfo's TYPEATTR
+{
+ HRESULT hr=S_OK; // A result.
+
+ HREFTYPE href; // Base interface href.
+ ITypeInfo *pITIBase=0; // Base interface ITypeInfo.
+ TYPEATTR *psAttrBase=0; // TYPEATTR of base interface.
+
+ // If IDispatch, we've recursed far enough.
+ if (IsEqualGUID(psAttr->guid, IID_IDispatch))
+ {
+ goto ErrExit;
+ }
+
+ if (psAttr->typekind == TKIND_DISPATCH)
+ {
+ IfFailGo(pITI->GetRefTypeOfImplType(-1, &href));
+ IfFailGo(pITI->GetRefTypeInfo(href, &pITIBase));
+ IfFailGo(pITIBase->GetTypeAttr(&psAttrBase));
+
+ // IDispatch derived if base interface is.
+ hr = IsIDispatchDerived(pITIBase, psAttrBase);
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ psAttrBase = 0;
+ pITIBase->Release();
+ pITIBase = 0;
+
+ goto ErrExit;
+ }
+
+ // Handle base interface.
+ if (psAttr->cImplTypes == 1)
+ {
+ IfFailGo(pITI->GetRefTypeOfImplType(0, &href));
+ IfFailGo(pITI->GetRefTypeInfo(href, &pITIBase));
+ IfFailGo(pITIBase->GetTypeAttr(&psAttrBase));
+
+ // IDispatch derived if base interface is.
+ hr = IsIDispatchDerived(pITIBase, psAttrBase);
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ psAttrBase = 0;
+ pITIBase->Release();
+ pITIBase = 0;
+ }
+ else
+ { // No base interface, not IDispatch. Done.
+ hr = S_FALSE;
+ }
+
+ErrExit:
+ if (psAttrBase)
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ if (pITIBase)
+ pITIBase->Release();
+ return (hr);
+} // HRESULT CImportTlb::IsIDispatchDerived()
+
+//*****************************************************************************
+// Determine if an interface has a member with a DISPID of DISPID_NEWENUM.
+//*****************************************************************************
+HRESULT CImportTlb::HasNewEnumMember( // S_OK if has NewEnum, S_FALSE otherwise.
+ ITypeInfo *pItfTI) // The interface in question.
+{
+ HRESULT hr = S_OK; // A result.
+ BOOL bHasNewEnumMember=FALSE;// If true, has a NewEnum
+ TYPEATTR *pAttr = NULL; // A TypeInfo's typeattr
+ FUNCDESC *pFuncDesc = NULL; // A Function's FuncDesc
+ VARDESC *pVarDesc = NULL; // A properties VarDesc
+ int i; // Loop control.
+ ITypeInfo *pITISelf2=0; // Partner interface.
+ HREFTYPE href; // HREF of partner.
+ WCHAR IEnumCA[] = W("{CD2BC5C9-F452-4326-B714-F9C539D4DA58}");
+
+
+ // If there is a partner interface, prefer it.
+ if (pItfTI->GetRefTypeOfImplType(-1, &href) == S_OK)
+ {
+ IfFailGo(pItfTI->GetRefTypeInfo(href, &pITISelf2));
+ pItfTI = pITISelf2;
+ }
+
+ // Retrieve the attributes of the interface.
+ IfFailGo(pItfTI->GetTypeAttr(&pAttr));
+
+ if ((pAttr->typekind == TKIND_DISPATCH) || ((pAttr->typekind == TKIND_INTERFACE) && (IsIDispatchDerived(pItfTI, pAttr) == S_OK)))
+ {
+ // Check to see if the ForceIEnumerable custom value exists on the type
+ _ForceIEnumerableCVExists(pItfTI, &bHasNewEnumMember);
+
+ // Check to see if the interface has a function with a DISPID of DISPID_NEWENUM.
+ for (i = 0; i < pAttr->cFuncs; i++)
+ {
+ IfFailGo(TryGetFuncDesc(pItfTI, i, &pFuncDesc));
+
+ if (FuncIsNewEnum(pItfTI, pFuncDesc, i) == S_OK)
+ {
+ // Throw a warning if we find more than one func with DISPID_NEWENUM.
+ if (bHasNewEnumMember == TRUE)
+ {
+ BSTR ObjectName;
+ pItfTI->GetDocumentation(-1, &ObjectName, NULL, NULL, NULL);
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_INVALID_TYPEINFO, ObjectName);
+ SysFreeString(ObjectName);
+ }
+
+ // The interface has a function with a DISPID of DISPID_NEWENUM.
+ bHasNewEnumMember = TRUE;
+ break;
+ }
+
+ pItfTI->ReleaseFuncDesc(pFuncDesc);
+ pFuncDesc = NULL;
+ }
+
+ // Check to see if the interface as a property with a DISPID of DISPID_NEWENUM.
+ for (i = 0; i < pAttr->cVars; i++)
+ {
+ IfFailGo(pItfTI->GetVarDesc(i, &pVarDesc));
+
+ if (PropertyIsNewEnum(pItfTI, pVarDesc, i) == S_OK)
+ {
+ // Throw a warning if we find more than one func with DISPID_NEWENUM.
+ if (bHasNewEnumMember == TRUE)
+ {
+ BSTR ObjectName;
+ pItfTI->GetDocumentation(-1, &ObjectName, NULL, NULL, NULL);
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_INVALID_TYPEINFO, ObjectName);
+ SysFreeString(ObjectName);
+ }
+
+ // The interface has a property with a DISPID of DISPID_NEWENUM.
+ bHasNewEnumMember = TRUE;
+ break;
+ }
+
+ pItfTI->ReleaseVarDesc(pVarDesc);
+ pVarDesc = NULL;
+ }
+ }
+ else
+ {
+ // Check to see if the ForceIEnumerable custom value exists on the type
+ // If it does, spit out a warning.
+ _ForceIEnumerableCVExists(pItfTI, &bHasNewEnumMember);
+
+ if (bHasNewEnumMember)
+ {
+ // Invalid custom attribute on the iface.
+ BSTR CustomValue = SysAllocString((const WCHAR*)&IEnumCA[0]);
+ BSTR ObjectName;
+ pItfTI->GetDocumentation(-1, &ObjectName, NULL, NULL, NULL);
+
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_W_IENUM_CA_ON_IUNK, CustomValue, ObjectName);
+
+ SysFreeString(CustomValue);
+ SysFreeString(ObjectName);
+
+ bHasNewEnumMember = FALSE;
+ }
+ }
+
+ hr = bHasNewEnumMember ? S_OK : S_FALSE;
+
+ErrExit:
+ if (pAttr)
+ pItfTI->ReleaseTypeAttr(pAttr);
+ if (pFuncDesc)
+ pItfTI->ReleaseFuncDesc(pFuncDesc);
+ if (pVarDesc)
+ pItfTI->ReleaseVarDesc(pVarDesc);
+ if (pITISelf2)
+ pITISelf2->Release();
+ return hr;
+} // HRESULT CImportTlb::HasNewEnumMember(ITypeInfo *pItfTI)
+
+//*****************************************************************************
+// Determine if a given function is a valid NewEnum member.
+//*****************************************************************************
+HRESULT CImportTlb::FuncIsNewEnum( // S_OK if the function is the NewEnum member S_FALSE otherwise.
+ ITypeInfo *pITI, // The ITypeInfo that contains the function.
+ FUNCDESC *pFuncDesc, // The function in question.
+ DWORD index) // The function index
+{
+
+ HRESULT hr = S_OK;
+ BOOL bIsValidNewEnum = FALSE;
+ TYPEDESC* pType = NULL;
+ TYPEATTR* pAttr = NULL;
+ ITypeInfo* pITIUD = NULL;
+ long lDispSet = 0;
+
+ _GetDispIDCA(pITI, index, &lDispSet, TRUE);
+
+ if ((pFuncDesc->memid == DISPID_NEWENUM) || (lDispSet == DISPID_NEWENUM))
+ {
+ if (pFuncDesc->funckind == FUNC_DISPATCH)
+ {
+ if ((pFuncDesc->invkind == INVOKE_PROPERTYGET) || (pFuncDesc->invkind == INVOKE_FUNC))
+ {
+ if (pFuncDesc->cParams == 0)
+ {
+ pType = &pFuncDesc->elemdescFunc.tdesc;
+ }
+ else if ((m_bTransformDispRetVals) && (pFuncDesc->cParams == 1) && (pFuncDesc->lprgelemdescParam[0].paramdesc.wParamFlags & PARAMFLAG_FRETVAL))
+ {
+ pType = pFuncDesc->lprgelemdescParam[0].tdesc.lptdesc;
+ }
+ }
+ }
+ else if (pFuncDesc->funckind == FUNC_PUREVIRTUAL)
+ {
+ if ((pFuncDesc->cParams == 1) &&
+ ((pFuncDesc->invkind == INVOKE_PROPERTYGET) || (pFuncDesc->invkind == INVOKE_FUNC)) &&
+ (pFuncDesc->lprgelemdescParam[0].paramdesc.wParamFlags & PARAMFLAG_FRETVAL) &&
+ (pFuncDesc->lprgelemdescParam[0].tdesc.vt == VT_PTR))
+ {
+ pType = pFuncDesc->lprgelemdescParam[0].tdesc.lptdesc;
+ }
+ }
+
+ if (pType)
+ {
+ if (pType->vt == VT_UNKNOWN || pType->vt == VT_DISPATCH)
+ {
+ // The member returns an IUnknown * or an IDispatch * which is valid.
+ bIsValidNewEnum = TRUE;
+ }
+ else if (pType->vt == VT_PTR)
+ {
+ pType = pType->lptdesc;
+ if (pType->vt == VT_USERDEFINED)
+ {
+ IfFailGo(pITI->GetRefTypeInfo(pType->hreftype, &pITIUD));
+ IfFailGo(pITIUD->GetTypeAttr(&pAttr));
+ if (IsEqualGUID(pAttr->guid, IID_IEnumVARIANT) ||
+ IsEqualGUID(pAttr->guid, IID_IUnknown) ||
+ IsEqualGUID(pAttr->guid, IID_IDispatch))
+ {
+ // The member returns a valid interface type for a NewEnum member.
+ bIsValidNewEnum = TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ErrExit:
+ if (pAttr)
+ pITIUD->ReleaseTypeAttr(pAttr);
+ if (pITIUD)
+ pITIUD->Release();
+
+ if (FAILED(hr))
+ return hr;
+ else
+ return bIsValidNewEnum ? S_OK : S_FALSE;
+} // HRESULT CImportTlb::FuncIsNewEnum(FUNCDESC *pFuncDesc)
+
+//*****************************************************************************
+// Determine if a given function is a valid NewEnum member.
+//*****************************************************************************
+HRESULT CImportTlb::PropertyIsNewEnum( // S_OK if the function is the NewEnum member S_FALSE otherwise.
+ ITypeInfo *pITI, // The ITypeInfo that contains the property.
+ VARDESC *pVarDesc, // The function in question.
+ DWORD index) // The property index.
+{
+ HRESULT hr = S_OK;
+ BOOL bIsValidNewEnum = FALSE;
+ TYPEDESC* pType = NULL;
+ TYPEATTR* pAttr = NULL;
+ ITypeInfo* pITIUD = NULL;
+ long lDispSet = 0;
+
+ _GetDispIDCA(pITI, index, &lDispSet, FALSE);
+
+ if ( ((pVarDesc->memid == DISPID_NEWENUM) || (lDispSet == DISPID_NEWENUM)) &&
+ (pVarDesc->elemdescVar.paramdesc.wParamFlags & PARAMFLAG_FRETVAL) &&
+ (pVarDesc->wVarFlags & VARFLAG_FREADONLY))
+ {
+ pType = &pVarDesc->elemdescVar.tdesc;
+ if (pType->vt == VT_UNKNOWN || pType->vt == VT_DISPATCH)
+ {
+ // The member returns an IUnknown * or an IDispatch * which is valid.
+ bIsValidNewEnum = TRUE;
+ }
+ else if (pType->vt == VT_PTR)
+ {
+ pType = pType->lptdesc;
+ if (pType->vt == VT_USERDEFINED)
+ {
+ IfFailGo(pITI->GetRefTypeInfo(pType->hreftype, &pITIUD));
+ IfFailGo(pITIUD->GetTypeAttr(&pAttr));
+ if (IsEqualGUID(pAttr->guid, IID_IEnumVARIANT) ||
+ IsEqualGUID(pAttr->guid, IID_IUnknown) ||
+ IsEqualGUID(pAttr->guid, IID_IDispatch))
+ {
+ // The member returns a valid interface type for a NewEnum member.
+ bIsValidNewEnum = TRUE;
+ }
+ }
+ }
+ }
+
+ErrExit:
+ if (pAttr)
+ pITIUD->ReleaseTypeAttr(pAttr);
+ if (pITIUD)
+ pITIUD->Release();
+
+ if (FAILED(hr))
+ return hr;
+ else
+ return bIsValidNewEnum ? S_OK : S_FALSE;
+} // HRESULT CImportTlb::FuncIsNewEnum(FUNCDESC *pFuncDesc)
+
+//*****************************************************************************
+// Determine is a TypeInfo has any object fields.
+//*****************************************************************************
+HRESULT CImportTlb::HasObjectFields( // S_OK, S_FALSE, or error.
+ ITypeInfo *pITI, // The TypeInfo in question.
+ TYPEATTR *psAttr) // Attributes of the typeinfo.
+{
+ HRESULT hr; // A result.
+
+ int i; // Loop control.
+ VARDESC *psVar=0; // VARDESC for a member.
+
+ // Iterate over the vars.
+ for (i=0; i<psAttr->cVars; ++i)
+ {
+ // Get variable information.
+ IfFailGo(pITI->GetVarDesc(i, &psVar));
+
+ // See if it is an object type.
+ IfFailGo(IsObjectType(pITI, &psVar->elemdescVar.tdesc));
+ // If result is S_FALSE, not an Object; keep looking.
+ if (hr == S_OK)
+ goto ErrExit;
+
+ // Release for next var.
+ pITI->ReleaseVarDesc(psVar);
+ psVar = 0;
+ }
+
+ hr = S_FALSE;
+
+ErrExit:
+ if (psVar)
+ pITI->ReleaseVarDesc(psVar);
+ return hr;
+} // HRESULT CImportTlb::HasObjectFields()
+
+//*****************************************************************************
+// Is a given type an Object type?
+//*****************************************************************************
+HRESULT CImportTlb::IsObjectType( // S_OK, S_FALSE, or error.
+ ITypeInfo *pITI, // The TypeInfo in question.
+ const TYPEDESC *pType) // The type.
+{
+ HRESULT hr; // A result.
+ TYPEDESC tdTemp; // Copy of TYPEDESC, for R/W.
+ ITypeInfo *pITIAlias=0; // Typeinfo of the aliased type.
+ TYPEATTR *psAttrAlias=0; // TYPEATTR of the aliased typeinfo.
+ int bObjectField=false; // The question to be answered.
+ int iByRef=0; // Indirection.
+
+ // Strip off leading VT_PTR and VT_BYREF
+ while (pType->vt == VT_PTR)
+ pType = pType->lptdesc, ++iByRef;
+ if (pType->vt & VT_BYREF)
+ {
+ tdTemp = *pType;
+ tdTemp.vt &= ~VT_BYREF;
+ pType = &tdTemp;
+ ++iByRef;
+ }
+
+ // Determine if the field is/has object type.
+ switch (pType->vt)
+ {
+ case VT_PTR:
+ _ASSERTE(!"Should not have VT_PTR here");
+ break;
+
+ // These are object types.
+ case VT_BSTR:
+ case VT_DISPATCH:
+ case VT_VARIANT:
+ case VT_UNKNOWN:
+ case VT_SAFEARRAY:
+ case VT_LPSTR:
+ case VT_LPWSTR:
+ bObjectField = true;
+ break;
+
+ // A user-defined may or may not be/contain Object type.
+ case VT_USERDEFINED:
+ // User defined type. Get the TypeInfo.
+ IfFailGo(pITI->GetRefTypeInfo(pType->hreftype, &pITIAlias));
+ IfFailGo(pITIAlias->GetTypeAttr(&psAttrAlias));
+
+ // Some user defined class. Is it a value class, or a VOS class?
+ switch (psAttrAlias->typekind)
+ {
+ // Alias -- Is the aliased thing an Object type?
+ case TKIND_ALIAS:
+ hr = IsObjectType(pITIAlias, &psAttrAlias->tdescAlias);
+ goto ErrExit;
+ // Record/Enum/Union -- Does it contain an Object type?
+ case TKIND_RECORD:
+ case TKIND_ENUM:
+ case TKIND_UNION:
+ // Byref/Ptrto record is Object. Contained record might be.
+ if (iByRef)
+ bObjectField = true;
+ else
+ {
+ hr = HasObjectFields(pITIAlias, psAttrAlias);
+ goto ErrExit;
+ }
+ break;
+ // Class/Interface -- An Object Type.
+ case TKIND_INTERFACE:
+ case TKIND_DISPATCH:
+ case TKIND_COCLASS:
+ bObjectField = true;
+ break;
+ default:
+ //case TKIND_MODULE: -- can't pass one of these as a parameter.
+ _ASSERTE(!"Unexpected typekind for user defined type");
+ bObjectField = true;
+ } // switch (psAttrAlias->typekind)
+ break;
+
+ case VT_CY:
+ case VT_DATE:
+ case VT_DECIMAL:
+ // Pointer to the value type is an object. Contained one isn't.
+ if (iByRef)
+ bObjectField = true;
+ else
+ bObjectField = false;
+ break;
+
+ // A fixed array is an Object type.
+ case VT_CARRAY:
+ bObjectField = true;
+ break;
+
+ // Other types I4, etc., are not Object types.
+ default:
+ bObjectField = false;
+ break;
+ } // switch (vt=pType->vt)
+
+
+ hr = bObjectField ? S_OK : S_FALSE;
+
+ErrExit:
+ if (psAttrAlias)
+ pITIAlias->ReleaseTypeAttr(psAttrAlias);
+ if (pITIAlias)
+ pITIAlias->Release();
+
+ return hr;
+} // HRESULT CImportTlb::IsObjectType()
+
+//*****************************************************************************
+// Convert the functions on an interface. Convert the functions on the
+// base interface first, because in COM Classic, parent's functions are also
+// in the derived interface's vtable.
+//*****************************************************************************
+HRESULT CImportTlb::_ConvIfaceMembers(
+ ITypeInfo *pITI, // The containing ITypeInfo.
+ TYPEATTR *psAttr, // The ITypeInfo's TYPEATTR
+ BOOL bVtblGapFuncs, // Add functions for vtblGaps?
+ BOOL bAddDispIds, // Add DispIds to the member?
+ BOOL bInheritsIEnum) // Inherits from IEnumerable.
+{
+ HRESULT hr=S_OK; // A result.
+ int i; // Loop control.
+ FUNCDESC *psFunc=0; // FUNCDESC for a member.
+
+ HREFTYPE href; // Base interface href.
+ ITypeInfo *pITIBase=0; // Base interface ITypeInfo.
+ TYPEATTR *psAttrBase=0; // TYPEATTR of base interface.
+ BOOL bConversionLoss=false; // If true, some attributes were lost on conversion.
+
+ _ASSERTE( (psAttr->typekind == TKIND_INTERFACE) || (psAttr->typekind == TKIND_DISPATCH) );
+
+ // If IDispatch or IUnknown, we've recursed far enough.
+ if (IsEqualGUID(psAttr->guid, IID_IUnknown) || IsEqualGUID(psAttr->guid, IID_IDispatch))
+ {
+ if (m_cbVtableSlot == 0)
+ {
+ m_cbVtableSlot = psAttr->cbSizeInstance;
+ }
+ m_Slot = (psAttr->cbSizeVft / m_cbVtableSlot);
+ goto ErrExit;
+ }
+
+ // Handle base interface.
+ if (psAttr->cImplTypes == 1)
+ {
+ IfFailGo(pITI->GetRefTypeOfImplType(0, &href));
+ IfFailGo(pITI->GetRefTypeInfo(href, &pITIBase));
+ IfFailGo(pITIBase->GetTypeAttr(&psAttrBase));
+
+ IfFailGo(_ConvIfaceMembers(pITIBase, psAttrBase, bVtblGapFuncs, bAddDispIds, bInheritsIEnum));
+
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ psAttrBase = 0;
+ pITIBase->Release();
+ pITIBase = 0;
+ }
+ else
+ { // No base interface, not IUnknown, not IDispatch. We shouldn't be here.
+ m_Slot = 0;
+ if (m_cbVtableSlot == 0)
+ {
+ m_cbVtableSlot = psAttr->cbSizeInstance;
+ }
+ _ASSERTE(!"Interface does not derive from IUnknown.");
+ }
+
+ // Loop over functions.
+ IfFailGo(_FindFirstUserMethod(pITI, psAttr, &i));
+ IfFailGo(BuildMemberList(pITI, i, psAttr->cFuncs, bInheritsIEnum));
+
+ BOOL bAllowIEnum = !bInheritsIEnum;
+ for (i=0; i<(int)m_MemberList.Size(); ++i)
+ {
+ // Convert the function.
+ IfFailGo(_ConvFunction(pITI, &m_MemberList[i], bVtblGapFuncs, bAddDispIds, FALSE, &bAllowIEnum));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+ }
+
+ // Add the property info.
+ IfFailGo(_ConvPropertiesForFunctions(pITI, psAttr));
+
+ if (bConversionLoss)
+ hr = S_CONVERSION_LOSS;
+
+ErrExit:
+ // Release FuncDescs.
+ FreeMemberList(pITI);
+
+ if (psAttrBase)
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ if (pITIBase)
+ pITIBase->Release();
+ if (psFunc)
+ pITI->ReleaseFuncDesc(psFunc);
+ return (hr);
+} // HRESULT CImportTlb::_ConvIfaceMembers()
+
+//*****************************************************************************
+// Convert the functions on a source interface to add_ and remove_ method.
+// Convert the functions on the base interface first, because in COM Classic,
+// parent's functions are also in the derived interface's vtable.
+//*****************************************************************************
+HRESULT CImportTlb::_ConvSrcIfaceMembers(
+ ITypeInfo *pITI, // The containing ITypeInfo.
+ TYPEATTR *psAttr, // The ITypeInfo's TYPEATTR
+ BOOL fInheritsIEnum)
+{
+ HRESULT hr=S_OK; // A result.
+ int i; // Loop control.
+ FUNCDESC *psFunc=0; // FUNCDESC for a member.
+ HREFTYPE href; // Base interface href.
+ ITypeInfo *pITIBase=0; // Base interface ITypeInfo.
+ TYPEATTR *psAttrBase=0; // TYPEATTR of base interface.
+ BOOL bConversionLoss=false; // If true, some attributes were lost on conversion.
+
+ _ASSERTE( (psAttr->typekind == TKIND_INTERFACE) || (psAttr->typekind == TKIND_DISPATCH) );
+
+ // If IDispatch or IUnknown, we've recursed far enough.
+ if (IsEqualGUID(psAttr->guid, IID_IUnknown) || IsEqualGUID(psAttr->guid, IID_IDispatch))
+ {
+ if (m_cbVtableSlot == 0)
+ {
+ m_cbVtableSlot = psAttr->cbSizeInstance;
+ }
+ m_Slot = (psAttr->cbSizeVft / m_cbVtableSlot);
+ goto ErrExit;
+ }
+
+ // Handle base interface.
+ if (psAttr->cImplTypes == 1)
+ {
+ IfFailGo(pITI->GetRefTypeOfImplType(0, &href));
+ IfFailGo(pITI->GetRefTypeInfo(href, &pITIBase));
+ IfFailGo(pITIBase->GetTypeAttr(&psAttrBase));
+
+ IfFailGo(_ConvSrcIfaceMembers(pITIBase, psAttrBase, fInheritsIEnum));
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ psAttrBase = 0;
+ pITIBase->Release();
+ pITIBase = 0;
+ }
+ else
+ { // No base interface, not IUnknown, not IDispatch. We shouldn't be here.
+ m_Slot = 0;
+ if (m_cbVtableSlot == 0)
+ {
+ m_cbVtableSlot = psAttr->cbSizeInstance;
+ }
+ _ASSERTE(!"Interface does not derive from IUnknown.");
+ }
+
+ // Loop over functions.
+ IfFailGo(_FindFirstUserMethod(pITI, psAttr, &i));
+ IfFailGo(BuildMemberList(pITI, i, psAttr->cFuncs, fInheritsIEnum));
+
+ // If we have any properties, we want to skip them. Should we add gaps?
+ if (m_cMemberProps != 0)
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_W_NO_PROPS_IN_EVENTS, m_szName);
+ bConversionLoss = true;
+ }
+
+ for (i = m_cMemberProps; i<(int)m_MemberList.Size(); ++i)
+ {
+ // Convert the function.
+ IfFailGo(_GenerateEvent(pITI, &m_MemberList[i], fInheritsIEnum));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+ }
+
+ if (bConversionLoss)
+ hr = S_CONVERSION_LOSS;
+
+ErrExit:
+ // Release FuncDescs.
+ FreeMemberList(pITI);
+
+ if (psAttrBase)
+ pITIBase->ReleaseTypeAttr(psAttrBase);
+ if (pITIBase)
+ pITIBase->Release();
+ if (psFunc)
+ pITI->ReleaseFuncDesc(psFunc);
+ return (hr);
+} // HRESULT CImportTlb::_ConvIfaceMembers()
+
+//*****************************************************************************
+// Add the property definitions for property functions.
+//*****************************************************************************
+HRESULT CImportTlb::_ConvPropertiesForFunctions(
+ ITypeInfo *pITI, // ITypeInfo* being converted.
+ TYPEATTR *psAttr) // TypeAttr for the typeinfo.
+{
+ HRESULT hr=S_OK; // A result.
+ int ix; // Loop control.
+ int ix2; // More loop control.
+ mdProperty pd; // A property token.
+ USHORT ms; // Some method's semantics.
+ mdToken tk; // A method's token.
+ mdMethodDef mdFuncs[6] ={0}; // Array of setter, getter, other.
+ FUNCDESC *psF=0; // FUNCDESC of Get, Put, or PutRef.
+ TYPEDESC *pProperty; // TYPEDESC of property type.
+ BOOL bPropRetval; // Is the property type a [retval]?
+ ULONG ixValue; // Index of the value parameter for putters.
+ int ixVarArg; // Index of vararg param, if any.
+ CQuickBytes qbComSig; // new signature
+ BYTE *pbSig; // Pointer into the signature.
+ ULONG sigFlags; // Signature handling flags.
+ ULONG cbTotal; // Size of the signature.
+ ULONG cb; // Size of a signature element.
+ LPWSTR pszName; // Possibly decorated name of property.
+ CQuickArray<WCHAR> qbName; // Buffer for name decoration.
+ int iSrcParam; // Param count, as looping through params.
+ int cDestParams; // Count of destination params.
+ CQuickArray<BYTE> qbDummyNativeTypeBuf; // A dummy native type array.
+ ULONG iNativeOfs=0; // Current offset in native type buffer.
+ BOOL bNewEnumMember=FALSE; // Is this a NewEnum property?
+ BOOL bConversionLoss=FALSE; // Was some type not fully converted?
+ int cFound; // Functions found matching a given property.
+
+ // Using semantics as an index, so be sure array is big enough.
+ _ASSERTE(lengthof(mdFuncs) > msOther);
+
+ for (ix=m_cMemberProps; ix<(int)m_MemberList.Size(); ++ix)
+ { // See if this one needs to be processed.
+ if (m_MemberList[ix].m_mdFunc == 0)
+ continue;
+
+ MemberInfo *pMember = &m_MemberList[ix];
+ pMember->GetFuncInfo(tk, ms);
+
+ // Get the name.
+ if (m_szMember)
+ ::SysFreeString(m_szMember), m_szMember = 0;
+ IfFailGo(pITI->GetDocumentation(pMember->m_psFunc->memid, &m_szMember, 0,0,0));
+
+ // Found one. Put in the right slot.
+ _ASSERTE(ms == msGetter || ms == msSetter || ms==msOther);
+ mdFuncs[msSetter] = mdFuncs[msGetter] = mdFuncs[msOther] = 0;
+ mdFuncs[ms] = tk;
+ pMember->m_mdFunc = 0;
+
+ // Look for related functions.
+ cFound = 1;
+ for (ix2=ix+1; ix2<(int)m_MemberList.Size(); ++ix2)
+ {
+ MemberInfo *pMember2 = &m_MemberList[ix2];
+ if (pMember2->m_mdFunc != 0 && pMember2->m_psFunc->memid == pMember->m_psFunc->memid)
+ { // Found a related function.
+ pMember2->GetFuncInfo(tk, ms);
+ _ASSERTE(ms == msGetter || ms == msSetter || ms==msOther);
+ _ASSERTE(mdFuncs[ms] == 0);
+ mdFuncs[ms] = tk;
+ pMember2->m_mdFunc = 0;
+ // If have found all three, don't bother looking for more.
+ if (++cFound == 3)
+ break;
+ }
+ }
+
+ // Build the signature for the property.
+ hr = _GetFunctionPropertyInfo(pMember->m_psFunc, &ms, &psF, &pProperty, &bPropRetval, TRUE, m_szMember);
+
+ // The function really should have a property associated with it, to get here. Check anyway.
+ _ASSERTE(pProperty);
+ if (!pProperty)
+ continue;
+
+ // Some sort of property accessor.
+ IfFailGo(qbComSig.ReSizeNoThrow(CB_MAX_ELEMENT_TYPE + 1));
+ pbSig = (BYTE *)qbComSig.Ptr();
+ cbTotal = cb = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_PROPERTY, pbSig);
+ // Count of parameters.
+
+ // If this is a getter, see if there is a retval.
+ if (psF->invkind == INVOKE_PROPERTYGET)
+ { // Examine each param, and count all except the [retval].
+ for (cDestParams=iSrcParam=0; iSrcParam<psF->cParams; ++iSrcParam)
+ {
+ if ((psF->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & NON_CONVERTED_PARAMS_FLAGS) == 0)
+ ++cDestParams;
+ }
+ // There is no new value param for getters.
+ ixValue = -1;
+ }
+ else
+ {
+ // This is a putter, so 1 param is new value, others are indices (or lcid).
+ for (cDestParams=iSrcParam=0; iSrcParam<psF->cParams-1; ++iSrcParam)
+ {
+ if ((psF->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & NON_CONVERTED_PARAMS_FLAGS) == 0)
+ ++cDestParams;
+ }
+ // The last parameter is the new value.
+ ixValue = psF->cParams - 1;
+ }
+
+ //-------------------------------------------------------------------------
+ // See if there is a vararg param.
+ ixVarArg = psF->cParams + 1;
+ if (psF->cParamsOpt == -1)
+ {
+ // If this is a PROPERTYPUT or PROPERTYPUTREF, skip the last non-retval parameter (it
+ // is the new value to be set).
+ BOOL bPropVal = (psF->invkind & (INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF)) ? TRUE : FALSE;
+ // Find the vararg param.
+ for (iSrcParam=psF->cParams-1; iSrcParam>=0; --iSrcParam)
+ {
+ // The count of optional params does not include any lcid params, nor does
+ // it include the return value, so skip those.
+ if ((psF->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & (PARAMFLAG_FRETVAL|PARAMFLAG_FLCID)) != 0)
+ continue;
+ // If haven't yet seen the property value, this param is it, so skip it, too.
+ if (bPropVal)
+ {
+ bPropVal = FALSE;
+ continue;
+ }
+ ixVarArg = iSrcParam;
+ break;
+ } // for (iSrcParam=cParams-1...
+ }
+
+ // Put in the count of index parameters.
+ _ASSERTE(cDestParams >= 0);
+ cb = CorSigCompressData(cDestParams, &pbSig[cbTotal]);
+ cbTotal += cb;
+
+ // Create the signature for the property type.
+ sigFlags = SIG_ELEM | (bPropRetval ? SIG_RET : (SigFlags)0);
+ IfFailGo(_ConvSignature(pITI, pProperty, sigFlags, qbComSig, cbTotal, &cbTotal, qbDummyNativeTypeBuf, 0, &iNativeOfs, bNewEnumMember));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+
+ // Fill in the "index" part of the property's signature.
+ for (iSrcParam=0; iSrcParam<psF->cParams; ++iSrcParam)
+ {
+ if (psF->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & NON_CONVERTED_PARAMS_FLAGS)
+ continue;
+ if (iSrcParam == static_cast<int>(ixValue))
+ continue;
+ sigFlags = SIG_FUNC | SIG_USE_BYREF;
+ if (iSrcParam == ixVarArg)
+ sigFlags |= SIG_VARARG;
+ IfFailGo(_ConvSignature(pITI, &psF->lprgelemdescParam[iSrcParam].tdesc, sigFlags, qbComSig, cbTotal, &cbTotal, qbDummyNativeTypeBuf, 0, &iNativeOfs, bNewEnumMember));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+ }
+
+ // Get the property name. Add interface name and make unique, if needed.
+ // m_szInterface should be non-null if processing an implemented interface; should be null otherwise.
+ _ASSERTE(m_ImplIface == eImplIfaceNone || m_szInterface != 0);
+ IfFailGo(qbName.ReSizeNoThrow(wcslen(m_szMember)+2));
+ wcscpy_s(qbName.Ptr(), wcslen(m_szMember)+2, m_szMember);
+ IfFailGo(GenerateUniqueMemberName(qbName, (PCCOR_SIGNATURE)qbComSig.Ptr(), cbTotal, m_szInterface, mdtProperty));
+ pszName = qbName.Ptr();
+
+ // Define the property.
+ IfFailGo(m_pEmit->DefineProperty(m_tdTypeDef, pszName, 0/*dwFlags*/,
+ (PCCOR_SIGNATURE) qbComSig.Ptr(), cbTotal, 0, 0, -1,
+ mdFuncs[msSetter], mdFuncs[msGetter], &mdFuncs[msOther],
+ &pd));
+
+ // Handle dispids for non-implemented interfaces, and for default interface
+ if (m_ImplIface != eImplIface)
+ {
+ // Set the dispid CA on the property.
+ long lDispSet = 1;
+ _SetDispIDCA(pITI, pMember->m_iMember, psF->memid, pd, TRUE, &lDispSet, TRUE);
+
+ // If this property is default property, add a custom attribute to the class.
+ if (lDispSet == DISPID_VALUE)
+ IfFailGo(_AddDefaultMemberCa(m_tdTypeDef, m_szMember));
+ }
+
+ // Add the alias information if the type is an alias.
+ IfFailGo(_HandleAliasInfo(pITI, pProperty, pd));
+ }
+
+ if (bConversionLoss)
+ hr = S_CONVERSION_LOSS;
+
+ErrExit:
+ if (m_szMember)
+ ::SysFreeString(m_szMember), m_szMember=0;
+
+ return hr;
+} // HRESULT CImportTlb::_ConvPropertiesForFunctions()
+
+//*****************************************************************************
+// Convert the vars and functions of a dispinterface. Vars actually turn
+// into a getter and possibly a setter.
+//*****************************************************************************
+HRESULT CImportTlb::_ConvDispatchMembers(
+ ITypeInfo *pITI, // ITypeInfo* to convert.
+ TYPEATTR *psAttr, // TypeAttr of ITypeInfo.
+ BOOL fInheritsIEnum)
+{
+ HRESULT hr; // A result.
+ int i; // Loop control.
+ BOOL bConversionLoss=FALSE; // If true, some attributes were lost on conversion.
+
+ IfFailGo(_FindFirstUserMethod(pITI, psAttr, &i));
+ IfFailGo(BuildMemberList(pITI, i, psAttr->cFuncs, fInheritsIEnum));
+
+ // Dispatch members really have no slot.
+ m_Slot = 0;
+
+ // Loop over properties.
+ for (i=0; i<m_cMemberProps; ++i)
+ {
+ IfFailGo(_ConvProperty(pITI, &m_MemberList[i]));
+ }
+
+ // Loop over functions.
+ BOOL bAllowIEnum = !fInheritsIEnum;
+ for (; i<(int)m_MemberList.Size(); ++i)
+ {
+ // Get variable information.
+ IfFailGo(_ConvFunction(pITI, &m_MemberList[i], FALSE, TRUE, FALSE, &bAllowIEnum));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = TRUE;
+ }
+
+ // Add the property info.
+ IfFailGo(_ConvPropertiesForFunctions(pITI, psAttr));
+
+ if (bConversionLoss)
+ hr = S_CONVERSION_LOSS;
+
+ErrExit:
+ // Free the func descs.
+ FreeMemberList(pITI);
+
+ return (hr);
+} // HRESULT CImportTlb::_ConvDispatchMembers()
+
+//*****************************************************************************
+// Examine the functions on an interface, and skip the first 3 or first 7
+// if the functions are IUnknown or IDispatch members.
+//*****************************************************************************
+HRESULT CImportTlb::_FindFirstUserMethod(
+ ITypeInfo *pITI, // The Typedef to examine.
+ TYPEATTR *psAttr, // TYPEATTR for the typedef.
+ int *pIx) // Put index of first user function here.
+{
+ HRESULT hr = S_OK; // A result.
+ int i; // Loop control.
+ FUNCDESC *psFunc=0; // FUNCDESC for a member.
+ BSTR szName=0; // A function's name.
+
+ // Note: this is a terrible workaround, but in some situations the methods from IUnknown / IDispatch will
+ // show up as though native dispatch functions.
+ i = 0;
+ if (psAttr->cFuncs >= 3)
+ {
+ IfFailGo(TryGetFuncDesc(pITI, i, &psFunc));
+ if (psFunc->memid == 0x60000000 &&
+ psFunc->elemdescFunc.tdesc.vt == VT_VOID &&
+ psFunc->cParams == 2 &&
+ psFunc->lprgelemdescParam[0].tdesc.vt == VT_PTR && // -> VT_USERDEFINED
+ psFunc->lprgelemdescParam[1].tdesc.vt == VT_PTR && // -> VT_PTR -> VT_VOID
+ SUCCEEDED(pITI->GetDocumentation(psFunc->memid, &szName, 0,0,0)) &&
+ (wcscmp(szName, W("QueryInterface")) == 0) )
+ i = 3;
+ pITI->ReleaseFuncDesc(psFunc);
+ psFunc=0;
+ if (szName)
+ ::SysFreeString(szName);
+ szName = 0;
+ if (psAttr->cFuncs >= 7)
+ {
+ IfFailGo(TryGetFuncDesc(pITI, i, &psFunc));
+ if (psFunc->memid == 0x60010000 &&
+ psFunc->elemdescFunc.tdesc.vt == VT_VOID &&
+ psFunc->cParams == 1 &&
+ psFunc->lprgelemdescParam[0].tdesc.vt == VT_PTR && // -> VT_UINT
+ SUCCEEDED(pITI->GetDocumentation(psFunc->memid, &szName, 0,0,0)) &&
+ (wcscmp(szName, W("GetTypeInfoCount")) == 0) )
+ i = 7;
+ pITI->ReleaseFuncDesc(psFunc);
+ psFunc=0;
+ if (szName)
+ ::SysFreeString(szName);
+ szName = 0;
+ }
+ }
+
+ *pIx = i;
+
+ErrExit:
+ if (psFunc)
+ pITI->ReleaseFuncDesc(psFunc);
+ if (szName)
+ ::SysFreeString(szName);
+ return (hr);
+} // HRESULT CImportTlb::_FindFirstUserMethod()
+
+//*****************************************************************************
+// Given a FUNCDESC that is has INVOKE_PROPERTY* decoration, determine
+// the role of the function, and the property signature type.
+//*****************************************************************************
+HRESULT CImportTlb::_GetFunctionPropertyInfo(
+ FUNCDESC *psFunc, // Function for which to get info.
+ USHORT *pSemantics, // Put appropriate semantics here.
+ FUNCDESC **ppSig, // Put FUNCDESC for signature here.
+ TYPEDESC **ppProperty, // Put TYPEDESC for return here.
+ BOOL *pbRetval, // If true, the type is [retval]
+ BOOL fUseLastParam, // If true, default to the last parameter as the return value
+ BSTR strName) // Name of the property
+{
+ FUNCDESC *psTmp; // FUNCDESC for some method.
+ FUNCDESC *psGet=0; // FUNCDESC for Get method defining a property.
+ FUNCDESC *psPut=0; // FUNCDESC for Put method defining a property.
+ FUNCDESC *psPutRef=0; // FUNCDESC for PutRef method defining a property.
+ FUNCDESC *psF; // A FUNCDESC.
+ TYPEDESC *pReturn=0; // The FUNCDESC's return type.
+ int cFound=0; // Count of functions found.
+ int i; // Loop control.
+ HRESULT hr = S_OK;
+
+ if (psFunc->invkind & INVOKE_PROPERTYGET)
+ { // A "Get", so return type is property type.
+ *ppSig = psFunc;
+ *pSemantics = msGetter;
+ }
+ else
+ {
+ _ASSERTE(psFunc->invkind & (INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF));
+ // Search for the "best" method from which to grab the signature. We prefer the Get(),
+ // Followed by the Put(), followed by the PutRef()
+ // Also look for Put() and PutRef(), so we can
+ for (int iFunc=0; iFunc<(int)m_MemberList.Size() && cFound<3; ++iFunc)
+ {
+ // Get a FUNCDESC from the list.
+ psTmp = m_MemberList[iFunc].m_psFunc;
+
+ // Is it for the same func?
+ if (psTmp->memid != psFunc->memid)
+ continue;
+
+ // Is it the Get()? If so, it is the one we want.
+ if (psTmp->invkind & INVOKE_PROPERTYGET)
+ {
+ psGet = psTmp;
+ ++cFound;
+ continue;
+ }
+
+ // Is it the Put()? Use it if we don't find a Get().
+ if (psTmp->invkind & INVOKE_PROPERTYPUT)
+ {
+ psPut = psTmp;
+ ++cFound;
+ continue;
+ }
+
+ // Is it the PutRef()? Keep track of it.
+ if (psTmp->invkind & INVOKE_PROPERTYPUTREF)
+ {
+ psPutRef = psTmp;
+ ++cFound;
+ }
+ }
+ // Get the best FUNCDESC for the signature.
+ *ppSig = psGet ? psGet : (psPut ? psPut : psFunc);
+
+ // Determine whether this is a the "Set" or "VB specific Let" function.
+ if (psFunc->invkind & INVOKE_PROPERTYPUTREF)
+ { // This function is the PROPERTYPUTREF. Make it the setter. If
+ // there is also a PROPERTYPUT, it will be the "letter".
+ *pSemantics = msSetter;
+ }
+ else
+ { // We are looking at the PROPERTYPUT function (the "Let" function in native VB6.).
+
+ // If there is also a PROPERTYPUTREF, make this the "VB Specific Let" function.
+ if (psPutRef)
+ { // A PPROPERTYPUTREF also exists, so make this the "Let" function.
+ *pSemantics = msOther;
+ }
+ else
+ { // There is no PROPERTYPUTREF, so make this the setter.
+ *pSemantics = msSetter;
+ }
+ }
+ }
+
+ // Occasionally there is a property with no discernable type. In that case, lose the
+ // property on conversion.
+
+ // Determine the type of the property, based on the "best" accessor.
+ psF = *ppSig;
+ *pbRetval = FALSE;
+ if (psF->invkind & INVOKE_PROPERTYGET)
+ { // look for [retval].
+ for (i=psF->cParams-1; i>=0; --i)
+ {
+ if (psF->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FRETVAL)
+ { // will consume a level of indirection (later).
+ *pbRetval = TRUE;
+ pReturn = &psF->lprgelemdescParam[i].tdesc;
+ break;
+ }
+ }
+ // If no [retval], check return type.
+ if (!pReturn && psF->elemdescFunc.tdesc.vt != VT_VOID && psF->elemdescFunc.tdesc.vt != VT_HRESULT)
+ pReturn = &psF->elemdescFunc.tdesc;
+
+ if (fUseLastParam)
+ {
+ // We may have stripped the [retval] if this is a disp-only interface. Just use the last parameter.
+ if (!pReturn && (psF->cParams > 0))
+ pReturn = &psF->lprgelemdescParam[psF->cParams-1].tdesc;
+ }
+ else
+ {
+ // If there is no type, don't try to set the getter.
+ if (pReturn && pReturn->vt == VT_VOID)
+ pReturn = NULL;
+ }
+
+ if (!pReturn)
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_PROPGET_WITHOUT_RETURN, strName, m_szMngName);
+ }
+ }
+ else
+ { // Find lastmost param that isn't [retval]. (Should be the last param, but it is
+ // possible to write an IDL with a PROPERTYPUT that has a [retval].
+ for (i=psF->cParams-1; i>=0; --i)
+ {
+ if ((psF->lprgelemdescParam[psF->cParams-1].paramdesc.wParamFlags & PARAMFLAG_FRETVAL) == 0)
+ {
+ { // First, and possibly only, param.
+ pReturn = &psF->lprgelemdescParam[i].tdesc;
+ break;
+ }
+ }
+ }
+ }
+
+//ErrExit:
+ if (pReturn == 0)
+ *pSemantics = 0;
+ *ppProperty = pReturn;
+
+ return hr;
+} // HRESULT CImportTlb::_GetFunctionPropertyInfo()
+
+//*****************************************************************************
+// Convert a function description to metadata entries.
+//
+// This can be rather involved. If the function is a INVOKE_PROPERTY*,
+// determine if it will be converted as a COM+ property, and if so, which
+// of up to three functions will be selected to provide the property
+// signature.
+// The function return type is found by scaning the parameters looking for
+// [retval]s.
+//*****************************************************************************
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+HRESULT CImportTlb::_ConvFunction(
+ ITypeInfo *pITI, // Containing TypeInfo.
+ MemberInfo *pMember, // iNFO for the function.
+ BOOL bVtblGapFuncs, // Add functions for vtblGaps?
+ BOOL bAddDispIds, // Add DispIds to the member?
+ BOOL bDelegateInvokeMeth, // Convert function for a delegate invoke
+ BOOL* bAllowIEnum) // Allowed to change this function to GetEnumerator
+{
+ HRESULT hr; // A result.
+ int iSrcParam; // Param count, as looping through params.
+ int iDestParam; // Param count, as looping through params.
+ int cDestParams; // Count of destination params.
+ int ixOpt; // Index of first param that is optional due to cParamsOpt.
+ int ixVarArg; // Index of vararg param, if any.
+ mdMethodDef mdFunc; // Token of new member.
+ BSTR szTypeName=0; // Name of the type.
+ DWORD dwFlags=0; // Member flags.
+ DWORD dwImplFlags=0; // The impl flags.
+ WCHAR *pszName=0; // Possibly decorated name of member.
+ CQuickArray<WCHAR> qbName; // Buffer for decorated name.
+ TYPEDESC *pReturn=0; // Return type.
+ int bRetval=false; // Is the return result a [retval] parameter?
+ int ixRetval; // Which param is the [retval]?
+ TYPEDESC *pReturnRetval=0; // Return type from [retval] (incl. indirection).
+ WORD wRetFlags=0; // Return type flags.
+ ULONG offset=0; // Offset of function
+ BSTR *rszParamNames=0; // Parameter names.
+ UINT iNames; // Count of actual names.
+ CQuickBytes qbComSig; // new signature
+ BYTE *pbSig; // Pointer into the signature.
+ ULONG sigFlags; // Signature handling flags.
+ CQuickArray<BYTE> qbNativeBuf; // Native type buffer.
+ CQuickArray<BYTE> qbDummyNativeTypeBuf; // A dummy native type array.
+ CQuickArray<ULONG> qbNativeOfs; // Offset of native type for each param.
+ CQuickArray<ULONG> qbNativeLen; // Length of native type for each param.
+ ULONG iNativeOfs=0; // Current offset in native type buffer.
+ ULONG iNewNativeOfs=0; // New offset in native type buffer.
+ ULONG cb; // Size of an element.
+ ULONG cbTotal = 0; // Size of the signature.
+ int bOleCall=false; // Is the implementation OLE style?(HRESULT or IDispatch)
+ USHORT msSemantics=0; // Property's methodsemantics.
+ WCHAR szSpecial[40]; // To build name of special function.
+ mdToken tkAttr; // Token for custom attribute type.
+ BOOL bConversionLoss=false; // If true, some attributes were lost on conversion.
+ enum {ParamRetval=-1, ParamNone=-2};
+ int iParamError=ParamNone; // Index of param with conversion error.
+ BOOL bNewEnumMember = FALSE; // A flag indicating if the member is the NewEnum member.
+ int iLCIDParam = -1; // Index of the LCID parameter.
+ FUNCDESC *psFunc = pMember->m_psFunc;
+
+ // If we might insert vtable gaps, then we'd better have initialized the vtable slot size.
+ _ASSERTE(!bVtblGapFuncs || (m_cbVtableSlot != 0));
+
+ // Retrieve the member name from the member info.
+ IfNullGo(m_szMember = SysAllocString(bDelegateInvokeMeth ? DELEGATE_INVOKE_METH_NAME : pMember->m_pName));
+
+#ifdef _DEBUG
+ LPWSTR funcName = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TlbImpShouldBreakOnConvFunction);
+ if (funcName)
+ {
+ if (wcscmp(funcName, pMember->m_pName) == 0)
+ _ASSERTE(!"TlbImpBreakOnConvFunction");
+
+ delete [] funcName;
+ }
+#endif //_DEBUG
+
+ // Determine if the member is the new enum member.
+ if ((*bAllowIEnum))
+ {
+ bNewEnumMember = FuncIsNewEnum(pITI, psFunc, pMember->m_iMember) == S_OK;
+
+ // Once a method is converted in this interface, don't convert any more.
+ if (bNewEnumMember)
+ *bAllowIEnum = FALSE;
+ }
+
+
+ // We should NEVER have a new enum member when we are dealing with a delegate invoke meth.
+ if (bNewEnumMember && bDelegateInvokeMeth)
+ {
+ // Get the real name of the method
+ BSTR szTypeInfoName = NULL;
+ BSTR szMemberName = NULL;
+ hr = m_pITI->GetDocumentation(MEMBERID_NIL, &szTypeInfoName, 0, 0, 0);
+ if (FAILED(hr))
+ szTypeInfoName = SysAllocString(W("???"));
+
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_EVENT_WITH_NEWENUM, szTypeInfoName);
+
+ SysFreeString(szMemberName);
+ SysFreeString(szTypeInfoName);
+
+ IfFailGo(TLBX_E_EVENT_WITH_NEWENUM);
+ }
+
+ // If there is a gap in the vtable, emit a special function.
+ if (bVtblGapFuncs)
+ {
+ if ((psFunc->oVft / m_cbVtableSlot) != m_Slot)
+ {
+ ULONG n = psFunc->oVft / m_cbVtableSlot;
+ // Make sure slot numbers are monotonically increasing.
+ if (n < m_Slot)
+ {
+ IfFailGo(pITI->GetDocumentation(MEMBERID_NIL, &szTypeName, 0, 0, 0));
+ IfFailGo(PostError(TLBX_E_BAD_VTABLE, m_szMember, szTypeName, m_szLibrary));
+ }
+
+ n -= m_Slot;
+ if (n == 1)
+ _snwprintf_s(szSpecial, lengthof(szSpecial), lengthof(szSpecial) - 1, VTBL_GAP_FORMAT_1, VTBL_GAP_FUNCTION, m_Slot);
+ else
+ _snwprintf_s(szSpecial, lengthof(szSpecial), lengthof(szSpecial) - 1, VTBL_GAP_FORMAT_N, VTBL_GAP_FUNCTION, m_Slot, n);
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, szSpecial, VTBL_GAP_FUNCTION_FLAGS, VTBL_GAP_SIGNATURE,sizeof(VTBL_GAP_SIGNATURE),
+ 0/* rva*/, VTBL_GAP_FUNC_IMPL_FLAGS, &mdFunc));
+ m_Slot += n;
+ }
+ // What we will expect next time.
+ ++m_Slot;
+ }
+
+ //-------------------------------------------------------------------------
+ // Determine the return type.
+ // If this is an hresult function, prepare to munge return, params.
+ if (psFunc->elemdescFunc.tdesc.vt == VT_HRESULT)
+ {
+ bOleCall = true;
+ }
+ else
+ {
+ if ((psFunc->elemdescFunc.tdesc.vt != VT_VOID) && (psFunc->elemdescFunc.tdesc.vt != VT_HRESULT))
+ pReturn = &psFunc->elemdescFunc.tdesc;
+ }
+
+ // Look for [RETVAL].
+ for (iSrcParam=0; iSrcParam<psFunc->cParams; ++iSrcParam)
+ {
+ if (psFunc->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & PARAMFLAG_FRETVAL)
+ {
+ // If already have a return, or a DISPATCH function, error.
+ if (pReturn != 0)
+ { // Unexpected return found.
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_AMBIGUOUS_RETURN, m_szName, m_szMember);
+ IfFailGo(TLBX_E_AMBIGUOUS_RETURN);
+ }
+ else
+ { // Found a return type.
+ wRetFlags = psFunc->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags;
+ pReturn = &psFunc->lprgelemdescParam[iSrcParam].tdesc;
+ bRetval = true;
+ ixRetval = iSrcParam;
+ }
+ break;
+ }
+ }
+
+ // Check to see if there is an LCID parameter.
+ for (iSrcParam=0;iSrcParam<psFunc->cParams;iSrcParam++)
+ {
+ if (psFunc->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & PARAMFLAG_FLCID)
+ {
+ if (iLCIDParam != -1)
+ IfFailGo(PostError(TLBX_E_MULTIPLE_LCIDS, m_szName, m_szMember));
+ iLCIDParam = iSrcParam;
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Size buffers to accomodate parameters.
+ // Resize the native type length array.
+ IfFailGo(qbNativeBuf.ReSizeNoThrow(1));
+ IfFailGo(qbNativeLen.ReSizeNoThrow(psFunc->cParams + 1));
+ IfFailGo(qbNativeOfs.ReSizeNoThrow(psFunc->cParams + 1));
+ memset(qbNativeLen.Ptr(), 0, (psFunc->cParams + 1)*sizeof(int));
+ memset(qbNativeOfs.Ptr(), 0, (psFunc->cParams + 1)*sizeof(int));
+
+ // resize to make room for calling convention and count of argument
+ IfFailGo(qbComSig.ReSizeNoThrow(CB_MAX_ELEMENT_TYPE + 1));
+ pbSig = (BYTE *)qbComSig.Ptr();
+
+ //-------------------------------------------------------------------------
+ // Determine which params need to be marked optional, by virtue of cParamsOpt count.
+ if (psFunc->cParamsOpt == 0)
+ ixVarArg = ixOpt = psFunc->cParams + 1;
+ else
+ {
+ if (psFunc->cParamsOpt == -1)
+ { // Varargs.
+ ixVarArg = ixOpt = psFunc->cParams + 1;
+ // If this is a PROPERTYPUT or PROPERTYPUTREF, skip the last non-retval parameter (it
+ // is the new value to be set).
+ BOOL bPropVal = (psFunc->invkind & (INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF)) ? TRUE : FALSE;
+ // Find the vararg param.
+ for (iSrcParam=psFunc->cParams-1; iSrcParam>=0; --iSrcParam)
+ {
+ // The count of optional params does not include any lcid params, nor does
+ // it include the return value, so skip those.
+ if ((psFunc->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & (PARAMFLAG_FRETVAL|PARAMFLAG_FLCID)) != 0)
+ continue;
+ // If haven't yet seen the property value, this param is it, so skip it, too.
+ if (bPropVal)
+ {
+ bPropVal = FALSE;
+ continue;
+ }
+ ixVarArg = iSrcParam;
+ break;
+ } // for (iSrcParam=cParams-1...
+ }
+ else
+ { // ixOpt will be index of first optional parameter.
+ short cOpt = psFunc->cParamsOpt;
+ ixOpt = 0;
+ ixVarArg = psFunc->cParams + 1;
+ for (iSrcParam=psFunc->cParams-1; iSrcParam>=0; --iSrcParam)
+ {
+ // The count of optional params does not include any lcid params, nor does
+ // it include the return value, so skip those.
+ if ((psFunc->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & (PARAMFLAG_FRETVAL|PARAMFLAG_FLCID)) == 0)
+ {
+ if (--cOpt == 0)
+ {
+ ixOpt = iSrcParam;
+ break;
+ }
+ }
+ } // for (iSrcParam=cParams-1...
+ }
+ }
+
+
+ //-------------------------------------------------------------------------
+ // Get the parameter names.
+ rszParamNames = reinterpret_cast<BSTR*>(_alloca((psFunc->cParams+1) * sizeof(BSTR*)));
+
+ // Get list of names.
+ IfFailGo(pITI->GetNames(psFunc->memid, rszParamNames, psFunc->cParams+1, &iNames));
+
+ // zero name pointer for non-named params.
+ for (iSrcParam=iNames; iSrcParam<=psFunc->cParams; ++iSrcParam)
+ rszParamNames[iSrcParam] = 0;
+
+ //-------------------------------------------------------------------------
+ // Convert the calling convention, param count, and return type.
+ cDestParams = psFunc->cParams;
+ if (bRetval)
+ --cDestParams;
+ if (iLCIDParam != -1)
+ --cDestParams;
+
+ if (pReturn)
+ {
+ // Param count
+ cbTotal = cb = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS, pbSig);
+ cb = CorSigCompressData(cDestParams, &(pbSig[cbTotal]));
+ cbTotal += cb;
+ // Return type or [retval].
+ if (bRetval)
+ sigFlags = (SigFlags)(wRetFlags & SIG_FLAGS_MASK) | SIG_FUNC, iParamError=ixRetval;
+ else
+ sigFlags = SIG_FUNC, iParamError=ParamRetval;
+ IfFailGo(_ConvSignature(pITI, pReturn, sigFlags, qbComSig, cbTotal, &cbTotal, qbNativeBuf, iNativeOfs, &iNewNativeOfs, bNewEnumMember));
+ qbNativeLen[0] = iNewNativeOfs - iNativeOfs;
+ qbNativeOfs[0] = iNativeOfs;
+ iNativeOfs = iNewNativeOfs;
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+ }
+ else
+ { // No return value
+ cbTotal = cb = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS, pbSig);
+ cb = CorSigCompressData(cDestParams, &(pbSig[cbTotal]));
+ cbTotal += cb;
+ cb = CorSigCompressData(ELEMENT_TYPE_VOID, &pbSig[cbTotal]);
+ cbTotal += cb;
+ }
+
+ //-------------------------------------------------------------------------
+ // Translate each parameter.
+ for (iSrcParam=0, iDestParam=0; iSrcParam<psFunc->cParams; ++iSrcParam)
+ {
+ if (!(psFunc->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & NON_CONVERTED_PARAMS_FLAGS))
+ {
+ sigFlags = (SigFlags)(psFunc->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & SIG_FLAGS_MASK) | SIG_FUNC | SIG_USE_BYREF;
+ if (iSrcParam == ixVarArg)
+ sigFlags |= SIG_VARARG;
+ iParamError = iSrcParam;
+ IfFailGo(_ConvSignature(pITI, &psFunc->lprgelemdescParam[iSrcParam].tdesc, sigFlags, qbComSig, cbTotal, &cbTotal, qbNativeBuf, iNativeOfs, &iNewNativeOfs, bNewEnumMember));
+ qbNativeLen[iDestParam+1] = iNewNativeOfs - iNativeOfs;
+ qbNativeOfs[iDestParam+1] = iNativeOfs;
+ iNativeOfs = iNewNativeOfs;
+ iDestParam++;
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+ }
+ }
+ iParamError = ParamNone;
+
+ //-------------------------------------------------------------------------
+ // Get the previously decorated name. Add interface name and make unique.
+ if (bDelegateInvokeMeth)
+ {
+ pszName = (WCHAR*)DELEGATE_INVOKE_METH_NAME;
+ }
+ else
+ {
+ // m_szInterface should be non-null if processing an implemented interface; should be null otherwise.
+ _ASSERTE(m_ImplIface == eImplIfaceNone || m_szInterface != 0);
+ IfFailGo(qbName.ReSizeNoThrow(wcslen(pMember->m_pName)+2));
+ wcscpy_s(qbName.Ptr(), wcslen(pMember->m_pName)+2, pMember->m_pName);
+ IfFailGo(GenerateUniqueMemberName(qbName, (PCCOR_SIGNATURE)qbComSig.Ptr(), cbTotal, m_szInterface, mdtMethodDef));
+ pszName = qbName.Ptr();
+ }
+
+ // Determine the function's semantics, flags and impl flags.
+ if (!bDelegateInvokeMeth)
+ {
+ msSemantics = pMember->m_msSemantics;
+ dwImplFlags = DEFAULT_ITF_FUNC_IMPL_FLAGS;
+ dwFlags = msSemantics ? DEFAULT_PROPERTY_FUNC_FLAGS : DEFAULT_INTERFACE_FUNC_FLAGS;
+ // If processing an implemented interface, remove the abstract bit. Methods on classes are not abstract.
+ if (m_ImplIface != eImplIfaceNone)
+ dwFlags &= ~mdAbstract;
+ }
+ else
+ {
+ msSemantics = 0;
+ dwImplFlags = miRuntime;
+ dwFlags = DELEGATE_INVOKE_FUNC_FLAGS;
+ }
+
+ //-------------------------------------------------------------------------
+ // Create the function definition in the metadata.
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, pszName, dwFlags, (PCCOR_SIGNATURE) qbComSig.Ptr(),cbTotal,
+ 0 /* rva*/, dwImplFlags | (bOleCall ? 0 : miPreserveSig), &mdFunc));
+
+ // If the method is part of a property, save info to set up the property.
+ if (msSemantics)
+ pMember->SetFuncInfo(mdFunc, msSemantics);
+
+ // Handle dispids for non-implemented interfaces, and for default interface
+ if (m_ImplIface != eImplIface)
+ {
+ // Add the DispIds if the flag is set.
+ long lDispSet = 1;
+ _SetDispIDCA(pITI, pMember->m_iMember, psFunc->memid, mdFunc, bAddDispIds, &lDispSet, TRUE);
+
+ // If this method is the default, and not a property accessor, add a custom attribute to the class.
+ if (lDispSet == DISPID_VALUE && msSemantics == 0)
+ IfFailGo(_AddDefaultMemberCa(m_tdTypeDef, m_szMember));
+ }
+
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(int));
+
+ // If this method has an LCID then set the LCIDConversion attribute.
+ if (iLCIDParam != -1)
+ {
+ // Dispid for the function.
+ BUILD_CUSTOM_ATTRIBUTE(int, iLCIDParam);
+ IfFailGo(GetAttrType(ATTR_LCIDCONVERSION, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(mdFunc, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+ }
+
+ // Save the func flags for anyone that needs typelib's flags.
+ if (psFunc->wFuncFlags)
+ {
+ IfFailGo(GetAttrType(ATTR_TYPELIBFUNC, &tkAttr));
+ INIT_CUSTOM_ATTRIBUTE(sizeof(WORD));
+ BUILD_CUSTOM_ATTRIBUTE(WORD, psFunc->wFuncFlags);
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(mdFunc, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+
+ //-------------------------------------------------------------------------
+ // Convert the param info for the return type.
+ if (pReturn)
+ { // store return value parameter as sequence 0
+ if (bRetval)
+ {
+ hr = _IsAlias(pITI, &psFunc->lprgelemdescParam[ixRetval].tdesc);
+ IfFailGo(hr);
+ if (qbNativeLen[0] || hr == S_OK)
+ {
+ IfFailGo(_ConvParam(pITI, mdFunc, 0, &psFunc->lprgelemdescParam[ixRetval], ParamNormal, 0 /*rszParamNames[ixRetval+1]*/,
+ &qbNativeBuf[qbNativeOfs[0]], qbNativeLen[0]));
+ }
+ }
+ else
+ {
+ hr = _IsAlias(pITI, &psFunc->elemdescFunc.tdesc);
+ IfFailGo(hr);
+ if (qbNativeLen[0] || hr == S_OK)
+ {
+ IfFailGo(_ConvParam(pITI, mdFunc, 0, &psFunc->elemdescFunc, ParamNormal, 0,
+ &qbNativeBuf[qbNativeOfs[0]], qbNativeLen[0]));
+ }
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Convert parameter info (flags, native type, default value).
+ for (iSrcParam=iDestParam=0; iSrcParam<psFunc->cParams; ++iSrcParam)
+ {
+ if ((psFunc->lprgelemdescParam[iSrcParam].paramdesc.wParamFlags & NON_CONVERTED_PARAMS_FLAGS) == 0)
+ {
+ ParamOpts opt = ParamNormal;
+ if (iSrcParam >= ixOpt)
+ opt = ParamOptional;
+ else
+ if (iSrcParam == ixVarArg)
+ opt = ParamVarArg;
+ iDestParam++;
+ IfFailGo(_ConvParam(pITI, mdFunc, iDestParam, &psFunc->lprgelemdescParam[iSrcParam], opt, rszParamNames[iSrcParam + 1],
+ &qbNativeBuf[qbNativeOfs[iDestParam]], qbNativeLen[iDestParam]));
+ }
+ }
+
+
+ //-------------------------------------------------------------------------
+ // If processing an implemented interface, set up MethodImpls.
+ if (m_ImplIface != eImplIfaceNone)
+ {
+ // Define a memberref on the implemented interface.
+ mdToken mrItfMember;
+ IfFailGo(m_pEmit->DefineMemberRef(m_tkInterface, pMember->m_pName, (PCCOR_SIGNATURE) qbComSig.Ptr(),cbTotal, &mrItfMember));
+
+ // Define a method impl.
+ IfFailGo(m_pEmit->DefineMethodImpl(m_tdTypeDef, mdFunc, mrItfMember));
+ }
+
+ if (bConversionLoss)
+ {
+ hr = S_CONVERSION_LOSS;
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_I_UNCONVERTABLE_ARGS, m_szName, m_szMember);
+ }
+
+ErrExit:
+ // Special case for typeload load failures -- they're very hard to diagnose.
+ if (hr == TYPE_E_CANTLOADLIBRARY)
+ {
+ if (iParamError >= 0 && iParamError < psFunc->cParams && rszParamNames[iParamError+1])
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_PARAM_ERROR_NAMED, m_szName, rszParamNames[iParamError+1], m_szMember);
+ else
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_PARAM_ERROR_UNNAMED, m_szName, iParamError, m_szMember);
+ }
+ if (rszParamNames)
+ {
+ for (iSrcParam=0; iSrcParam<=psFunc->cParams; ++iSrcParam)
+ if (rszParamNames[iSrcParam])
+ ::SysFreeString(rszParamNames[iSrcParam]);
+ }
+ if (m_szMember)
+ ::SysFreeString(m_szMember), m_szMember=0;
+ if (szTypeName)
+ ::SysFreeString(szTypeName);
+
+ return (hr);
+} // HRESULT CImportTlb::_ConvFunction()
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+
+HRESULT CImportTlb::_SetHiddenCA(mdTypeDef token)
+{
+ mdToken tkAttr;
+ HRESULT hr = S_OK;
+
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(short));
+ BUILD_CUSTOM_ATTRIBUTE(short, TYPEFLAG_FHIDDEN);
+ IfFailGo(GetAttrType(ATTR_TYPELIBTYPE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ m_pEmit->DefineCustomAttribute(token, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0);
+
+ErrExit:
+ return S_OK;
+}
+
+HRESULT CImportTlb::_ForceIEnumerableCVExists(ITypeInfo* pITI, BOOL* CVExists)
+{
+ ITypeInfo2 *pITI2 = 0;
+ *CVExists = FALSE;
+ HRESULT hr = S_OK;
+
+ pITI->QueryInterface(IID_ITypeInfo2, reinterpret_cast<void**>(&pITI2));
+
+ if (pITI2)
+ {
+ VARIANT vCustomData;
+ VariantInit(&vCustomData);
+
+ IfFailGo(pITI2->GetCustData(GUID_ForceIEnumerable, &vCustomData));
+
+ if (V_VT(&vCustomData) != VT_EMPTY)
+ *CVExists = TRUE;
+
+ VariantClear(&vCustomData);
+ }
+
+ErrExit:
+ if (pITI2)
+ pITI2->Release();
+
+ return S_OK;
+}
+
+
+HRESULT CImportTlb::_GetDispIDCA(
+ ITypeInfo* pITI,
+ int iMember,
+ long* lDispSet,
+ BOOL bFunc
+ )
+{
+ ITypeInfo2 *pITI2=0; // For getting custom value.
+ HRESULT hr = S_OK;
+ long lDispId = DISPID_UNKNOWN;
+
+ // Get the ITypeInfo2 interface if possible
+ pITI->QueryInterface(IID_ITypeInfo2, reinterpret_cast<void**>(&pITI2));
+
+ if (pITI2)
+ {
+ VARIANT vCustomData;
+ VariantInit(&vCustomData);
+
+ if (bFunc)
+ IfFailGo(pITI2->GetFuncCustData(iMember, GUID_DispIdOverride, &vCustomData));
+ else
+ IfFailGo(pITI2->GetVarCustData(iMember, GUID_DispIdOverride, &vCustomData));
+
+ if ((V_VT(&vCustomData) == VT_I2) || (V_VT(&vCustomData) == VT_I4))
+ {
+ hr = VariantChangeType(&vCustomData, &vCustomData, 0, VT_I4);
+ if (hr == S_OK)
+ lDispId = vCustomData.lVal;
+ }
+
+ VariantClear(&vCustomData);
+ }
+
+ErrExit:
+ if (lDispSet != NULL)
+ *lDispSet = lDispId;
+
+ if (pITI2)
+ pITI2->Release();
+
+ return S_OK;
+}
+
+
+
+HRESULT CImportTlb::_SetDispIDCA(
+ ITypeInfo* pITI,
+ int iMember,
+ long lDispId,
+ mdToken func,
+ BOOL fAlwaysAdd,
+ long* lDispSet,
+ BOOL bFunc
+ )
+{
+ WCHAR DispIDCA[] = W("{CD2BC5C9-F452-4326-B714-F9C539D4DA58}");
+ ITypeInfo2 *pITI2=0; // For getting custom value.
+ HRESULT hr = S_OK;
+
+ // Get the ITypeInfo2 interface if possible
+ pITI->QueryInterface(IID_ITypeInfo2, reinterpret_cast<void**>(&pITI2));
+
+ if (pITI2)
+ {
+ VARIANT vCustomData;
+ VariantInit(&vCustomData);
+
+ if (bFunc)
+ IfFailGo(pITI2->GetFuncCustData(iMember, GUID_DispIdOverride, &vCustomData));
+ else
+ IfFailGo(pITI2->GetVarCustData(iMember, GUID_DispIdOverride, &vCustomData));
+
+ if ((V_VT(&vCustomData) == VT_I2) || (V_VT(&vCustomData) == VT_I4))
+ {
+ hr = VariantChangeType(&vCustomData, &vCustomData, 0, VT_I4);
+ if (hr == S_OK)
+ {
+ lDispId = vCustomData.lVal;
+ fAlwaysAdd = true;
+ }
+ }
+ else if (V_VT(&vCustomData) != VT_EMPTY)
+ {
+ // Invalid Variant type on the data - spit out a warning.
+ BSTR CustomValue = SysAllocString((const WCHAR*)&DispIDCA[0]);
+ BSTR ObjectName;
+ IfFailGo(pITI2->GetDocumentation(iMember+1, &ObjectName, NULL, NULL, NULL));
+
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_W_NON_INTEGRAL_CA_TYPE, CustomValue, ObjectName);
+
+ SysFreeString(CustomValue);
+ SysFreeString(ObjectName);
+ }
+
+ VariantClear(&vCustomData);
+ }
+
+ // Set the dispid CA on the property.
+ if (fAlwaysAdd)
+ {
+ mdToken tkAttr;
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(DISPID));
+ BUILD_CUSTOM_ATTRIBUTE(DISPID, lDispId);
+ IfFailGo(GetAttrType(ATTR_DISPID, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(func, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+ }
+
+ErrExit:
+ if (lDispSet != NULL)
+ {
+ *lDispSet = lDispId;
+ }
+
+ if (pITI2)
+ pITI2->Release();
+
+ return S_OK;
+}
+
+
+HRESULT CImportTlb::_CheckForPropertyCustomAttributes(ITypeInfo* pITI, int index, INVOKEKIND* ikind)
+{
+ HRESULT hr;
+ VARIANT vCustomData;
+ ITypeInfo2* pITI2 = 0;
+ BOOL found = FALSE;
+
+ VariantInit(&vCustomData);
+
+ // Get the ITypeInfo2 interface if possible
+ pITI->QueryInterface(IID_ITypeInfo2, reinterpret_cast<void**>(&pITI2));
+ if (pITI2)
+ {
+ // First, check for PropGet
+ hr = pITI2->GetFuncCustData(index, GUID_PropGetCA, &vCustomData);
+ IfFailGo(hr);
+ if (V_VT(&vCustomData) != VT_EMPTY)
+ {
+ *ikind = INVOKE_PROPERTYGET;
+ found = TRUE;
+ goto ErrExit;
+ }
+
+ // Second, check for PropPut
+ VariantClear(&vCustomData);
+ hr = pITI2->GetFuncCustData(index, GUID_PropPutCA, &vCustomData);
+ IfFailGo(hr);
+ if (V_VT(&vCustomData) != VT_EMPTY)
+ {
+ *ikind = INVOKE_PROPERTYPUT;
+ found = TRUE;
+ goto ErrExit;
+ }
+ }
+
+ErrExit:
+ VariantClear(&vCustomData);
+
+ if (pITI2)
+ pITI2->Release();
+
+ if (found)
+ return S_OK;
+
+ return S_FALSE;
+}
+
+//*****************************************************************************
+// Generate an event with an add and remove method
+//*****************************************************************************
+HRESULT CImportTlb::_GenerateEvent(
+ ITypeInfo *pITI, // Containing TypeInfo.
+ MemberInfo *pMember, // Info for the function
+ BOOL fInheritsIEnum)
+{
+ HRESULT hr = S_OK; // A result.
+ mdMethodDef mdAdd; // Token of add_XXX method.
+ mdMethodDef mdRemove; // Token of remove_XXX method.
+ CQuickArray<WCHAR> qbName; // Buffer for decorated name.
+ CQuickArray<BYTE> qbSig; // The signature.
+ ULONG cb; // Size of an element.
+ ULONG cbTotal = 0; // Size of the signature.
+ mdTypeDef tdDelegate; // The delegate type def.
+ mdEvent tkEvent; // The token for the event.
+
+ // If this method is a property method, then skip the event.
+ // Also look at the property semantic - it might be we couldn't import this as a property
+ // and fell back to a method.
+ if ((pMember->m_psFunc->invkind != INVOKE_FUNC) && (pMember->m_msSemantics != 0))
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_W_NO_PROPS_IN_EVENTS, m_szName);
+ return S_CONVERSION_LOSS;
+ }
+
+ // Generate the delegate.
+ IfFailGo(_GenerateEventDelegate(pITI, pMember, &tdDelegate, fInheritsIEnum));
+
+ // Generate the sig for the add and remove methods.
+ IfFailGo(qbSig.ReSizeNoThrow(CB_MAX_ELEMENT_TYPE * 2 + 1));
+ cbTotal = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS, qbSig.Ptr());
+ cb = CorSigCompressData(1, &(qbSig[cbTotal]));
+ cbTotal += cb;
+ cb = CorSigCompressData(ELEMENT_TYPE_VOID, &qbSig[cbTotal]);
+ cbTotal += cb;
+ cb = CorSigCompressData(ELEMENT_TYPE_CLASS, &qbSig[cbTotal]);
+ cbTotal += cb;
+ cb = CorSigCompressToken(tdDelegate, &qbSig[cbTotal]);
+ cbTotal += cb;
+
+ // Generate the add method.
+ IfFailGo(qbName.ReSizeNoThrow(EVENT_ADD_METH_PREFIX_LENGTH + wcslen(pMember->m_pName) + 1));
+ StringCchPrintf(qbName.Ptr(), qbName.Size(), W("%s%s"), EVENT_ADD_METH_PREFIX, pMember->m_pName);
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, qbName.Ptr(), DEFAULT_INTERFACE_FUNC_FLAGS,
+ qbSig.Ptr(), cbTotal, 0 /* rva*/, DEFAULT_ITF_FUNC_IMPL_FLAGS, &mdAdd));
+
+ // Generate the remove method.
+ IfFailGo(qbName.ReSizeNoThrow(EVENT_REM_METH_PREFIX_LENGTH + wcslen(pMember->m_pName) + 1));
+ StringCchPrintf(qbName.Ptr(), qbName.Size(), W("%s%s"), EVENT_REM_METH_PREFIX, pMember->m_pName);
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, qbName.Ptr(), DEFAULT_INTERFACE_FUNC_FLAGS,
+ qbSig.Ptr(), cbTotal, 0 /* rva*/, DEFAULT_ITF_FUNC_IMPL_FLAGS, &mdRemove));
+
+ // Define the event itself.
+ IfFailGo(m_pEmit->DefineEvent(m_tdTypeDef, pMember->m_pName, 0, tdDelegate,
+ mdAdd, mdRemove, mdTokenNil, NULL, &tkEvent));
+
+ErrExit:
+
+ return (hr);
+} // HRESULT CImportTlb::_GenerateEvent()
+
+//*****************************************************************************
+// Generate an add and remove method
+//*****************************************************************************
+HRESULT CImportTlb::_GenerateEventDelegate(
+ ITypeInfo *pITI, // Containing TypeInfo.
+ MemberInfo *pMember, // Info for the source interface func
+ mdTypeDef *ptd, // The output typedef.
+ BOOL fInheritsIEnum)
+{
+ HRESULT hr = S_OK; // A result.
+ BSTR bstrSrcItfName = NULL; // The name of the source interface.
+ CQuickArray<WCHAR> qbEventHandlerName; // The name of the event handler.
+ BSTR szOldName = NULL; // The old value m_tdTypeDef.
+ mdTypeDef tdOldTypeDef = NULL; // The old value m_szName.
+ CQuickArray<BYTE> qbSig; // The signature.
+ ULONG cb; // Size of an element.
+ ULONG cbTotal = 0; // Total size of signature.
+ mdMethodDef mdFunc; // The defined function.
+ mdTypeRef trMulticastDelegate; // The type ref for System.MulticastDelegate.
+ mdToken tkAttr; // Custom attribute type.
+
+ // Store the old values of the ITypeInfo name and of the current type def.
+ szOldName = m_szName;
+ tdOldTypeDef = m_tdTypeDef;
+ m_szName = NULL;
+
+ // Retrieve the full name of the source interface.
+ IfFailGo(GetManagedNameForTypeInfo(pITI, m_wzNamespace, NULL, &bstrSrcItfName));
+
+ // Generate a unique name for the event handler which will be of the form:
+ // <SrcItfName>_<MethodName>_EventHandler<PotentialSuffix>
+ IfFailGo(qbEventHandlerName.ReSizeNoThrow(wcslen(bstrSrcItfName) + wcslen(pMember->m_pName) + EVENT_HANDLER_SUFFIX_LENGTH + 6));
+ StringCchPrintf(qbEventHandlerName.Ptr(), qbEventHandlerName.Size(), W("%s_%s%s"), bstrSrcItfName, pMember->m_pName, EVENT_HANDLER_SUFFIX);
+ IfFailGo(GenerateUniqueTypeName(qbEventHandlerName));
+
+ // Set the information on the current type.
+ IfNullGo(m_szName = SysAllocString(qbEventHandlerName.Ptr()));
+
+ // Retrieve the parent type ref.
+ IfFailGo(GetKnownTypeToken(VT_SLOT_FOR_MULTICASTDEL, &trMulticastDelegate));
+
+ // Create the typedef for the event interface.
+ IfFailGo(m_pEmit->DefineTypeDef(m_szName, tdPublic | tdSealed, trMulticastDelegate, NULL, &m_tdTypeDef));
+
+ // Hide the interface from Object Browsers (EventHandler)
+ _SetHiddenCA(m_tdTypeDef);
+
+ // Make the interface ComVisible(false).
+ {
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(BYTE));
+ BUILD_CUSTOM_ATTRIBUTE(BYTE, FALSE);
+ IfFailGo(GetAttrType(ATTR_COMVISIBLE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+ }
+
+ // Generate the sig for the constructor.
+ IfFailGo(qbSig.ReSizeNoThrow(CB_MAX_ELEMENT_TYPE * 2 + 1));
+ cbTotal = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS, qbSig.Ptr());
+ cb = CorSigCompressData(2, &(qbSig[cbTotal]));
+ cbTotal += cb;
+ cb = CorSigCompressData(ELEMENT_TYPE_VOID, &qbSig[cbTotal]);
+ cbTotal += cb;
+ cb = CorSigCompressData(ELEMENT_TYPE_OBJECT, &qbSig[cbTotal]);
+ cbTotal += cb;
+ cb = CorSigCompressData(ELEMENT_TYPE_U, &qbSig[cbTotal]);
+ cbTotal += cb;
+
+ // Generate the constructor.
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, OBJECT_INITIALIZER_NAME, OBJECT_INITIALIZER_FLAGS,
+ qbSig.Ptr(), cbTotal, 0 /* rva*/, miRuntime, &mdFunc));
+
+ // Generate the invoke method.
+ BOOL bAllowIEnum = !fInheritsIEnum;
+ IfFailGo(_ConvFunction(pITI, pMember, FALSE, FALSE, TRUE, &bAllowIEnum));
+
+ // Set the output typedef.
+ *ptd = m_tdTypeDef;
+
+ErrExit:
+ if (m_szName)
+ ::SysFreeString(m_szName);
+ if (m_szMember)
+ ::SysFreeString(m_szMember); m_szMember=0;
+ if (bstrSrcItfName)
+ ::SysFreeString(bstrSrcItfName);
+
+ // Restore the initial values for the ITypeInfo name and the type def.
+ m_szName = szOldName;
+ m_tdTypeDef = tdOldTypeDef;
+
+ return (hr);
+} // HRESULT CImportTlb::_GenerateEventDelegate()
+
+//*****************************************************************************
+//*****************************************************************************
+struct MDTOKENHASH : HASHLINK
+{
+ mdToken tkKey;
+ mdToken tkData;
+}; // struct MDTOKENHASH : HASHLINK
+
+class CTokenHash : public CChainedHash<MDTOKENHASH>
+{
+public:
+ virtual bool InUse(MDTOKENHASH *pItem)
+ { return (pItem->tkKey != NULL); }
+
+ virtual void SetFree(MDTOKENHASH *pItem)
+ {
+ pItem->tkKey = NULL;
+ pItem->tkKey = NULL;
+ }
+
+ virtual ULONG Hash(const void *pData)
+ {
+ // Do case-insensitive hash
+ return (ULONG)(ULONG_PTR)pData;
+ }
+
+ virtual int Cmp(const void *pData, void *pItem){
+ return (mdToken)(ULONG_PTR)pData != reinterpret_cast<MDTOKENHASH*>(pItem)->tkKey;
+ }
+}; // CTokenHash : public CChainedHash<MDTOKENHASH>
+
+//*****************************************************************************
+// Copy methods and events from a source interface to a class that sources the
+// given interface.
+//*****************************************************************************
+HRESULT CImportTlb::_AddSrcItfMembersToClass( // S_OK or error.
+ mdTypeRef trSrcItf) // Typeref of the source interface.
+{
+ HRESULT hr=S_OK; // A result.
+ ULONG i; // Generic counter.
+ HCORENUM MemberEnum = NULL; // The enum of members.
+ ULONG cMembers = 0; // Temp count of members.
+ mdTypeDef tdSrcItf; // A type def to the interface.
+ mdEvent tkItfEvent; // The token of the interface event.
+ mdEvent tkClassEvent; // The token of the class event.
+ mdToken tkEventType; // The event type.
+ mdMethodDef mdItfMethod; // The method def of the interface method.
+ mdMethodDef mdAddMethod; // The add method.
+ mdMethodDef mdRemoveMethod; // The remove method.
+ mdMethodDef mdFireMethod; // The fire method.
+ mdMethodDef mdClassMethod; // The method def of the class method.
+ CQuickArray<mdMethodDef> qbOtherMethods; // The other methods for the property.
+ ULONG cchOtherMethods; // The cound of other methods.
+ CQuickArray<WCHAR> qbMemberName; // Name of the member.
+ CQuickArray<WCHAR> qbEventItfFullName; // Full name of the event interface.
+ CQuickArray<WCHAR> qbEventItfName; // Name of the event interface.
+ ULONG cchName; // Length of a name, in wide chars.
+ ULONG ItfMemberAttr; // The attributes of the interface member.
+ ULONG ItfMemberImplFlags; // The impl flags of the interface member.
+ PCCOR_SIGNATURE ItfMemberSig; // The signature of the interface member.
+ ULONG ItfMemberSigSize; // The size of the member signature.
+ mdMemberRef mrItfMember; // A member ref to the interface member def.
+ BSTR bstrSrcItfName = NULL; // The name of the CoClass.
+ mdAssemblyRef ar; // The assembly ref.
+ CTokenHash ItfMDToClassMDMap; // The interface MD to class MD map.
+ MDTOKENHASH * pItem; // An item in the token hashtable.
+
+ // Retrieve the name of the event interface.
+ do {
+ IfFailGo(m_pImport->GetTypeRefProps(
+ trSrcItf,
+ &ar,
+ qbEventItfFullName.Ptr(),
+ (ULONG)qbEventItfFullName.MaxSize(),
+ &cchName));
+ if (hr == CLDB_S_TRUNCATION)
+ {
+ IfFailGo(qbEventItfFullName.ReSizeNoThrow(cchName));
+ continue;
+ }
+ break;
+ } while (1);
+ IfFailGo(qbEventItfName.ReSizeNoThrow(cchName));
+ ns::SplitPath(qbEventItfFullName.Ptr(), NULL, 0, qbEventItfName.Ptr(), (int)qbEventItfName.Size());
+
+ // Resolve the typeref to a typedef.
+ IfFailGo(m_pImport->FindTypeDefByName(qbEventItfFullName.Ptr(), mdTokenNil, &tdSrcItf));
+
+ // Define methods and method impl's for all the methods in the interface.
+ while ((hr = m_pImport->EnumMethods(&MemberEnum, tdSrcItf, &mdItfMethod, 1, &cMembers)) == S_OK)
+ {
+ // Retrieve the method properties.
+ do {
+ IfFailGo(m_pImport->GetMethodProps(
+ mdItfMethod,
+ NULL,
+ qbMemberName.Ptr(),
+ (ULONG)qbMemberName.MaxSize(),
+ &cchName,
+ &ItfMemberAttr,
+ &ItfMemberSig,
+ &ItfMemberSigSize,
+ NULL,
+ &ItfMemberImplFlags));
+ if (hr == CLDB_S_TRUNCATION)
+ {
+ IfFailGo(qbMemberName.ReSizeNoThrow(cchName));
+ continue;
+ }
+ break;
+ } while (1);
+
+ // Define a member ref on the class to the interface member def.
+ IfFailGo(m_pEmit->DefineMemberRef(trSrcItf, qbMemberName.Ptr(), ItfMemberSig, ItfMemberSigSize, &mrItfMember));
+
+ // Generate a unique name for the class member.
+ IfFailGo(GenerateUniqueMemberName(qbMemberName, NULL, 0, qbEventItfName.Ptr(), mdtMethodDef));
+
+ // Define a member on the class.
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, qbMemberName.Ptr(), ItfMemberAttr & ~mdAbstract,
+ ItfMemberSig, ItfMemberSigSize, 0/*rva*/, ItfMemberImplFlags, &mdClassMethod));
+
+ // Define a method impl.
+ IfFailGo(m_pEmit->DefineMethodImpl(m_tdTypeDef, mdClassMethod, mrItfMember));
+
+ // Add the interface member to the map.
+ if ((pItem = ItfMDToClassMDMap.Add((const void *)(ULONG_PTR)mdItfMethod)) == NULL)
+ IfFailGo(E_FAIL);
+ PREFIX_ASSUME(pItem != NULL);
+ pItem->tkKey = mdItfMethod;
+ pItem->tkData = mdClassMethod;
+ }
+ IfFailGo(hr);
+
+ m_pImport->CloseEnum(MemberEnum);
+ MemberEnum = NULL;
+
+ // Define all the events in the interface on the class.
+ while ((hr = m_pImport->EnumEvents(&MemberEnum, tdSrcItf, &tkItfEvent, 1, &cMembers)) == S_OK)
+ {
+ // Retrieve the properties of the property.
+ do {
+ IfFailGo(m_pImport->GetEventProps(
+ tkItfEvent,
+ NULL,
+ qbMemberName.Ptr(),
+ (ULONG)qbMemberName.MaxSize(),
+ &cchName,
+ &ItfMemberAttr,
+ &tkEventType,
+ &mdAddMethod,
+ &mdRemoveMethod,
+ &mdFireMethod,
+ qbOtherMethods.Ptr(),
+ (ULONG)qbOtherMethods.MaxSize(),
+ &cchOtherMethods));
+ if (hr == CLDB_S_TRUNCATION)
+ {
+ IfFailGo(qbMemberName.ReSizeNoThrow(cchName));
+ IfFailGo(qbOtherMethods.ReSizeNoThrow(cchOtherMethods));
+ continue;
+ }
+ break;
+ } while (1);
+
+ // NULL terminate the array of other methods.
+ IfFailGo(qbOtherMethods.ReSizeNoThrow(cchOtherMethods + 1));
+ qbOtherMethods[cchOtherMethods] = NULL;
+
+ // Replace all the interface method def's with the equivalent class method def's.
+ if (!IsNilToken(mdAddMethod))
+ {
+ pItem = ItfMDToClassMDMap.Find((const void *)(ULONG_PTR)mdAddMethod);
+ _ASSERTE(pItem);
+ mdAddMethod = pItem->tkData;
+ }
+ if (!IsNilToken(mdRemoveMethod))
+ {
+ pItem = ItfMDToClassMDMap.Find((const void *)(ULONG_PTR)mdRemoveMethod);
+ _ASSERTE(pItem);
+ mdRemoveMethod = pItem->tkData;
+ }
+ _ASSERTE(IsNilToken(mdFireMethod));
+ _ASSERTE(cchOtherMethods == 0);
+
+ // Generate a unique name for the event.
+ IfFailGo(GenerateUniqueMemberName(qbMemberName, NULL, 0, qbEventItfName.Ptr(), mdtEvent));
+
+ // Define property on the class.
+ IfFailGo(m_pEmit->DefineEvent(m_tdTypeDef, qbMemberName.Ptr(), ItfMemberAttr,
+ tkEventType, mdAddMethod, mdRemoveMethod, mdFireMethod, qbOtherMethods.Ptr(), &tkClassEvent));
+ }
+ IfFailGo(hr);
+
+ m_pImport->CloseEnum(MemberEnum);
+ MemberEnum = NULL;
+
+ErrExit:
+ if (MemberEnum)
+ m_pImport->CloseEnum(MemberEnum);
+ if (bstrSrcItfName)
+ ::SysFreeString(bstrSrcItfName);
+
+ return hr;
+
+#undef ITF_MEMBER_SIG
+#undef ITF_MEMBER_SIG_SIZE
+} // HRESULT CImportTlb::_AddSrcItfMembersToClass()
+
+//*****************************************************************************
+// Compare the two signatures ignoring the return type. If the signatures
+// match then TRUE will be returned, FALSE will be returned otherwise.
+// This method assumes the two signatures are in the same scope.
+//*****************************************************************************
+HRESULT CImportTlb::CompareSigsIgnoringRetType(
+ PCCOR_SIGNATURE pbSig1, // The 1st method signature.
+ ULONG cbSig1, // Size of the 1st method signature.
+ PCCOR_SIGNATURE pbSig2, // The 2nd method signature.
+ ULONG cbSig2) // Size of the 2nd method signature.
+{
+ HRESULT hr = S_OK;
+ PCCOR_SIGNATURE pbSig1Start;
+ PCCOR_SIGNATURE pbSig2Start;
+ ULONG Sig1ParamCount;
+ ULONG Sig2ParamCount;
+ ULONG cbSig1RetType;
+ ULONG cbSig2RetType;
+
+ // Save the start of the signatures.
+ pbSig1Start = pbSig1;
+ pbSig2Start = pbSig2;
+
+ // Skip the calling conventions.
+ CorSigUncompressData(pbSig1);
+ CorSigUncompressData(pbSig2);
+
+ // Compare the param count.
+ Sig1ParamCount = CorSigUncompressData(pbSig1);
+ Sig2ParamCount = CorSigUncompressData(pbSig2);
+ if (Sig1ParamCount != Sig2ParamCount)
+ return S_FALSE;
+
+ // Skip the return type.
+ cbSig1RetType = cbSig1 - (ULONG)(pbSig1 - pbSig1Start);
+ IfFailGo(_CountBytesOfOneArg(pbSig1, &cbSig1RetType));
+ pbSig1 += cbSig1RetType;
+ cbSig2RetType = cbSig2 - (ULONG)(pbSig2 - pbSig2Start);
+ IfFailGo(_CountBytesOfOneArg(pbSig2, &cbSig2RetType));
+ pbSig2 += cbSig2RetType;
+
+ // Update the remaining sig sizes.
+ cbSig1 -= (ULONG) (pbSig1 - pbSig1Start);
+ cbSig2 -= (ULONG) (pbSig2 - pbSig2Start);
+
+ // If the remaining sig sizes are different then the sigs don't match.
+ if (cbSig1 != cbSig2)
+ return S_FALSE;
+
+ // Compare the rest of the sigs using memcmp.
+ if (memcmp(pbSig1, pbSig2, cbSig1) != 0)
+ return S_FALSE;
+
+ // The parameters match.
+ return S_OK;
+
+ErrExit:
+ // An error occured.
+ return hr;
+} // HRESULT CImportTlb::CompareSigsIgnoringRetType()
+
+//*****************************************************************************
+// Look up a method in the emit scope. This lookup method does not take the
+// return type into account when comparing using a sig. So 2 methods with
+// the same name, same parameters and a different return type will be
+// considered the same.
+//*****************************************************************************
+HRESULT CImportTlb::FindMethod( // S_OK or CLDB_E_RECORD_NOTFOUND, or error.
+ mdTypeDef td, // The method typedef.
+ LPCWSTR szName, // The method name.
+ PCCOR_SIGNATURE pbReqSig, // The method signature.
+ ULONG cbReqSig, // Size of the method signature.
+ mdMethodDef *pmb) // Put the method here.
+{
+ HRESULT hr = S_OK; // A result.
+ PCCOR_SIGNATURE pbFoundSig = NULL;
+ ULONG cbFoundSig = 0;
+ ULONG MethodAttr;
+ ULONG MethodImplFlags;
+ mdMethodDef md;
+ CQuickArray<WCHAR> qbMethodName;
+ HCORENUM MethodEnum = NULL;
+ ULONG cMethods = 0;
+ ULONG cchName;
+ BOOL bMethodFound = FALSE;
+
+ // Go through all the methods on the class looking for one with the
+ // same name and same parameters.
+ while ((hr = m_pImport->EnumMethods(&MethodEnum, td, &md, 1, &cMethods)) == S_OK)
+ {
+ // Retrieve the method properties.
+ do {
+ IfFailGo(m_pImport->GetMethodProps(
+ md,
+ NULL,
+ qbMethodName.Ptr(),
+ (ULONG)qbMethodName.MaxSize(),
+ &cchName,
+ &MethodAttr,
+ &pbFoundSig,
+ &cbFoundSig,
+ NULL,
+ &MethodImplFlags));
+ if (hr == CLDB_S_TRUNCATION)
+ {
+ IfFailGo(qbMethodName.ReSizeNoThrow(cchName));
+ continue;
+ }
+ break;
+ } while (1);
+
+ // Compare the name of the method.
+ if (wcscmp(szName, qbMethodName.Ptr()) != 0)
+ continue;
+
+ // If the signature of the requested method is specified, then compare
+ // the signature against the signature of the found method ignoring
+ // the return type.
+ if (pbReqSig)
+ {
+ IfFailGo(hr = CompareSigsIgnoringRetType(pbReqSig, cbReqSig, pbFoundSig, cbFoundSig));
+ if (hr == S_FALSE)
+ continue;
+ }
+
+ // We have found the member.
+ bMethodFound = TRUE;
+ break;
+ }
+ IfFailGo(hr);
+
+ErrExit:
+ if (MethodEnum)
+ m_pImport->CloseEnum(MethodEnum);
+
+ return bMethodFound ? S_OK : CLDB_E_RECORD_NOTFOUND;
+}
+
+//*****************************************************************************
+// Look up a property in the emit scope.
+//*****************************************************************************
+HRESULT CImportTlb::FindProperty( // S_OK or CLDB_E_RECORD_NOTFOUND, or error.
+ mdTypeDef td, // The property typedef.
+ LPCWSTR szName, // The property name.
+ PCCOR_SIGNATURE pbSig, // The property signature.
+ ULONG cbSig, // Size of the property signature.
+ mdProperty *ppr) // Put the property here.
+{
+ HRESULT hr; // A result.
+ RegMeta *pRegMeta = (RegMeta*)(m_pEmit);
+ LPUTF8 szNameAnsi;
+
+ if (szName == NULL)
+ {
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+ UTF8STR(szName, szNameAnsi);
+
+ hr = ImportHelper::FindProperty(
+ &(pRegMeta->GetMiniStgdb()->m_MiniMd),
+ m_tdTypeDef,
+ szNameAnsi,
+ pbSig,
+ cbSig,
+ ppr);
+ return hr;
+} // HRESULT CImportTlb::FindProperty()
+
+//*****************************************************************************
+// Look up a event in the emit scope.
+//*****************************************************************************
+HRESULT CImportTlb::FindEvent( // S_OK or CLDB_E_RECORD_NOTFOUND, or error.
+ mdTypeDef td, // The event typedef.
+ LPCWSTR szName, // The event name.
+ mdEvent *pev) // Put the event here.
+{
+ HRESULT hr; // A result.
+ RegMeta *pRegMeta = (RegMeta*)(m_pEmit);
+ LPUTF8 szNameAnsi;
+
+ if (szName == NULL)
+ {
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+ UTF8STR(szName, szNameAnsi);
+
+ hr = ImportHelper::FindEvent(
+ &(pRegMeta->GetMiniStgdb()->m_MiniMd),
+ m_tdTypeDef,
+ szNameAnsi,
+ pev);
+ return hr;
+} // HRESULT CImportTlb::FindEvent()
+
+//*****************************************************************************
+// Checks to see if the specified TYPEDESC is an alias.
+//*****************************************************************************
+HRESULT CImportTlb::_IsAlias(
+ ITypeInfo *pITI, // The ITypeInfo containing the TYPEDESC.
+ TYPEDESC *pTypeDesc) // The token of the param, field, etc.
+{
+ HRESULT hr = S_FALSE; // A result.
+ ITypeInfo *pTypeITI=0; // The ITypeInfo of the type.
+ ITypeLib *pTypeTLB=0; // The TLB that contains the type.
+ TYPEATTR *psTypeAttr=0; // TYPEATTR of the type.
+
+ // Drill down to the actual type that is pointed to.
+ while (pTypeDesc->vt == VT_PTR)
+ pTypeDesc = pTypeDesc->lptdesc;
+
+ // If the parameter is an alias then we need to add a custom attribute to the
+ // parameter that describes the alias.
+ if (pTypeDesc->vt == VT_USERDEFINED)
+ {
+ IfFailGo(pITI->GetRefTypeInfo(pTypeDesc->hreftype, &pTypeITI));
+ IfFailGo(pTypeITI->GetTypeAttr(&psTypeAttr));
+ if (psTypeAttr->typekind == TKIND_ALIAS)
+ {
+ hr = S_OK;
+ }
+ }
+
+ErrExit:
+ if (psTypeAttr)
+ pTypeITI->ReleaseTypeAttr(psTypeAttr);
+ if (pTypeITI)
+ pTypeITI->Release();
+ return hr;
+} // HRESULT CImportTlb::_IsAlias()
+
+//*****************************************************************************
+// Add alias information if the TYPEDESC represents an alias.
+//*****************************************************************************
+HRESULT CImportTlb::_HandleAliasInfo(
+ ITypeInfo *pITI, // The ITypeInfo containing the TYPEDESC.
+ TYPEDESC *pTypeDesc, // The TYPEDESC.
+ mdToken tk) // The token of the param, field, etc.
+{
+ HRESULT hr = S_OK; // A result.
+ ITypeInfo *pTypeITI=0; // The ITypeInfo of the type.
+ ITypeLib *pTypeTLB=0; // The TLB that contains the type.
+ TYPEATTR *psTypeAttr=0; // TYPEATTR of the type.
+ BSTR bstrAliasTypeName=0; // The name of the alias type.
+ BSTR bstrAliasTypeLibName=0; // The name of the typelib that contains the alias type.
+
+ // Drill down to the actual type that is pointed to.
+ while (pTypeDesc->vt == VT_PTR)
+ pTypeDesc = pTypeDesc->lptdesc;
+
+ // If the parameter is an alias then we need to add a custom attribute to the
+ // parameter that describes the alias.
+ if (pTypeDesc->vt == VT_USERDEFINED)
+ {
+ IfFailGo(pITI->GetRefTypeInfo(pTypeDesc->hreftype, &pTypeITI));
+ IfFailGo(pTypeITI->GetTypeAttr(&psTypeAttr));
+ if (psTypeAttr->typekind == TKIND_ALIAS)
+ {
+ // Retrieve the name of the alias type.
+ IfFailGo(pTypeITI->GetContainingTypeLib(&pTypeTLB, NULL));
+ IfFailGo(GetNamespaceOfRefTlb(pTypeTLB, &bstrAliasTypeLibName, NULL));
+ IfFailGo(GetManagedNameForTypeInfo(pTypeITI, bstrAliasTypeLibName, NULL, &bstrAliasTypeName));
+
+ // Add the ComAliasName CA to the parameter.
+ _AddStringCa(ATTR_COMALIASNAME, tk, bstrAliasTypeName);
+ }
+ }
+
+ErrExit:
+ if (psTypeAttr)
+ pTypeITI->ReleaseTypeAttr(psTypeAttr);
+ if (pTypeITI)
+ pTypeITI->Release();
+ if (pTypeTLB)
+ pTypeTLB->Release();
+ if (bstrAliasTypeLibName)
+ ::SysFreeString(bstrAliasTypeLibName);
+ if (bstrAliasTypeName)
+ ::SysFreeString(bstrAliasTypeName);
+ return hr;
+} // HRESULT CImportTlb::_HandleAliasInfo()
+
+//*****************************************************************************
+// Convert one of a function's parameters.
+//*****************************************************************************
+HRESULT CImportTlb::_ConvParam(
+ ITypeInfo *pITI, // Containing TypeInfo.
+ mdMethodDef mdFunc, // Owning member.
+ int iSequence, // Parameter sequence.
+ const ELEMDESC *pdesc, // Param flags, default value.
+ ParamOpts paramOpts, // Is param normal, optional, or vararg?
+ LPCWSTR szName, // Name of the parameter.
+ BYTE *pbNative, // Native type info, if any.
+ ULONG cbNative) // Size of native type info.
+{
+ HRESULT hr; // A result.
+ mdParamDef pdParam; // Token of the parameter.
+ DWORD dwFlags; // Param flags.
+ USHORT Sequence = static_cast<USHORT>(iSequence);
+ BYTE cvType = ELEMENT_TYPE_VOID; // ELEMENT_TYPE_* flag for constant value
+ void *pcvValue=0; // constant value blob
+ __int64 d; // For cases where value is a date.
+ int bDecimal=0; // If true, constant is a decimal.
+ mdToken tkAttr; // For custom attribute token.
+ DECIMAL decVal; // Decimal constant value.
+
+ // Compute the flags. Only make sense on non-return params.
+ dwFlags = 0;
+ if (iSequence > 0)
+ {
+ if (pdesc->paramdesc.wParamFlags & PARAMFLAG_FIN)
+ dwFlags |= pdIn;
+ if (pdesc->paramdesc.wParamFlags & PARAMFLAG_FOUT)
+ dwFlags |= pdOut;
+ if (pdesc->paramdesc.wParamFlags & PARAMFLAG_FOPT)
+ dwFlags |= pdOptional;
+ if (paramOpts == ParamOptional)
+ dwFlags |= pdOptional;
+ }
+
+ // Get any default values. Return type, param with iSequence==0, has no default.
+ if (pdesc->paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT && iSequence != 0)
+ {
+ switch (pdesc->paramdesc.pparamdescex->varDefaultValue.vt)
+ {
+ case VT_CY:
+ case VT_DECIMAL:
+ case VT_DATE:
+ case VT_UNKNOWN:
+ case VT_DISPATCH:
+ break;
+ default:
+ // This workaround is because a typelib can store anything that can convert to VT_I4 with a value of 0
+ // for the default value of an interface pointer. But, a VT_I2(0) confuses the consumers
+ // of the managed wrapper dll. So, if it is an interface on the unmanaged side, make
+ // the constant value an ET_CLASS.
+ if (cbNative > 0 && (*pbNative == NATIVE_TYPE_INTF ||
+ *pbNative == NATIVE_TYPE_IUNKNOWN ||
+ *pbNative == NATIVE_TYPE_IDISPATCH))
+ {
+ cvType = ELEMENT_TYPE_CLASS;
+ pcvValue = 0;
+ }
+ else
+ IfFailGo( _UnpackVariantToConstantBlob(&pdesc->paramdesc.pparamdescex->varDefaultValue, &cvType, &pcvValue, &d) );
+ }
+ }
+
+ // Create the param definition.
+ IfFailGo(m_pEmit->DefineParam(mdFunc, iSequence, szName, dwFlags, cvType, pcvValue, -1, &pdParam));
+
+ // Add the native type if it there is any.
+ if (cbNative > 0)
+ IfFailGo(m_pEmit->SetFieldMarshal(pdParam, (PCCOR_SIGNATURE) pbNative, cbNative));
+
+ if (pdesc->paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT && iSequence != 0)
+ {
+ switch (pdesc->paramdesc.pparamdescex->varDefaultValue.vt)
+ {
+ case VT_CY:
+ IfFailGo(VarDecFromCy(pdesc->paramdesc.pparamdescex->varDefaultValue.cyVal, &decVal));
+ IfFailGo(DecimalCanonicalize(&decVal));
+ goto StoreDecimal;
+ case VT_DECIMAL:
+ // If there is a decimal constant value, set it as a custom attribute.
+ {
+ decVal = pdesc->paramdesc.pparamdescex->varDefaultValue.decVal;
+ StoreDecimal:
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(BYTE)+sizeof(BYTE)+sizeof(UINT)+sizeof(UINT)+sizeof(UINT));
+ BUILD_CUSTOM_ATTRIBUTE(BYTE, decVal.scale);
+ BUILD_CUSTOM_ATTRIBUTE(BYTE, decVal.sign);
+ BUILD_CUSTOM_ATTRIBUTE(UINT, decVal.Hi32);
+ BUILD_CUSTOM_ATTRIBUTE(UINT, decVal.Mid32);
+ BUILD_CUSTOM_ATTRIBUTE(UINT, decVal.Lo32);
+ IfFailGo(GetAttrType(ATTR_DECIMALVALUE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(pdParam, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+ break;
+ case VT_DATE:
+ {
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(__int64));
+ __int64 date = _DoubleDateToTicks(pdesc->paramdesc.pparamdescex->varDefaultValue.date);
+ BUILD_CUSTOM_ATTRIBUTE(__int64, date);
+ IfFailGo(GetAttrType(ATTR_DATETIMEVALUE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(pdParam, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+ break;
+ case VT_UNKNOWN:
+ {
+ DECLARE_CUSTOM_ATTRIBUTE(0);
+ IfFailGo(GetAttrType(ATTR_IUNKNOWNVALUE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(pdParam, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+ break;
+ case VT_DISPATCH:
+ {
+ DECLARE_CUSTOM_ATTRIBUTE(0);
+ IfFailGo(GetAttrType(ATTR_IDISPATCHVALUE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(pdParam, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Add the alias information if the param is an alias.
+ IfFailGo(_HandleAliasInfo(pITI, (TYPEDESC*)&pdesc->tdesc, pdParam));
+
+ // If a vararg param, set the custom attribute.
+ if (paramOpts == ParamVarArg)
+ {
+ mdToken tkAttribute;
+ DECLARE_CUSTOM_ATTRIBUTE(0);
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(GetAttrType(ATTR_PARAMARRAY, &tkAttribute));
+ IfFailGo(m_pEmit->DefineCustomAttribute(pdParam, tkAttribute, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT CImportTlb::_ConvParam()
+
+//*****************************************************************************
+// Convert a constant into a field with a default value.
+//*****************************************************************************
+HRESULT CImportTlb::_ConvConstant(
+ ITypeInfo *pITI, // Containing TypeInfo.
+ VARDESC *psVar, // VARDESC for the property.
+ BOOL bEnumMember) // If true, type is containing class.
+{
+ HRESULT hr; // A result.
+ mdFieldDef mdField; // Token of the new field.
+ DWORD dwFlags; // Member flags.
+ CQuickBytes qbComSig; // The COM+ Signature of the field.
+ ULONG cb, cbTotal;
+ BYTE cvType = ELEMENT_TYPE_VOID; // E_T_Type for constant value
+ void *pcvValue; // Pointer to constant value data.
+ mdToken tkAttr; // Type for custom attribute.
+ __int64 d; // For cases where value is a date.
+ BOOL bConversionLoss=false; // If true, some attributes were lost on conversion.
+ BYTE *pbSig; // Pointer to signature bytes.
+ CQuickArray<BYTE> qbNativeBuf; // Native type buffer.
+ ULONG cbNative = 0; // Size of native type.
+ int bDecimal = 0; // If the value is a decimal.
+ DECIMAL decVal; // Decimal constant value.
+
+ // Information about the member.
+ IfFailGo(pITI->GetDocumentation(psVar->memid, &m_szMember, 0,0,0));
+
+ // resize to make room for calling convention and count of argument
+ IfFailGo(qbComSig.ReSizeNoThrow(CB_MAX_ELEMENT_TYPE * 4));
+ pbSig = (BYTE *)qbComSig.Ptr();
+
+ // Compute properties.
+ dwFlags = DEFAULT_CONST_FIELD_FLAGS;
+
+ // Build the signature.
+ cbTotal = cb = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_FIELD, pbSig);
+ if (bEnumMember)
+ {
+ cb = CorSigCompressData(ELEMENT_TYPE_VALUETYPE, &pbSig[cbTotal]);
+ cbTotal += cb;
+ cb = CorSigCompressToken(m_tdTypeDef, reinterpret_cast<ULONG*>(&pbSig[cbTotal]));
+ cbTotal += cb;
+ }
+ else
+ {
+ // Use the conversion function to get the signature.
+ ULONG cbSave = cbTotal;
+ IfFailGo(_ConvSignature(pITI, &psVar->elemdescVar.tdesc, SIG_FLAGS_NONE, qbComSig, cbTotal, &cbTotal, qbNativeBuf, 0, &cbNative, FALSE));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+
+ if (psVar->elemdescVar.tdesc.vt == VT_DATE)
+ {
+ // But for dates, convert it as float -- DateTime is reported as R4 in a typelib!
+ cbTotal = cbSave;
+ cb = CorSigCompressData(ELEMENT_TYPE_R4, &pbSig[cbTotal]);
+ cbTotal += cb;
+ }
+ }
+
+ // Get the default value.
+ switch (psVar->lpvarValue->vt)
+ {
+ case VT_CY:
+ case VT_DECIMAL:
+ case VT_DATE:
+ case VT_UNKNOWN:
+ case VT_DISPATCH:
+ break;
+ default:
+ // This workaround is because a typelib can store anything that can convert to VT_I4 with a value of 0
+ // for the default value of an interface pointer. But, a VT_I2(0) confuses the consumers
+ // of the managed wrapper dll. So, if it is an interface on the unmanaged side, make
+ // the constant value an ET_CLASS.
+ BYTE *pbNative = NULL;
+ pbNative = qbNativeBuf.Ptr();
+ if (cbNative > 0 && (*pbNative == NATIVE_TYPE_INTF ||
+ *pbNative == NATIVE_TYPE_IUNKNOWN ||
+ *pbNative == NATIVE_TYPE_IDISPATCH))
+ {
+ cvType = ELEMENT_TYPE_CLASS;
+ pcvValue = 0;
+ }
+ else
+ IfFailGo( _UnpackVariantToConstantBlob(psVar->lpvarValue, &cvType, &pcvValue, &d) );
+ }
+
+ // Create the field definition.
+ IfFailGo(m_pEmit->DefineField(m_tdTypeDef, m_szMember, dwFlags, (PCCOR_SIGNATURE)pbSig, cbTotal,
+ cvType, pcvValue, -1, &mdField));
+
+ switch (psVar->lpvarValue->vt)
+ {
+ case VT_CY:
+ IfFailGo(VarDecFromCy(psVar->lpvarValue->cyVal, &decVal));
+ IfFailGo(DecimalCanonicalize(&decVal));
+ goto StoreDecimal;
+ case VT_DECIMAL:
+ // If there is a decimal constant value, set it as a custom attribute.
+ {
+ decVal = psVar->lpvarValue->decVal;
+ StoreDecimal:
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(BYTE)+sizeof(BYTE)+sizeof(UINT)+sizeof(UINT)+sizeof(UINT));
+ BUILD_CUSTOM_ATTRIBUTE(BYTE, decVal.scale);
+ BUILD_CUSTOM_ATTRIBUTE(BYTE, decVal.sign);
+ BUILD_CUSTOM_ATTRIBUTE(UINT, decVal.Hi32);
+ BUILD_CUSTOM_ATTRIBUTE(UINT, decVal.Mid32);
+ BUILD_CUSTOM_ATTRIBUTE(UINT, decVal.Lo32);
+ IfFailGo(GetAttrType(ATTR_DECIMALVALUE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(mdField, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+ break;
+ case VT_DATE:
+ {
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(__int64));
+ __int64 date = _DoubleDateToTicks(psVar->lpvarValue->date);
+ BUILD_CUSTOM_ATTRIBUTE(__int64, date);
+ IfFailGo(GetAttrType(ATTR_DATETIMEVALUE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(mdField, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+ break;
+ case VT_UNKNOWN:
+ {
+ DECLARE_CUSTOM_ATTRIBUTE(0);
+ IfFailGo(GetAttrType(ATTR_IUNKNOWNVALUE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(mdField, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+ break;
+ case VT_DISPATCH:
+ {
+ DECLARE_CUSTOM_ATTRIBUTE(0);
+ IfFailGo(GetAttrType(ATTR_IDISPATCHVALUE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(mdField, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Save the field flags.
+ if (psVar->wVarFlags)
+ {
+ IfFailGo(GetAttrType(ATTR_TYPELIBVAR, &tkAttr));
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(WORD));
+ BUILD_CUSTOM_ATTRIBUTE(WORD, psVar->wVarFlags);
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(mdField, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+
+ // Set up the native description, if any.
+ if (cbNative > 0)
+ IfFailGo(m_pEmit->SetFieldMarshal(mdField, (PCCOR_SIGNATURE) qbNativeBuf.Ptr(), cbNative));
+
+ // Add the alias information if the type is an alias.
+ IfFailGo(_HandleAliasInfo(pITI, &psVar->elemdescVar.tdesc, mdField));
+
+ if (bConversionLoss)
+ {
+ hr = S_CONVERSION_LOSS;
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_I_UNCONVERTABLE_FIELD, m_szName, m_szMember);
+ }
+
+ErrExit:
+ if (m_szMember)
+ ::SysFreeString(m_szMember), m_szMember=0;
+ return (hr);
+} // HRESULT CImportTlb::_ConvConstant()
+
+//*****************************************************************************
+// Convert a (record) field into a member.
+//*****************************************************************************
+HRESULT CImportTlb::_ConvField(
+ ITypeInfo *pITI, // Containing TypeInfo.
+ VARDESC *psVar, // VARDESC for the property.
+ mdFieldDef *pmdField, // Put field token here.
+ BOOL bUnion) // Convert as a union?
+{
+ HRESULT hr; // A result.
+ DWORD dwFlags; // Member flags.
+ CQuickBytes qbComSig; // The COM+ Signature of the field.
+ ULONG cb, cbTotal; // Size of a sig element, signature.
+ BYTE *pbSig; // Pointer to signature bytes.
+ CQuickArray<BYTE> qbNativeBuf; // Native type buffer.
+ ULONG cbNative; // Size of native type.
+ mdToken tkAttr; // CustomAttribute type.
+ BOOL bConversionLoss=false; // If true, some attributes were lost on conversion.
+
+ // Information about the member.
+ IfFailGo(pITI->GetDocumentation(psVar->memid, &m_szMember, 0,0,0));
+
+ // Compute properties.
+ dwFlags = DEFAULT_RECORD_FIELD_FLAGS;
+
+ // resize to make room for calling convention and count of argument
+ IfFailGo(qbComSig.ReSizeNoThrow(CB_MAX_ELEMENT_TYPE * 2));
+ pbSig = (BYTE *)qbComSig.Ptr();
+
+ // Build the signature.
+ cbTotal = cb = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_FIELD, pbSig);
+ IfFailGo(_ConvSignature(pITI, &psVar->elemdescVar.tdesc, SIG_FIELD, qbComSig, cbTotal, &cbTotal, qbNativeBuf, 0, &cbNative, FALSE));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+
+ // Create the field definition.
+ IfFailGo(m_pEmit->DefineField(m_tdTypeDef, m_szMember, dwFlags, (PCCOR_SIGNATURE) qbComSig.Ptr(),cbTotal,
+ ELEMENT_TYPE_VOID, NULL, -1, pmdField));
+
+ // Save the field flags.
+ if (psVar->wVarFlags)
+ {
+ IfFailGo(GetAttrType(ATTR_TYPELIBVAR, &tkAttr));
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(WORD));
+ BUILD_CUSTOM_ATTRIBUTE(WORD, psVar->wVarFlags);
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(*pmdField, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+
+ if (bConversionLoss)
+ {
+ IfFailGo(GetAttrType(ATTR_COMCONVERSIONLOSS, &tkAttr));
+ DECLARE_CUSTOM_ATTRIBUTE(0);
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(*pmdField, tkAttr, PTROF_CUSTOM_ATTRIBUTE(),SIZEOF_CUSTOM_ATTRIBUTE(),0));
+ }
+
+ // Set up the native description, if any.
+ if (cbNative > 0)
+ IfFailGo(m_pEmit->SetFieldMarshal(*pmdField, (PCCOR_SIGNATURE) qbNativeBuf.Ptr(), cbNative));
+
+ // Add the alias information if the type is an alias.
+ IfFailGo(_HandleAliasInfo(pITI, &psVar->elemdescVar.tdesc, *pmdField));
+
+ if (bConversionLoss)
+ {
+ hr = S_CONVERSION_LOSS;
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_I_UNCONVERTABLE_FIELD, m_szName, m_szMember);
+ }
+
+ErrExit:
+ if (m_szMember)
+ ::SysFreeString(m_szMember), m_szMember=0;
+ return (hr);
+} // HRESULT CImportTlb::_ConvField()
+
+//*****************************************************************************
+// Convert a dispatch property into a pair of get/set functions.
+//*****************************************************************************
+HRESULT CImportTlb::_ConvProperty(
+ ITypeInfo *pITI, // Containing TypeInfo.
+ MemberInfo *pMember) // VARDESC for the property.
+{
+ HRESULT hr; // A result.
+ mdMethodDef mdFuncGet; // A get function.
+ mdMethodDef mdFuncSet; // A set function.
+ mdProperty pdProperty; // Property on the two functions.
+ DWORD dwFlags; // Function flags.
+ WCHAR *pszName=0; // Decorated name of member.
+ CQuickArray<WCHAR> qbName; // Buffer for decorated name.
+ CQuickBytes qbComSig; // com signature buffer
+ ULONG cb; // Size of an element.
+ ULONG cbTotal = 0; // Total size of signature.
+ BYTE *pbSig; // Pointer to signature buffer.
+ BOOL bConversionLoss=false; // If true, some attributes were lost on conversion.
+ CQuickArray<BYTE> qbNativeBuf; // Native type buffer.
+ ULONG iNativeOfs=0; // Current offset in native type buffer.
+ VARDESC *psVar = pMember->m_psVar;
+
+ // Check to see if the property is the NewEnum member.
+ if (PropertyIsNewEnum(pITI, psVar, pMember->m_iMember) == S_OK)
+ return _ConvNewEnumProperty(pITI, psVar, pMember);
+
+ // Get the name.
+ IfFailGo(pITI->GetDocumentation(psVar->memid, &m_szMember, 0,0,0));
+
+ // Create the get signature.
+ IfFailGo(qbComSig.ReSizeNoThrow(CB_MAX_ELEMENT_TYPE * 2));
+ pbSig = reinterpret_cast<BYTE*>(qbComSig.Ptr());
+ cbTotal = cb = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS, pbSig);
+ // Getter takes zero parameters.
+ cb = CorSigCompressData(0, &(pbSig[cb]));
+ cbTotal += cb;
+ // Getter returns the property type.
+ IfFailGo(_ConvSignature(pITI, &psVar->elemdescVar.tdesc, SIG_ELEM, qbComSig, cbTotal, &cbTotal, qbNativeBuf, 0, &iNativeOfs, FALSE));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+
+ // Getter properties.
+ dwFlags = DEFAULT_PROPERTY_FUNC_FLAGS;
+ // If processing an implemented interface, remove the abstract bit. Methods on classes are not abstract.
+ if (m_ImplIface != eImplIfaceNone)
+ dwFlags &= ~mdAbstract;
+
+ // Get the previously decorated name. Add interface name and make unique.
+ // m_szInterface should be non-null if processing an implemented interface; should be null otherwise.
+ _ASSERTE(m_ImplIface == eImplIfaceNone || m_szInterface != 0);
+ IfFailGo(qbName.ReSizeNoThrow(wcslen(pMember->m_pName)+2));
+ wcscpy_s(qbName.Ptr(), wcslen(pMember->m_pName)+2, pMember->m_pName);
+ IfFailGo(GenerateUniqueMemberName(qbName, (PCCOR_SIGNATURE)qbComSig.Ptr(), cbTotal, m_szInterface, mdtMethodDef));
+ pszName = qbName.Ptr();
+
+ // Create the get Accessor.
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, pszName, dwFlags, (PCCOR_SIGNATURE) qbComSig.Ptr(), cbTotal,
+ 0/*RVA*/, DEFAULT_ITF_FUNC_IMPL_FLAGS, &mdFuncGet));
+
+ // Handle dispids for non-implemented interfaces, and for default interface
+ if (m_ImplIface != eImplIface)
+ {
+ // Set the Dispid CA.
+ _SetDispIDCA(pITI, pMember->m_iMember, psVar->memid, mdFuncGet, TRUE, NULL, FALSE);
+ }
+
+ // If processing an implemented interface, set up MethodImpls.
+ if (m_ImplIface != eImplIfaceNone)
+ {
+ // Define a memberref on the implemented interface.
+ mdToken mrItfMember;
+ IfFailGo(m_pEmit->DefineMemberRef(m_tkInterface, pMember->m_pName, (PCCOR_SIGNATURE) qbComSig.Ptr(),cbTotal, &mrItfMember));
+
+ // Define a method impl.
+ IfFailGo(m_pEmit->DefineMethodImpl(m_tdTypeDef, mdFuncGet, mrItfMember));
+ }
+
+ // If not a read-only var, create the setter.
+ if ((psVar->wVarFlags & VARFLAG_FREADONLY) == 0)
+ {
+ // Create the setter signature.
+ IfFailGo(qbComSig.ReSizeNoThrow(CB_MAX_ELEMENT_TYPE * 3));
+ pbSig = reinterpret_cast<BYTE*>(qbComSig.Ptr());
+ cbTotal = cb = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS, pbSig);
+ // Setter takes one parameter.
+ cb = CorSigCompressData(1, &(pbSig[cb]));
+ cbTotal += cb;
+ // Setter returns nothing.
+ cb = CorSigCompressData(ELEMENT_TYPE_VOID, &pbSig[cbTotal]);
+ cbTotal += cb;
+ // Setter takes the property type.
+ IfFailGo(_ConvSignature(pITI, &psVar->elemdescVar.tdesc, SIG_ELEM, qbComSig, cbTotal, &cbTotal, qbNativeBuf, 0, &iNativeOfs, FALSE));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+
+ // Setter properties.
+ dwFlags = DEFAULT_PROPERTY_FUNC_FLAGS;
+ // If processing an implemented interface, remove the abstract bit. Methods on classes are not abstract.
+ if (m_ImplIface != eImplIfaceNone)
+ dwFlags &= ~mdAbstract;
+
+ // Get the previously decorated name. Add interface name and make unique.
+ // m_szInterface should be non-null if processing an implemented interface; should be null otherwise.
+ _ASSERTE(m_ImplIface == eImplIfaceNone || m_szInterface != 0);
+ IfFailGo(qbName.ReSizeNoThrow(wcslen(pMember->m_pName2)+2));
+ wcscpy_s(qbName.Ptr(), wcslen(pMember->m_pName2)+2, pMember->m_pName2);
+ IfFailGo(GenerateUniqueMemberName(qbName, (PCCOR_SIGNATURE)qbComSig.Ptr(), cbTotal, m_szInterface, mdtMethodDef));
+ pszName = qbName.Ptr();
+
+ // Create the setter Accessor.
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, pszName, dwFlags, (PCCOR_SIGNATURE) qbComSig.Ptr(),cbTotal,
+ 0/*RVA*/, DEFAULT_ITF_FUNC_IMPL_FLAGS, &mdFuncSet));
+
+ // Handle dispids for non-implemented interfaces, and for default interface
+ if (m_ImplIface != eImplIface)
+ {
+ // Set the Dispid CA.
+ _SetDispIDCA(pITI, pMember->m_iMember, psVar->memid, mdFuncSet, TRUE, NULL, FALSE);
+ }
+
+ // If processing an implemented interface, set up MethodImpls.
+ if (m_ImplIface != eImplIfaceNone)
+ {
+ // Define a memberref on the implemented interface.
+ mdToken mrItfMember;
+ IfFailGo(m_pEmit->DefineMemberRef(m_tkInterface, pMember->m_pName2, (PCCOR_SIGNATURE) qbComSig.Ptr(),cbTotal, &mrItfMember));
+
+ // Define a method impl.
+ IfFailGo(m_pEmit->DefineMethodImpl(m_tdTypeDef, mdFuncSet, mrItfMember));
+ }
+ }
+ else
+ { // read-only, setter method is nil.
+ mdFuncSet = mdMethodDefNil;
+ }
+
+ // Create the property signature: 'type', or <fieldcallconv><type>
+ cbTotal = cb = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_PROPERTY, pbSig);
+ cb = CorSigCompressData(0, &(pbSig[cb]));
+ cbTotal += cb;
+ // Property is just the property type.
+ IfFailGo(_ConvSignature(pITI, &psVar->elemdescVar.tdesc, SIG_ELEM, qbComSig, cbTotal, &cbTotal, qbNativeBuf, 0, &iNativeOfs, FALSE));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+
+ // Get the property name. Add interface name and make unique, if needed.
+ // m_szInterface should be non-null if processing an implemented interface; should be null otherwise.
+ _ASSERTE(m_ImplIface == eImplIfaceNone || m_szInterface != 0);
+ IfFailGo(qbName.ReSizeNoThrow(wcslen(m_szMember)+2));
+ wcscpy_s(qbName.Ptr(), wcslen(m_szMember)+2, m_szMember);
+ IfFailGo(GenerateUniqueMemberName(qbName, (PCCOR_SIGNATURE)qbComSig.Ptr(), cbTotal, m_szInterface, mdtProperty));
+ pszName = qbName.Ptr();
+
+ // Set up the Property on the two methods.
+ IfFailGo(m_pEmit->DefineProperty(m_tdTypeDef, pszName, 0/*dwFlags*/, (PCCOR_SIGNATURE) qbComSig.Ptr(),cbTotal, ELEMENT_TYPE_VOID, NULL/*default*/, -1,
+ mdFuncSet, mdFuncGet, NULL, &pdProperty));
+
+ // Handle dispids for non-implemented interfaces, and for default interface
+ if (m_ImplIface != eImplIface)
+ {
+ // Set the Dispid CA on the property.
+ long lDispSet = 1;
+ _SetDispIDCA(pITI, pMember->m_iMember, psVar->memid, pdProperty, TRUE, &lDispSet, FALSE);
+
+ // If this property is default property, add a custom attribute to the class.
+ if (lDispSet == DISPID_VALUE)
+ IfFailGo(_AddDefaultMemberCa(m_tdTypeDef, m_szMember));
+ }
+
+ // Add the alias information if the type is an alias.
+ IfFailGo(_HandleAliasInfo(pITI, &psVar->elemdescVar.tdesc, pdProperty));
+
+ if (bConversionLoss)
+ {
+ hr = S_CONVERSION_LOSS;
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_I_UNCONVERTABLE_ARGS, m_szName, m_szMember);
+ }
+
+ErrExit:
+ if (m_szMember)
+ ::SysFreeString(m_szMember), m_szMember=0;
+ return (hr);
+} // HRESULT CImportTlb::_ConvProperty()
+
+//*****************************************************************************
+// Convert the NewEnum dispatch property into the GetEnumerator method.
+//*****************************************************************************
+HRESULT CImportTlb::_ConvNewEnumProperty(
+ ITypeInfo *pITI, // Containing TypeInfo.
+ VARDESC *psVar, // VARDESC for the property.
+ MemberInfo *pMember)
+{
+ HRESULT hr; // A result.
+ mdMethodDef mdGetEnum; // The GetEnumerator method.
+ CQuickBytes qbComSig; // com signature buffer
+ ULONG cb; // Size of an element.
+ ULONG cbTotal = 0; // Total size of signature.
+ BYTE *pbSig; // Pointer to signature buffer.
+ BOOL bConversionLoss=false; // If true, some attributes were lost on conversion.
+ CQuickArray<BYTE> qbNativeBuf; // Native type buffer.
+ ULONG iNativeOfs=0; // Current offset in native type buffer.
+
+ // Get the name.
+ IfFailGo(pITI->GetDocumentation(psVar->memid, &m_szMember, 0,0,0));
+
+ // Create the GetEnumerator signature.
+ IfFailGo(qbComSig.ReSizeNoThrow(CB_MAX_ELEMENT_TYPE * 2));
+ pbSig = reinterpret_cast<BYTE*>(qbComSig.Ptr());
+ cbTotal = cb = CorSigCompressData((ULONG)IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS, pbSig);
+
+ // GetEnumerator takes zero parameters.
+ cb = CorSigCompressData(0, &(pbSig[cb]));
+ cbTotal += cb;
+
+ // Getter returns the property type.
+ IfFailGo(_ConvSignature(pITI, &psVar->elemdescVar.tdesc, SIG_ELEM, qbComSig, cbTotal, &cbTotal, qbNativeBuf, 0, &iNativeOfs, TRUE));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+
+ // Create the GetEnumerator method.
+ IfFailGo(m_pEmit->DefineMethod(m_tdTypeDef, GET_ENUMERATOR_MEMBER_NAME, DEFAULT_INTERFACE_FUNC_FLAGS, (PCCOR_SIGNATURE) qbComSig.Ptr(), cbTotal,
+ 0/*RVA*/, DEFAULT_ITF_FUNC_IMPL_FLAGS, &mdGetEnum));
+
+ // Set the Dispid CA.
+ _SetDispIDCA(pITI, pMember->m_iMember, psVar->memid, mdGetEnum, TRUE, NULL, FALSE);
+
+ // Add the alias information if the type is an alias.
+ IfFailGo(_HandleAliasInfo(pITI, &psVar->elemdescVar.tdesc, mdGetEnum));
+
+ if (bConversionLoss)
+ {
+ hr = S_CONVERSION_LOSS;
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_I_UNCONVERTABLE_ARGS, m_szName, m_szMember);
+ }
+
+ErrExit:
+ if (m_szMember)
+ ::SysFreeString(m_szMember), m_szMember=0;
+ return (hr);
+} // HRESULT CImportTlb::_ConvNewEnumProperty()
+
+//*****************************************************************************
+// Given an ITypeLib*, come up with a namespace name. Use the typelib name
+// unless there is one specified via custom attribute.
+//
+// NOTE: This returns the member variable m_wzNamespace if the typelib
+// is the importing typelib. That must not be freed!
+//*****************************************************************************
+HRESULT CImportTlb::GetNamespaceOfRefTlb(
+ ITypeLib *pITLB, // TypeLib for which to get namespace name.
+ BSTR *pwzNamespace, // Put the name here.
+ CImpTlbDefItfToClassItfMap **ppDefItfToClassItfMap) // Put def itf to class itf map here.
+{
+ mdAssemblyRef arDummy;
+ BSTR wzAsmName = NULL;
+ HRESULT hr = S_OK;
+
+ // If already resolved, just return assembly ref.
+ if (!m_LibRefs.Find(pITLB, &arDummy, pwzNamespace, &wzAsmName, NULL, ppDefItfToClassItfMap))
+ {
+ // Add a reference to the typelib.
+ IfFailGo(_AddTlbRef(pITLB, &arDummy, pwzNamespace, &wzAsmName, ppDefItfToClassItfMap));
+ }
+
+ErrExit:
+ if (wzAsmName)
+ ::SysFreeString(wzAsmName);
+
+ return hr;
+} // HRESULT CImportTlb::GetNamespaceOfRefTlb()
+
+//*****************************************************************************
+// Given a TYPEDESC, resolve the USERDEFINED to the TYPEKIND.
+//*****************************************************************************
+HRESULT CImportTlb::_ResolveTypeDescAliasTypeKind(
+ ITypeInfo *pITIAlias, // The typeinfo containing the typedesc.
+ TYPEDESC *ptdesc, // The typedesc.
+ TYPEKIND *ptkind) // Put the aliased typekind.
+{
+ HRESULT hr; // A result.
+ ITypeInfo *pTIResolved=0; // The resolved ITypeInfo.
+ TYPEATTR *psResolved=0; // The resolved TypeInfo's TYPEATTR
+
+ if (ptdesc->vt != VT_USERDEFINED)
+ {
+ *ptkind = TKIND_MAX;
+ return S_FALSE;
+ }
+
+ hr = _ResolveTypeDescAlias(pITIAlias, ptdesc, &pTIResolved, &psResolved);
+ if (hr == S_OK)
+ *ptkind = psResolved->typekind;
+ else
+ *ptkind = TKIND_MAX;
+
+ if (psResolved)
+ pTIResolved->ReleaseTypeAttr(psResolved);
+ if (pTIResolved)
+ pTIResolved->Release();
+
+ return hr;
+} // HRESULT CImportTlb::_ResolveTypeDescAliasTypeKind()
+
+//*****************************************************************************
+// Given a TYPEDESC in a TypeInfo, eliminate aliases (get to the aliased
+// type).
+//*****************************************************************************
+HRESULT CImportTlb::_ResolveTypeDescAlias(
+ ITypeInfo *pITIAlias, // The typeinfo containing the typedesc.
+ const TYPEDESC *ptdesc, // The typedesc.
+ ITypeInfo **ppTIResolved, // Put the aliased ITypeInfo here.
+ TYPEATTR **ppsAttrResolved, // Put the ITypeInfo's TYPEATTR here.
+ GUID *pGuid) // Caller may want aliased object's guid.
+{
+ HRESULT hr; // A result.
+ ITypeInfo *pITI=0; // Referenced typeinfo.
+ TYPEATTR *psAttr=0; // TYPEATTR of referenced typeinfo.
+
+ // If the TDESC isn't a USERDEFINED, it is already resolved.
+ if (ptdesc->vt != VT_USERDEFINED)
+ {
+ *ppTIResolved = pITIAlias;
+ pITIAlias->AddRef();
+ // Need to addref the [out] psAttr. Only way to do it:
+ IfFailGo(pITIAlias->GetTypeAttr(ppsAttrResolved));
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+ // The TYPEDESC is a USERDEFINED. Get the TypeInfo.
+ IfFailGo(pITIAlias->GetRefTypeInfo(ptdesc->hreftype, &pITI));
+ IfFailGo(pITI->GetTypeAttr(&psAttr));
+
+ // If the caller needs the aliased object's guid, get it now.
+ if (pGuid && *pGuid == GUID_NULL && psAttr->guid != GUID_NULL)
+ *pGuid = psAttr->guid;
+
+ // If the userdefined typeinfo is not itself an alias, then it is what the alias aliases.
+ // Also, if the userdefined typeinfo is an alias to a builtin type, then the builtin
+ // type is what the alias aliases.
+ if (psAttr->typekind != TKIND_ALIAS || psAttr->tdescAlias.vt != VT_USERDEFINED)
+ {
+ *ppsAttrResolved = psAttr;
+ *ppTIResolved = pITI;
+ if (psAttr->typekind == TKIND_ALIAS)
+ hr = S_FALSE;
+ psAttr = 0;
+ pITI = 0;
+ goto ErrExit;
+ }
+
+ // The userdefined type was itself an alias to a userdefined type. Alias to what?
+ hr = _ResolveTypeDescAlias(pITI, &psAttr->tdescAlias, ppTIResolved, ppsAttrResolved, pGuid);
+
+ErrExit:
+ if (psAttr)
+ pITI->ReleaseTypeAttr(psAttr);
+ if (pITI)
+ pITI->Release();
+ return hr;
+} // HRESULT CImportTlb::_ResolveTypeDescAlias()
+
+//*****************************************************************************
+// Create the TypeInfo records (AKA classes, AKA critters).
+//*****************************************************************************
+HRESULT CImportTlb::GetKnownTypeToken(
+ VARTYPE vt, // The type for which the token is desired.
+ mdTypeRef *ptr) // Put the token here.
+{
+ HRESULT hr = S_OK; // A result.
+
+ _ASSERTE(
+ (vt >= VT_CY && vt <= VT_DECIMAL) || (vt == VT_SAFEARRAY) || (vt == VT_SLOT_FOR_GUID) ||
+ (vt == VT_SLOT_FOR_IENUMERABLE) || (vt == VT_SLOT_FOR_MULTICASTDEL) || (vt == VT_SLOT_FOR_TYPE) ||
+ (vt == VT_SLOT_FOR_STRINGBUF));
+
+ // If it has already been added, just return it.
+ if (m_tkKnownTypes[vt])
+ {
+ *ptr = m_tkKnownTypes[vt];
+ goto ErrExit;
+ }
+
+ // Not yet created, so create the typeref now.
+ switch (vt)
+ {
+ //=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ // WARNING: the VT_EMPTY slot is used for System.GUID!!
+ case VT_SLOT_FOR_GUID:
+ _ASSERTE(VT_SLOT_FOR_GUID == VT_EMPTY);
+ IfFailGo(m_TRMap.DefineTypeRef(
+ m_pEmit, // The emit scope.
+ m_arSystem, // The system assemblyref.
+ TLB_CLASSLIB_GUID, // URL of the TypeDef, wide chars.
+ &m_tkKnownTypes[VT_SLOT_FOR_GUID])); // Put mdTypeRef here
+ break;
+
+ // WARNING: the VT_NULL slot is used for System.Collections.IEnumerable!!
+ case VT_SLOT_FOR_IENUMERABLE:
+ _ASSERTE(VT_SLOT_FOR_IENUMERABLE == VT_NULL);
+ IfFailGo(m_TRMap.DefineTypeRef(
+ m_pEmit, // The emit scope.
+ m_arSystem, // The system assemblyref.
+ TLB_CLASSLIB_IENUMERABLE, // URL of the TypeDef, wide chars.
+ &m_tkKnownTypes[VT_SLOT_FOR_IENUMERABLE])); // Put mdTypeRef here
+ break;
+
+ // WARNING: the VT_I2 slot is used for System.MulticastDelegate!!
+ case VT_SLOT_FOR_MULTICASTDEL:
+ _ASSERTE(VT_SLOT_FOR_MULTICASTDEL == VT_I2);
+ IfFailGo(m_TRMap.DefineTypeRef(
+ m_pEmit, // The emit scope.
+ m_arSystem, // The system assemblyref.
+ TLB_CLASSLIB_MULTICASTDELEGATE, // URL of the TypeDef, wide chars.
+ &m_tkKnownTypes[VT_SLOT_FOR_MULTICASTDEL])); // Put mdTypeRef here
+ break;
+
+ // WARNING: the VT_I4 slot is used for System.Type!!
+ case VT_SLOT_FOR_TYPE:
+ _ASSERTE(VT_SLOT_FOR_TYPE == VT_I4);
+ IfFailGo(m_TRMap.DefineTypeRef(
+ m_pEmit, // The emit scope.
+ m_arSystem, // The system assemblyref.
+ TLB_CLASSLIB_TYPE, // URL of the TypeDef, wide chars.
+ &m_tkKnownTypes[VT_SLOT_FOR_TYPE])); // Put mdTypeRef here
+ break;
+
+ // WARNING: the VT_I8 slot is used for System.Text.StringBuilder!!
+ case VT_SLOT_FOR_STRINGBUF:
+ _ASSERTE(VT_SLOT_FOR_STRINGBUF == VT_I8);
+ IfFailGo(m_TRMap.DefineTypeRef(
+ m_pEmit, // The emit scope.
+ m_arSystem, // The system assemblyref.
+ TLB_CLASSLIB_STRINGBUFFER, // URL of the TypeDef, wide chars.
+ &m_tkKnownTypes[VT_SLOT_FOR_STRINGBUF])); // Put mdTypeRef here
+ break;
+
+ case VT_CY:
+ IfFailGo(m_TRMap.DefineTypeRef(
+ m_pEmit, // The emit scope.
+ m_arSystem, // The system assemblyref.
+ TLB_CLASSLIB_DECIMAL, // URL of the TypeDef, wide chars.
+ &m_tkKnownTypes[VT_CY])); // Put mdTypeRef here
+ break;
+
+ case VT_DATE:
+ IfFailGo(m_TRMap.DefineTypeRef(
+ m_pEmit, // The emit scope.
+ m_arSystem, // The system assemblyref.
+ TLB_CLASSLIB_DATE, // URL of the TypeDef, wide chars.
+ &m_tkKnownTypes[VT_DATE])); // Put mdTypeRef here
+ break;
+
+ case VT_DECIMAL:
+ IfFailGo(m_TRMap.DefineTypeRef(
+ m_pEmit, // The emit scope.
+ m_arSystem, // The system assemblyref.
+ TLB_CLASSLIB_DECIMAL, // URL of the TypeDef, wide chars.
+ &m_tkKnownTypes[VT_DECIMAL])); // Put mdTypeRef here
+ break;
+
+ case VT_SAFEARRAY:
+ IfFailGo(m_TRMap.DefineTypeRef(
+ m_pEmit, // The emit scope.
+ m_arSystem, // The system assemblyref.
+ TLB_CLASSLIB_ARRAY, // URL of the TypeDef, wide chars.
+ &m_tkKnownTypes[VT_SAFEARRAY])); // Put mdTypeRef here
+ break;
+
+ default:
+ _ASSERTE(!"Unknown type in GetKnownTypes");
+ IfFailGo(E_INVALIDARG);
+ }
+
+ _ASSERTE(!IsNilToken(m_tkKnownTypes[vt]));
+ *ptr = m_tkKnownTypes[vt];
+
+ErrExit:
+ return hr;
+} // HRESULT CImportTlb::GetKnownTypeToken()
+
+
+//*****************************************************************************
+// Given an ITypeInfo for a coclass, return an ITypeInfo for the default
+// interface. This is either the explicitly marked default, or the first
+// non-source interface.
+//*****************************************************************************
+HRESULT CImportTlb::GetDefaultInterface( // Error, S_OK or S_FALSE.
+ ITypeInfo *pCoClassTI, // The TypeInfo of the coclass.
+ ITypeInfo **pDefaultItfTI) // The returned default interface.
+{
+ HRESULT hr; // A result
+ HREFTYPE href; // HREFTYPE of an implemented interface.
+ INT ImplFlags; // ImplType flags.
+ ITypeInfo *pITI=NULL; // ITypeInfo for an interface.
+ TYPEATTR *pCoClassTypeAttr; // The type attributes of the coclass.
+ int NumInterfaces; // The number of interfaces on the coclass.
+ int i; // A counter.
+
+ // Initialize the default interface to NULL.
+ *pDefaultItfTI = NULL;
+
+ // Retrieve the number of interfaces the coclass has
+ IfFailGo(pCoClassTI->GetTypeAttr(&pCoClassTypeAttr));
+ NumInterfaces = pCoClassTypeAttr->cImplTypes;
+ pCoClassTI->ReleaseTypeAttr(pCoClassTypeAttr);
+
+ for (i=0; i < NumInterfaces; i++)
+ {
+ IfFailGo(pCoClassTI->GetImplTypeFlags(i, &ImplFlags));
+
+ if ((ImplFlags & (IMPLTYPEFLAG_FSOURCE | IMPLTYPEFLAG_FDEFAULT)) == IMPLTYPEFLAG_FDEFAULT)
+ {
+ // We have found a default interface.
+ if (*pDefaultItfTI)
+ (*pDefaultItfTI)->Release();
+
+ IfFailGo(pCoClassTI->GetRefTypeOfImplType(i, &href));
+ IfFailGo(pCoClassTI->GetRefTypeInfo(href, pDefaultItfTI));
+ break;
+ }
+ else if (!(ImplFlags & IMPLTYPEFLAG_FSOURCE) && !(*pDefaultItfTI))
+ {
+ // If this is the first normal interface we encounter then we need to
+ // hang on to it in case we don't find any default interfaces. If that
+ // happens then this is the one that will be returned.
+ IfFailGo(pCoClassTI->GetRefTypeOfImplType(i, &href));
+ IfFailGo(pCoClassTI->GetRefTypeInfo(href, pDefaultItfTI));
+ }
+ }
+
+ // Return either S_OK or S_FALSE depending on if we have found a default interface.
+ if (*pDefaultItfTI)
+ return S_OK;
+ else
+ return S_FALSE;
+
+ErrExit:
+ if (pITI)
+ pITI->Release();
+
+ return hr;
+} // HRESULT CImportTlb::GetDefaultInterface()
+
+//*****************************************************************************
+// Given a TypeInfo, return a TypeDef/TypeRef token.
+//*****************************************************************************
+HRESULT CImportTlb::_GetTokenForTypeInfo(
+ ITypeInfo *pITI, // ITypeInfo for which to get token.
+ BOOL bConvDefItfToClassItf, // If TRUE, convert the def itf to its class itf.
+ mdToken *pToken, // Put the token here.
+ __out_ecount (chTypeRef) __out_opt LPWSTR pszTypeRef, // Optional, put the name here.
+ int chTypeRef, // Size of the name buffer in characters.
+ int *pchTypeRef, // Optional, put size of name here.
+ BOOL bAsmQualifiedName) // Assembly qualified name or not?
+{
+ HRESULT hr; // A result.
+ ITypeLib *pITLB=0; // Containing typelib.
+ BSTR bstrNamespace=0; // Namespace of the type.
+ BSTR bstrFullName=0; // Fully qualified name of type.
+ BSTR bstrTempName=0; // Temp name.
+ BSTR bstrAsmName=0; // Assembly name.
+ LPCWSTR strTypeName=0; // The type name.
+ mdAssemblyRef ar; // The typelib's assembly ref.
+ TYPEATTR* psAttr = 0; // The TYPEATTR for the type info.
+ CImpTlbDefItfToClassItfMap *pDefItfToClassItfMap; // The default interface to class interface map.
+
+ // Get the library.
+ IfFailGo(pITI->GetContainingTypeLib(&pITLB, 0));
+
+ // Resolve the external reference.
+ IfFailGo(_AddTlbRef(pITLB, &ar, &bstrNamespace, &bstrAsmName, &pDefItfToClassItfMap));
+
+ // If are converting default interfaces to class interfaces, then check
+ // to see if we need to do the convertion for the current ITypeInfo.
+ if (bConvDefItfToClassItf)
+ {
+ // Retrieve the TYPEATTR.
+ IfFailGo(pITI->GetTypeAttr(&psAttr));
+
+ // If we are dealing with an interface, then check to see if there
+ // is a class interface we should use.
+ if (psAttr->typekind == TKIND_INTERFACE || psAttr->typekind == TKIND_DISPATCH)
+ {
+ strTypeName = pDefItfToClassItfMap->GetClassItfName(psAttr->guid);
+ }
+ }
+
+ // If we haven't found a class interface, then use the current interface.
+ if (!strTypeName)
+ {
+ // Get the name of the typeinfo.
+ IfFailGo(GetManagedNameForTypeInfo(pITI, bstrNamespace, NULL, &bstrFullName));
+ strTypeName = bstrFullName;
+ }
+
+ // Give name back to caller, if desired.
+ if (pszTypeRef)
+ wcsncpy_s(pszTypeRef, chTypeRef, strTypeName, chTypeRef-1);
+ if (pchTypeRef)
+ *pchTypeRef = (int)(wcslen(pszTypeRef) + 1);
+
+ // Define the TypeRef (will return any existing typeref).
+ IfFailGo(m_TRMap.DefineTypeRef(m_pEmit, ar, strTypeName, pToken));
+
+ // If the caller desires an assembly qualified name, then provide it.
+ if (bAsmQualifiedName)
+ {
+ int cchAsmQualifiedName = SysStringLen(bstrFullName) + SysStringLen(bstrAsmName) + 2;
+ IfNullGo(bstrTempName = ::SysAllocStringLen(0, cchAsmQualifiedName));
+ ns::MakeAssemblyQualifiedName(bstrTempName, cchAsmQualifiedName + 1, bstrFullName, SysStringLen(bstrFullName), bstrAsmName, SysStringLen(bstrAsmName));
+ SysFreeString(bstrFullName);
+ bstrFullName = bstrTempName;
+ }
+
+ // Give name back to caller, if desired.
+ if (pszTypeRef)
+ wcsncpy_s(pszTypeRef, chTypeRef, bstrFullName, chTypeRef-1);
+ if (pchTypeRef)
+ *pchTypeRef = (int)(wcslen(pszTypeRef) + 1);
+
+ErrExit:
+ if (bstrNamespace)
+ ::SysFreeString(bstrNamespace);
+ if (bstrFullName)
+ ::SysFreeString(bstrFullName);
+ if (bstrAsmName)
+ ::SysFreeString(bstrAsmName);
+ if (pITLB)
+ pITLB->Release();
+ if (psAttr)
+ pITI->ReleaseTypeAttr(psAttr);
+
+ return (hr);
+} // HRESULT CImportTlb::_GetTokenForTypeInfo()
+
+//*****************************************************************************
+// Given a TypeInfo for a source interface, creates a new event interface
+// if none exists or returns an existing one.
+//*****************************************************************************
+HRESULT CImportTlb::_GetTokenForEventItf(ITypeInfo *pSrcItfITI, mdTypeRef *ptr)
+{
+#ifndef DACCESS_COMPILE
+ HRESULT hr = S_OK; // A result.
+ ImpTlbEventInfo* pEventInfo; // The event information.
+ BSTR bstrSrcItfName = NULL; // The name of the CoClass.
+ CQuickArray<WCHAR> qbEventItfName; // The name of the event interface.
+ CQuickArray<WCHAR> qbEventProviderName; // The name of the event provider.
+ mdToken tkAttr; // Custom attribute type.
+ BSTR szOldName = NULL; // The old value m_tdTypeDef.
+ mdTypeDef tdOldTypeDef = NULL; // The old value m_szName.
+ TYPEATTR* psAttr = 0; // The TYPEATTR for the source interface.
+ mdTypeRef trEventItf; // A type ref to the event interface.
+ ITypeLib* pTypeTLB; // The typelib containing this interface.
+ mdAssemblyRef ar; // Dummy AssmRef.
+ BSTR wzNamespace=0; // Namespace of the event interface assembly.
+ BSTR wzAsmName=0; // Assembly name of the event interface assembly.
+ Assembly* SrcItfAssembly=0; // The Source Event Interface assembly.
+ CQuickArray<WCHAR> qbSrcItfName; // The name of the source interface.
+ CImpTlbDefItfToClassItfMap *pDefItfToClassItfMap; // The default interface to class interface map.
+ BOOL fInheritsIEnum = FALSE;
+
+ // Retrieve the namespace of the typelib containing this source interface.
+ IfFailGo(pSrcItfITI->GetContainingTypeLib(&pTypeTLB, NULL));
+
+ // Resolve the external reference.
+ IfFailGo(_AddTlbRef(pTypeTLB, &ar, &wzNamespace, &wzAsmName, &pDefItfToClassItfMap));
+
+ // Get the assembly + namespace the source interface resides in.
+ // May return all NULL - indicating the importing assembly.
+ m_LibRefs.Find(pTypeTLB, &ar, &wzNamespace, &wzAsmName, &SrcItfAssembly, NULL);
+ if (SrcItfAssembly == NULL)
+ SrcItfAssembly = m_pAssembly;
+
+ // Retrieve the full name of the source interface.
+ if (wzNamespace)
+ IfFailGo(GetManagedNameForTypeInfo(pSrcItfITI, (WCHAR*)wzNamespace, NULL, &bstrSrcItfName));
+ else
+ IfFailGo(GetManagedNameForTypeInfo(pSrcItfITI, m_wzNamespace, NULL, &bstrSrcItfName));
+
+ // Start by looking up the event information for the source itf type info.
+ pEventInfo = m_EventInfoMap.FindEventInfo(bstrSrcItfName);
+ if (pEventInfo)
+ {
+ SysFreeString(bstrSrcItfName);
+ *ptr = pEventInfo->trEventItf;
+ return S_OK;
+ }
+
+ // Store the old values of the ITypeInfo name and of the current type def.
+ szOldName = m_szName;
+ tdOldTypeDef = m_tdTypeDef;
+ m_szName = NULL;
+
+ // Get some information about the TypeInfo.
+ IfFailGo(pSrcItfITI->GetDocumentation(MEMBERID_NIL, &m_szName, 0, 0, 0));
+ IfFailGo(pSrcItfITI->GetTypeAttr(&psAttr));
+
+ if (ExplicitlyImplementsIEnumerable(pSrcItfITI, psAttr) == S_OK)
+ fInheritsIEnum = TRUE;
+
+ // Generate a unique name for the event interface which will be of the form:
+ // <ImportingAssemblyNamespace>.<SrcItfName>_Event<PotentialSuffix>
+
+ // Strip the namespace
+ IfFailGo(qbSrcItfName.ReSizeNoThrow(wcslen(bstrSrcItfName) + 2));
+ ns::SplitPath((WCHAR*)bstrSrcItfName, NULL, 0, qbSrcItfName.Ptr(), (int)wcslen(bstrSrcItfName) + 1);
+
+ // Add the namespace of the importing typelib and the event suffix
+ IfFailGo(qbEventItfName.ReSizeNoThrow(qbSrcItfName.Size() + wcslen(m_wzNamespace) + EVENT_ITF_SUFFIX_LENGTH + 7));
+ StringCchPrintf(qbEventItfName.Ptr(), qbEventItfName.Size(), W("%s.%s%s"), m_wzNamespace, qbSrcItfName.Ptr(), EVENT_ITF_SUFFIX);
+ IfFailGo(GenerateUniqueTypeName(qbEventItfName));
+
+ // Generate a unique name for the event provider which will be of the form:
+ // <ImportingAssemblyNamespace>.<SrcItfName>_EventProvider<PotentialSuffix>
+
+ // Add the namespace of the imporing typelib and the event suffix
+ IfFailGo(qbEventProviderName.ReSizeNoThrow(qbSrcItfName.Size() + wcslen(m_wzNamespace) + EVENT_PROVIDER_SUFFIX_LENGTH + 7));
+ StringCchPrintf(qbEventProviderName.Ptr(), qbEventProviderName.Size(), W("%s.%s%s"), m_wzNamespace, qbSrcItfName.Ptr(), EVENT_PROVIDER_SUFFIX);
+ IfFailGo(GenerateUniqueTypeName(qbEventProviderName));
+
+ // Add the event provider as a reserved name.
+ m_ReservedNames.AddReservedName(qbEventProviderName.Ptr());
+
+ // Create the typedef for the event interface.
+ IfFailGo(m_pEmit->DefineTypeDef(qbEventItfName.Ptr(), tdPublic | tdInterface | tdAbstract, mdTypeDefNil, NULL, &m_tdTypeDef));
+
+ // Hide the event interface from the VB object browser (_Event)
+ _SetHiddenCA(m_tdTypeDef);
+
+ // Make the interface ComVisible(false).
+ {
+ DECLARE_CUSTOM_ATTRIBUTE(sizeof(BYTE));
+ BUILD_CUSTOM_ATTRIBUTE(BYTE, FALSE);
+ IfFailGo(GetAttrType(ATTR_COMVISIBLE, &tkAttr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+ }
+
+ // Set the ComEventInterface CA on the interface.
+ {
+ CQuickBytes asmQualifiedSrcItfName;
+ if (!ns::MakeAssemblyQualifiedName(asmQualifiedSrcItfName, bstrSrcItfName, wzAsmName))
+ IfFailGo(E_OUTOFMEMORY);
+ DECLARE_DYNLEN_CUSTOM_ATTRIBUTE(wcslen((WCHAR*)asmQualifiedSrcItfName.Ptr()) + 5 + wcslen(qbEventProviderName.Ptr()) + 5);
+ APPEND_WIDE_STRING_TO_CUSTOM_ATTRIBUTE((WCHAR*)asmQualifiedSrcItfName.Ptr());
+ APPEND_WIDE_STRING_TO_CUSTOM_ATTRIBUTE(qbEventProviderName.Ptr());
+ IfFailGo(GetAttrType(ATTR_COMEVENTINTERFACE, &tkAttr));
+ FINISH_DYNLEN_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(m_tdTypeDef, tkAttr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+ }
+
+ // Add the add_XXX and remove_XXX methods to the event interface.
+ IfFailGo(_ConvSrcIfaceMembers(pSrcItfITI, psAttr, fInheritsIEnum));
+
+ // Define a typeref for the event interface.
+ IfFailGo(m_pEmit->DefineTypeRefByName(TokenFromRid(1, mdtModule), qbEventItfName.Ptr(), &trEventItf));
+
+ // Add the event info to the map.
+ IfFailGo(m_EventInfoMap.AddEventInfo(bstrSrcItfName, trEventItf, qbEventItfName.Ptr(), qbEventProviderName.Ptr(), SrcItfAssembly));
+
+ // Set the out type ref.
+ *ptr = trEventItf;
+
+ErrExit:
+ if (bstrSrcItfName)
+ ::SysFreeString(bstrSrcItfName);
+ if (m_szName)
+ ::SysFreeString(m_szName);
+ if (psAttr)
+ pSrcItfITI->ReleaseTypeAttr(psAttr);
+ if (pTypeTLB)
+ pTypeTLB->Release();
+
+ // Restore the initial values for the ITypeInfo name and the type def.
+ m_szName = szOldName;
+ m_tdTypeDef = tdOldTypeDef;
+
+ return (hr);
+#else
+ DacNotImpl();
+ return E_NOTIMPL;
+#endif // #ifndef DACCESS_COMPILE
+} // HRESULT CImportTlb::_GetTokenForEventItf()
+
+//*****************************************************************************
+// Creates an interface with the same name as the class and which implements
+// the default interface and the default event interface.
+//*****************************************************************************
+HRESULT CImportTlb::_CreateClassInterface(ITypeInfo *pCoClassITI, ITypeInfo *pDefItfITI, mdTypeRef trDefItf, mdTypeRef rtDefEvItf, mdToken *ptr)
+{
+ HRESULT hr = S_OK; // A result.
+ CQuickArray<mdToken> rImpls; // Array of implemented interfaces.
+ int ixImpl = -1; // Index into rImpls for implemented interface.
+ mdTypeDef tdTypeDef; // The class interface typedef.
+ BSTR bstrFullName = NULL; // The name of the CoClass.
+ TYPEATTR *psAttrIface=0; // TYPEATTR for an interface.
+ CQuickArray<WCHAR> qbClassName; // The name of the class.
+
+ IfFailGo(rImpls.ReSizeNoThrow(3));
+ memset(rImpls.Ptr(), 0, 3 * sizeof(mdToken));
+ if (trDefItf)
+ rImpls[++ixImpl] = trDefItf;
+ if (rtDefEvItf)
+ rImpls[++ixImpl] = rtDefEvItf;
+
+ // Retrieve the TypeAttr for the interface.
+ if (pDefItfITI)
+ IfFailGo(pDefItfITI->GetTypeAttr(&psAttrIface));
+
+ // Retrieve the name of the CoClass (use the original name if this is an alias).
+ IfFailGo(GetManagedNameForTypeInfo(m_pOrigITI, m_wzNamespace, NULL, &bstrFullName));
+
+ // Create the typedef.
+ IfFailGo(m_pEmit->DefineTypeDef(bstrFullName, rdwTypeFlags[TKIND_INTERFACE], mdTypeDefNil, 0, &tdTypeDef));
+
+ // Set the IID to the IID of the default interface.
+ IfFailGo(_AddGuidCa(tdTypeDef, psAttrIface ? psAttrIface->guid : GUID_NULL));
+
+ // Add the CoClass CA to the interface.
+ _AddStringCa(ATTR_COCLASS, tdTypeDef, m_szMngName);
+
+ // Add the implemented interfaces and event interfaces to the TypeDef.
+ IfFailGo(m_pEmit->SetTypeDefProps(tdTypeDef, ULONG_MAX/*Classflags*/,
+ ULONG_MAX, (mdToken*)rImpls.Ptr()));
+
+ // Set the out type def.
+ *ptr = tdTypeDef;
+
+ErrExit:
+ if (bstrFullName)
+ ::SysFreeString(bstrFullName);
+ if (psAttrIface)
+ pDefItfITI->ReleaseTypeAttr(psAttrIface);
+
+ return (hr);
+} // HRESULT CImportTlb::_CreateClassInterface()
+
+//*****************************************************************************
+// Creates an interface with the same name as the class and which implements
+// the default interface and the default event interface.
+//*****************************************************************************
+HRESULT CImportTlb::GetManagedNameForCoClass(ITypeInfo *pITI, CQuickArray<WCHAR> &qbClassName)
+{
+ HRESULT hr = S_OK; // A result.
+ BSTR bstrFullName=0; // Fully qualified name of type.
+
+ // Retrieve the name of the CoClass.
+ IfFailGo(GetManagedNameForTypeInfo(pITI, m_wzNamespace, NULL, &bstrFullName));
+
+ // Resize the class name to accomodate the Class and potential suffix.
+ IfFailGo(qbClassName.ReSizeNoThrow(wcslen(bstrFullName) + CLASS_SUFFIX_LENGTH + 6));
+
+ // Set the class name to the CoClass name suffixed with Class.
+ StringCchPrintf(qbClassName.Ptr(), qbClassName.Size(), W("%s%s"), bstrFullName, CLASS_SUFFIX);
+
+ // Generate a unique name for the class.
+ IfFailGo(GenerateUniqueTypeName(qbClassName));
+
+ErrExit:
+ if (bstrFullName)
+ ::SysFreeString(bstrFullName);
+
+ return (hr);
+} // HRESULT CImportTlb::GetManagedNameForCoClass()
+
+//*****************************************************************************
+// Creates an interface with the same name as the class and which implements
+// the default interface and the default event interface.
+//*****************************************************************************
+HRESULT CImportTlb::GenerateUniqueTypeName(CQuickArray<WCHAR> &qbTypeName)
+{
+ HRESULT hr = S_OK; // A result.
+ WCHAR *pSuffix=0; // Location for suffix.
+ size_t cchSuffix;
+ WCHAR *pName=0; // The name without the namespace.
+ int iSuffix=2; // Starting value for suffix.
+ mdToken td; // For looking up a TypeDef.
+ BSTR szTypeInfoName=0; // Name of a typeinfo.
+ ITypeInfo *pITI=0; // A typeinfo.
+
+ // Resize the class name to accomodate the Class and potential suffix.
+ IfFailGo(qbTypeName.ReSizeNoThrow(wcslen(qbTypeName.Ptr()) + 6));
+
+ // Set the suffix pointer.
+ pSuffix = qbTypeName.Ptr() + wcslen(qbTypeName.Ptr());
+ cchSuffix = qbTypeName.Size() - wcslen(qbTypeName.Ptr());
+
+ // Set the name pointer.
+ WCHAR* pTemp = ns::FindSep(qbTypeName.Ptr());
+ if (pTemp == NULL)
+ pName = qbTypeName.Ptr();
+ else
+ pName = pTemp + 1;
+
+ // Attempt to find a class name that is not in use.
+ for (;;)
+ {
+ // First check to see if the type name is in use in the metadata we
+ // have emitted so far.
+ hr = m_pImport->FindTypeDefByName(qbTypeName.Ptr(), mdTypeDefNil, &td);
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ // It is not in use in the metadata but we still need to check the
+ // typelib because the type might not have been emitted yet.
+ USHORT cReq = 4;
+ USHORT cFound = cReq;
+ BOOL bTypeInTlb = FALSE;
+ CQuickArray<ITypeInfo *> qbTI;
+ CQuickArray<MEMBERID> qbMemId;
+
+ // Retrieve all the instances of the name in the typelib.
+ do
+ {
+ // Double the number of requested names.
+ cReq *= 2;
+
+ // Resize the array's to accomodate the resquested names.
+ IfFailGo(qbTI.ReSizeNoThrow(cReq));
+ IfFailGo(qbMemId.ReSizeNoThrow(cReq));
+
+ // Request the names.
+ cFound = cReq;
+ IfFailGo(m_pITLB->FindName(pName, 0, qbTI.Ptr(), qbMemId.Ptr(), &cFound));
+
+ // Release all the ITypeInfo's.
+ for (int i = 0; i < cFound; i++)
+ qbTI[i]->Release();
+ }
+ while (cReq == cFound);
+
+ // Check to see if one of the instances of the name is for a type.
+ for (int i = 0; i < cFound; i++)
+ {
+ if (qbMemId[i] == MEMBERID_NIL)
+ {
+ bTypeInTlb = TRUE;
+ break;
+ }
+ }
+
+ // If the type name exists in the typelib, but we didn't find it as a type,
+ // we still need to do a deeper check, due to how FindName() works.
+ if (!bTypeInTlb && cFound > 0)
+ {
+ int cTi; // Count of TypeInfos.
+ int i; // Loop control.
+
+ //@todo: this iterates over every typeinfo every time! We could cache
+ // the names, and skip the types already converted. However, this should
+ // be pretty rare.
+
+ // How many TypeInfos?
+ IfFailGo(cTi = m_pITLB->GetTypeInfoCount());
+
+ // Iterate over them.
+ for (i=0; i<cTi; ++i)
+ {
+ // Get the TypeInfo, and its name.
+ IfFailGo(m_pITLB->GetTypeInfo(i, &pITI));
+ IfFailGo(pITI->GetDocumentation(MEMBERID_NIL, &szTypeInfoName, 0, 0, 0));
+ if (wcscmp(pName, szTypeInfoName) == 0)
+ {
+ bTypeInTlb = TRUE;
+ break;
+ }
+
+ // Release for next TypeInfo.
+ ::SysFreeString(szTypeInfoName);
+ szTypeInfoName = 0;
+ pITI->Release();
+ pITI = 0;
+ }
+ }
+
+ // The type name is not in the typelib and not in the metadata then we still
+ // need to check to see if is a reserved name.
+ if (!bTypeInTlb)
+ {
+ if (!m_ReservedNames.IsReservedName(qbTypeName.Ptr()))
+ {
+ // The name is not a reserved name so we can use it.
+ break;
+ }
+ }
+ }
+ IfFailGo(hr);
+
+ // Append the new suffix to the class name.
+ StringCchPrintf(pSuffix, cchSuffix, W("_%i"), iSuffix++);
+ }
+
+ErrExit:
+ if (szTypeInfoName)
+ ::SysFreeString(szTypeInfoName);
+ if (pITI)
+ pITI->Release();
+ return (hr);
+} // HRESULT CImportTlb::GenerateUniqueTypeName()
+
+//*****************************************************************************
+// Generate a unique member name based on the interface member name.
+//*****************************************************************************
+HRESULT CImportTlb::GenerateUniqueMemberName(// S_OK or error
+ CQuickArray<WCHAR> &qbMemberName, // Original name of member.
+ PCCOR_SIGNATURE pSig, // Signature of the member.
+ ULONG cSig, // Length of the signature.
+ LPCWSTR szPrefix, // Possible prefix for decoration.
+ mdToken type) // Is it a property? (Not a method?)
+{
+ HRESULT hr; // A result.
+ mdToken tkMember; // Dummy location for token.
+ WCHAR *pSuffix=0; // Location for suffix.
+ size_t cchSuffix = 0;
+ int iSuffix=2; // Starting value for suffix.
+
+ // Try to find a member name that is not already in use.
+ for (;;)
+ { // See if this is (finally) a unique member or property.
+ switch (type)
+ {
+ case mdtProperty:
+ hr = FindProperty(m_tdTypeDef, qbMemberName.Ptr(), 0, 0, &tkMember);
+ // If name is OK as property, check that there is no method or
+ // property with the name.
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = FindMethod(m_tdTypeDef, qbMemberName.Ptr(), 0,0, &tkMember);
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = FindEvent(m_tdTypeDef, qbMemberName.Ptr(), &tkMember);
+ break;
+ case mdtMethodDef:
+ hr = FindMethod(m_tdTypeDef, qbMemberName.Ptr(), pSig, cSig, &tkMember);
+ // If name is OK as method, check that there is no property or
+ // event with the name.
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = FindProperty(m_tdTypeDef, qbMemberName.Ptr(), 0,0, &tkMember);
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = FindEvent(m_tdTypeDef, qbMemberName.Ptr(), &tkMember);
+ break;
+ case mdtEvent:
+ hr = FindEvent(m_tdTypeDef, qbMemberName.Ptr(), &tkMember);
+ // If name is OK as event, check that there is no property or
+ // method with the name.
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = FindProperty(m_tdTypeDef, qbMemberName.Ptr(), 0,0, &tkMember);
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = FindMethod(m_tdTypeDef, qbMemberName.Ptr(), 0,0, &tkMember);
+ break;
+ default:
+ // Unexpected type. Make noise, but let it pass.
+ _ASSERTE(!"Unexpected token type in GenerateUniqueMemberName");
+ hr = CLDB_E_RECORD_NOTFOUND;
+ }
+
+ // If name was not found, it is unique.
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ hr = S_OK;
+ goto ErrExit;
+ }
+ // Test for failure.
+ IfFailGo(hr);
+
+ // Make a test decoration.
+ if (szPrefix)
+ {
+ size_t iLenPrefix, iLenName;
+ iLenPrefix = wcslen(szPrefix);
+ iLenName = wcslen(qbMemberName.Ptr());
+ IfFailGo(qbMemberName.ReSizeNoThrow(iLenName + iLenPrefix + 2));
+ // Shift by prefix length, plus '_'. Note use of overlap-safe move.
+ memmove(&qbMemberName[iLenPrefix+1], &qbMemberName[0], (iLenName+1)*sizeof(WCHAR));
+ wcscpy_s(qbMemberName.Ptr(), iLenPrefix + 1, szPrefix);
+ qbMemberName[iLenPrefix] = W('_');
+ szPrefix = 0;
+ // Try again with prefix before trying a suffix.
+ continue;
+ }
+ if (!pSuffix)
+ {
+ IfFailGo(qbMemberName.ReSizeNoThrow(wcslen(qbMemberName.Ptr()) + 6));
+ pSuffix = qbMemberName.Ptr() + wcslen(qbMemberName.Ptr());
+ cchSuffix = qbMemberName.Size() - wcslen(qbMemberName.Ptr());
+ }
+ StringCchPrintf(pSuffix, cchSuffix, W("_%i"), iSuffix++);
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT CImportTlb::GenerateUniqueMemberName()
+
+//*****************************************************************************
+// Convert a TYPEDESC to a COM+ signature.
+//
+// Conversion rules:
+// integral types are converted as-is.
+// strings to strings, with native type decoration.
+// VT_UNKNOWN, VT_DISPATCH as ref class (ie, Object)
+// VT_PTR -> VT_USERDEFINED interface as Object
+// VT_USERDEFINED record as value type.
+//
+// With SIG_FUNC:
+// PTR to valuetype depends on other flags:
+// [IN] or [RETVAL] valuetype + NATIVE_TYPE_LPSTRUCT
+// [OUT] or [IN, OUT] byref valuetype
+// PTR to integral type:
+// [IN] @todo: see atti
+// [OUT] [IN, OUT] byref type
+// [RETVAL] type
+// PTR to object
+// [IN] @todo: see atti
+// [OUT] [IN, OUT] byref object
+// [RETVAL] object
+//
+// With SIG_FIELD:
+// PTR to integral type adds ELEMENT_TYPE_PTR.
+//
+// Conversion proceeds in three steps.
+// 1) Parse the COM type info. Accumulate VT_PTR and VT_BYREF into a count
+// of indirections. Follow TKIND_ALIAS to determine the ultimate aliased
+// type, and for non-user-defined types, convert that ultimate type.
+// Collect array sizes and udt names. Determine element type and native
+// type.
+// 2) Normalize to COM+ types. Determine if there is conversion loss.
+// 3) Emit the COM+ signature. Recurse to handle array types. Add native
+// type info if there is any.
+//*****************************************************************************
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+HRESULT CImportTlb::_ConvSignature( // S_OK, S_CONVERSION_LOSS, or error.
+ ITypeInfo *pITI, // [IN] The typeinfo containing the TYPEDESC.
+ const TYPEDESC *pType, // [IN] The TYPEDESC to convert.
+ ULONG Flags, // [IN] Flags describing the TYPEDESC.
+ CQuickBytes &qbSigBuf, // [IN, OUT] A CQuickBytes containing the signature.
+ ULONG cbSig, // [IN] Where to start building the signature.
+ ULONG *pcbSig, // [OUT] Where the signature ends (ix of first byte past; where to start next).
+ CQuickArray<BYTE> &qbNativeTypeBuf, // [IN, OUT] A CQuickBytes containing the native type.
+ ULONG cbNativeType, // [IN] Where to start building the native type.
+ ULONG *pcbNativeType, // [OUT] Where the native type ends (ix of first byte past; where to start next).
+ BOOL bNewEnumMember, // [IN] A flag indicating if the member is the NewEnum member.
+ int iByRef) // [IN] ByRef count of caller (for recursive calls).
+{
+ HRESULT hr=S_OK; // A result.
+ TYPEDESC tdTemp; // Copy of TYPEDESC, for R/W.
+ VARTYPE vt; // The typelib signature element.
+ int bByRef=false; // If true, convert first pointer as "ELEMENT_TYPE_BYREF".
+ COR_SIGNATURE et=0; // The COM+ signature element.
+ mdToken tk=0; // Token from some COM+ signature element.
+ ULONG nt=NATIVE_TYPE_NONE; // Native type decoration.
+ ITypeInfo *pITIAlias=0; // Typeinfo of the aliased type.
+ TYPEATTR *psAttrAlias=0; // TYPEATTR of the aliased typeinfo.
+ ITypeInfo *pITIUD=0; // TypeInfo of an aliased UserDefined type.
+ ITypeLib *pITLBUD=0; // TypeLib of an aliased UserDefined type.
+ BSTR bstrNamespace=0; // Namespace name.
+ BSTR bstrName=0; // UserDefined name.
+ int bConversionLoss=false; // If true, the conversion was lossy.
+ BYTE *pbSig; // Byte pointer for easy pointer math.
+ ULONG cb; // Size of a signature element.
+ ULONG cElems=0; // Count of elements in an array.
+ int i; // Loop control.
+ TYPEATTR *psAttr = 0; // The TYPEATTR for the user defined type being converted.
+ const StdConvertibleItfInfo *pConvertionInfo = 0; // The standard convertible interface information.
+ CQuickArray<BYTE> qbNestedNativeType;// A native type buffer used for array sig convertion.
+ ULONG iNestedNativeTypeOfs=0; // A native type offset.
+ ULONG nested=NATIVE_TYPE_NONE; // A nested native type.
+
+ // VT_ to ELEMENT_TYPE_ translation table.
+ struct VtSig
+ {
+ CorElementType et;
+ CorNativeType nt;
+ short flags;
+ };
+
+ // The VARIANT_TYPE to sig mapping table.
+ static const VtSig
+ _VtInfo[MAX_TLB_VT] =
+ {
+ // Relies on {0} initializing the entire sub-structure to 0.
+ {ELEMENT_TYPE_MAX, NATIVE_TYPE_NONE, 0}, // VT_EMPTY = 0
+ {ELEMENT_TYPE_MAX, NATIVE_TYPE_NONE, 0}, // VT_NULL = 1
+ {ELEMENT_TYPE_I2, NATIVE_TYPE_NONE, 0}, // VT_I2 = 2
+ {ELEMENT_TYPE_I4, NATIVE_TYPE_NONE, 0}, // VT_I4 = 3
+ {ELEMENT_TYPE_R4, NATIVE_TYPE_NONE, 0}, // VT_R4 = 4
+ {ELEMENT_TYPE_R8, NATIVE_TYPE_NONE, 0}, // VT_R8 = 5
+ {ELEMENT_TYPE_VALUETYPE,NATIVE_TYPE_CURRENCY, 0}, // VT_CY = 6
+ {ELEMENT_TYPE_VALUETYPE,NATIVE_TYPE_NONE, 0}, // VT_DATE = 7
+ {ELEMENT_TYPE_STRING, NATIVE_TYPE_BSTR, 0}, // VT_BSTR = 8
+ {ELEMENT_TYPE_OBJECT, NATIVE_TYPE_IDISPATCH, 0}, // VT_DISPATCH = 9
+ {ELEMENT_TYPE_I4, NATIVE_TYPE_ERROR, 0}, // VT_ERROR = 10 scode
+ {ELEMENT_TYPE_BOOLEAN, NATIVE_TYPE_NONE, 0}, // VT_BOOL = 11
+ {ELEMENT_TYPE_OBJECT, NATIVE_TYPE_STRUCT, 0}, // VT_VARIANT = 12
+ {ELEMENT_TYPE_OBJECT, NATIVE_TYPE_IUNKNOWN, 0}, // VT_UNKNOWN = 13
+ {ELEMENT_TYPE_VALUETYPE,NATIVE_TYPE_NONE, 0}, // VT_DECIMAL = 14
+ {ELEMENT_TYPE_MAX, NATIVE_TYPE_NONE, 0}, // = 15
+ {ELEMENT_TYPE_I1, NATIVE_TYPE_NONE, 0}, // VT_I1 = 16
+ {ELEMENT_TYPE_U1, NATIVE_TYPE_NONE, 0}, // VT_UI1 = 17
+ {ELEMENT_TYPE_U2, NATIVE_TYPE_NONE, 0}, // VT_UI2 = 18
+ {ELEMENT_TYPE_U4, NATIVE_TYPE_NONE, 0}, // VT_UI4 = 19
+ {ELEMENT_TYPE_I8, NATIVE_TYPE_NONE, 0}, // VT_I8 = 20
+ {ELEMENT_TYPE_U8, NATIVE_TYPE_NONE, 0}, // VT_UI8 = 21
+
+ // it would be nice to convert these as I and U, with NT_I4 and NT_U4, but that doesn't work.
+ {ELEMENT_TYPE_I4, NATIVE_TYPE_NONE, 0}, // VT_INT = 22 INT is I4 on win32
+ {ELEMENT_TYPE_U4, NATIVE_TYPE_NONE, 0}, // VT_UINT = 23 UINT is UI4 on win32
+
+ {ELEMENT_TYPE_VOID, NATIVE_TYPE_NONE, 0}, // VT_VOID = 24
+
+ {ELEMENT_TYPE_I4, NATIVE_TYPE_ERROR, 0}, // VT_HRESULT = 25
+ {ELEMENT_TYPE_MAX, NATIVE_TYPE_NONE, 0}, // VT_PTR = 26
+ {ELEMENT_TYPE_MAX, NATIVE_TYPE_NONE, 0}, // VT_SAFEARRAY = 27
+ {ELEMENT_TYPE_SZARRAY, NATIVE_TYPE_FIXEDARRAY, 0}, // VT_CARRAY = 28
+ {ELEMENT_TYPE_MAX, NATIVE_TYPE_NONE, 0}, // VT_USERDEFINED = 29
+ {ELEMENT_TYPE_STRING, NATIVE_TYPE_LPSTR, 0}, // VT_LPSTR = 30
+ {ELEMENT_TYPE_STRING, NATIVE_TYPE_LPWSTR, 0}, // VT_LPWSTR = 31
+ };
+
+ _ASSERTE(pType && pcbSig && pcbNativeType);
+
+ //-------------------------------------------------------------------------
+ // Parse COM signature
+
+ // Strip off leading VT_PTR and VT_BYREF
+ while (pType->vt == VT_PTR)
+ pType = pType->lptdesc, ++iByRef;
+ if (pType->vt & VT_BYREF)
+ {
+ tdTemp = *pType;
+ tdTemp.vt &= ~VT_BYREF;
+ ++iByRef;
+ pType = &tdTemp;
+ }
+
+ // Determine the element type, and possibly the token and/or native type.
+ switch (vt=pType->vt)
+ {
+ case VT_PTR:
+ _ASSERTE(!"Should not have VT_PTR here");
+ break;
+
+ // These are all known types (plus GUID).
+ case VT_CY:
+ case VT_DATE:
+ case VT_DECIMAL:
+ IfFailGo(GetKnownTypeToken(vt, &tk));
+ et = _VtInfo[vt].et;
+ nt = _VtInfo[vt].nt;
+ break;
+
+ case VT_SAFEARRAY:
+ if (m_bSafeArrayAsSystemArray && !IsSigVarArg(Flags))
+ {
+ IfFailGo(GetKnownTypeToken(vt, &tk));
+ et = ELEMENT_TYPE_CLASS;
+ nt = NATIVE_TYPE_SAFEARRAY;
+ }
+ else
+ {
+ IfFailGo(GetKnownTypeToken(vt, &tk));
+ et = ELEMENT_TYPE_SZARRAY;
+ nt = NATIVE_TYPE_SAFEARRAY;
+ }
+ break;
+
+ case VT_USERDEFINED:
+ // Resolve the alias to the ultimate aliased type.
+ IfFailGo(_ResolveTypeDescAlias(pITI, pType, &pITIAlias, &psAttrAlias));
+
+ // If the aliased type was built-in, convert that built-in type.
+ if (psAttrAlias->typekind == TKIND_ALIAS)
+ { // Recurse to follow the alias chain.
+ _ASSERTE(psAttrAlias->tdescAlias.vt != VT_USERDEFINED);
+ hr = _ConvSignature(pITIAlias, &psAttrAlias->tdescAlias, Flags, qbSigBuf, cbSig, pcbSig, qbNativeTypeBuf, cbNativeType, pcbNativeType, bNewEnumMember, iByRef);
+ goto ErrExit;
+ }
+
+ // If the type is a coclass then we need to retrieve the default interface and
+ // substitute it for the coclass. Look up on the resolved alias, because it is
+ // that class that has a default interface.
+ if (psAttrAlias->typekind == TKIND_COCLASS)
+ {
+ ITypeInfo *pDefaultItf = NULL;
+ hr = GetDefaultInterface(pITIAlias, &pDefaultItf);
+ if ((hr != S_OK) || !pDefaultItf)
+ {
+ hr = E_UNEXPECTED;
+ goto ErrExit;
+ }
+
+ pITIUD = pDefaultItf;
+ }
+ else
+ { // USERDEFINED class/interface/record/union/enum. Retrieve the type
+ // info for the user defined type. Note: use the TKIND_ALIAS typeinfo
+ // itself for this conversion (not the aliased type) to preserve
+ // names, lib locations, etc.
+ IfFailGo(pITI->GetRefTypeInfo(pType->hreftype, &pITIUD));
+ }
+
+ // pITIUD points to the typeinfo for which we'll create a signature.
+ IfFailGo(pITIUD->GetDocumentation(MEMBERID_NIL, &bstrName, 0,0,0));
+ IfFailGo(pITIUD->GetContainingTypeLib(&pITLBUD, 0));
+ IfFailGo(pITIUD->GetTypeAttr(&psAttr));
+ IfFailGo(GetNamespaceNameForTypeLib(pITLBUD, &bstrNamespace));
+
+ // If the "User Defined Type" is GUID in StdOle2, convert to M.R.GUID
+ if (SString::_wcsicmp(bstrNamespace, COM_STDOLE2) == 0 && wcscmp(bstrName, COM_GUID) == 0)
+ { // Classlib valuetype GUID.
+ et = ELEMENT_TYPE_VALUETYPE;
+ IfFailGo(GetKnownTypeToken(VT_SLOT_FOR_GUID, &tk));
+ }
+ else
+ { // Some user defined class. Is it a value class, or a VOS class?
+ tk = 0;
+ switch (psAttrAlias->typekind)
+ {
+ case TKIND_RECORD:
+ case TKIND_ENUM:
+ case TKIND_UNION:
+ et = ELEMENT_TYPE_VALUETYPE;
+ break;
+ case TKIND_INTERFACE:
+ case TKIND_DISPATCH:
+ case TKIND_COCLASS:
+ // A pointer to a user defined type of interface/dispatch/coclass
+ // is a straight COM+ object (the ref is implicit), so eliminate
+ // one byref count for those.
+ // Somehow, there are typelibs written with ([out, retval] IFoo *pOut);
+ if (iByRef <= 0)
+ {
+ // convert to an int.
+ bConversionLoss = true;
+ tk = 0;
+ et = ELEMENT_TYPE_I;
+ nt = NATIVE_TYPE_NONE;
+ iByRef = 0;
+ break;
+ }
+ else
+ {
+ --iByRef;
+
+ // Check for references to Stdole2.IUnknown or Stdole2.IDispatch.
+ if (psAttr->guid == IID_IUnknown)
+ {
+ vt = VT_UNKNOWN;
+ goto IsReallyUnknown;
+ }
+ else if (psAttr->guid == IID_IDispatch)
+ {
+ vt = VT_DISPATCH;
+ goto IsReallyUnknown;
+ }
+
+ // Check to see if this user defined type is one of the standard ones
+ // we generate custom marshalers for.
+ pConvertionInfo = GetConvertionInfoFromNativeIID(psAttr->guid);
+ if (pConvertionInfo)
+ {
+ // Convert the UTF8 string to unicode.
+ int MngTypeNameStrLen = (int)(strlen(pConvertionInfo->m_strMngTypeName) + 1);
+ WCHAR *strFullyQualifiedMngTypeName = (WCHAR *)_alloca(MngTypeNameStrLen * sizeof(WCHAR));
+ int ret = WszMultiByteToWideChar(CP_UTF8, 0, pConvertionInfo->m_strMngTypeName, MngTypeNameStrLen, strFullyQualifiedMngTypeName, MngTypeNameStrLen);
+ _ASSERTE(ret != 0);
+ if (!ret)
+ IfFailGo(HRESULT_FROM_GetLastError());
+
+ // Create a TypeRef to the marshaller.
+ IfFailGo(m_TRMap.DefineTypeRef(m_pEmit, m_arSystem, strFullyQualifiedMngTypeName, &tk));
+
+ // The type is a standard interface that we need to convert.
+ et = ELEMENT_TYPE_CLASS;
+ nt = NATIVE_TYPE_CUSTOMMARSHALER;
+ break;
+ }
+ }
+ et = ELEMENT_TYPE_CLASS;
+ nt = NATIVE_TYPE_INTF;
+ break;
+ default:
+ //case TKIND_MODULE: -- can't pass one of these as a parameter.
+ //case TKIND_ALIAS: -- should already be resolved.
+ _ASSERTE(!"Unexpected typekind for user defined type");
+ et = ELEMENT_TYPE_END;
+ } // switch (psAttrAlias->typekind)
+ }
+ break;
+
+ IsReallyUnknown:
+ case VT_UNKNOWN:
+ case VT_DISPATCH:
+ // If the NewEnum member, retrieve the custom marshaler information for IEnumVARIANT.
+ if (bNewEnumMember && (pConvertionInfo=GetConvertionInfoFromNativeIID(IID_IEnumVARIANT)))
+ {
+ // Convert the UTF8 string to unicode.
+ int MngTypeNameStrLen = (int)(strlen(pConvertionInfo->m_strMngTypeName) + 1);
+ WCHAR *strFullyQualifiedMngTypeName = (WCHAR *)_alloca(MngTypeNameStrLen * sizeof(WCHAR));
+ int ret = WszMultiByteToWideChar(CP_UTF8, 0, pConvertionInfo->m_strMngTypeName, MngTypeNameStrLen, strFullyQualifiedMngTypeName, MngTypeNameStrLen);
+ _ASSERTE(ret != 0);
+ if (!ret)
+ IfFailGo(HRESULT_FROM_GetLastError());
+
+ // Create a TypeRef to the marshaller.
+ IfFailGo(m_TRMap.DefineTypeRef(m_pEmit, m_arSystem, strFullyQualifiedMngTypeName, &tk));
+
+ // The type is a standard interface that we need to convert.
+ et = ELEMENT_TYPE_CLASS;
+ nt = NATIVE_TYPE_CUSTOMMARSHALER;
+ }
+ else
+ {
+ et = _VtInfo[vt].et;
+ nt = _VtInfo[vt].nt;
+ }
+ break;
+
+ case VT_CARRAY:
+ // Determine the count of elements.
+ for (cElems=1, i=0; i<pType->lpadesc->cDims; ++i)
+ cElems *= pType->lpadesc->rgbounds[i].cElements;
+
+ // Set the native type based on weither we are dealing with a field or a method sig.
+ if (IsSigField(Flags))
+ {
+ nt = NATIVE_TYPE_FIXEDARRAY;
+ }
+ else
+ {
+ nt = NATIVE_TYPE_ARRAY;
+ }
+
+ // Set the element type.
+ et = _VtInfo[vt].et;
+ break;
+
+ case VT_BOOL:
+ // Special case for VARIANT_BOOL: If a field of a struct or union, convert
+ // as ET_I2.
+ if (IsSigField(Flags))
+ vt = VT_I2;
+ // Fall through to default case.
+
+ default:
+ if (vt > VT_LPWSTR)
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_E_BAD_VT_TYPE, vt, m_szName, m_szMember);
+ IfFailGo(PostError(TLBX_E_BAD_VT_TYPE, vt, m_szName, m_szMember));
+ }
+ _ASSERTE(vt <= VT_LPWSTR && _VtInfo[vt].et != ELEMENT_TYPE_MAX);
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:26000) // "Disable PREFast/espX warning about buffer overflow"
+#endif
+ et = _VtInfo[vt].et;
+ nt = _VtInfo[vt].nt;
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ break;
+ } // switch (vt=pType->vt)
+
+ //-------------------------------------------------------------------------
+ // Normalize to COM+ types.
+
+ // At this point the type, flags, and pointer nesting are known. Is this a legal combination?
+ // If not, what is the appropriate "simplifing assumption"?
+
+ if (et == ELEMENT_TYPE_VOID)
+ {
+ if (IsSigField(Flags))
+ { // A void as a field. No byref.
+ iByRef = 0;
+ }
+ else
+ {
+ // Param or return type. "void *" -> ET_I, "void **", "void ***",... -> ET_BYREF ET_I
+ if (iByRef > 1)
+ iByRef = 1;
+ else
+ if (iByRef == 1)
+ iByRef = 0;
+ }
+ tk = 0;
+ et = ELEMENT_TYPE_I;
+ nt = NATIVE_TYPE_NONE;
+ }
+
+ if (et == ELEMENT_TYPE_STRING && iByRef == 0 && !IsSigField(Flags) && IsSigOut(Flags))
+ {
+ // This is an [out] or [in, out] string parameter without indirections.
+ if (vt == VT_BSTR)
+ {
+ // [in, out] System.String does not make much sense. Managed strings are
+ // immutable and we do not have BSTR <-> StringBuilder marshaling support.
+ // Convert them to IntPtr.
+ bConversionLoss = true;
+ tk = 0;
+ et = ELEMENT_TYPE_I;
+ nt = NATIVE_TYPE_NONE;
+ }
+ else
+ {
+ _ASSERTE(vt == VT_LPSTR || vt == VT_LPWSTR);
+
+ // [in, out] C-strings and wide strings have a lossless conversion to StringBuilder.
+ IfFailGo(GetKnownTypeToken(VT_SLOT_FOR_STRINGBUF, &tk));
+ et = ELEMENT_TYPE_CLASS;
+
+ // nt already has the right value
+ _ASSERTE(nt == (vt == VT_LPSTR ? NATIVE_TYPE_LPSTR : NATIVE_TYPE_LPWSTR));
+ }
+ }
+
+ if (iByRef)
+ {
+ if (et == ELEMENT_TYPE_VALUETYPE && iByRef >= 2)
+ {
+ bConversionLoss = true;
+ tk = 0;
+ et = ELEMENT_TYPE_I;
+ nt = NATIVE_TYPE_NONE;
+ iByRef = 0;
+ }
+ else
+ {
+ switch (Flags & SIG_TYPE_MASK)
+ {
+ case SIG_FIELD:
+ // If ptr to valuetype or class type, we can't handle it.
+ if (et == ELEMENT_TYPE_END ||
+ et == ELEMENT_TYPE_CLASS ||
+ et == ELEMENT_TYPE_OBJECT ||
+ et == ELEMENT_TYPE_VALUETYPE)
+ {
+ bConversionLoss = true;
+ tk = 0;
+ et = ELEMENT_TYPE_I;
+ nt = NATIVE_TYPE_NONE;
+ iByRef = 0;
+ }
+ break;
+ case SIG_FUNC:
+ // Pointer to value type?
+ if (et == ELEMENT_TYPE_VALUETYPE)
+ {
+ // For [retval], eat one level of indirection; otherwise turn one into BYREF
+ if (IsSigOutRet(Flags))
+ { // [out, retval], so reduce one level of indirection.
+ --iByRef;
+ }
+ else
+ { // Favor BYREF over NATIVE_TYPE_LPSTRUCT
+ if (IsSigUseByref(Flags))
+ {
+ bByRef = true;
+ --iByRef;
+ }
+ if (iByRef > 0)
+ {
+ nt = NATIVE_TYPE_LPSTRUCT;
+ --iByRef;
+ }
+ }
+ }
+ else // Pointer to Object or base type.
+ {
+ if (IsSigRet(Flags))
+ { // [retval] so consume one indirection.
+ _ASSERTE(iByRef > 0);
+ --iByRef;
+ }
+ if (iByRef > 0 && IsSigUseByref(Flags))
+ {
+ bByRef = true;
+ --iByRef;
+ }
+ }
+ break;
+ case SIG_ELEM:
+ // This case comes up when a property type is from a [retval].
+ if (IsSigRet(Flags))
+ {
+ if (iByRef > 0)
+ --iByRef;
+ }
+ break;
+ }
+ }
+ } // if (iByRef)
+
+ //-------------------------------------------------------------------------
+ // We don't want any ET_PTR, so if there are any byref counts left, bail.
+ if (iByRef)
+ {
+ bConversionLoss = true;
+ tk = 0;
+ et = ELEMENT_TYPE_I;
+ nt = NATIVE_TYPE_NONE;
+ iByRef = 0;
+ bByRef = false;
+ }
+
+ //-------------------------------------------------------------------------
+ // Build COM+ signature.
+
+ // Type has been analyzed, and possibly modified. Emit the COM+ signature.
+ _ASSERTE(et != ELEMENT_TYPE_MAX);
+ _ASSERTE(et != ELEMENT_TYPE_END);
+
+ // If it is a pointer to something, emit that now.
+ if (bByRef || iByRef)
+ {
+ // Size the array to hold the elements.
+ IfFailGo(qbSigBuf.ReSizeNoThrow(cbSig + CB_MAX_ELEMENT_TYPE * (iByRef+(bByRef?1:0))));
+ pbSig = reinterpret_cast<BYTE*>(qbSigBuf.Ptr());
+
+ // Put in any leading "BYREF"
+ if (bByRef)
+ {
+ pbSig = reinterpret_cast<BYTE*>(qbSigBuf.Ptr());
+ cb = CorSigCompressData(ELEMENT_TYPE_BYREF, &pbSig[cbSig]);
+ cbSig += cb;
+ }
+
+ // Put in the "PTR"s.
+ while (iByRef-- > 0)
+ {
+ cb = CorSigCompressData(ELEMENT_TYPE_PTR, &pbSig[cbSig]);
+ cbSig += cb;
+ }
+ }
+
+ // Emit the type.
+ IfFailGo(qbSigBuf.ReSizeNoThrow(cbSig + CB_MAX_ELEMENT_TYPE));
+ pbSig = reinterpret_cast<BYTE*>(qbSigBuf.Ptr());
+ cb = CorSigCompressData(et, &pbSig[cbSig]);
+ cbSig += cb;
+
+ // Add the class type, the array information, etc.
+ switch (et)
+ {
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_VALUETYPE:
+ // Size the array to hold the token.
+ IfFailGo(qbSigBuf.ReSizeNoThrow(cbSig + CB_MAX_ELEMENT_TYPE));
+ pbSig = reinterpret_cast<BYTE*>(qbSigBuf.Ptr());
+
+ // If the token hasn't been resolved yet, do that now.
+ if (tk == 0)
+ {
+ _ASSERTE(pITIUD);
+ IfFailGo(_GetTokenForTypeInfo(pITIUD, TRUE, &tk));
+ }
+ cb = CorSigCompressToken(tk, reinterpret_cast<ULONG*>(&pbSig[cbSig]));
+ cbSig += cb;
+ break;
+
+ case ELEMENT_TYPE_SZARRAY:
+ // map to SZARRAY <subtype>
+ IfFailGo(qbSigBuf.ReSizeNoThrow(cbSig + CB_MAX_ELEMENT_TYPE));
+ pbSig = reinterpret_cast<BYTE*>(qbSigBuf.Ptr());
+ // Recurse on the type.
+ IfFailGo(_ConvSignature(pITI, &pType->lpadesc->tdescElem, SIG_ELEM, qbSigBuf, cbSig, &cbSig, qbNestedNativeType, 0, &iNestedNativeTypeOfs, bNewEnumMember));
+ if (hr == S_CONVERSION_LOSS)
+ bConversionLoss = true;
+ break;
+
+ case VT_DISPATCH:
+ case VT_UNKNOWN:
+ default:
+ _ASSERTE(tk == 0);
+ // et, nt assigned above.
+ break;
+ } // switch (et)
+
+ // Do any native type info.
+ if (nt != NATIVE_TYPE_NONE)
+ {
+ if (iNestedNativeTypeOfs > 0)
+ CorSigUncompressData(reinterpret_cast<PCCOR_SIGNATURE>(qbNestedNativeType.Ptr()), &nested);
+
+ if (nt == NATIVE_TYPE_FIXEDARRAY)
+ {
+ IfFailGo(qbNativeTypeBuf.ReSizeNoThrow(cbNativeType + NATIVE_TYPE_MAX_CB * 2 + DWORD_MAX_CB));
+ cbNativeType += CorSigCompressData(nt, &qbNativeTypeBuf[cbNativeType]);
+ cbNativeType += CorSigCompressData(cElems, &qbNativeTypeBuf[cbNativeType]);
+ if (nested == NATIVE_TYPE_BSTR || nested == NATIVE_TYPE_LPWSTR || nested == NATIVE_TYPE_LPSTR)
+ { // Use the nested type.
+ cbNativeType += CorSigCompressData(nested, &qbNativeTypeBuf[cbNativeType]);
+ }
+ else
+ { // Use a default sub type.
+ cbNativeType += CorSigCompressData(NATIVE_TYPE_MAX, &qbNativeTypeBuf[cbNativeType]);
+ }
+ }
+ else if (nt == NATIVE_TYPE_ARRAY)
+ {
+ IfFailGo(qbNativeTypeBuf.ReSizeNoThrow(cbNativeType + NATIVE_TYPE_MAX_CB * 2 + DWORD_MAX_CB * 2));
+ cbNativeType += CorSigCompressData(nt, &qbNativeTypeBuf[cbNativeType]);
+ if (nested == NATIVE_TYPE_BSTR || nested == NATIVE_TYPE_LPWSTR || nested == NATIVE_TYPE_LPSTR)
+ { // Use the nested type.
+ cbNativeType += CorSigCompressData(nested, &qbNativeTypeBuf[cbNativeType]);
+ }
+ else
+ { // Use a default sub type.
+ cbNativeType += CorSigCompressData(NATIVE_TYPE_MAX, &qbNativeTypeBuf[cbNativeType]);
+ }
+ // Use zero for param index.
+ cbNativeType += CorSigCompressData(0, &qbNativeTypeBuf[cbNativeType]);
+ // Use count from typelib for elem count.
+ cbNativeType += CorSigCompressData(cElems, &qbNativeTypeBuf[cbNativeType]);
+ }
+ else if (nt == NATIVE_TYPE_SAFEARRAY)
+ {
+ BOOL bPtrArray = FALSE;
+ CQuickArray<WCHAR> rTemp;
+ CQuickArray<char> rTypeName;
+ LPUTF8 strTypeName = "";
+ TYPEDESC *pTypeDesc = &pType->lpadesc->tdescElem;
+ VARTYPE ArrayElemVT = pTypeDesc->vt;
+
+ if (ArrayElemVT == VT_PTR)
+ {
+ bPtrArray = TRUE;
+ pTypeDesc = pType->lpadesc->tdescElem.lptdesc;
+ ArrayElemVT = pTypeDesc->vt;
+ if ((ArrayElemVT != VT_USERDEFINED) && (ArrayElemVT != VT_VOID))
+ {
+ // We do not support deep marshalling pointers.
+ ArrayElemVT = VT_INT;
+ bConversionLoss = TRUE;
+ }
+ }
+
+ // If we are dealing with a safe array of user defined types and if we
+ // are importing safe array's as System.Array then add the SafeArrayUserDefSubType.
+ if (ArrayElemVT == VT_USERDEFINED)
+ {
+ // Resolve the alias to the ultimate aliased type.
+ IfFailGo(_ResolveTypeDescAlias(pITI, pTypeDesc, &pITIAlias, &psAttrAlias));
+
+ // If the type is a coclass then we need to retrieve the default interface and
+ // substitute it for the coclass. Look up on the resolved alias, because it is
+ // that class that has a default interface.
+ if (psAttrAlias->typekind == TKIND_COCLASS)
+ {
+ ITypeInfo *pDefaultItf = NULL;
+ hr = GetDefaultInterface(pITIAlias, &pDefaultItf);
+ if ((hr != S_OK) || !pDefaultItf)
+ {
+ hr = E_UNEXPECTED;
+ goto ErrExit;
+ }
+
+ pITIUD = pDefaultItf;
+ }
+ else
+ { // USERDEFINED interface/record/union/enum. Retrieve the type
+ // info for the user defined type. Note: use the TKIND_ALIAS typeinfo
+ // itself for this conversion (not the aliased type) to preserve
+ // names, lib locations, etc.
+ IfFailGo(pITI->GetRefTypeInfo(pTypeDesc->hreftype, &pITIUD));
+ }
+
+ // pITIUD points to the typeinfo for which we'll create a signature.
+ IfFailGo(pITIUD->GetTypeAttr(&psAttr));
+
+ // Get the typeref name for the type.
+ for(;;)
+ {
+ int cchReq;
+ mdToken tkDummy;
+ IfFailGo(_GetTokenForTypeInfo(pITIUD, TRUE, &tkDummy, rTemp.Ptr(), (int)rTemp.MaxSize(), &cchReq, TRUE));
+ if (cchReq <= (int)rTemp.MaxSize())
+ break;
+ IfFailGo(rTemp.ReSizeNoThrow(cchReq));
+ }
+
+ // Convert the type name to UTF8.
+ ULONG cbReq = WszWideCharToMultiByte(CP_UTF8, 0, rTemp.Ptr(), -1, 0, 0, 0, 0);
+ IfFailGo(rTypeName.ReSizeNoThrow(cbReq + 1));
+ WszWideCharToMultiByte(CP_UTF8, 0, rTemp.Ptr(), -1, rTypeName.Ptr(), cbReq, 0, 0);
+
+ // Determine the safe array element VT.
+ switch (psAttrAlias->typekind)
+ {
+ case TKIND_RECORD:
+ case TKIND_ENUM:
+ case TKIND_UNION:
+ if (bPtrArray)
+ {
+ ArrayElemVT = VT_INT;
+ bConversionLoss = TRUE;
+ }
+ else
+ {
+ ArrayElemVT = psAttrAlias->typekind == TKIND_ENUM ? VT_I4 : VT_RECORD;
+ strTypeName = rTypeName.Ptr();
+ }
+ break;
+
+ case TKIND_INTERFACE:
+ case TKIND_DISPATCH:
+ case TKIND_COCLASS:
+ if (!bPtrArray)
+ {
+ ArrayElemVT = VT_INT;
+ bConversionLoss = TRUE;
+ }
+ else
+ {
+ if (IsIDispatchDerived(pITIUD, psAttr) == S_FALSE)
+ ArrayElemVT = VT_UNKNOWN;
+ else
+ ArrayElemVT = VT_DISPATCH;
+ strTypeName = rTypeName.Ptr();
+ }
+ break;
+ }
+
+ // If we are not converting the SAFEARRAY to a System.Array, then
+ // we don't need to encode the name of the user defined type.
+ if (!m_bSafeArrayAsSystemArray)
+ strTypeName = "";
+ }
+
+ // Make sure the native type buffer is large enough.
+ ULONG TypeNameStringLen = (ULONG)strlen(strTypeName);
+ IfFailGo(qbNativeTypeBuf.ReSizeNoThrow(cbNativeType + NATIVE_TYPE_MAX_CB * 2 + DWORD_MAX_CB + TypeNameStringLen + STRING_OVERHEAD_MAX_CB));
+
+ // Add the native type to the native type info.
+ cbNativeType += CorSigCompressData(nt, &qbNativeTypeBuf[cbNativeType]);
+
+ // Add the VARTYPE of the array.
+ cbNativeType += CorSigCompressData(ArrayElemVT, &qbNativeTypeBuf[cbNativeType]);
+
+ // Add the type name to the native type info.
+ BYTE *pNativeType = (BYTE*)CPackedLen::PutLength(&qbNativeTypeBuf[cbNativeType], TypeNameStringLen);
+ cbNativeType += (ULONG)(pNativeType - &qbNativeTypeBuf[cbNativeType]);
+ memcpy(&qbNativeTypeBuf[cbNativeType], strTypeName, TypeNameStringLen);
+ cbNativeType += TypeNameStringLen;
+ }
+ else if (nt == NATIVE_TYPE_CUSTOMMARSHALER)
+ {
+ // Calculate the length of each string and then the total length of the native type info.
+ ULONG MarshalerTypeNameStringLen = (ULONG)strlen(pConvertionInfo->m_strCustomMarshalerTypeName);
+ ULONG CookieStringLen = (ULONG)strlen(pConvertionInfo->m_strCookie);
+ ULONG TotalNativeTypeLen = MarshalerTypeNameStringLen + CookieStringLen;
+ BYTE *pNativeType = 0;
+
+ // Make sure the native type buffer is large enough.
+ IfFailGo(qbNativeTypeBuf.ReSizeNoThrow(cbNativeType + NATIVE_TYPE_MAX_CB + TotalNativeTypeLen + STRING_OVERHEAD_MAX_CB * 4));
+
+ // Add the native type to the native type info.
+ cbNativeType += CorSigCompressData(nt, &qbNativeTypeBuf[cbNativeType]);
+
+ // Add an empty string for the typelib guid.
+ pNativeType = (BYTE*)CPackedLen::PutLength(&qbNativeTypeBuf[cbNativeType], 0);
+ cbNativeType += (ULONG)(pNativeType - &qbNativeTypeBuf[cbNativeType]);
+
+ // Add an empty string for the unmanaged type name.
+ pNativeType = (BYTE*)CPackedLen::PutLength(&qbNativeTypeBuf[cbNativeType], 0);
+ cbNativeType += (ULONG)(pNativeType - &qbNativeTypeBuf[cbNativeType]);
+
+ // Add the name of the custom marshaler to the native type info.
+ pNativeType = (BYTE*)CPackedLen::PutLength(&qbNativeTypeBuf[cbNativeType], MarshalerTypeNameStringLen);
+ cbNativeType += (ULONG)(pNativeType - &qbNativeTypeBuf[cbNativeType]);
+ memcpy(&qbNativeTypeBuf[cbNativeType], pConvertionInfo->m_strCustomMarshalerTypeName, MarshalerTypeNameStringLen);
+ cbNativeType += MarshalerTypeNameStringLen;
+
+ // Add the cookie to the native type info.
+ pNativeType = (BYTE*)CPackedLen::PutLength(&qbNativeTypeBuf[cbNativeType], CookieStringLen);
+ cbNativeType += (ULONG)(pNativeType - &qbNativeTypeBuf[cbNativeType]);
+ memcpy(&qbNativeTypeBuf[cbNativeType], pConvertionInfo->m_strCookie, CookieStringLen);
+ cbNativeType += CookieStringLen;
+ }
+ else
+ {
+ IfFailGo(qbNativeTypeBuf.ReSizeNoThrow(cbNativeType + NATIVE_TYPE_MAX_CB + 1));
+ cbNativeType += CorSigCompressData(nt, &qbNativeTypeBuf[cbNativeType]);
+ }
+ }
+
+ // Return the size of the native type to the caller.
+ *pcbNativeType = cbNativeType;
+
+ // Return size to caller.
+ *pcbSig = cbSig;
+
+ // If there was a conversion loss, change the return code.
+ if (bConversionLoss)
+ hr = S_CONVERSION_LOSS;
+
+ErrExit:
+ if (bstrNamespace)
+ ::SysFreeString(bstrNamespace);
+ if (bstrName)
+ ::SysFreeString(bstrName);
+ if(psAttrAlias)
+ pITIAlias->ReleaseTypeAttr(psAttrAlias);
+ if (pITIAlias)
+ pITIAlias->Release();
+ if (psAttr)
+ pITIUD->ReleaseTypeAttr(psAttr);
+ if (pITIUD)
+ pITIUD->Release();
+ if (pITLBUD)
+ pITLBUD->Release();
+
+ return hr;
+} // HRESULT CImportTlb::_ConvSignature()
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+// Build a sorted list of functions to convert. (Sort by vtable offset.)
+//*****************************************************************************
+HRESULT CImportTlb::BuildMemberList(
+ ITypeInfo *pITI, // TypeInfo with functions.
+ int iStart, // First function to take.
+ int iEnd, // Last function to take.
+ BOOL bInheritsIEnum) // Inherits from IEnumerable.
+{
+ HRESULT hr; // A result.
+ int bNeedSort = false; // If true, need to sort the array.
+ int ix = 0; // Loop counter.
+ int oVftPrev = -1; // To see if oVft is increasing.
+ TYPEATTR *psAttr = 0; // TypeAttr for pITI.
+ FUNCDESC *psFunc; // A FUNCDESC.
+ LPWSTR pszName; // Working pointer for name.
+ BSTR bstrName=0; // Name from typelib.
+ ITypeInfo2 *pITI2=0; // To get custom attributes.
+ VARIANT vt; // Variant type.
+ BOOL bFunctionToGetter; // Did a given getter come from a managed function?
+
+ ::VariantInit(&vt);
+
+ IfFailGo(pITI->GetTypeAttr(&psAttr));
+ pITI->QueryInterface(IID_ITypeInfo2, reinterpret_cast<void**>(&pITI2));
+
+ // Get the vars.
+ IfFailGo(m_MemberList.ReSizeNoThrow(psAttr->cVars + iEnd - iStart));
+ memset(m_MemberList.Ptr(), 0, m_MemberList.Size()*sizeof(MemberInfo));
+ for (ix=0; ix<psAttr->cVars; ++ix)
+ {
+ IfFailGo(pITI->GetVarDesc(ix, &(m_MemberList[ix].m_psVar)));
+ m_MemberList[ix].m_iMember = ix;
+ }
+ m_cMemberProps = psAttr->cVars;
+
+ // Get the funcs.
+ for (; iStart<iEnd; ++iStart, ++ix)
+ {
+ IfFailGo(TryGetFuncDesc(pITI, iStart, &(m_MemberList[ix].m_psFunc)));
+ psFunc = m_MemberList[ix].m_psFunc;
+ if (psFunc->oVft < oVftPrev)
+ bNeedSort = true;
+ oVftPrev = psFunc->oVft;
+ m_MemberList[ix].m_iMember = iStart;
+ }
+
+ if (bNeedSort)
+ {
+ class Sorter : public CQuickSort<MemberInfo>
+ {
+ typedef CImportTlb::MemberInfo MemberInfo;
+ public:
+ Sorter(MemberInfo *p, int n) : CQuickSort<MemberInfo>(p,n) {}
+ virtual int Compare(MemberInfo *p1, MemberInfo *p2)
+ {
+ if (p1->m_psFunc->oVft < p2->m_psFunc->oVft)
+ return -1;
+ if (p1->m_psFunc->oVft == p2->m_psFunc->oVft)
+ return 0;
+ return 1;
+ }
+ };
+ Sorter sorter(m_MemberList.Ptr()+m_cMemberProps, (int)m_MemberList.Size()-m_cMemberProps);
+ sorter.Sort();
+ // Check for duplicates.
+ oVftPrev = -1;
+ for (ix=m_cMemberProps; ix<(int)m_MemberList.Size(); ++ix)
+ {
+ if (m_MemberList[ix].m_psFunc->oVft == oVftPrev)
+ {
+ hr = TLBX_E_BAD_VTABLE;
+ break;
+ }
+ oVftPrev = m_MemberList[ix].m_psFunc->oVft;
+ }
+ }
+
+ // Build the list of unique names.
+ m_pMemberNames = new (nothrow) CWCHARPool;
+ IfNullGo(m_pMemberNames);
+
+ // Property names. No possibility of collisions.
+ for (ix=0; ix<m_cMemberProps; ++ix)
+ {
+ IfFailGo(pITI->GetDocumentation(m_MemberList[ix].m_psVar->memid, &bstrName, 0,0,0));
+ IfNullGo(pszName = m_pMemberNames->Alloc((ULONG)wcslen(bstrName)+PROP_DECORATION_LEN+1));
+ wcscpy_s(pszName, wcslen(bstrName)+PROP_DECORATION_LEN+1, PROP_DECORATION_GET);
+ wcscat_s(pszName, wcslen(bstrName)+PROP_DECORATION_LEN+1, bstrName);
+ m_MemberList[ix].m_pName = pszName;
+ if ((m_MemberList[ix].m_psVar->wVarFlags & VARFLAG_FREADONLY) == 0)
+ {
+ IfNullGo(pszName = m_pMemberNames->Alloc((ULONG)wcslen(bstrName)+PROP_DECORATION_LEN+1));
+ wcscpy_s(pszName, wcslen(bstrName)+PROP_DECORATION_LEN+1, PROP_DECORATION_SET);
+ wcscat_s(pszName, wcslen(bstrName)+PROP_DECORATION_LEN+1, bstrName);
+ m_MemberList[ix].m_pName2 = pszName;
+ }
+ ::SysFreeString(bstrName);
+ bstrName = 0;
+ }
+
+ // Function names. Because of get_/set_ decoration, collisions are possible.
+ for (ix=m_cMemberProps; ix<(int)m_MemberList.Size(); ++ix)
+ {
+ int bNewEnumMember = FALSE;
+
+ // Build a name based on invkind.
+ psFunc = m_MemberList[ix].m_psFunc;
+
+ // Unless we are doing the [out, retval] transformation for disp only interfaces,
+ // we need to clear the [retval] flag.
+ if (!m_bTransformDispRetVals)
+ {
+ if (psFunc->funckind == FUNC_DISPATCH)
+ { // If [RETVAL] is set, clear it.
+ for (int i=0; i<psFunc->cParams; ++i)
+ if ((psFunc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FRETVAL) != 0)
+ psFunc->lprgelemdescParam[i].paramdesc.wParamFlags &= ~PARAMFLAG_FRETVAL;
+ }
+ }
+
+ BOOL bExplicitManagedName = FALSE;
+ if ( (!bNewEnumMember) && (!bInheritsIEnum) && (FuncIsNewEnum(pITI, psFunc, m_MemberList[ix].m_iMember) == S_OK) )
+ {
+ // The member is the new enum member so set its name to GetEnumerator.
+ IfNullGo(bstrName = SysAllocString(GET_ENUMERATOR_MEMBER_NAME));
+ bNewEnumMember = TRUE;
+
+ // To prevent additional methods from implementing the NewEnum method, we mark the interface
+ bInheritsIEnum = TRUE;
+ }
+ else
+ {
+ // If the managed name custom value is set for this member, then use it.
+ if (pITI2)
+ {
+ hr = pITI2->GetFuncCustData(m_MemberList[ix].m_iMember, GUID_ManagedName, &vt);
+ if (hr == S_OK && vt.vt == VT_BSTR)
+ {
+ IfNullGo(bstrName = SysAllocString(vt.bstrVal));
+ bExplicitManagedName = TRUE;
+ }
+ ::VariantClear(&vt);
+ }
+
+ if (!bstrName)
+ IfFailGo(pITI->GetDocumentation(psFunc->memid, &bstrName, 0,0,0));
+ }
+
+ // If this is a property getter, see if it was originally a function.
+ bFunctionToGetter = FALSE;
+ if (psFunc->invkind == INVOKE_PROPERTYGET && pITI2)
+ {
+ hr = pITI2->GetFuncCustData(m_MemberList[ix].m_iMember, GUID_Function2Getter, &vt);
+ if (hr == S_OK && vt.vt == VT_I4 && vt.lVal == 1)
+ bFunctionToGetter = TRUE;
+ ::VariantClear(&vt);
+ }
+
+
+ // Check for the propget and propset custom attributes if this not already a property.
+ if ( (psFunc->invkind & (INVOKE_PROPERTYGET | INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF)) == 0 )
+ {
+ INVOKEKIND ikind;
+ if (S_OK == _CheckForPropertyCustomAttributes(pITI, m_MemberList[ix].m_iMember, &ikind))
+ psFunc->invkind = ikind;
+ }
+
+
+ // If this is a property accessor, but not the 'new enum member', and not
+ // originally from a managed function (that was exported as a getter),
+ // decorate the name appropriately. If the managed name was set explicitly by
+ // the Guid_ManagedName attribute, then don't try an decorate it.
+ ULONG nChars = 0;
+ if (!bExplicitManagedName && (psFunc->invkind & (INVOKE_PROPERTYGET | INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF) && !bNewEnumMember && !bFunctionToGetter))
+ {
+ nChars = (ULONG)wcslen(bstrName)+PROP_DECORATION_LEN+1;
+ IfNullGo(pszName = m_pMemberNames->Alloc(nChars));
+
+ USHORT msSemantics=0; // Property's methodsemantics.
+ FUNCDESC *psF; // FUNCDESC of Get, Put, or PutRef.
+ TYPEDESC *pProperty; // TYPEDESC of property type.
+ BOOL bPropRetval; // Is the property type a [retval]?
+ IfFailGo(_GetFunctionPropertyInfo(psFunc, &msSemantics, &psF, &pProperty, &bPropRetval, FALSE, bstrName));
+
+ m_MemberList[ix].m_msSemantics = msSemantics;
+ switch(msSemantics)
+ {
+ case msGetter:
+ wcscpy_s(pszName, nChars, PROP_DECORATION_GET);
+ break;
+ case msSetter:
+ wcscpy_s(pszName, nChars, PROP_DECORATION_SET);
+ break;
+ case msOther:
+ wcscpy_s(pszName, nChars, PROP_DECORATION_LET);
+ break;
+ default:
+ _ASSERTE(msSemantics == 0);
+ *pszName = 0;
+ break;
+ }
+ wcscat_s(pszName, nChars, bstrName);
+ }
+ else
+ {
+ nChars = (ULONG)wcslen(bstrName)+1;
+ IfNullGo(pszName = m_pMemberNames->Alloc(nChars));
+ wcscpy_s(pszName, nChars, bstrName);
+ }
+
+ // Check for name collision, restore original name if collision occurs.
+ for (int index=0; index<ix; index++)
+ {
+ if ( (m_MemberList[index].m_pName) && (wcscmp(pszName, m_MemberList[index].m_pName) == 0) )
+ {
+ wcscpy_s(pszName, nChars, bstrName);
+ m_MemberList[ix].m_msSemantics = 0;
+ }
+ }
+
+ // Save the unique name.
+ m_MemberList[ix].m_pName = pszName;
+ ::SysFreeString(bstrName);
+ bstrName = 0;
+ }
+
+ErrExit:
+ if (pITI2)
+ pITI2->Release();
+ if (psAttr)
+ pITI->ReleaseTypeAttr(psAttr);
+ if (bstrName)
+ ::SysFreeString(bstrName);
+ ::VariantClear(&vt);
+ return hr;
+} // HRESULT CImportTlb::BuildMemberList()
+
+//*****************************************************************************
+// Free the list built in BuildMemberList().
+//*****************************************************************************
+HRESULT CImportTlb::FreeMemberList(
+ ITypeInfo *pITI) // TypeInfo with functions.
+{
+ int ix; // Loop control.
+ for (ix=0; ix<m_cMemberProps; ++ix)
+ pITI->ReleaseVarDesc(m_MemberList[ix].m_psVar);
+ m_cMemberProps = 0;
+ for (; ix<(int)m_MemberList.Size(); ++ix)
+ pITI->ReleaseFuncDesc(m_MemberList[ix].m_psFunc);
+ m_MemberList.Shrink(0);
+ if (m_pMemberNames)
+ {
+ delete m_pMemberNames;
+ m_pMemberNames = 0;
+ }
+ return S_OK;
+} // HRESULT CImportTlb::FreeMemberList()
+
+//*****************************************************************************
+// Set a GUID CustomAttribute on an object.
+//*****************************************************************************
+HRESULT CImportTlb::_AddGuidCa( // S_OK or error.
+ mdToken tkObj, // Object to be attributed.
+ REFGUID guid) // The GUID.
+{
+ HRESULT hr; // A result.
+ mdMemberRef mr; // MemberRef for GUID CA.
+ WCHAR wzGuid[40]; // Buffer for Guid, Unicode.
+ CHAR szGuid[40]; // Buffer for Guid, Ansi.
+ DECLARE_CUSTOM_ATTRIBUTE(40);
+
+ // If GUID_NULL, don't store it.
+ if (guid == GUID_NULL)
+ return S_OK;
+
+ // Get the GUID as a string.
+ // ----+----1----+----2----+----3----+----4
+ // {12345678-1234-1234-1234-123456789012}
+ GuidToLPWSTR(guid, wzGuid, lengthof(wzGuid));
+ _ASSERTE(wzGuid[37] == W('}'));
+ wzGuid[37] = W('\0');
+ WszWideCharToMultiByte(CP_UTF8, 0, wzGuid+1,-1, szGuid,sizeof(szGuid), 0,0);
+
+ // Put it in the Custom Attribute.
+ APPEND_STRING_TO_CUSTOM_ATTRIBUTE(szGuid);
+
+ // Store the attribute
+ IfFailGo(GetAttrType(ATTR_GUID, &mr));
+ FINISH_CUSTOM_ATTRIBUTE();
+ IfFailGo(m_pEmit->DefineCustomAttribute(tkObj, mr, PTROF_CUSTOM_ATTRIBUTE(), SIZEOF_CUSTOM_ATTRIBUTE(), 0));
+
+ErrExit:
+ return hr;
+} // HRESULT CImportTlb::_AddGuidCa()
+
+//*****************************************************************************
+// Add a default member as a custom attribute.
+//*****************************************************************************
+HRESULT CImportTlb::_AddDefaultMemberCa(// S_OK or error.
+ mdToken tkObj, // TypeDef with default member.
+ LPCWSTR wzName) // Name of the default member.
+{
+ // Only set once per typedef.
+ if (tkObj == m_tdHasDefault)
+ return S_OK;
+ m_tdHasDefault = tkObj;
+
+ return _AddStringCa(ATTR_DEFAULTMEMBER, tkObj, wzName);
+} // HRESULT CImportTlb::_AddDefaultMemberCa()
+
+//*****************************************************************************
+// Add a string custom attribute of the given type to the token.
+//*****************************************************************************
+HRESULT CImportTlb::_AddStringCa( // S_OK or error.
+ int attr, // The type of the CA.
+ mdToken tk, // Token to add the CA to.
+ LPCWSTR wzString) // String to put in the CA.
+{
+ HRESULT hr = S_OK; // A result.
+ mdMemberRef mr; // MemberRef for DefaultMember CA.
+ BYTE *pca; // Pointer to custom attribute.
+ BYTE *ca; // Pointer to custom attribute.
+ int wzLen; // Length of wide string.
+ int len; // Length of the string.
+ CQuickArray<BYTE> buf;
+
+ if (wzString == NULL)
+ {
+ hr = E_INVALIDARG;
+ goto ErrExit;
+ }
+
+ // Prolog, up to 4 bytes length, string, epilog
+ wzLen = (int)wcslen(wzString);
+ len = WszWideCharToMultiByte(CP_UTF8,0, wzString, wzLen, 0,0, 0,0);
+ IfFailGo(buf.ReSizeNoThrow(2 + 4 + len + 2));
+ ca = pca = buf.Ptr();
+
+ // Add prolog.
+ *reinterpret_cast<UNALIGNED USHORT*>(pca) = 1;
+ pca += sizeof(USHORT);
+
+ // Add length.
+ pca = reinterpret_cast<BYTE*>(CPackedLen::PutLength(pca, len));
+
+ // Add string.
+ WszWideCharToMultiByte(CP_UTF8,0, wzString, wzLen, reinterpret_cast<char*>(pca), len, 0, 0);
+ pca += len;
+
+ // Add epilog.
+ *reinterpret_cast<UNALIGNED USHORT*>(pca) = 0;
+ pca += sizeof(USHORT);
+
+ // Store the attribute
+ IfFailGo(GetAttrType(attr, &mr));
+ IfFailGo(m_pEmit->DefineCustomAttribute(tk, mr, ca, (ULONG)(pca-ca), 0));
+
+ErrExit:
+ return hr;
+} // HRESULT CImportTlb::_AddStringCa()
+
+//*****************************************************************************
+// Add a referenced typelib to the list of referenced typelibs. Check if
+// it is "this" typelib first.
+//*****************************************************************************
+HRESULT CImportTlb::_AddTlbRef( // S_OK or error.
+ ITypeLib *pITLB, // The referenced typelib.
+ mdAssemblyRef *par, // The AssemblyRef in this module.
+ BSTR *pwzNamespace, // The namespace contained in the resolved assembly.
+ BSTR *pwzAsmName, // The name of the resolved assembly.
+ CImpTlbDefItfToClassItfMap **ppDefItfToClassItfMap) // The default interface to class interface map.
+{
+ HRESULT hr = S_OK; // A result.
+ IUnknown *pIUnk=0; // IUnknown for external assembly.
+ mdAssemblyRef ar=0; // Assembly ref in the module containing the typeref.
+ ITypeLib2 *pITLB2=0; // To get custom attributes.
+ VARIANT vt; // Variant type.
+ Assembly* ResolvedAssembly=0; // The resolved assembly.
+ CImpTlbDefItfToClassItfMap *pDefItfToClassItfMap; // Temp def itf to class itf map.
+
+ // Validate the arguments.
+ _ASSERTE(pITLB && par && pwzNamespace && pwzAsmName);
+
+ // Initialize the out parameters to NULL.
+ *par = mdTokenNil;
+ *pwzNamespace = NULL;
+ *pwzAsmName = NULL;
+ if (ppDefItfToClassItfMap)
+ *ppDefItfToClassItfMap = NULL;
+
+ ::VariantInit(&vt);
+
+ // If not the importing typelib, add it to the list.
+ if (pITLB == m_pITLB)
+ { // Not an external assembly.
+ //*par = mdAssemblyRefNil;
+ *par = TokenFromRid(1, mdtModule);
+ IfNullGo(*pwzNamespace = SysAllocStringLen(m_wzNamespace, SysStringLen(m_wzNamespace)));
+ *pwzAsmName = NULL;
+ if (ppDefItfToClassItfMap)
+ *ppDefItfToClassItfMap = &m_DefItfToClassItfMap;
+ return S_OK;
+ }
+
+ // If already resolved, just return assembly ref.
+ if (m_LibRefs.Find(pITLB, par, pwzNamespace, pwzAsmName, NULL, ppDefItfToClassItfMap))
+ return S_OK;
+
+ // See if the typelib was exported, in which case it already has assembly ref information.
+ if (pITLB->QueryInterface(IID_ITypeLib2, reinterpret_cast<void**>(&pITLB2)) == S_OK)
+ {
+ hr = pITLB2->GetCustData(GUID_ExportedFromComPlus, &vt);
+ if (vt.vt == VT_BSTR)
+ {
+ // Use the CA data to get a reference.
+ //CQuickArray<BYTE> rBuf;
+ //int iLen;
+ // The buffer should have been converted with CP_ACP, and should convert back directly.
+ //IfFailGo(rBuf.ReSizeNoThrow(iLen=::SysStringLen(vt.bstrVal)));
+ //if (iLen=WszWideCharToMultiByte(CP_ACP,0, vt.bstrVal,iLen, (char*)rBuf.Ptr(),iLen, 0,0))
+ {
+ // Define the assembly ref for the exported assembly.
+ //ar = DefineAssemblyRefForExportedAssembly(rBuf.Ptr(),(DWORD)rBuf.Size(), m_pEmit);
+ ar = DefineAssemblyRefForExportedAssembly(vt.bstrVal, m_pEmit);
+
+ // Retrieve the namespace from the typelib.
+ IfFailGo(GetNamespaceNameForTypeLib(pITLB, pwzNamespace));
+
+ // Set the assembly name.
+ IfNullGo(*pwzAsmName = SysAllocStringLen(vt.bstrVal, SysStringLen(vt.bstrVal)));
+ }
+ }
+ }
+
+ // If it wasn't directly converted to a reference, callback to the resolver.
+ if (IsNilToken(ar))
+ {
+ // Get the assembly for that typelib.
+ if (FAILED(m_Notify->ResolveRef(pITLB, &pIUnk)))
+ IfFailGo(TLBX_I_RESOLVEREFFAILED);
+
+ // If a NULL assembly was returned, then stop converting the type but
+ // continue the import.
+ if (pIUnk == NULL)
+ IfFailGo(TLBX_E_INVALID_TYPEINFO);
+
+ // Create an assembly ref in local assembly for referenced assembly.
+ ar = DefineAssemblyRefForImportedTypeLib(m_pAssembly, m_pModule, m_pEmit, pIUnk, pwzNamespace, pwzAsmName, &ResolvedAssembly);
+ }
+
+ // Make sure the ref was resolved before adding to cache.
+ if (IsNilToken(ar))
+ IfFailGo(TLBX_I_RESOLVEREFFAILED);
+
+ // Add the TLB to the list of references.
+ IfFailGo(m_LibRefs.Add(pITLB, this, ar, *pwzNamespace, *pwzAsmName, ResolvedAssembly, &pDefItfToClassItfMap));
+
+ // Set the output parameters.
+ *par = ar;
+ if (ppDefItfToClassItfMap)
+ *ppDefItfToClassItfMap = pDefItfToClassItfMap;
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (*pwzNamespace)
+ {
+ SysFreeString(*pwzNamespace);
+ *pwzNamespace = NULL;
+ }
+ if (*pwzAsmName)
+ {
+ SysFreeString(*pwzAsmName);
+ *pwzAsmName = NULL;
+ }
+ }
+ if (pIUnk)
+ pIUnk->Release();
+ if (pITLB2)
+ pITLB2->Release();
+ VariantClear(&vt);
+
+ return hr;
+} // HRESULT CImportTlb::_AddTlbRef()
+
+//*****************************************************************************
+// Error reporting helper.
+//*****************************************************************************
+HRESULT CImportTlb::ReportEvent( // Returns the original HR.
+ int ev, // The event kind.
+ int hrRpt, // HR.
+ ...) // Variable args.
+{
+ HRESULT hr; // A result.
+ va_list marker; // User text.
+ BSTR bstrBuf=0; // BSTR for bufferrr.
+ BSTR bstrMsg=0; // BSTR for message.
+ const int iSize = 1024; // Message size;
+
+ // We need a BSTR anyway for the call to ReportEvent, so just allocate a
+ // big one for the buffer.
+ IfNullGo(bstrBuf = ::SysAllocStringLen(0, iSize));
+
+ // Format the message.
+ va_start(marker, hrRpt);
+ hr = FormatRuntimeErrorVa(bstrBuf, iSize, hrRpt, marker);
+ va_end(marker);
+
+ // Display it.
+ IfNullGo(bstrMsg = ::SysAllocString(bstrBuf));
+ m_Notify->ReportEvent(static_cast<ImporterEventKind>(ev), hrRpt, bstrMsg);
+
+ErrExit:
+ // Clean up.
+ if (bstrBuf)
+ ::SysFreeString(bstrBuf);
+ if (bstrMsg)
+ ::SysFreeString(bstrMsg);
+ return hrRpt;
+} // HRESULT CImportTlb::ReportEvent()
+
+//*****************************************************************************
+// Helper function to perform the shared functions of creating a TypeRef.
+//*****************************************************************************
+HRESULT CImpTlbTypeRef::DefineTypeRef( // S_OK or error.
+ IMetaDataEmit *pEmit, // Emit interface.
+ mdAssemblyRef ar, // The system assemblyref.
+ const LPCWSTR szURL, // URL of the TypeDef, wide chars.
+ mdTypeRef *ptr) // Put mdTypeRef here
+{
+ HRESULT hr = S_OK; // A result.
+ LPCWSTR szLookup; // The name to look up.
+ mdToken tkNester; // Token of enclosing class.
+
+ // If the name contains a '+', this is a nested type. The first part becomes
+ // the resolution scope for the part after the '+'.
+ szLookup = wcsrchr(szURL, NESTED_SEPARATOR_WCHAR);
+ if (szLookup)
+ {
+ CQuickArray<WCHAR> qbName;
+ IfFailGo(qbName.ReSizeNoThrow(szLookup - szURL + 1));
+ wcsncpy_s(qbName.Ptr(), (szLookup - szURL + 1), szURL, szLookup - szURL);
+ IfFailGo(DefineTypeRef(pEmit, ar, qbName.Ptr(), &tkNester));
+ ar = tkNester;
+ ++szLookup;
+ }
+ else
+ szLookup = szURL;
+
+ // Look for the item in the map.
+ CImpTlbTypeRef::TokenOfTypeRefHashKey sSearch, *pMapped;
+
+ sSearch.tkResolutionScope = ar;
+ sSearch.szName = szLookup;
+ pMapped = m_Map.Find(&sSearch);
+
+ if (pMapped)
+ {
+ *ptr = pMapped->tr;
+ goto ErrExit;
+ }
+
+ // Wasn't found, create a new one and add to the map.
+ hr = pEmit->DefineTypeRefByName(ar, szLookup, ptr);
+ if (SUCCEEDED(hr))
+ {
+ sSearch.tr = *ptr;
+ pMapped = m_Map.Add(&sSearch);
+ IfNullGo(pMapped);
+ }
+
+ErrExit:
+ return (hr);
+} // HRESULT CImpTlbTypeRef::DefineTypeRef()
+
+//*****************************************************************************
+// Free the held typelibs in the list of imported typelibs.
+//*****************************************************************************
+CImpTlbLibRef::~CImpTlbLibRef()
+{
+ for (ULONG i = 0; i < Size(); i++)
+ {
+ SysFreeString(operator[](i).szNameSpace);
+ delete operator[](i).pDefItfToClassItfMap;
+ }
+} // CImpTlbLibRef::~CImpTlbLibRef()
+
+//*****************************************************************************
+// Add a new typelib reference to the list.
+//*****************************************************************************
+HRESULT CImpTlbLibRef::Add(
+ ITypeLib *pITLB,
+ CImportTlb *pImporter,
+ mdAssemblyRef ar,
+ BSTR wzNamespace,
+ BSTR wzAsmName,
+ Assembly* assm,
+ CImpTlbDefItfToClassItfMap **ppMap)
+{
+ HRESULT hr = S_OK; // A result.
+ TLIBATTR *pAttr=0; // A typelib attribute.
+ ULONG i; // Index.
+ CTlbRef *pTlbRef=0; // A pointer to the TlbRef struct.
+ CImpTlbDefItfToClassItfMap *pDefItfToClassItfMap = NULL; // ptr to the default interface to class interface map.
+
+ // Validate the arguments.
+ _ASSERTE(wzNamespace);
+ _ASSERTE(wzAsmName);
+
+ IfFailGo(pITLB->GetLibAttr(&pAttr));
+
+#if defined(_DEBUG)
+ for (i=0; i<Size(); ++i)
+ {
+ if (operator[](i).guid == pAttr->guid)
+ {
+ _ASSERTE(!"External TypeLib already referenced");
+ goto ErrExit;
+ }
+ }
+#else
+ i = (ULONG)Size();
+#endif
+
+ // Allocate and initialize the default interface to class interface map.
+ pDefItfToClassItfMap = new (nothrow) CImpTlbDefItfToClassItfMap();
+ IfNullGo(pDefItfToClassItfMap);
+ IfFailGo(pDefItfToClassItfMap->Init(pITLB, wzNamespace));
+
+ // Attemp to resize the array.
+ IfFailGo(ReSizeNoThrow(i+1));
+ pTlbRef = &operator[](i);
+ pTlbRef->guid = pAttr->guid;
+ pTlbRef->ar = ar;
+ IfNullGo(pTlbRef->szNameSpace = SysAllocString(wzNamespace));
+ IfNullGo(pTlbRef->szAsmName = SysAllocString(wzAsmName));
+ pTlbRef->pDefItfToClassItfMap = pDefItfToClassItfMap;
+ pTlbRef->Asm = assm;
+
+ErrExit:
+ if (pAttr)
+ pITLB->ReleaseTLibAttr(pAttr);
+ if (FAILED(hr))
+ {
+ if (pTlbRef && pTlbRef->szNameSpace)
+ SysFreeString(pTlbRef->szNameSpace);
+ if (pTlbRef && pTlbRef->szAsmName)
+ SysFreeString(pTlbRef->szAsmName);
+ delete pDefItfToClassItfMap;
+ }
+ else
+ {
+ *ppMap = pDefItfToClassItfMap;
+ }
+
+ return hr;
+} // void CImpTlbLibRef::Add()
+
+//*****************************************************************************
+// Find an existing typelib reference.
+//*****************************************************************************
+int CImpTlbLibRef::Find(
+ ITypeLib *pITLB,
+ mdAssemblyRef *par,
+ BSTR *pwzNamespace,
+ BSTR *pwzAsmName,
+ Assembly** assm,
+ CImpTlbDefItfToClassItfMap **ppDefItfToClassItfMap)
+{
+ HRESULT hr; // A result.
+ TLIBATTR *pAttr=0; // A typelib attribute.
+ int rslt = FALSE; // Return result.
+ ULONG i; // Loop control.
+
+ _ASSERTE(pwzNamespace);
+ _ASSERTE(pwzAsmName);
+
+ // Initalize the out parameters to NULL.
+ *pwzNamespace = NULL;
+ *pwzAsmName = NULL;
+
+ if (assm)
+ *assm = NULL;
+
+ IfFailGo(pITLB->GetLibAttr(&pAttr));
+
+ for (i=0; i<Size(); ++i)
+ {
+ if (operator[](i).guid == pAttr->guid)
+ {
+ *par = operator[](i).ar;
+ IfNullGo(*pwzNamespace = SysAllocString(operator[](i).szNameSpace));
+ IfNullGo(*pwzAsmName = SysAllocString(operator[](i).szAsmName));
+ if (ppDefItfToClassItfMap)
+ *ppDefItfToClassItfMap = operator[](i).pDefItfToClassItfMap;
+ if (assm)
+ *assm = operator[](i).Asm;
+ rslt = TRUE;
+ goto ErrExit;
+ }
+ }
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (*pwzNamespace)
+ SysFreeString(*pwzNamespace);
+ if (*pwzAsmName)
+ SysFreeString(*pwzAsmName);
+ }
+ if (pAttr)
+ pITLB->ReleaseTLibAttr(pAttr);
+ return rslt;
+} // void CImpTlbLibRef::Find()
+
+//*****************************************************************************
+// unpack variant to an ELEMENT_TYPE_* plus a blob value
+// If VT_BOOL, it is a two-byte value.
+//*****************************************************************************
+HRESULT _UnpackVariantToConstantBlob(VARIANT *pvar, BYTE *pcvType, void **pvValue, __int64 *pd)
+{
+ HRESULT hr = NOERROR;
+
+ switch (pvar->vt)
+ {
+ case VT_BOOL:
+ *pcvType = ELEMENT_TYPE_BOOLEAN;
+ *((VARIANT_BOOL **)pvValue) = &(pvar->boolVal);
+ break;
+ case VT_I1:
+ *pcvType = ELEMENT_TYPE_I1;
+ *((CHAR **)pvValue) = &(pvar->cVal);
+ break;
+ case VT_UI1:
+ *pcvType = ELEMENT_TYPE_U1;
+ *((BYTE **)pvValue) = &(pvar->bVal);
+ break;
+ case VT_I2:
+ *pcvType = ELEMENT_TYPE_I2;
+ *((SHORT **)pvValue) = &(pvar->iVal);
+ break;
+ case VT_UI2:
+ *pcvType = ELEMENT_TYPE_U2;
+ *((USHORT **)pvValue) = &(pvar->uiVal);
+ break;
+ case VT_I4:
+ case VT_INT:
+ *pcvType = ELEMENT_TYPE_I4;
+ *((LONG **)pvValue) = &(pvar->lVal);
+ break;
+ case VT_UI4:
+ case VT_UINT:
+ *pcvType = ELEMENT_TYPE_U4;
+ *((ULONG **)pvValue) = &(pvar->ulVal);
+ break;
+ case VT_R4:
+ *pcvType = ELEMENT_TYPE_R4;
+ *((float **)pvValue) = &(pvar->fltVal);
+ break;
+ case VT_I8:
+ *pcvType = ELEMENT_TYPE_I8;
+ *((LONGLONG **)pvValue) = &(pvar->cyVal.int64);
+ break;
+ case VT_R8:
+ *pcvType = ELEMENT_TYPE_R8;
+ *((double **)pvValue) = &(pvar->dblVal);
+ break;
+ case VT_BSTR:
+ *pcvType = ELEMENT_TYPE_STRING;
+ *((BSTR *)pvValue) = pvar->bstrVal;
+ break;
+
+ case VT_DATE:
+ *pcvType = ELEMENT_TYPE_I8;
+ *pd = _DoubleDateToTicks(pvar->date);
+ *((LONGLONG **)pvValue) = pd;
+ break;
+ case VT_UNKNOWN:
+ case VT_DISPATCH:
+ *pcvType = ELEMENT_TYPE_CLASS;
+ _ASSERTE(pvar->punkVal == NULL);
+ *((IUnknown ***)pvValue) = &(pvar->punkVal);
+ break;
+ default:
+ _ASSERTE(!"Not a valid type to specify default value!");
+ IfFailGo( META_E_BAD_INPUT_PARAMETER );
+ break;
+ }
+ErrExit:
+ return hr;
+} // HRESULT _UnpackVariantToConstantBlob()
+
+//*****************************************************************************
+// Stolen from classlib.
+//*****************************************************************************
+INT64 _DoubleDateToTicks(const double d)
+{
+ const INT64 MillisPerSecond = 1000;
+ const INT64 MillisPerDay = MillisPerSecond * 60 * 60 * 24;
+ const INT64 TicksPerMillisecond = 10000;
+ const INT64 TicksPerSecond = TicksPerMillisecond * 1000;
+ const INT64 TicksPerMinute = TicksPerSecond * 60;
+ const INT64 TicksPerHour = TicksPerMinute * 60;
+ const INT64 TicksPerDay = TicksPerHour * 24;
+ const int DaysPer4Years = 365 * 4 + 1;
+ const int DaysPer100Years = DaysPer4Years * 25 - 1;
+ const int DaysPer400Years = DaysPer100Years * 4 + 1;
+ const int DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367;
+ const INT64 DoubleDateOffset = DaysTo1899 * TicksPerDay;
+ const int DaysTo10000 = DaysPer400Years * 25 - 366;
+ const INT64 MaxMillis = DaysTo10000 * MillisPerDay;
+
+ INT64 millis = (INT64)(d * MillisPerDay + (d >= 0? 0.5: -0.5));
+ if (millis < 0) millis -= (millis % MillisPerDay) * 2;
+ millis += DoubleDateOffset / TicksPerMillisecond;
+ if (millis < 0 || millis >= MaxMillis) {
+ return 0;
+ }
+ return millis * TicksPerMillisecond;
+} // INT64 _DoubleDateToTicks()
+
+
+//*****************************************************************************
+// Wrapper for GetFuncDesc to catch errors.
+//*****************************************************************************
+static HRESULT TryGetFuncDesc( // S_OK or error.
+ ITypeInfo *pITI, // ITypeInfo with function.
+ int i, // Function index.
+ FUNCDESC **ppFunc) // Put FUNCDESC here.
+{
+ HRESULT hr; // A return code.
+ __try
+ {
+ hr = pITI->GetFuncDesc(i, ppFunc);
+ }
+ __except(1)
+ {
+ hr = PostError(TLBX_E_TLB_EXCEPTION, _exception_code());
+ }
+
+ return hr;
+} // static HRESULT TryGetFuncDesc()
+
+//*****************************************************************************
+// Implementation of a hashed ResolutionScope+Name to TypeRef map.
+//*****************************************************************************
+void CImpTlbTypeRef::CTokenOfTypeRefHash::Clear()
+{
+#if defined(_DEBUG)
+ // printf("Name to TypeRef cache: %d buckets, %d used, %d collisions\n", Buckets(), Count(), Collisions());
+#endif
+ CClosedHash<class TokenOfTypeRefHashKey>::Clear();
+} // void CImpTlbTypeRef::CTokenOfTypeRefHash::Clear()
+
+unsigned int CImpTlbTypeRef::CTokenOfTypeRefHash::Hash(const TokenOfTypeRefHashKey *pData)
+{
+ // Starting value for hash.
+ ULONG hash = 5381;
+
+ // Hash in the resolution scope token.
+ const BYTE *pbData = reinterpret_cast<const BYTE *>(&pData->tkResolutionScope);
+ int iSize = 4;
+ while (--iSize >= 0)
+ {
+ hash = ((hash << 5) + hash) ^ *pbData;
+ ++pbData;
+ }
+
+ // Hash in the typeref name.
+ LPCWSTR szStr = pData->szName;
+ int c;
+ while ((c = *szStr) != 0)
+ {
+ hash = ((hash << 5) + hash) ^ c;
+ ++szStr;
+ }
+
+ return hash;
+} // unsigned int CImpTlbTypeRef::CTokenOfTypeRefHash::Hash()
+
+unsigned int CImpTlbTypeRef::CTokenOfTypeRefHash::Compare(const TokenOfTypeRefHashKey *p1, TokenOfTypeRefHashKey *p2)
+{
+ // Resolution scopes are fast to compare.
+ if (p1->tkResolutionScope < p2->tkResolutionScope)
+ return -1;
+ if (p1->tkResolutionScope > p2->tkResolutionScope)
+ return 1;
+ // But if they are the same, compare the names.
+ return wcscmp(p1->szName, p2->szName);
+} // unsigned int CImpTlbTypeRef::CTokenOfTypeRefHash::Compare()
+
+CImpTlbTypeRef::CTokenOfTypeRefHash::ELEMENTSTATUS CImpTlbTypeRef::CTokenOfTypeRefHash::Status(TokenOfTypeRefHashKey *p)
+{
+ if (p->tkResolutionScope == static_cast<mdToken>(FREE))
+ return (FREE);
+ if (p->tkResolutionScope == static_cast<mdToken>(DELETED))
+ return (DELETED);
+ return (USED);
+} // CImpTlbTypeRef::CTokenOfTypeRefHash::ELEMENTSTATUS CImpTlbTypeRef::CTokenOfTypeRefHash::Status()
+
+void CImpTlbTypeRef::CTokenOfTypeRefHash::SetStatus(TokenOfTypeRefHashKey *p, ELEMENTSTATUS s)
+{
+ p->tkResolutionScope = static_cast<mdToken>(s);
+} // void CImpTlbTypeRef::CTokenOfTypeRefHash::SetStatus()
+
+void *CImpTlbTypeRef::CTokenOfTypeRefHash::GetKey(TokenOfTypeRefHashKey *p)
+{
+ return p;
+} // void *CImpTlbTypeRef::CTokenOfTypeRefHash::GetKey()
+
+CImpTlbTypeRef::TokenOfTypeRefHashKey* CImpTlbTypeRef::CTokenOfTypeRefHash::Add(const TokenOfTypeRefHashKey *pData)
+{
+ LPWSTR pName;
+ const void *pvData = pData;
+ TokenOfTypeRefHashKey *pNew = Super::Add(const_cast<void*>(pvData));
+ if (pNew == 0)
+ return 0;
+ pNew->szName = pName = m_Names.Alloc((ULONG)wcslen(pData->szName)+1);
+ if (pNew->szName == 0)
+ return 0;
+ wcscpy_s(pName, wcslen(pData->szName)+1, pData->szName);
+ pNew->tkResolutionScope = pData->tkResolutionScope;
+ pNew->tr = pData->tr;
+
+ return pNew;
+} // TokenOfTypeRefHashKey* CImpTlbTypeRef::CTokenOfTypeRefHash::Add()
+
+//*****************************************************************************
+// Implementation of a hashed ITypeInfo * source interface to event information
+// map.
+//*****************************************************************************
+HRESULT CImpTlbEventInfoMap::AddEventInfo(LPCWSTR szSrcItfName, mdTypeRef trEventItf, LPCWSTR szEventItfName, LPCWSTR szEventProviderName, Assembly* SrcItfAssembly)
+{
+ ImpTlbEventInfo sNew;
+ sNew.szSrcItfName = szSrcItfName;
+ sNew.trEventItf = trEventItf;
+ sNew.szEventItfName = szEventItfName;
+ sNew.szEventProviderName = szEventProviderName;
+ sNew.SrcItfAssembly = SrcItfAssembly;
+ return Add(&sNew) != NULL ? S_OK : E_OUTOFMEMORY;
+} // BOOL CImpTlbEventInfoMap::AddEventInfo()
+
+ImpTlbEventInfo *CImpTlbEventInfoMap::FindEventInfo(LPCWSTR szSrcItfName)
+{
+ ImpTlbEventInfo sSearch, *pMapped;
+ sSearch.szSrcItfName = szSrcItfName;
+ pMapped = Find(&sSearch);
+ return pMapped;
+} // ImpTlbEventInfo *CImpTlbEventInfoMap::FindEventInfo()
+
+HRESULT CImpTlbEventInfoMap::GetEventInfoList(CQuickArray<ImpTlbEventInfo*> &qbEvInfoList)
+{
+ HRESULT hr = S_OK;
+ int cCurrEvInfo = 0;
+
+ // Resise the event info list.
+ IfFailGo(qbEvInfoList.ReSizeNoThrow(Count()));
+
+ // Retrieve the first event info.
+ ImpTlbEventInfo *pEvInfo = GetFirst();
+
+ // Add all the event info's to the list.
+ while (pEvInfo)
+ {
+ qbEvInfoList[cCurrEvInfo++] = pEvInfo;
+ pEvInfo = GetNext(pEvInfo);
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT CImpTlbEventInfoMap::GetEventInfoList()
+
+unsigned int CImpTlbEventInfoMap::Hash(const ImpTlbEventInfo *pData)
+{
+ // Starting value for hash.
+ ULONG hash = 5381;
+
+ // Hash in the source interface name.
+ LPCWSTR szStr = pData->szSrcItfName;
+ int c;
+ while ((c = *szStr) != 0)
+ {
+ hash = ((hash << 5) + hash) ^ c;
+ ++szStr;
+ }
+
+ return hash;
+} // unsigned int CImpTlbEventInfoMap::Hash()
+
+unsigned int CImpTlbEventInfoMap::Compare(const ImpTlbEventInfo *p1, ImpTlbEventInfo *p2)
+{
+ // Compare the source interface names.
+ return wcscmp(p1->szSrcItfName, p2->szSrcItfName);
+} // unsigned int CImpTlbEventInfoMap::Compare()
+
+CImpTlbEventInfoMap::ELEMENTSTATUS CImpTlbEventInfoMap::Status(ImpTlbEventInfo *p)
+{
+ if (p->szSrcItfName == reinterpret_cast<LPCWSTR>(FREE))
+ return (FREE);
+ if (p->szSrcItfName == reinterpret_cast<LPCWSTR>(DELETED))
+ return (DELETED);
+ return (USED);
+} // CImpTlbEventInfoMap::ELEMENTSTATUS CImpTlbEventInfoMap::Status()
+
+void CImpTlbEventInfoMap::SetStatus(ImpTlbEventInfo *p, ELEMENTSTATUS s)
+{
+ p->szSrcItfName = reinterpret_cast<LPCWSTR>(s);
+} // void CImpTlbEventInfoMap::SetStatus()
+
+void *CImpTlbEventInfoMap::GetKey(ImpTlbEventInfo *p)
+{
+ return p;
+} // void *CImpTlbEventInfoMap::GetKey()
+
+ImpTlbEventInfo* CImpTlbEventInfoMap::Add(const ImpTlbEventInfo *pData)
+{
+ // Add the new entry to the map.
+ const void *pvData = pData;
+ ImpTlbEventInfo *pNew = Super::Add(const_cast<void*>(pvData));
+ if (pNew == 0)
+ return 0;
+
+ // Copy the source interface name.
+ pNew->szSrcItfName = m_Names.Alloc((ULONG)wcslen(pData->szSrcItfName)+1);
+ if (pNew->szSrcItfName == 0)
+ return 0;
+ wcscpy_s((LPWSTR)pNew->szSrcItfName, wcslen(pData->szSrcItfName)+1, pData->szSrcItfName);
+
+ // Copy the event interface type def.
+ pNew->trEventItf = pData->trEventItf;
+
+ // Copy the event interface name.
+ pNew->szEventItfName = m_Names.Alloc((ULONG)wcslen(pData->szEventItfName)+1);
+ if (pNew->szEventItfName == 0)
+ return 0;
+ wcscpy_s((LPWSTR)pNew->szEventItfName, wcslen(pData->szEventItfName)+1, pData->szEventItfName);
+
+ // Copy the event provider name.
+ pNew->szEventProviderName = m_Names.Alloc((ULONG)wcslen(pData->szEventProviderName)+1);
+ if (pNew->szEventProviderName == 0)
+ return 0;
+ wcscpy_s((LPWSTR)pNew->szEventProviderName, wcslen(pData->szEventProviderName)+1, pData->szEventProviderName);
+
+ // Copy the Source Interface Assembly pointer
+ pNew->SrcItfAssembly = pData->SrcItfAssembly;
+
+ // Return the new entry.
+ return pNew;
+} // ImpTlbEventInfo* CImpTlbEventInfoMap::Add()
+
+CImpTlbDefItfToClassItfMap::CImpTlbDefItfToClassItfMap()
+: CClosedHash<class ImpTlbClassItfInfo>(101)
+, m_bstrNameSpace(NULL)
+{
+}
+
+CImpTlbDefItfToClassItfMap::~CImpTlbDefItfToClassItfMap()
+{
+ Clear();
+ if (m_bstrNameSpace)
+ {
+ ::SysFreeString(m_bstrNameSpace);
+ m_bstrNameSpace = NULL;
+ }
+}
+
+HRESULT CImpTlbDefItfToClassItfMap::Init(ITypeLib *pTlb, BSTR bstrNameSpace)
+{
+ HRESULT hr; // A result.
+ int cTi; // Count of TypeInfos.
+ int i; // Loop control.
+ TYPEATTR *psAttr=0; // TYPEATTR for the ITypeInfo.
+ TYPEATTR *psDefItfAttr=0; // TYPEATTR for the default interface.
+ ITypeInfo *pITI=0; // The ITypeInfo.
+ ITypeInfo *pDefItfITI=0; // The ITypeInfo for the default interface.
+
+ // Save the namespace.
+ IfNullGo(m_bstrNameSpace = SysAllocString(bstrNameSpace));
+
+ // How many TypeInfos?
+ IfFailGo(cTi = pTlb->GetTypeInfoCount());
+
+ // Iterate over them.
+ for (i = 0; i < cTi; ++i)
+ {
+ // Get the TypeInfo.
+ hr = pTlb->GetTypeInfo(i, &pITI);
+ if (SUCCEEDED(hr))
+ {
+ // Retrieve the attributes of the type info.
+ IfFailGo(pITI->GetTypeAttr(&psAttr));
+
+ // If we are dealing with a CoClass, then set up the default interface to
+ // class interface mapping.
+ if (psAttr->typekind == TKIND_COCLASS)
+ IfFailGo(AddCoClassInterfaces(pITI, psAttr));
+
+ // Release for next TypeInfo.
+ if (psAttr)
+ {
+ pITI->ReleaseTypeAttr(psAttr);
+ psAttr = 0;
+ }
+ if (pITI)
+ {
+ pITI->Release();
+ pITI = 0;
+ }
+ }
+ }
+
+ErrExit:
+ if (psAttr)
+ pITI->ReleaseTypeAttr(psAttr);
+ if (pITI)
+ pITI->Release();
+
+ return (hr);
+}
+
+HRESULT CImpTlbDefItfToClassItfMap::AddCoClassInterfaces(ITypeInfo *pCoClassITI, TYPEATTR *pCoClassTypeAttr)
+{
+ HRESULT hr; // A result
+ HREFTYPE href; // HREFTYPE of an implemented interface.
+ INT ImplFlags; // ImplType flags.
+ int NumInterfaces; // The number of interfaces on the coclass.
+ int i; // A counter.
+ ITypeInfo *pItfITI=0; // The ITypeInfo for the current interface.
+ ITypeInfo *pBaseItfITI=0; // The ITypeInfo for the base interface.
+ TYPEATTR *psItfAttr=0; // TYPEATTR for the interface.
+ BSTR bstrClassItfName=0; // The name of the class interface.
+
+ // Retrieve the name of the CoClass.
+ IfFailGo(GetManagedNameForTypeInfo(pCoClassITI, m_bstrNameSpace, NULL, &bstrClassItfName));
+
+ // Retrieve the default interface for the CoClass.
+ IfFailGo(CImportTlb::GetDefaultInterface(pCoClassITI, &pItfITI));
+
+ // If there is a default interface, then add it to the map.
+ if (hr == S_OK)
+ {
+ // Retrieve the attributes of the default interface type info.
+ IfFailGo(pItfITI->GetTypeAttr(&psItfAttr));
+
+ // If there already is a CoClass that implements this
+ // interface then we do not want to do the mapping.
+ ImpTlbClassItfInfo sSearch, *pMapped;
+ sSearch.ItfIID = psItfAttr->guid;
+ pMapped = Find(&sSearch);
+ if (pMapped)
+ {
+ // There already is a CoClass that implements the interface so
+ // we set the class itf name to NULL to indicate not to do the def
+ // itf to class itf convertion for this interface.
+ pMapped->szClassItfName = NULL;
+ }
+ else
+ {
+ // Unless the default interface is IUnknown or IDispatch, add the
+ // def itf to class itf entry to the map.
+ if (psItfAttr->guid != IID_IUnknown && psItfAttr->guid != IID_IDispatch)
+ {
+ ImpTlbClassItfInfo sNew;
+ sNew.ItfIID = psItfAttr->guid;
+ sNew.szClassItfName = bstrClassItfName;
+ IfNullGo(Add(&sNew));
+ }
+ }
+
+ // Release for next interface.
+ pItfITI->ReleaseTypeAttr(psItfAttr);
+ psItfAttr = 0;
+ pItfITI->Release();
+ pItfITI = 0;
+ }
+
+ // Retrieve the number of interfaces the coclass has
+ NumInterfaces = pCoClassTypeAttr->cImplTypes;
+
+ // Go through all the interfaces and add them to the map.
+ for (i=0; i < NumInterfaces; i++)
+ {
+ // Get the impl flags.
+ IfFailGo(pCoClassITI->GetImplTypeFlags(i, &ImplFlags));
+
+ // If this is an implemented interface.
+ if (!(ImplFlags & IMPLTYPEFLAG_FSOURCE))
+ {
+ IfFailGo(pCoClassITI->GetRefTypeOfImplType(i, &href));
+ IfFailGo(pCoClassITI->GetRefTypeInfo(href, &pItfITI));
+
+ do
+ {
+ // Retrieve the attributes of the interface type info.
+ IfFailGo(pItfITI->GetTypeAttr(&psItfAttr));
+
+ // If there already is a CoClass that implements this
+ // interface then we do not want to do the mapping.
+ ImpTlbClassItfInfo sSearch, *pMapped;
+ sSearch.ItfIID = psItfAttr->guid;
+ pMapped = Find(&sSearch);
+ if (pMapped)
+ {
+ // There already is a CoClass that implements the interface. If that
+ // CoClass is not the current one, then we we set the class itf name
+ // to NULL to indicate not to do the def itf to class itf convertion
+ // for this interface.
+ if (pMapped->szClassItfName && wcscmp(pMapped->szClassItfName, bstrClassItfName) != 0)
+ pMapped->szClassItfName = NULL;
+ }
+ else
+ {
+ // Add an entry with a NULL name to prevent future substitutions.
+ ImpTlbClassItfInfo sNew;
+ sNew.ItfIID = psItfAttr->guid;
+ sNew.szClassItfName = NULL;
+ IfNullGo(Add(&sNew));
+ }
+
+ // If there is a base interface, then handle it also.
+ if (psItfAttr->cImplTypes == 1)
+ {
+ IfFailGo(pItfITI->GetRefTypeOfImplType(0, &href));
+ IfFailGo(pItfITI->GetRefTypeInfo(href, &pBaseItfITI));
+ }
+
+ // Release for next interface.
+ if (psItfAttr)
+ {
+ pItfITI->ReleaseTypeAttr(psItfAttr);
+ psItfAttr = 0;
+ }
+ if (pItfITI)
+ {
+ pItfITI->Release();
+ pItfITI = 0;
+ }
+
+ // Set the current interface to the base interface.
+ pItfITI = pBaseItfITI;
+ pBaseItfITI = 0;
+ }
+ while(pItfITI);
+ }
+ }
+
+ErrExit:
+ if (psItfAttr)
+ pItfITI->ReleaseTypeAttr(psItfAttr);
+ if (pItfITI)
+ pItfITI->Release();
+ if (bstrClassItfName)
+ ::SysFreeString(bstrClassItfName);
+
+ return hr;
+}
+
+LPCWSTR CImpTlbDefItfToClassItfMap::GetClassItfName(IID &rItfIID)
+{
+ ImpTlbClassItfInfo sSearch, *pMapped;
+ sSearch.ItfIID = rItfIID;
+ pMapped = Find(&sSearch);
+ return pMapped ? pMapped->szClassItfName : NULL;
+}
+
+unsigned int CImpTlbDefItfToClassItfMap::Hash(const ImpTlbClassItfInfo *pData)
+{
+ // Starting value for hash.
+ ULONG hash = 5381;
+
+ // Hash in the IID.
+ const BYTE *pbData = reinterpret_cast<const BYTE *>(&pData->ItfIID);
+ int iSize = sizeof(IID);
+ while (--iSize >= 0)
+ {
+ hash = ((hash << 5) + hash) ^ *pbData;
+ ++pbData;
+ }
+
+ return hash;
+} // unsigned int CImpTlbDefItfToClassItfMap::Hash()
+
+unsigned int CImpTlbDefItfToClassItfMap::Compare(const ImpTlbClassItfInfo *p1, ImpTlbClassItfInfo *p2)
+{
+ // Compare the IID's.
+ return memcmp(&p1->ItfIID, &p2->ItfIID, sizeof(IID));
+} // unsigned int CImpTlbEventInfoMap::Compare()
+
+CImpTlbDefItfToClassItfMap::ELEMENTSTATUS CImpTlbDefItfToClassItfMap::Status(ImpTlbClassItfInfo *p)
+{
+ if (IsEqualGUID(p->ItfIID, FREE_STATUS_GUID))
+ {
+ return (FREE);
+ }
+ else if (IsEqualGUID(p->ItfIID, DELETED_STATUS_GUID))
+ {
+ return (DELETED);
+ }
+ return (USED);
+} // CImpTlbDefItfToClassItfMap::ELEMENTSTATUS CImpTlbEventInfoMap::Status()
+
+void CImpTlbDefItfToClassItfMap::SetStatus(ImpTlbClassItfInfo *p, ELEMENTSTATUS s)
+{
+ if (s == FREE)
+ {
+ p->ItfIID = FREE_STATUS_GUID;
+ }
+ else if (s == DELETED)
+ {
+ p->ItfIID = DELETED_STATUS_GUID;
+ }
+ else
+ {
+ _ASSERTE(!"Invalid status!");
+ }
+} // void CImpTlbDefItfToClassItfMap::SetStatus()
+
+void *CImpTlbDefItfToClassItfMap::GetKey(ImpTlbClassItfInfo *p)
+{
+ return p;
+} // void *CImpTlbDefItfToClassItfMap::GetKey()
+
+ImpTlbClassItfInfo* CImpTlbDefItfToClassItfMap::Add(const ImpTlbClassItfInfo *pData)
+{
+ // Add the new entry to the map.
+ const void *pvData = pData;
+ ImpTlbClassItfInfo *pNew = Super::Add(const_cast<void*>(pvData));
+ if (pNew == 0)
+ return 0;
+
+ // Copy the IID.
+ pNew->ItfIID = pData->ItfIID;
+
+ // Copy the class interface name.
+ if (pData->szClassItfName)
+ {
+ pNew->szClassItfName = m_Names.Alloc((ULONG)wcslen(pData->szClassItfName)+1);
+ if (pNew->szClassItfName == 0)
+ return 0;
+ wcscpy_s((LPWSTR)pNew->szClassItfName, wcslen(pData->szClassItfName)+1, pData->szClassItfName);
+ }
+ else
+ {
+ pNew->szClassItfName = NULL;
+ }
+
+ // Return the new entry.
+ return pNew;
+} // ImpTlbEventInfo* CImpTlbEventInfoMap::Add()
+
+// EOF =======================================================================
diff --git a/src/md/enc/liteweightstgdbrw.cpp b/src/md/enc/liteweightstgdbrw.cpp
new file mode 100644
index 0000000000..9e75ae7635
--- /dev/null
+++ b/src/md/enc/liteweightstgdbrw.cpp
@@ -0,0 +1,1273 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+
+//
+// LiteWeightStgdb.cpp
+//
+// This contains definition of class CLiteWeightStgDB. This is light weight
+// read-only implementation for accessing compressed meta data format.
+//
+//*****************************************************************************
+#include "stdafx.h" // Precompiled header.
+
+#include "metamodelrw.h"
+#include "liteweightstgdb.h"
+
+// include stgdatabase.h for GUID_POOL_STREAM definition
+// #include "stgdatabase.h"
+
+// include StgTiggerStorage for TiggerStorage definition
+#include "stgtiggerstorage.h"
+#include "stgio.h"
+#include "pedecoder.h"
+
+#include <log.h>
+
+
+#ifndef TYPELIB_SIG
+#define TYPELIB_SIG_MSFT 0x5446534D // MSFT
+#define TYPELIB_SIG_SLTG 0x47544C53 // SLTG
+#endif
+
+//*****************************************************************************
+// Checks the given storage object to see if it is an NT PE image.
+//*****************************************************************************
+int _IsNTPEImage( // true if file is NT PE image.
+ StgIO *pStgIO) // Storage object.
+{
+ LONG lfanew=0; // Offset in DOS header to NT header.
+ ULONG lSignature=0; // For NT header signature.
+ HRESULT hr;
+
+ // Read DOS header to find the NT header offset.
+ if (FAILED(hr = pStgIO->Seek(60, FILE_BEGIN)) ||
+ FAILED(hr = pStgIO->Read(&lfanew, sizeof(LONG), 0)))
+ {
+ return (false);
+ }
+
+ // Seek to the NT header and read the signature.
+ if (FAILED(hr = pStgIO->Seek(VAL32(lfanew), FILE_BEGIN)) ||
+ FAILED(hr = pStgIO->Read(&lSignature, sizeof(ULONG), 0)) ||
+ FAILED(hr = pStgIO->Seek(0, FILE_BEGIN)))
+ {
+ return (false);
+ }
+
+ // If the signature is a match, then we have a PE format.
+ if (lSignature == VAL32(IMAGE_NT_SIGNATURE))
+ return (true);
+ else
+ return (false);
+}
+
+BOOL _GetFileTypeForPathExt(StgIO * pStgIO, FILETYPE * piType)
+{
+ // Avoid confusion.
+ *piType = pStgIO->GetFileType();
+
+ // All file types except .obj have a signature built in. You should
+ // not get to this code for those file types unless that file is corrupt,
+ // or someone has changed a format without updating this code.
+ _ASSERTE((*piType == FILETYPE_UNKNOWN) || (*piType == FILETYPE_NTOBJ) || (*piType == FILETYPE_TLB));
+
+ // If we found a type, then you're ok.
+ return (*piType != FILETYPE_UNKNOWN);
+}
+
+HRESULT _GetFileTypeForPath(StgIO *pStgIO, FILETYPE *piType)
+{
+ ULONG lSignature=0;
+ HRESULT hr;
+
+ // Assume native file.
+ *piType = FILETYPE_CLB;
+
+ // Need to read signature to see what type it is.
+ if (!(pStgIO->GetFlags() & DBPROP_TMODEF_CREATE))
+ {
+ if (FAILED(hr = pStgIO->Read(&lSignature, sizeof(ULONG), 0)) ||
+ FAILED(hr = pStgIO->Seek(0, FILE_BEGIN)))
+ {
+ return (hr);
+ }
+ lSignature = VAL32(lSignature);
+ if (lSignature == STORAGE_MAGIC_SIG)
+ *piType = FILETYPE_CLB;
+ else if ((WORD) lSignature ==IMAGE_DOS_SIGNATURE && _IsNTPEImage(pStgIO))
+ *piType = FILETYPE_NTPE;
+ else if (lSignature == TYPELIB_SIG_MSFT || lSignature == TYPELIB_SIG_SLTG)
+ *piType = FILETYPE_TLB;
+ else if (!_GetFileTypeForPathExt(pStgIO, piType))
+ return CLDB_E_FILE_CORRUPT;
+ }
+ return S_OK;
+}
+
+//*****************************************************************************
+// Prepare to go away.
+//*****************************************************************************
+CLiteWeightStgdbRW::~CLiteWeightStgdbRW()
+{
+ // Free up this stacks reference on the I/O object.
+ if (m_pStgIO != NULL)
+ {
+ m_pStgIO->Release();
+ m_pStgIO = NULL;
+ }
+
+ if (m_pStreamList != NULL)
+ {
+ delete m_pStreamList;
+ }
+
+ if (m_wszFileName != NULL)
+ {
+ delete [] m_wszFileName;
+ }
+}
+
+//*****************************************************************************
+// Open an in-memory metadata section for read
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::InitOnMem(
+ ULONG cbData, // count of bytes in pData
+ LPCVOID pData, // points to meta data section in memory
+ int bReadOnly) // If true, read-only.
+{
+ StgIO *pStgIO = NULL; // For file i/o.
+ HRESULT hr = NOERROR;
+
+ if ((pStgIO = new (nothrow) StgIO) == 0)
+ IfFailGo( E_OUTOFMEMORY);
+
+ // Open the storage based on the pbData and cbData
+ IfFailGo( pStgIO->Open(
+ NULL, // filename
+ STGIO_READ,
+ pData,
+ cbData,
+ NULL, // IStream*
+ NULL) // LPSecurityAttributes
+ );
+
+ IfFailGo( InitFileForRead(pStgIO, bReadOnly) );
+
+ErrExit:
+ if (SUCCEEDED(hr))
+ {
+ m_pStgIO = pStgIO;
+ }
+ else
+ {
+ if (pStgIO)
+ pStgIO->Release();
+ }
+ return hr;
+} // CLiteWeightStgdbRW::InitOnMem
+
+
+//*****************************************************************************
+// Given an StgIO, opens compressed streams and do proper initialization.
+// This is a helper for other Init functions.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CLiteWeightStgdbRW::InitFileForRead(
+ StgIO * pStgIO, // For file i/o.
+ int bReadOnly) // If read-only open.
+{
+ TiggerStorage * pStorage = NULL;
+ void * pvData;
+ ULONG cbData;
+ HRESULT hr = NOERROR;
+
+ // Allocate a new storage object which has IStorage on it.
+ pStorage = new (nothrow) TiggerStorage();
+ IfNullGo(pStorage);
+
+ // Init the storage object on the backing storage.
+ OptionValue ov;
+ IfFailGo(m_MiniMd.GetOption(&ov));
+ IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion));
+
+ // Save pointers to header structure for version string.
+ _ASSERTE((m_pvMd == NULL) && (m_cbMd == 0));
+ IfFailGo(pStorage->GetHeaderPointer(&m_pvMd, &m_cbMd));
+
+ // Check to see if this is a minimal metadata
+ if (SUCCEEDED(pStorage->OpenStream(MINIMAL_MD_STREAM, &cbData, &pvData)))
+ {
+ m_MiniMd.m_fMinimalDelta = TRUE;
+ }
+
+ // Load the string pool.
+ if (SUCCEEDED(hr = pStorage->OpenStream(STRING_POOL_STREAM, &cbData, &pvData)))
+ {
+ // String pool has to end with a null-terminator, therefore we don't have to check string pool
+ // content on access.
+ // Shrink size of the pool to the last null-terminator found.
+ while (cbData != 0)
+ {
+ if (((LPBYTE)pvData)[cbData - 1] == 0)
+ { // We have found last null terminator
+ break;
+ }
+ // Shrink size of the pool
+ cbData--;
+ Debug_ReportError("String heap/pool does not end with null-terminator ... shrinking the heap.");
+ }
+ IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolStrings, pvData, cbData, bReadOnly));
+ }
+ else
+ {
+ if (hr != STG_E_FILENOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolStrings, NULL, 0, bReadOnly));
+ }
+
+ // Load the user string blob pool.
+ if (SUCCEEDED(hr = pStorage->OpenStream(US_BLOB_POOL_STREAM, &cbData, &pvData)))
+ {
+ IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolUSBlobs, pvData, cbData, bReadOnly));
+ }
+ else
+ {
+ if (hr != STG_E_FILENOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolUSBlobs, NULL, 0, bReadOnly));
+ }
+
+ // Load the guid pool.
+ if (SUCCEEDED(hr = pStorage->OpenStream(GUID_POOL_STREAM, &cbData, &pvData)))
+ {
+ IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolGuids, pvData, cbData, bReadOnly));
+ }
+ else
+ {
+ if (hr != STG_E_FILENOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolGuids, NULL, 0, bReadOnly));
+ }
+
+ // Load the blob pool.
+ if (SUCCEEDED(hr = pStorage->OpenStream(BLOB_POOL_STREAM, &cbData, &pvData)))
+ {
+ IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolBlobs, pvData, cbData, bReadOnly));
+ }
+ else
+ {
+ if (hr != STG_E_FILENOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolBlobs, NULL, 0, bReadOnly));
+ }
+
+ // Open the metadata.
+ hr = pStorage->OpenStream(COMPRESSED_MODEL_STREAM, &cbData, &pvData);
+ if (hr == STG_E_FILENOTFOUND)
+ {
+ IfFailGo(pStorage->OpenStream(ENC_MODEL_STREAM, &cbData, &pvData));
+ }
+ IfFailGo(m_MiniMd.InitOnMem(pvData, cbData, bReadOnly));
+ IfFailGo(m_MiniMd.PostInit(0));
+
+ErrExit:
+ if (pStorage != NULL)
+ {
+ delete pStorage;
+ }
+ return hr;
+} // CLiteWeightStgdbRW::InitFileForRead
+
+//*****************************************************************************
+// Open a metadata section for read
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::OpenForRead(
+ LPCWSTR szDatabase, // Name of database.
+ void *pbData, // Data to open on top of, 0 default.
+ ULONG cbData, // How big is the data.
+ DWORD dwFlags) // Flags for the open.
+{
+ LPCWSTR pNoFile=W(""); // Constant for empty file name.
+ StgIO *pStgIO = NULL; // For file i/o.
+ HRESULT hr;
+
+ m_pImage = NULL;
+ m_dwImageSize = 0;
+ m_eFileType = FILETYPE_UNKNOWN;
+ // szDatabase, and pbData are mutually exclusive. Only one may be
+ // non-NULL. Having both NULL means empty stream creation.
+ //
+ _ASSERTE(!(szDatabase && (pbData)));
+ _ASSERTE(!(pbData && (szDatabase)));
+
+ // Open on memory needs there to be something to work with.
+ if (pbData && cbData == 0)
+ IfFailGo(CLDB_E_NO_DATA);
+
+ // Make sure we have a path to work with.
+ if (!szDatabase)
+ szDatabase = pNoFile;
+
+ // Sanity check the name lentgh.
+ if (!IsValidFileNameLength(szDatabase))
+ {
+ IfFailGo(E_INVALIDARG);
+ }
+
+ // If we have storage to work with, init it and get type.
+ if (*szDatabase || pbData)
+ {
+ // Allocate a storage instance to use for i/o.
+ if ((pStgIO = new (nothrow) StgIO) == 0)
+ IfFailGo( E_OUTOFMEMORY );
+
+ DBPROPMODE dmOpenFlags = DBPROP_TMODEF_READ;
+
+ // If we're taking ownership of this memory.....
+ if (IsOfTakeOwnership(dwFlags))
+ {
+#ifdef FEATURE_METADATA_STANDALONE_WINRT_RO
+ // Shared memory uses ole32.dll - we cannot depend on it in the standalone WinRT Read-Only DLL
+ IfFailGo(E_INVALIDARG);
+#else
+ dmOpenFlags = (DBPROPMODE)(dmOpenFlags | DBPROP_TMODEF_SHAREDMEM);
+#endif //!FEATURE_METADATA_STANDALONE_WINRT_RO
+ }
+#ifdef FEATURE_METADATA_LOAD_TRUSTED_IMAGES
+ if (IsOfTrustedImage(dwFlags))
+ dmOpenFlags = (DBPROPMODE)(dmOpenFlags | DBPROP_TMODEF_TRYLOADLIBRARY);
+#endif
+
+ // Open the storage so we can read the signature if there is already data.
+ IfFailGo( pStgIO->Open(szDatabase,
+ dmOpenFlags,
+ pbData,
+ cbData,
+ 0, // IStream*
+ NULL) );
+
+ // Determine the type of file we are working with.
+ IfFailGo( _GetFileTypeForPath(pStgIO, &m_eFileType) );
+ }
+
+ // Check for default type.
+ if (m_eFileType == FILETYPE_CLB)
+ {
+ // If user wanted us to make a local copy of the data, do that now.
+ if (IsOfCopyMemory(dwFlags))
+ IfFailGo(pStgIO->LoadFileToMemory());
+
+ // Try the native .clb file.
+ IfFailGo( InitFileForRead(pStgIO, IsOfRead(dwFlags)) );
+ }
+ // PE/COFF executable/object format. This requires us to find the .clb
+ // inside the binary before doing the Init.
+ else if (m_eFileType == FILETYPE_NTPE || m_eFileType == FILETYPE_NTOBJ)
+ {
+ //<TODO>@FUTURE: Ideally the FindImageMetaData function
+ //@FUTURE: would take the pStgIO and map only the part of the file where
+ //@FUTURE: our data lives, leaving the rest alone. This would be smaller
+ //@FUTURE: working set for us.</TODO>
+ void *ptr;
+ ULONG cbSize;
+
+ // Map the entire binary for the FindImageMetaData function.
+ IfFailGo( pStgIO->MapFileToMem(ptr, &cbSize) );
+
+ // Find the .clb inside of the content.
+ if (m_eFileType == FILETYPE_NTPE)
+ {
+ m_pImage = ptr;
+ m_dwImageSize = cbSize;
+ hr = FindImageMetaData(ptr,
+ cbSize,
+ pStgIO->GetMemoryMappedType() == MTYPE_IMAGE,
+ &ptr,
+ &cbSize);
+ }
+ else
+ {
+ _ASSERTE(pStgIO->GetMemoryMappedType() != MTYPE_IMAGE);
+ hr = FindObjMetaData(ptr, cbSize, &ptr, &cbSize);
+ }
+ // Was the metadata found inside the PE file?
+ if (FAILED(hr))
+ {
+ if (hr == E_OUTOFMEMORY)
+ IfFailGo(E_OUTOFMEMORY);
+
+ // No clb in the PE, assume it is a type library.
+ m_eFileType = FILETYPE_TLB;
+
+ // Let the caller deal with a TypeLib.
+ IfFailGo(hr);
+ }
+ else
+ {
+ // Metadata was found inside the file.
+ // Now reset the base of the stg object so that all memory accesses
+ // are relative to the .clb content.
+ //
+ IfFailGo( pStgIO->SetBaseRange(ptr, cbSize) );
+
+ // If user wanted us to make a local copy of the data, do that now.
+ if (IsOfCopyMemory(dwFlags))
+ {
+ // Cache the PEKind, Machine.
+ GetPEKind(pStgIO->GetMemoryMappedType(), NULL, NULL);
+ // Copy the file into memory; releases the file.
+ IfFailGo(pStgIO->LoadFileToMemory());
+ // No longer have the image.
+ m_pImage = NULL;
+ m_dwImageSize = 0;
+ }
+
+ // Defer to the normal lookup.
+ IfFailGo( InitFileForRead(pStgIO, IsOfRead(dwFlags)) );
+ }
+ }
+ else if (m_eFileType == FILETYPE_TLB)
+ {
+ // Let the caller deal with a TypeLib.
+ IfFailGo(CLDB_E_NO_DATA);
+ }
+ // This spells trouble, we need to handle all types we might find.
+ else
+ {
+ _ASSERTE(!"Unknown file type.");
+ IfFailGo( E_FAIL );
+ }
+
+ // Save off everything.
+ IfFailGo(SetFileName(szDatabase));
+
+ // If this was a file...
+ if (pbData == NULL)
+ {
+ WIN32_FILE_ATTRIBUTE_DATA faData;
+ if (!WszGetFileAttributesEx(szDatabase, GetFileExInfoStandard, &faData))
+ IfFailGo(E_FAIL);
+ m_dwDatabaseLFS = faData.nFileSizeLow;
+ m_dwDatabaseLFT = faData.ftLastWriteTime.dwLowDateTime;
+ }
+
+ErrExit:
+ if (SUCCEEDED(hr))
+ {
+ m_pStgIO = pStgIO;
+ }
+ else
+ {
+ if (pStgIO != NULL)
+ pStgIO->Release();
+ }
+ return hr;
+}
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+// Open a metadata section for read/write
+__checkReturn
+HRESULT CLiteWeightStgdbRW::OpenForRead(
+ IMDCustomDataSource *pDataSource, // data to open on top of
+ DWORD dwFlags) // Flags for the open.
+{
+ LPCWSTR pNoFile = W(""); // Constant for empty file name.
+ StgIO *pStgIO = NULL; // For file i/o.
+ HRESULT hr;
+
+ m_pImage = NULL;
+ m_dwImageSize = 0;
+ m_eFileType = FILETYPE_UNKNOWN;
+
+ IfFailGo(m_MiniMd.InitOnCustomDataSource(pDataSource));
+ IfFailGo(m_MiniMd.PostInit(0));
+
+ // Save off everything.
+ IfFailGo(SetFileName(pNoFile));
+
+ErrExit:
+ return hr;
+}
+#endif
+
+// Read/Write versions.
+//*****************************************************************************
+// Init the Stgdb and its subcomponents.
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::InitNew()
+{
+ InitializeLogging();
+ LOG((LF_METADATA, LL_INFO10, "Metadata logging enabled\n"));
+
+ //<TODO>@FUTURE: should probably init the pools here instead of in the MiniMd.</TODO>
+ return m_MiniMd.InitNew();
+}
+
+//*****************************************************************************
+// Determine what the size of the saved data will be.
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::GetSaveSize(// S_OK or error.
+ CorSaveSize fSave, // Quick or accurate?
+ UINT32 *pcbSaveSize, // Put the size here.
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData) // Profile data for working set optimization
+{
+ HRESULT hr = S_OK; // A result.
+ UINT32 cbTotal = 0; // The total size.
+ UINT32 cbSize = 0; // Size of a component.
+
+ m_cbSaveSize = 0;
+
+ // Allocate stream list if not already done.
+ if (m_pStreamList == NULL)
+ {
+ IfNullGo(m_pStreamList = new (nothrow) STORAGESTREAMLST);
+ }
+ else
+ {
+ m_pStreamList->Clear();
+ }
+
+ // Make sure the user string pool is not empty. An empty user string pool causes
+ // problems with edit and continue
+
+ if (m_MiniMd.m_UserStringHeap.GetUnalignedSize() <= 1)
+ {
+ if (!IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode) &&
+ !m_MiniMd.IsMinimalDelta())
+ {
+ BYTE rgData[] = {' ', 0, 0};
+ UINT32 nIndex_Ignore;
+ IfFailGo(m_MiniMd.PutUserString(
+ MetaData::DataBlob(rgData, sizeof(rgData)),
+ &nIndex_Ignore));
+ }
+ }
+
+ // If we're saving a delta metadata, figure out how much space it will take to
+ // save the minimal metadata stream (used only to identify that we have a delta
+ // metadata... nothing should be in that stream.
+ if ((m_MiniMd.m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateDelta)
+ {
+ IfFailGo(AddStreamToList(0, MINIMAL_MD_STREAM));
+ // Ask the storage system to add stream fixed overhead.
+ IfFailGo(TiggerStorage::GetStreamSaveSize(MINIMAL_MD_STREAM, 0, &cbSize));
+ cbTotal += cbSize;
+ }
+
+ if (reorderingOptions & ReArrangeStringPool)
+ {
+ if (pProfileData != NULL)
+ {
+ UINT32 cbHotSize = 0; // Size of pool data.
+ UINT32 cbStream; // Size of just the stream.
+ DWORD bCompressed; // Will the stream be compressed data?
+
+ // Ask the metadata to size its hot data.
+ IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbHotSize, &bCompressed, reorderingOptions, pProfileData));
+ cbStream = cbHotSize;
+ m_bSaveCompressed = bCompressed;
+
+ if (cbHotSize != 0)
+ {
+ // Add this item to the save list.
+ IfFailGo(AddStreamToList(cbHotSize, HOT_MODEL_STREAM));
+
+ // Ask the storage system to add stream fixed overhead.
+ IfFailGo(TiggerStorage::GetStreamSaveSize(HOT_MODEL_STREAM, cbHotSize, &cbHotSize));
+
+ // Log the size info.
+ LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n",
+ HOT_MODEL_STREAM, cbStream, cbHotSize));
+
+ cbTotal += cbHotSize;
+ }
+ }
+
+ // get string pool save size
+ IfFailGo(GetPoolSaveSize(STRING_POOL_STREAM, MDPoolStrings, &cbSize));
+ cbTotal += cbSize;
+ }
+
+ // Query the MiniMd for its size.
+ IfFailGo(GetTablesSaveSize(fSave, &cbSize, reorderingOptions, pProfileData));
+ cbTotal += cbSize;
+
+ // Get the pools' sizes.
+ if( !(reorderingOptions & ReArrangeStringPool) )
+ {
+ IfFailGo(GetPoolSaveSize(STRING_POOL_STREAM, MDPoolStrings, &cbSize));
+ cbTotal += cbSize;
+ }
+ IfFailGo(GetPoolSaveSize(US_BLOB_POOL_STREAM, MDPoolUSBlobs, &cbSize));
+ cbTotal += cbSize;
+ IfFailGo(GetPoolSaveSize(GUID_POOL_STREAM, MDPoolGuids, &cbSize));
+ cbTotal += cbSize;
+ IfFailGo(GetPoolSaveSize(BLOB_POOL_STREAM, MDPoolBlobs, &cbSize));
+ cbTotal += cbSize;
+
+ // Finally, ask the storage system to add fixed overhead it needs for the
+ // file format. The overhead of each stream has already be calculated as
+ // part of GetStreamSaveSize. What's left is the signature and header
+ // fixed size overhead.
+ IfFailGo(TiggerStorage::GetStorageSaveSize((ULONG *)&cbTotal, 0, m_MiniMd.m_OptionValue.m_RuntimeVersion));
+
+ // Log the size info.
+ LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize total is %d.\n", cbTotal));
+
+ // The list of streams that will be saved are now in the stream save list.
+ // Next step is to walk that list and fill out the correct offsets. This is
+ // done here so that the data can be streamed without fixing up the header.
+ TiggerStorage::CalcOffsets(m_pStreamList, 0, m_MiniMd.m_OptionValue.m_RuntimeVersion);
+
+ if (pcbSaveSize != NULL)
+ {
+ *pcbSaveSize = cbTotal;
+ }
+
+ // Don't cache the value for the EnC case
+ if (!IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode))
+ m_cbSaveSize = cbTotal;
+
+ErrExit:
+ return hr;
+} // CLiteWeightStgdbRW::GetSaveSize
+
+//*****************************************************************************
+// Get the save size of one of the pools. Also adds the pool's stream to
+// the list of streams to be saved.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CLiteWeightStgdbRW::GetPoolSaveSize(
+ LPCWSTR szHeap, // Name of the heap stream.
+ int iPool, // The pool of which to get size.
+ UINT32 *pcbSaveSize) // Add pool data to this value.
+{
+ UINT32 cbSize = 0; // Size of pool data.
+ UINT32 cbStream; // Size of just the stream.
+ HRESULT hr;
+
+ *pcbSaveSize = 0;
+
+ // If there is no data, then don't bother.
+ if (m_MiniMd.IsPoolEmpty(iPool))
+ return (S_OK);
+
+ // Ask the pool to size its data.
+ IfFailGo(m_MiniMd.GetPoolSaveSize(iPool, &cbSize));
+ cbStream = cbSize;
+
+ // Add this item to the save list.
+ IfFailGo(AddStreamToList(cbSize, szHeap));
+
+
+ // Ask the storage system to add stream fixed overhead.
+ IfFailGo(TiggerStorage::GetStreamSaveSize(szHeap, cbSize, &cbSize));
+
+ // Log the size info.
+ LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n",
+ szHeap, cbStream, cbSize));
+
+ // Give the size of the pool to the caller's total.
+ *pcbSaveSize = cbSize;
+
+ErrExit:
+ return hr;
+}
+
+//*****************************************************************************
+// Get the save size of the metadata tables. Also adds the tables stream to
+// the list of streams to be saved.
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::GetTablesSaveSize(
+ CorSaveSize fSave,
+ UINT32 *pcbSaveSize,
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData) // Add pool data to this value.
+{
+ UINT32 cbSize = 0; // Size of pool data.
+ UINT32 cbHotSize = 0; // Size of pool data.
+ UINT32 cbStream; // Size of just the stream.
+ DWORD bCompressed; // Will the stream be compressed data?
+ LPCWSTR szName; // What will the name of the pool be?
+ HRESULT hr;
+
+ *pcbSaveSize = 0;
+
+ if( !(reorderingOptions & ReArrangeStringPool) )
+ {
+ if (pProfileData != NULL)
+ {
+ // Ask the metadata to size its hot data.
+ IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbHotSize, &bCompressed, reorderingOptions, pProfileData));
+ cbStream = cbHotSize;
+ m_bSaveCompressed = bCompressed;
+
+ if (cbHotSize != 0)
+ {
+ szName = HOT_MODEL_STREAM;
+
+ // Add this item to the save list.
+ IfFailGo(AddStreamToList(cbHotSize, szName));
+
+ // Ask the storage system to add stream fixed overhead.
+ IfFailGo(TiggerStorage::GetStreamSaveSize(szName, cbHotSize, &cbHotSize));
+
+ // Log the size info.
+ LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n",
+ szName, cbStream, cbHotSize));
+ }
+ }
+ }
+ // Ask the metadata to size its data.
+ IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbSize, &bCompressed));
+ cbStream = cbSize;
+ m_bSaveCompressed = bCompressed;
+ szName = m_bSaveCompressed ? COMPRESSED_MODEL_STREAM : ENC_MODEL_STREAM;
+
+ // Add this item to the save list.
+ IfFailGo(AddStreamToList(cbSize, szName));
+
+ // Ask the storage system to add stream fixed overhead.
+ IfFailGo(TiggerStorage::GetStreamSaveSize(szName, cbSize, &cbSize));
+
+ // Log the size info.
+ LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n",
+ szName, cbStream, cbSize));
+
+ // Give the size of the pool to the caller's total.
+ *pcbSaveSize = cbHotSize + cbSize;
+
+ErrExit:
+ return hr;
+} // CLiteWeightStgdbRW::GetTablesSaveSize
+
+//*****************************************************************************
+// Add a stream, and its size, to the list of streams to be saved.
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::AddStreamToList(
+ UINT32 cbSize,
+ LPCWSTR szName)
+{
+ HRESULT hr = S_OK;
+ PSTORAGESTREAM pItem; // New item to allocate & fill.
+
+ // Add a new item to the end of the list.
+ IfNullGo(pItem = m_pStreamList->Append());
+
+ // Fill out the data.
+ pItem->SetOffset(0);
+ pItem->SetSize((ULONG)cbSize);
+ pItem->SetName(szName);
+
+ErrExit:
+ return hr;
+}
+
+//*****************************************************************************
+// Save the data to a stream. A TiggerStorage sub-allocates streams within
+// the stream.
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::SaveToStream(
+ IStream *pIStream,
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData)
+{
+ HRESULT hr = S_OK; // A result.
+ StgIO *pStgIO = 0;
+ TiggerStorage *pStorage = 0;
+
+ // Allocate a storage subsystem and backing store.
+ IfNullGo(pStgIO = new (nothrow) StgIO);
+ IfNullGo(pStorage = new (nothrow) TiggerStorage);
+
+ // Open around this stream for write.
+ IfFailGo(pStgIO->Open(W(""),
+ DBPROP_TMODEF_DFTWRITEMASK,
+ 0, 0, // pbData, cbData
+ pIStream,
+ 0)); // LPSecurityAttributes
+ OptionValue ov;
+ IfFailGo(m_MiniMd.GetOption(&ov));
+ IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion));
+
+ // Save worker will do tables, pools.
+ IfFailGo(SaveToStorage(pStorage, reorderingOptions, pProfileData));
+
+ErrExit:
+ if (pStgIO != NULL)
+ pStgIO->Release();
+ if (pStorage != NULL)
+ delete pStorage;
+ return hr;
+} // CLiteWeightStgdbRW::SaveToStream
+
+//*****************************************************************************
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::SaveToStorage(
+ TiggerStorage *pStorage,
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData)
+{
+ HRESULT hr; // A result.
+ LPCWSTR szName; // Name of the tables stream.
+ IStream *pIStreamTbl = 0;
+ UINT32 cb;
+ UINT32 cbSaveSize = m_cbSaveSize;
+
+ // Must call GetSaveSize to cache the streams up front.
+ // Don't trust cached values in the delta case... if there was a previous call to get
+ // a non-delta size, it will be incorrect.
+ if ((m_cbSaveSize == 0) || IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode))
+ {
+ IfFailGo(GetSaveSize(cssAccurate, &cbSaveSize));
+ }
+
+ // Save the header of the data file.
+ IfFailGo(pStorage->WriteHeader(m_pStreamList, 0, NULL));
+
+ // If this is a minimal delta, write a stream marker
+ if (IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode))
+ {
+ IfFailGo(pStorage->CreateStream(MINIMAL_MD_STREAM,
+ STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
+ 0, 0, &pIStreamTbl));
+ pIStreamTbl->Release();
+ pIStreamTbl = 0;
+ }
+
+ if (pProfileData != NULL)
+ {
+ DWORD bCompressed;
+ UINT32 cbHotSize;
+ // Will the stream be compressed data?
+
+ // Only create this additional stream if it will be non-empty
+ IfFailGo(m_MiniMd.GetSaveSize(cssAccurate, &cbHotSize, &bCompressed, reorderingOptions, pProfileData));
+
+ if (cbHotSize > 0)
+ {
+ // Create a stream and save the hot tables.
+ szName = HOT_MODEL_STREAM;
+ IfFailGo(pStorage->CreateStream(szName,
+ STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
+ 0, 0, &pIStreamTbl));
+ IfFailGo(m_MiniMd.SaveTablesToStream(pIStreamTbl, reorderingOptions, pProfileData));
+ pIStreamTbl->Release();
+ pIStreamTbl = 0;
+ }
+ }
+
+ if (reorderingOptions & ReArrangeStringPool)
+ {
+ // Save the string pool before the tables when we do not have the string pool cache
+ IfFailGo(SavePool(STRING_POOL_STREAM, pStorage, MDPoolStrings));
+ }
+
+ // Create a stream and save the tables.
+ szName = m_bSaveCompressed ? COMPRESSED_MODEL_STREAM : ENC_MODEL_STREAM;
+ IfFailGo(pStorage->CreateStream(szName,
+ STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
+ 0, 0, &pIStreamTbl));
+ IfFailGo(m_MiniMd.SaveTablesToStream(pIStreamTbl, NoReordering, NULL));
+ pIStreamTbl->Release();
+ pIStreamTbl = 0;
+
+ // Save the pools.
+ if (!(reorderingOptions & ReArrangeStringPool))
+ {
+ // string pool must be saved after the tables when we have the string pool cache
+ IfFailGo(SavePool(STRING_POOL_STREAM, pStorage, MDPoolStrings));
+ }
+ IfFailGo(SavePool(US_BLOB_POOL_STREAM, pStorage, MDPoolUSBlobs));
+ IfFailGo(SavePool(GUID_POOL_STREAM, pStorage, MDPoolGuids));
+ IfFailGo(SavePool(BLOB_POOL_STREAM, pStorage, MDPoolBlobs));
+
+ // Write the header to disk.
+ OptionValue ov;
+ IfFailGo(m_MiniMd.GetOption(&ov));
+
+ IfFailGo(pStorage->WriteFinished(m_pStreamList, (ULONG *)&cb, IsENCDelta(ov.m_UpdateMode)));
+
+ _ASSERTE(cbSaveSize == cb);
+
+ // Let the Storage release some memory.
+ pStorage->ResetBackingStore();
+
+ IfFailGo(m_MiniMd.SaveDone());
+
+ErrExit:
+ if (pIStreamTbl != NULL)
+ pIStreamTbl->Release();
+ delete m_pStreamList;
+ m_pStreamList = 0;
+ m_cbSaveSize = 0;
+ return hr;
+} // CLiteWeightStgdbRW::SaveToStorage
+
+//*****************************************************************************
+// Save a pool of data out to a stream.
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::SavePool( // Return code.
+ LPCWSTR szName, // Name of stream on disk.
+ TiggerStorage *pStorage, // The storage to put data in.
+ int iPool) // The pool to save.
+{
+ IStream *pIStream=0; // For writing.
+ HRESULT hr;
+
+ // If there is no data, then don't bother.
+ if (m_MiniMd.IsPoolEmpty(iPool))
+ return (S_OK);
+
+ // Create the new stream to hold this table and save it.
+ IfFailGo(pStorage->CreateStream(szName,
+ STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
+ 0, 0, &pIStream));
+ IfFailGo(m_MiniMd.SavePoolToStream(iPool, pIStream));
+
+ErrExit:
+ if (pIStream)
+ pIStream->Release();
+ return hr;
+} // CLiteWeightStgdbRW::SavePool
+
+
+//*****************************************************************************
+// Save the metadata to a file.
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::Save(
+ LPCWSTR szDatabase, // Name of file to which to save.
+ DWORD dwSaveFlags) // Flags for the save.
+{
+ TiggerStorage * pStorage = NULL; // IStorage object.
+ StgIO * pStgIO = NULL; // Backing storage.
+ HRESULT hr = S_OK;
+
+ if (m_wszFileName == NULL)
+ {
+ if (szDatabase == NULL)
+ {
+ // Make sure that a NULL is not passed in the first time around.
+ _ASSERTE(!"Not allowed to pass a NULL for filename on the first call to Save.");
+ return E_INVALIDARG;
+ }
+ else
+ {
+ // Save the file name.
+ IfFailGo(SetFileName(szDatabase));
+ }
+ }
+ else if ((szDatabase != NULL) && (SString::_wcsicmp(szDatabase, m_wszFileName) != 0))
+ {
+ // Save the file name.
+ IfFailGo(SetFileName(szDatabase));
+ }
+
+ // Sanity check the name.
+ if (!IsValidFileNameLength(m_wszFileName))
+ {
+ IfFailGo(E_INVALIDARG);
+ }
+
+ m_eFileType = FILETYPE_CLB;
+
+ // Allocate a new storage object.
+ IfNullGo(pStgIO = new (nothrow) StgIO);
+
+ // Create the output file.
+ IfFailGo(pStgIO->Open(m_wszFileName,
+ DBPROP_TMODEF_DFTWRITEMASK,
+ 0,0, // pbData, cbData
+ 0, // IStream*
+ 0)); // LPSecurityAttributes
+
+ // Allocate an IStorage object to use.
+ IfNullGo(pStorage = new (nothrow) TiggerStorage);
+
+ // Init the storage object on the i/o system.
+ OptionValue ov;
+ IfFailGo(m_MiniMd.GetOption(&ov));
+ IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion));
+
+ // Save the data.
+ IfFailGo(SaveToStorage(pStorage));
+
+ErrExit:
+ if (pStgIO != NULL)
+ pStgIO->Release();
+ if (pStorage != NULL)
+ delete pStorage;
+ return hr;
+} // CLiteWeightStgdbRW::Save
+
+//*****************************************************************************
+// Pull the PEKind and Machine out of PE headers -- if we have PE headers.
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::GetPEKind( // S_OK or error.
+ MAPPINGTYPE mtMapping, // The type of mapping the image has
+ DWORD *pdwPEKind, // [OUT] The kind of PE (0 - not a PE)
+ DWORD *pdwMachine) // [OUT] Machine as defined in NT header
+{
+ HRESULT hr = NOERROR;
+ DWORD dwPEKind=0; // Working copy of pe kind.
+ DWORD dwMachine=0; // Working copy of machine.
+
+#ifndef DACCESS_COMPILE
+ // Do we already have cached information?
+ if (m_dwPEKind != (DWORD)(-1))
+ {
+ dwPEKind = m_dwPEKind;
+ dwMachine = m_dwMachine;
+ }
+ else if (m_pImage)
+ {
+ PEDecoder pe;
+
+ // We need to use different PEDecoder initialization based on the type of data we give it.
+ // We use the one with a 'bool' as the second argument when dealing with a mapped file,
+ // and we use the one that takes a COUNT_T as the second argument when dealing with a
+ // flat file.
+
+ if (mtMapping == MTYPE_IMAGE)
+ {
+ if (FAILED(pe.Init(m_pImage, false)) ||
+ !pe.CheckNTHeaders())
+ {
+ IfFailRet(COR_E_BADIMAGEFORMAT);
+ }
+ }
+ else
+ {
+ pe.Init(m_pImage, (COUNT_T)(m_dwImageSize));
+ }
+
+ if (pe.HasContents() && pe.HasNTHeaders())
+ {
+ pe.GetPEKindAndMachine(&dwPEKind, &dwMachine);
+
+
+ // Cache entries.
+ m_dwPEKind = dwPEKind;
+ m_dwMachine = dwMachine;
+ }
+ else // if (pe.HasContents()...
+ {
+ hr = COR_E_BADIMAGEFORMAT;
+ }
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+#endif
+ if (pdwPEKind)
+ *pdwPEKind = dwPEKind;
+ if (pdwMachine)
+ *pdwMachine = dwMachine;
+
+ return hr;
+} // CLiteWeightStgdbRW::GetPEKind
+
+//*****************************************************************************
+// Low level access to the data. Intended for metainfo, and such.
+//*****************************************************************************
+__checkReturn
+HRESULT CLiteWeightStgdbRW::GetRawData(
+ const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB').
+ ULONG *pcbMd) // [OUT] put size of the stream here.
+{
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ if (m_pStgIO == NULL)
+ return COR_E_NOTSUPPORTED;
+#endif
+
+ *ppvMd = (const void*) m_pStgIO->m_pData;
+ *pcbMd = m_pStgIO->m_cbData;
+ return S_OK;
+} // CLiteWeightStgdbRW::GetRawData
+
+//*****************************************************************************
+// Get info about the MD stream.
+// Low level access to stream data. Intended for metainfo, and such.
+//*****************************************************************************
+__checkReturn
+STDMETHODIMP
+CLiteWeightStgdbRW::GetRawStreamInfo(
+ ULONG ix, // [IN] Stream ordinal desired.
+ const char **ppchName, // [OUT] put pointer to stream name here.
+ const void **ppv, // [OUT] put pointer to MD stream here.
+ ULONG *pcb) // [OUT] put size of the stream here.
+{
+ HRESULT hr = NOERROR;
+ STORAGEHEADER sHdr; // Header for the storage.
+ PSTORAGESTREAM pStream; // Pointer to each stream.
+ ULONG i; // Loop control.
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ if (m_pStgIO == NULL)
+ IfFailGo(COR_E_NOTSUPPORTED);
+#endif
+
+ void *pData = m_pStgIO->m_pData;
+ ULONG cbData = m_pStgIO->m_cbData;
+
+ // Validate the signature of the format, or it isn't ours.
+ IfFailGo(MDFormat::VerifySignature((PSTORAGESIGNATURE) pData, cbData));
+
+ // Get back the first stream.
+ pStream = MDFormat::GetFirstStream(&sHdr, pData);
+ if (pStream == NULL)
+ {
+ Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Check that the requested stream exists.
+ if (ix >= sHdr.GetiStreams())
+ return S_FALSE;
+
+ // Skip to the desired stream.
+ for (i = 0; i < ix; i++)
+ {
+ PSTORAGESTREAM pNext = pStream->NextStream();
+
+ // Check that stream header is within the buffer.
+ if (((LPBYTE)pStream >= ((LPBYTE)pData + cbData)) ||
+ ((LPBYTE)pNext > ((LPBYTE)pData + cbData)))
+ {
+ Debug_ReportError("Stream header is not within MetaData block.");
+ hr = CLDB_E_FILE_CORRUPT;
+ goto ErrExit;
+ }
+
+ // Check that the stream data starts and fits within the buffer.
+ // need two checks on size because of wraparound.
+ if ((pStream->GetOffset() > cbData) ||
+ (pStream->GetSize() > cbData) ||
+ ((pStream->GetSize() + pStream->GetOffset()) > cbData))
+ {
+ Debug_ReportError("Stream data are not within MetaData block.");
+ hr = CLDB_E_FILE_CORRUPT;
+ goto ErrExit;
+ }
+
+ // Pick off the next stream if there is one.
+ pStream = pNext;
+ }
+
+ if (pStream != NULL)
+ {
+ *ppv = (const void *)((const BYTE *)pData + pStream->GetOffset());
+ *pcb = pStream->GetSize();
+ *ppchName = pStream->GetName();
+ }
+ else
+ {
+ *ppv = NULL;
+ *pcb = 0;
+ *ppchName = NULL;
+
+ // Invalid input to the method
+ hr = CLDB_E_FILE_CORRUPT;
+ }
+
+ErrExit:
+ return hr;
+} // CLiteWeightStgdbRW::GetRawStreamInfo
+
+//=======================================================================================
+//
+// Set file name of this database (makes copy of the file name).
+//
+// Return value: S_OK or E_OUTOFMEMORY
+//
+__checkReturn
+HRESULT
+CLiteWeightStgdbRW::SetFileName(
+ const WCHAR * wszFileName)
+{
+ HRESULT hr = S_OK;
+
+ if (m_wszFileName != NULL)
+ {
+ delete [] m_wszFileName;
+ m_wszFileName = NULL;
+ }
+
+ if ((wszFileName == NULL) || (*wszFileName == 0))
+ { // The new file name is empty
+ _ASSERTE(m_wszFileName == NULL);
+
+ // No need to allocate anything, NULL means empty name
+ hr = S_OK;
+ goto ErrExit;
+ }
+
+ // Size of the file name incl. null terminator
+ size_t cchFileName;
+ cchFileName = wcslen(wszFileName) + 1;
+
+ // Allocate and copy the file name
+ m_wszFileName = new (nothrow) WCHAR[cchFileName];
+ IfNullGo(m_wszFileName);
+ wcscpy_s(m_wszFileName, cchFileName, wszFileName);
+
+ErrExit:
+ return hr;
+} // CLiteWeightStgdbRW::SetFileName
+
+//=======================================================================================
+//
+// Returns TRUE if wszFileName has valid path length (MAX_PATH or 32767 if prefixed with \\?\).
+//
+//static
+BOOL
+CLiteWeightStgdbRW::IsValidFileNameLength(
+ const WCHAR * wszFileName)
+{
+ static const WCHAR const_wszLongPathPrefix[] = W("\\\\?\\");
+
+ if (wszFileName == NULL)
+ {
+ return TRUE;
+ }
+ size_t cchFileName = wcslen(wszFileName);
+ if (cchFileName < _MAX_PATH)
+ {
+ return TRUE;
+ }
+ if (SString::_wcsnicmp(wszFileName, const_wszLongPathPrefix, _countof(const_wszLongPathPrefix) - 1) != 0)
+ { // Path does not have long path prefix \\?\ (as required by CreateFile API)
+ return FALSE;
+ }
+ if (cchFileName < 32767)
+ { // Limit for the long path length as defined in CreateFile API
+ return TRUE;
+ }
+ return FALSE;
+} // CLiteWeightStgdbRW::IsValidFileNameLength
diff --git a/src/md/enc/mdinternalrw.cpp b/src/md/enc/mdinternalrw.cpp
new file mode 100644
index 0000000000..0495101bc8
--- /dev/null
+++ b/src/md/enc/mdinternalrw.cpp
@@ -0,0 +1,4356 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ===========================================================================
+// File: MDInternalRW.cpp
+//
+
+// Notes:
+//
+//
+// ===========================================================================
+#include "stdafx.h"
+#include "../runtime/mdinternalro.h"
+#include "../compiler/regmeta.h"
+#include "../compiler/importhelper.h"
+#include "mdinternalrw.h"
+#include "metamodelro.h"
+#include "liteweightstgdb.h"
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+__checkReturn
+HRESULT _GetFixedSigOfVarArg( // S_OK or error.
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob of COM+ method signature
+ ULONG cbSigBlob, // [IN] size of signature
+ CQuickBytes *pqbSig, // [OUT] output buffer for fixed part of VarArg Signature
+ ULONG *pcbSigBlob); // [OUT] number of bytes written to the above output buffer
+
+__checkReturn
+HRESULT _FillMDDefaultValue(
+ BYTE bType,
+ void const *pValue,
+ ULONG cbValue,
+ MDDefaultValue *pMDDefaultValue);
+
+
+//*****************************************************************************
+// Serve as a delegator to call ImportHelper::MergeUpdateTokenInSig. Or we will
+// need to include ImportHelper into our md\runtime directory.
+//*****************************************************************************
+__checkReturn
+HRESULT TranslateSigHelper( // S_OK or error.
+ IMDInternalImport* pImport, // [IN] import scope.
+ IMDInternalImport* pAssemImport, // [IN] import assembly scope.
+ const void* pbHashValue, // [IN] hash value for the import assembly.
+ ULONG cbHashValue, // [IN] count of bytes in the hash value.
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit* pAssemEmit, // [IN] assembly emit scope.
+ IMetaDataEmit* emit, // [IN] emit interface
+ CQuickBytes* pqkSigEmit, // [OUT] buffer to hold translated signature
+ ULONG* pcbSig) // [OUT] count of bytes in the translated signature
+{
+#ifdef FEATURE_METADATA_EMIT
+ IMetaModelCommon *pCommon = pImport->GetMetaModelCommon();
+ RegMeta *pAssemEmitRM = static_cast<RegMeta*>(pAssemEmit);
+ RegMeta *pEmitRM = static_cast<RegMeta*>(emit);
+
+ CMiniMdRW *pMiniMdAssemEmit = pAssemEmitRM ? &pAssemEmitRM->m_pStgdb->m_MiniMd : NULL;
+ CMiniMdRW *pMiniMdEmit = &(pEmitRM->m_pStgdb->m_MiniMd);
+ IMetaModelCommon *pCommonAssemImport = pAssemImport ? pAssemImport->GetMetaModelCommon() : NULL;
+
+ return ImportHelper::MergeUpdateTokenInSig(
+ pMiniMdAssemEmit, // The assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ pCommonAssemImport, // Assembly scope where the signature is from.
+ pbHashValue, // Hash value for the import scope.
+ cbHashValue, // Size in bytes.
+ pCommon, // The scope where signature is from.
+ pbSigBlob, // signature from the imported scope
+ NULL, // Internal OID mapping structure.
+ pqkSigEmit, // [OUT] translated signature
+ NULL, // start from first byte of the signature
+ NULL, // don't care how many bytes consumed
+ pcbSig); // [OUT] total number of bytes write to pqkSigEmit
+
+#else //!FEATURE_METADATA_EMIT
+ // This API doesn't make sense without supporting public Emit APIs
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT
+} // TranslateSigHelper
+
+
+//*****************************************************************************
+// Given an IMDInternalImport on a CMiniMd[RO], convert to CMiniMdRW.
+//*****************************************************************************
+__checkReturn
+STDAPI ConvertRO2RW(
+ IUnknown *pRO, // [IN] The RO interface to convert.
+ REFIID riid, // [IN] The interface desired.
+ void **ppIUnk) // [OUT] Return interface on success.
+{
+ HRESULT hr = S_OK; // A result.
+ IMDInternalImportENC *pRW = 0; // To test the RW-ness of the input iface.
+ MDInternalRW *pInternalRW = 0; // Gets the new RW object.
+ MDInternalRO *pTrustedRO = 0;
+
+ // Avoid confusion.
+ *ppIUnk = 0;
+
+ IfFailGo(VerifyNotWinMD(pRO, "ConvertRO2RW() not supported on .winmd files."));
+
+ // If the interface is already RW, done, just return.
+ if (pRO->QueryInterface(IID_IMDInternalImportENC, (void**)&pRW) == S_OK)
+ {
+ hr = pRO->QueryInterface(riid, ppIUnk);
+ goto ErrExit;
+ }
+
+ // Create the new RW object.
+ pInternalRW = new (nothrow) MDInternalRW;
+ IfNullGo( pInternalRW );
+
+ // Init from the RO object. Convert as read-only; QI will make writable if
+ // so needed.
+
+ // ! QI for IID_IUnknown will return MDInternalRO*. Not that COM guarantees such a thing but MDInternalRO knows about
+ IfFailGo( pRO->QueryInterface(IID_IUnknown, (void**)&pTrustedRO) );
+ IfFailGo( pInternalRW->InitWithRO(pTrustedRO, true));
+ IfFailGo( pInternalRW->QueryInterface(riid, ppIUnk) );
+
+ErrExit:
+ if (pRW)
+ pRW->Release();
+ if (pTrustedRO)
+ pTrustedRO->Release();
+ // Clean up the object and [OUT] interface on error.
+ if (FAILED(hr))
+ {
+ if (pInternalRW)
+ delete pInternalRW;
+ *ppIUnk = 0;
+ }
+ else if (pInternalRW)
+ pInternalRW->Release();
+
+ return hr;
+} // ConvertRO2RW
+
+
+//*****************************************************************************
+// Helper to get the internal interface with RW format
+//*****************************************************************************
+__checkReturn
+HRESULT GetInternalWithRWFormat(
+ LPVOID pData,
+ ULONG cbData,
+ DWORD flags, // [IN] MDInternal_OpenForRead or MDInternal_OpenForENC
+ REFIID riid, // [in] The interface desired.
+ void **ppIUnk) // [out] Return interface on success.
+{
+ MDInternalRW *pInternalRW = NULL;
+ HRESULT hr;
+
+ *ppIUnk = 0;
+ pInternalRW = new (nothrow) MDInternalRW;
+ IfNullGo( pInternalRW );
+ IfFailGo( pInternalRW->Init(
+ const_cast<void*>(pData),
+ cbData,
+ (flags == ofRead) ? true : false) );
+ IfFailGo( pInternalRW->QueryInterface(riid, ppIUnk) );
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pInternalRW)
+ delete pInternalRW;
+ *ppIUnk = 0;
+ }
+ else if ( pInternalRW )
+ pInternalRW->Release();
+ return hr;
+} // GetInternalWithRWFormat
+
+
+//*****************************************************************************
+// This function returns a IMDInternalImport interface based on the given
+// public import interface i.e IMetaDataEmit or IMetaDataImport.
+//*****************************************************************************
+__checkReturn
+STDAPI GetMDInternalInterfaceFromPublic(
+ IUnknown *pIUnkPublic, // [IN] Given public interface. Must be QI of IUnknown
+ REFIID riid, // [in] The interface desired.
+ void **ppIUnkInternal) // [out] Return interface on success.
+{
+ HRESULT hr = S_OK;
+ ReleaseHolder<IGetIMDInternalImport> pGetIMDInternalImport;
+
+ // IMDInternalImport is the only internal import interface currently supported by
+ // this function.
+ _ASSERTE(riid == IID_IMDInternalImport && pIUnkPublic && ppIUnkInternal);
+
+ if (riid != IID_IMDInternalImport || pIUnkPublic == NULL || ppIUnkInternal == NULL)
+ IfFailGo(E_INVALIDARG);
+ IfFailGo( pIUnkPublic->QueryInterface(IID_IGetIMDInternalImport, &pGetIMDInternalImport));
+ IfFailGo( pGetIMDInternalImport->GetIMDInternalImport((IMDInternalImport **)ppIUnkInternal));
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (ppIUnkInternal)
+ *ppIUnkInternal = 0;
+ }
+ return hr;
+} // GetMDInternalInterfaceFromPublic
+
+
+//*****************************************************************************
+// This function returns the requested public interface based on the given
+// internal import interface. It is caller's responsibility to Release ppIUnkPublic
+//*****************************************************************************
+__checkReturn
+STDAPI GetMDPublicInterfaceFromInternal(
+ void *pIUnkInternal, // [IN] Given internal interface.
+ REFIID riid, // [in] The interface desired.
+ void **ppIUnkPublic) // [out] Return interface on success.
+{
+ HRESULT hr = S_OK;
+ IMDInternalImport *pInternalImport = 0;;
+ IUnknown *pIUnkPublic = NULL;
+ OptionValue optVal = { MDDupAll, MDRefToDefDefault, MDNotifyDefault, MDUpdateFull, MDErrorOutOfOrderDefault , MDThreadSafetyOn};
+ RegMeta *pMeta = 0;
+ bool isLockedForWrite = false;
+
+
+ _ASSERTE(pIUnkInternal && ppIUnkPublic);
+ *ppIUnkPublic = 0;
+
+ IfFailGo(VerifyNotWinMD((IUnknown*)pIUnkInternal, "GetMDPublicInterfaceFromInternal() not supported on .winmd files."));
+
+ IfFailGo(ConvertRO2RW((IUnknown*)pIUnkInternal, IID_IMDInternalImport, (void **)&pInternalImport));
+
+ pIUnkPublic = pInternalImport->GetCachedPublicInterface(TRUE);
+ if ( pIUnkPublic )
+ {
+ // There is already a cached public interface. GetCachedPublicInterface already AddRef the returned
+ // public interface. We want to QueryInterface the riid...
+ // We are done!
+ hr = pIUnkPublic->QueryInterface(riid, ppIUnkPublic);
+ pIUnkPublic->Release();
+ goto ErrExit;
+ }
+
+ // grab the write lock when we are creating the corresponding regmeta for the public interface
+ _ASSERTE( pInternalImport->GetReaderWriterLock() != NULL );
+ isLockedForWrite = true;
+ IfFailGo(pInternalImport->GetReaderWriterLock()->LockWrite());
+
+ // check again. Maybe someone else beat us to setting the public interface while we are waiting
+ // for the write lock. Don't need to grab the read lock since we already have the write lock.
+ *ppIUnkPublic = pInternalImport->GetCachedPublicInterface(FALSE);
+ if ( *ppIUnkPublic )
+ {
+ // there is already a cached public interface. GetCachedPublicInterface already AddRef the returned
+ // public interface.
+ // We are done!
+ goto ErrExit;
+ }
+
+ pMeta = new (nothrow) RegMeta();
+ IfNullGo(pMeta);
+ IfFailGo(pMeta->SetOption(&optVal));
+ IfFailGo( pMeta->InitWithStgdb((IUnknown*)pInternalImport, ((MDInternalRW*)pInternalImport)->GetMiniStgdb()) );
+ IfFailGo( pMeta->QueryInterface(riid, ppIUnkPublic) );
+
+ // The following makes the public object and the internal object point to each other.
+ _ASSERTE( pMeta->GetReaderWriterLock() == NULL );
+ IfFailGo( pMeta->SetCachedInternalInterface(pInternalImport) );
+ IfFailGo( pInternalImport->SetCachedPublicInterface((IUnknown *) *ppIUnkPublic) );
+ IfFailGo( pMeta->SetReaderWriterLock(pInternalImport->GetReaderWriterLock() ));
+
+ // Add the new RegMeta to the cache.
+ IfFailGo( pMeta->AddToCache() );
+
+ErrExit:
+ if (isLockedForWrite)
+ pInternalImport->GetReaderWriterLock()->UnlockWrite();
+
+ if (pInternalImport)
+ pInternalImport->Release();
+
+ if (FAILED(hr))
+ {
+ if (pMeta)
+ delete pMeta;
+ *ppIUnkPublic = 0;
+ }
+ return hr;
+} // GetMDPublicInterfaceFromInternal
+
+//*****************************************************************************
+// Converts an internal MD import API into the read/write version of this API.
+// This could support edit and continue, or modification of the metadata at
+// runtime (say for profiling).
+//*****************************************************************************
+__checkReturn
+STDAPI ConvertMDInternalImport( // S_OK, S_FALSE (no conversion), or error.
+ IMDInternalImport *pIMD, // [in] The metadata to be updated.
+ IMDInternalImport **ppIMD) // [out] Put the RW here.
+{
+ HRESULT hr; // A result.
+ IMDInternalImportENC *pENC = NULL; // ENC interface on the metadata.
+
+ _ASSERTE(pIMD != NULL);
+ _ASSERTE(ppIMD != NULL);
+
+ // Test whether the MD is already RW.
+ hr = pIMD->QueryInterface(IID_IMDInternalImportENC, (void**)&pENC);
+ if (FAILED(hr))
+ { // Not yet RW, so do the conversion.
+ IfFailGo(ConvertRO2RW(pIMD, IID_IMDInternalImport, (void**)ppIMD));
+ }
+ else
+ { // Already converted; give back same pointer.
+ *ppIMD = pIMD;
+ hr = S_FALSE;
+ }
+
+ErrExit:
+ if (pENC)
+ pENC->Release();
+ return hr;
+} // ConvertMDInternalImport
+
+
+
+
+
+//*****************************************************************************
+// Constructor
+//*****************************************************************************
+MDInternalRW::MDInternalRW()
+ : m_pStgdb(NULL),
+ m_cRefs(1),
+ m_fOwnStgdb(false),
+ m_pUnk(NULL),
+ m_pUserUnk(NULL),
+ m_pIMetaDataHelper(NULL),
+ m_pSemReadWrite(NULL),
+ m_fOwnSem(false)
+{
+} // MDInternalRW::MDInternalRW
+
+
+
+//*****************************************************************************
+// Destructor
+//*****************************************************************************
+MDInternalRW::~MDInternalRW()
+{
+ HRESULT hr = S_OK;
+
+ LOCKWRITENORET();
+
+ // This should have worked if we've cached the internal interface in the past
+ _ASSERTE(SUCCEEDED(hr) || m_pIMetaDataHelper == NULL || m_pIMetaDataHelper->GetCachedInternalInterface(false) == NULL);
+
+
+ if (SUCCEEDED(hr))
+ {
+
+ if (m_pIMetaDataHelper)
+ {
+ // The internal object is going away before the public object.
+ // If the internal object owns the reader writer lock, transfer the ownership
+ // to the public object and clear the cached internal interface from the public interface.
+
+ m_pIMetaDataHelper->SetCachedInternalInterface(NULL);
+ m_pIMetaDataHelper = NULL;
+ m_fOwnSem = false;
+
+ }
+
+ UNLOCKWRITE();
+ }
+ if (m_pSemReadWrite && m_fOwnSem)
+ delete m_pSemReadWrite;
+
+ if ( m_pStgdb && m_fOwnStgdb )
+ {
+ // we own the stgdb so we need to uninit and delete it.
+ m_pStgdb->Uninit();
+ delete m_pStgdb;
+ }
+ if ( m_pUserUnk )
+ m_pUserUnk->Release();
+ if ( m_pUnk )
+ m_pUnk->Release();
+} // MDInternalRW::~MDInternalRW
+
+
+//*****************************************************************************
+// Set or clear the cached public interfaces.
+// NOTE:: Caller should take a Write lock on the reader writer lock.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::SetCachedPublicInterface(IUnknown * pUnk)
+{
+ IMetaDataHelper * pHelper = NULL;
+ HRESULT hr = S_OK;
+
+ if (pUnk != NULL)
+ {
+ // Internal object and public regmeta should be one to one mapping!!
+ _ASSERTE(m_pIMetaDataHelper == NULL);
+
+ IfFailRet(pUnk->QueryInterface(IID_IMetaDataHelper, (void **) &pHelper));
+ _ASSERTE(pHelper != NULL);
+
+ m_pIMetaDataHelper = pHelper;
+ pHelper->Release();
+ }
+ else
+ {
+ // public object is going away before the internal object. If we don't own the
+ // reader writer lock, just take over the ownership.
+ m_fOwnSem = true;
+ m_pIMetaDataHelper = NULL;
+ }
+ return hr;
+} // MDInternalRW::SetCachedPublicInterface
+
+
+//*****************************************************************************
+// Clear the cached public interfaces.
+//*****************************************************************************
+IUnknown * MDInternalRW::GetCachedPublicInterface(BOOL fWithLock)
+{
+ HRESULT hr = S_OK;
+ IUnknown * pRet = NULL;
+ if (fWithLock)
+ {
+ LOCKREAD();
+
+ pRet = m_pIMetaDataHelper;
+ if (pRet != NULL)
+ pRet->AddRef();
+ }
+ else
+ {
+ pRet = m_pIMetaDataHelper;
+ if (pRet != NULL)
+ pRet->AddRef();
+ }
+
+ErrExit:
+ return pRet;
+} // MDInternalRW::GetCachedPublicInterface
+
+
+//*****************************************************************************
+// Get the Reader-Writer lock
+//*****************************************************************************
+UTSemReadWrite * MDInternalRW::GetReaderWriterLock()
+{
+ return getReaderWriterLock();
+} // MDInternalRW::GetReaderWriterLock
+
+//*****************************************************************************
+// IUnknown
+//*****************************************************************************
+ULONG MDInternalRW::AddRef()
+{
+ return InterlockedIncrement(&m_cRefs);
+} // MDInternalRW::AddRef
+
+ULONG MDInternalRW::Release()
+{
+ ULONG cRef;
+
+ cRef = InterlockedDecrement(&m_cRefs);
+ if (cRef == 0)
+ {
+ LOG((LOGMD, "MDInternalRW(0x%08x)::destruction\n", this));
+ delete this;
+ }
+ return cRef;
+} // MDInternalRW::Release
+
+__checkReturn
+HRESULT MDInternalRW::QueryInterface(REFIID riid, void **ppUnk)
+{
+ *ppUnk = 0;
+
+ if (riid == IID_IUnknown)
+ *ppUnk = (IUnknown *) (IMDInternalImport *) this;
+
+ else if (riid == IID_IMDInternalImport)
+ *ppUnk = (IMDInternalImport *) this;
+
+ else if (riid == IID_IMDInternalImportENC)
+ *ppUnk = (IMDInternalImportENC *) this;
+
+ else if (riid == IID_IMDCommon)
+ *ppUnk = (IMDCommon*)this;
+
+ else
+ return E_NOINTERFACE;
+ AddRef();
+ return S_OK;
+} // MDInternalRW::QueryInterface
+
+
+//*****************************************************************************
+// Initialize
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::Init(
+ LPVOID pData, // points to meta data section in memory
+ ULONG cbData, // count of bytes in pData
+ int bReadOnly) // Is it open for read only?
+{
+ CLiteWeightStgdbRW * pStgdb = NULL;
+ HRESULT hr = NOERROR;
+ OptionValue optVal = { MDDupAll, MDRefToDefDefault, MDNotifyDefault, MDUpdateFull, MDErrorOutOfOrderDefault, MDThreadSafetyOn };
+
+ pStgdb = new (nothrow) CLiteWeightStgdbRW;
+ IfNullGo(pStgdb);
+
+ m_pSemReadWrite = new (nothrow) UTSemReadWrite;
+ IfNullGo(m_pSemReadWrite);
+ IfFailGo(m_pSemReadWrite->Init());
+ m_fOwnSem = true;
+ INDEBUG(pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
+
+ IfFailGo(pStgdb->InitOnMem(cbData, (BYTE*)pData, bReadOnly));
+ IfFailGo(pStgdb->m_MiniMd.SetOption(&optVal));
+ m_tdModule = COR_GLOBAL_PARENT_TOKEN;
+ m_fOwnStgdb = true;
+ m_pStgdb = pStgdb;
+
+ErrExit:
+ // clean up upon errors
+ if (FAILED(hr) && (pStgdb != NULL))
+ {
+ delete pStgdb;
+ }
+ return hr;
+} // MDInternalRW::Init
+
+
+//*****************************************************************************
+// Initialize with an existing RegMeta.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::InitWithStgdb(
+ IUnknown *pUnk, // The IUnknow that owns the life time for the existing stgdb
+ CLiteWeightStgdbRW *pStgdb) // existing lightweight stgdb
+{
+ // m_fOwnSem should be false because this is the case where we create the internal interface given a public
+ // interface.
+
+ m_tdModule = COR_GLOBAL_PARENT_TOKEN;
+ m_fOwnStgdb = false;
+ m_pStgdb = pStgdb;
+
+ // remember the owner of the light weight stgdb
+ // AddRef it to ensure the lifetime
+ //
+ m_pUnk = pUnk;
+ m_pUnk->AddRef();
+ return NOERROR;
+} // MDInternalRW::InitWithStgdb
+
+
+//*****************************************************************************
+// Initialize with an existing RO format
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::InitWithRO(
+ MDInternalRO * pRO,
+ int bReadOnly)
+{
+ CLiteWeightStgdbRW * pStgdb = NULL;
+ HRESULT hr = NOERROR;
+ OptionValue optVal = { MDDupAll, MDRefToDefDefault, MDNotifyDefault, MDUpdateFull, MDErrorOutOfOrderDefault, MDThreadSafetyOn };
+
+ pStgdb = new (nothrow) CLiteWeightStgdbRW;
+ IfNullGo(pStgdb);
+
+ m_pSemReadWrite = new (nothrow) UTSemReadWrite;
+ IfNullGo(m_pSemReadWrite);
+ IfFailGo(m_pSemReadWrite->Init());
+ m_fOwnSem = true;
+ INDEBUG(pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
+
+ IfFailGo(pStgdb->m_MiniMd.InitOnRO(&pRO->m_LiteWeightStgdb.m_MiniMd, bReadOnly));
+ IfFailGo(pStgdb->m_MiniMd.SetOption(&optVal));
+ m_tdModule = COR_GLOBAL_PARENT_TOKEN;
+ m_fOwnStgdb = true;
+ pStgdb->m_pvMd=pRO->m_LiteWeightStgdb.m_pvMd;
+ pStgdb->m_cbMd=pRO->m_LiteWeightStgdb.m_cbMd;
+ m_pStgdb = pStgdb;
+
+ErrExit:
+ // clean up upon errors
+ if (FAILED(hr) && (pStgdb != NULL))
+ {
+ delete pStgdb;
+ }
+ return hr;
+} // MDInternalRW::InitWithRO
+
+
+#ifndef DACCESS_COMPILE
+//*****************************************************************************
+// Given a scope, determine whether imported from a typelib.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::TranslateSigWithScope(
+ IMDInternalImport* pAssemImport, // [IN] import assembly scope.
+ const void* pbHashValue, // [IN] hash value for the import assembly.
+ ULONG cbHashValue, // [IN] count of bytes in the hash value.
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit* pAssemEmit, // [IN] assembly emit scope.
+ IMetaDataEmit* emit, // [IN] emit interface
+ CQuickBytes* pqkSigEmit, // [OUT] buffer to hold translated signature
+ ULONG* pcbSig) // [OUT] count of bytes in the translated signature
+{
+ return TranslateSigHelper(
+ this,
+ pAssemImport,
+ pbHashValue,
+ cbHashValue,
+ pbSigBlob,
+ cbSigBlob,
+ pAssemEmit,
+ emit,
+ pqkSigEmit,
+ pcbSig);
+} // MDInternalRW::TranslateSigWithScope
+
+__checkReturn
+HRESULT MDInternalRW::GetTypeDefRefTokenInTypeSpec(// return S_FALSE if enclosing type does not have a token
+ mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look at
+ mdToken *tkEnclosedToken) // [OUT] The enclosed type token
+{
+ return m_pStgdb->m_MiniMd.GetTypeDefRefTokenInTypeSpec(tkTypeSpec, tkEnclosedToken);
+}// MDInternalRW::GetTypeDefRefTokenInTypeSpec
+
+//*****************************************************************************
+// Given a scope, return the number of tokens in a given table
+//*****************************************************************************
+ULONG MDInternalRW::GetCountWithTokenKind( // return hresult
+ DWORD tkKind) // [IN] pass in the kind of token.
+{
+ ULONG ulCount = 0;
+ HRESULT hr = S_OK;
+ LOCKREAD();
+
+ switch (tkKind)
+ {
+ case mdtTypeDef:
+ ulCount = m_pStgdb->m_MiniMd.getCountTypeDefs();
+ // Remove global typedef from the count of typedefs (and handle the case where there is no global typedef)
+ if (ulCount > 0)
+ ulCount--;
+ break;
+ case mdtTypeRef:
+ ulCount = m_pStgdb->m_MiniMd.getCountTypeRefs();
+ break;
+ case mdtMethodDef:
+ ulCount = m_pStgdb->m_MiniMd.getCountMethods();
+ break;
+ case mdtFieldDef:
+ ulCount = m_pStgdb->m_MiniMd.getCountFields();
+ break;
+ case mdtMemberRef:
+ ulCount = m_pStgdb->m_MiniMd.getCountMemberRefs();
+ break;
+ case mdtInterfaceImpl:
+ ulCount = m_pStgdb->m_MiniMd.getCountInterfaceImpls();
+ break;
+ case mdtParamDef:
+ ulCount = m_pStgdb->m_MiniMd.getCountParams();
+ break;
+ case mdtFile:
+ ulCount = m_pStgdb->m_MiniMd.getCountFiles();
+ break;
+ case mdtAssemblyRef:
+ ulCount = m_pStgdb->m_MiniMd.getCountAssemblyRefs();
+ break;
+ case mdtAssembly:
+ ulCount = m_pStgdb->m_MiniMd.getCountAssemblys();
+ break;
+ case mdtCustomAttribute:
+ ulCount = m_pStgdb->m_MiniMd.getCountCustomAttributes();
+ break;
+ case mdtModule:
+ ulCount = m_pStgdb->m_MiniMd.getCountModules();
+ break;
+ case mdtPermission:
+ ulCount = m_pStgdb->m_MiniMd.getCountDeclSecuritys();
+ break;
+ case mdtSignature:
+ ulCount = m_pStgdb->m_MiniMd.getCountStandAloneSigs();
+ break;
+ case mdtEvent:
+ ulCount = m_pStgdb->m_MiniMd.getCountEvents();
+ break;
+ case mdtProperty:
+ ulCount = m_pStgdb->m_MiniMd.getCountPropertys();
+ break;
+ case mdtModuleRef:
+ ulCount = m_pStgdb->m_MiniMd.getCountModuleRefs();
+ break;
+ case mdtTypeSpec:
+ ulCount = m_pStgdb->m_MiniMd.getCountTypeSpecs();
+ break;
+ case mdtExportedType:
+ ulCount = m_pStgdb->m_MiniMd.getCountExportedTypes();
+ break;
+ case mdtManifestResource:
+ ulCount = m_pStgdb->m_MiniMd.getCountManifestResources();
+ break;
+ case mdtGenericParam:
+ ulCount = m_pStgdb->m_MiniMd.getCountGenericParams();
+ break;
+ case mdtGenericParamConstraint:
+ ulCount = m_pStgdb->m_MiniMd.getCountGenericParamConstraints();
+ break;
+ case mdtMethodSpec:
+ ulCount = m_pStgdb->m_MiniMd.getCountMethodSpecs();
+ break;
+ default:
+#ifdef _DEBUG
+ if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1))
+ _ASSERTE(!"Invalid Blob Offset");
+#endif
+ ulCount = 0;
+ break;
+ }
+
+ErrExit:
+
+ return ulCount;
+} // MDInternalRW::GetCountWithTokenKind
+#endif //!DACCESS_COMPILE
+
+
+//*******************************************************************************
+// Enumerator helpers
+//*******************************************************************************
+
+
+//*****************************************************************************
+// enumerator init for typedef
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::EnumTypeDefInit( // return hresult
+ HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data
+{
+ HRESULT hr = NOERROR;
+ LOCKREAD();
+
+ _ASSERTE(phEnum);
+
+ memset(phEnum, 0, sizeof(HENUMInternal));
+ phEnum->m_tkKind = mdtTypeDef;
+
+ if ( m_pStgdb->m_MiniMd.HasDelete() )
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+
+ phEnum->m_tkKind = mdtTypeDef;
+ for (ULONG index = 2; index <= m_pStgdb->m_MiniMd.getCountTypeDefs(); index ++ )
+ {
+ TypeDefRec *pTypeDefRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(index, &pTypeDefRec));
+ LPCSTR szTypeDefName;
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfTypeDef(pTypeDefRec, &szTypeDefName));
+ if (IsDeletedName(szTypeDefName))
+ {
+ continue;
+ }
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(index, mdtTypeDef) ) );
+ }
+ }
+ else
+ {
+ phEnum->m_EnumType = MDSimpleEnum;
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountTypeDefs();
+
+ // Skip over the global model typedef
+ //
+ // phEnum->u.m_ulCur : the current rid that is not yet enumerated
+ // phEnum->u.m_ulStart : the first rid that will be returned by enumerator
+ // phEnum->u.m_ulEnd : the last rid that will be returned by enumerator
+ phEnum->u.m_ulStart = phEnum->u.m_ulCur = 2;
+ phEnum->u.m_ulEnd = phEnum->m_ulCount + 1;
+ if (phEnum->m_ulCount > 0)
+ phEnum->m_ulCount --;
+ }
+ErrExit:
+
+ return hr;
+} // MDInternalRW::EnumTypeDefInit
+
+
+//*****************************************************************************
+// get the number of typedef in a scope
+//*****************************************************************************
+ULONG MDInternalRW::EnumTypeDefGetCount(
+ HENUMInternal *phEnum) // [IN] the enumerator to retrieve information
+{
+ _ASSERTE(phEnum->m_tkKind == mdtTypeDef);
+ return phEnum->m_ulCount;
+} // MDInternalRW::EnumTypeDefGetCount
+
+
+//*****************************************************************************
+// enumerator for typedef
+//*****************************************************************************
+bool MDInternalRW::EnumTypeDefNext( // return hresult
+ HENUMInternal *phEnum, // [IN] input enum
+ mdTypeDef *ptd) // [OUT] return token
+{
+ return EnumNext(
+ phEnum,
+ ptd);
+} // MDInternalRW::EnumTypeDefNext
+
+
+//*****************************************
+// Reset the enumerator to the beginning.
+//*****************************************
+void MDInternalRW::EnumTypeDefReset(
+ HENUMInternal *phEnum) // [IN] the enumerator to be reset
+{
+ EnumReset(phEnum);
+} // MDInternalRW::EnumTypeDefReset
+
+
+//*****************************************
+// Close the enumerator. Only for read/write mode that we need to close the cursor.
+// Hopefully with readonly mode, it will be a no-op
+//*****************************************
+void MDInternalRW::EnumTypeDefClose(
+ HENUMInternal *phEnum) // [IN] the enumerator to be closed
+{
+ EnumClose(phEnum);
+} // MDInternalRW::EnumTypeDefClose
+
+#ifndef DACCESS_COMPILE
+//*****************************************************************************
+// enumerator init for MethodImpl
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::EnumMethodImplInit( // return hresult
+ mdTypeDef td, // [IN] TypeDef over which to scope the enumeration.
+ HENUMInternal *phEnumBody, // [OUT] buffer to fill for enumerator data for MethodBody tokens.
+ HENUMInternal *phEnumDecl) // [OUT] buffer to fill for enumerator data for MethodDecl tokens.
+{
+ HRESULT hr = NOERROR;
+ int ridCur;
+ mdToken tkMethodBody;
+ mdToken tkMethodDecl;
+ MethodImplRec *pRec;
+ HENUMInternal hEnum;
+
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && !IsNilToken(td));
+ _ASSERTE(phEnumBody && phEnumDecl);
+
+ memset(phEnumBody, 0, sizeof(HENUMInternal));
+ memset(phEnumDecl, 0, sizeof(HENUMInternal));
+ memset(&hEnum, 0, sizeof(HENUMInternal));
+
+ HENUMInternal::InitDynamicArrayEnum(phEnumBody);
+ HENUMInternal::InitDynamicArrayEnum(phEnumDecl);
+
+ phEnumBody->m_tkKind = (TBL_MethodImpl << 24);
+ phEnumDecl->m_tkKind = (TBL_MethodImpl << 24);
+
+ // Get the range of rids.
+ IfFailGo( m_pStgdb->m_MiniMd.FindMethodImplHelper(td, &hEnum) );
+
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur))
+ {
+ // Get the MethodBody and MethodDeclaration tokens for the current
+ // MethodImpl record.
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodImplRecord(ridCur, &pRec));
+ tkMethodBody = m_pStgdb->m_MiniMd.getMethodBodyOfMethodImpl(pRec);
+ tkMethodDecl = m_pStgdb->m_MiniMd.getMethodDeclarationOfMethodImpl(pRec);
+
+ // Add the Method body/declaration pairs to the Enum
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnumBody, tkMethodBody ) );
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnumDecl, tkMethodDecl ) );
+ }
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+ return hr;
+} // MDInternalRW::EnumMethodImplInit
+
+//*****************************************************************************
+// get the number of MethodImpls in a scope
+//*****************************************************************************
+ULONG MDInternalRW::EnumMethodImplGetCount(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+{
+ _ASSERTE((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl &&
+ (phEnumDecl->m_tkKind >> 24) == TBL_MethodImpl);
+ _ASSERTE(phEnumBody->m_EnumType == MDDynamicArrayEnum &&
+ phEnumDecl->m_EnumType == MDDynamicArrayEnum);
+ _ASSERTE(phEnumBody->m_ulCount == phEnumDecl->m_ulCount);
+
+ return phEnumBody->m_ulCount;
+} // MDInternalRW::EnumMethodImplGetCount
+
+
+//*****************************************************************************
+// enumerator for MethodImpl.
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::EnumMethodImplNext( // return hresult
+ HENUMInternal *phEnumBody, // [IN] input enum for MethodBody
+ HENUMInternal *phEnumDecl, // [IN] input enum for MethodDecl
+ mdToken *ptkBody, // [OUT] return token for MethodBody
+ mdToken *ptkDecl) // [OUT] return token for MethodDecl
+{
+ _ASSERTE((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl &&
+ (phEnumDecl->m_tkKind >> 24) == TBL_MethodImpl);
+ _ASSERTE(phEnumBody->m_EnumType == MDDynamicArrayEnum &&
+ phEnumDecl->m_EnumType == MDDynamicArrayEnum);
+ _ASSERTE(phEnumBody->m_ulCount == phEnumDecl->m_ulCount);
+ _ASSERTE(ptkBody && ptkDecl);
+
+ EnumNext(phEnumBody, ptkBody);
+ return EnumNext(phEnumDecl, ptkDecl) ? S_OK : S_FALSE;
+} // MDInternalRW::EnumMethodImplNext
+
+//*****************************************
+// Reset the enumerator to the beginning.
+//*****************************************
+void MDInternalRW::EnumMethodImplReset(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+{
+ _ASSERTE((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl &&
+ (phEnumDecl->m_tkKind >> 24) == TBL_MethodImpl);
+ _ASSERTE(phEnumBody->m_EnumType == MDDynamicArrayEnum &&
+ phEnumDecl->m_EnumType == MDDynamicArrayEnum);
+ _ASSERTE(phEnumBody->m_ulCount == phEnumDecl->m_ulCount);
+
+ EnumReset(phEnumBody);
+ EnumReset(phEnumDecl);
+} // MDInternalRW::EnumMethodImplReset
+
+
+//*****************************************
+// Close the enumerator.
+//*****************************************
+void MDInternalRW::EnumMethodImplClose(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+{
+ _ASSERTE((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl &&
+ (phEnumDecl->m_tkKind >> 24) == TBL_MethodImpl);
+ _ASSERTE(phEnumBody->m_EnumType == MDDynamicArrayEnum &&
+ phEnumDecl->m_EnumType == MDDynamicArrayEnum);
+ _ASSERTE(phEnumBody->m_ulCount == phEnumDecl->m_ulCount);
+
+ EnumClose(phEnumBody);
+ EnumClose(phEnumDecl);
+} // MDInternalRW::EnumMethodImplClose
+#endif //!DACCESS_COMPILE
+
+//******************************************************************************
+// enumerator for global functions
+//******************************************************************************
+__checkReturn
+HRESULT MDInternalRW::EnumGlobalFunctionsInit( // return hresult
+ HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data
+{
+ return EnumInit(mdtMethodDef, m_tdModule, phEnum);
+} // MDInternalRW::EnumGlobalFunctionsInit
+
+
+//******************************************************************************
+// enumerator for global fields
+//******************************************************************************
+__checkReturn
+HRESULT MDInternalRW::EnumGlobalFieldsInit( // return hresult
+ HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data
+{
+ return EnumInit(mdtFieldDef, m_tdModule, phEnum);
+} // MDInternalRW::EnumGlobalFieldsInit
+
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+//*****************************************
+// Enumerator initializer
+//*****************************************
+__checkReturn
+HRESULT MDInternalRW::EnumInit( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ mdToken tkParent, // [IN] token to scope the search
+ HENUMInternal *phEnum) // [OUT] the enumerator to fill
+{
+ HRESULT hr = S_OK;
+ ULONG ulStart, ulEnd, ulMax;
+ ULONG index;
+ LOCKREAD();
+
+ // Vars for query.
+ _ASSERTE(phEnum);
+ memset(phEnum, 0, sizeof(HENUMInternal));
+
+ // cache the tkKind and the scope
+ phEnum->m_tkKind = TypeFromToken(tkKind);
+
+ TypeDefRec *pRec;
+
+ phEnum->m_EnumType = MDSimpleEnum;
+
+ switch (TypeFromToken(tkKind))
+ {
+ case mdtFieldDef:
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tkParent), &pRec));
+ ulStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(tkParent), &ulEnd));
+ if ( m_pStgdb->m_MiniMd.HasDelete() )
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ FieldRec *pFieldRec;
+ RID fieldRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(index, &fieldRid));
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(index, &pFieldRec));
+ LPCSTR szFieldName;
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfField(pFieldRec, &szFieldName));
+ if (IsFdRTSpecialName(pFieldRec->GetFlags()) && IsDeletedName(szFieldName) )
+ {
+ continue;
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(index, &fieldRid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(fieldRid, mdtFieldDef)));
+ }
+ }
+ else if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Field))
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ RID fieldRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(index, &fieldRid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(fieldRid, mdtFieldDef)));
+ }
+ }
+ else
+ {
+ HENUMInternal::InitSimpleEnum( mdtFieldDef, ulStart, ulEnd, phEnum);
+ }
+ break;
+
+ case mdtMethodDef:
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tkParent), &pRec));
+ ulStart = m_pStgdb->m_MiniMd.getMethodListOfTypeDef(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(tkParent), &ulEnd));
+ if ( m_pStgdb->m_MiniMd.HasDelete() )
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ MethodRec *pMethodRec;
+ RID methodRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRid(index, &methodRid));
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(methodRid, &pMethodRec));
+ LPCSTR szMethodName;
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfMethod(pMethodRec, &szMethodName));
+ if (IsMdRTSpecialName(pMethodRec->GetFlags()) && IsDeletedName(szMethodName))
+ {
+ continue;
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRid(index, &methodRid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(methodRid, mdtMethodDef)));
+ }
+ }
+ else if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Method))
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ RID methodRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRid(index, &methodRid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(methodRid, mdtMethodDef)));
+ }
+ }
+ else
+ {
+ HENUMInternal::InitSimpleEnum( mdtMethodDef, ulStart, ulEnd, phEnum);
+ }
+ break;
+
+ case mdtInterfaceImpl:
+ if (!m_pStgdb->m_MiniMd.IsSorted(TBL_InterfaceImpl) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_InterfaceImpl))
+ {
+ // virtual sort table will be created!
+ //
+ CONVERT_READ_TO_WRITE_LOCK();
+ }
+
+ IfFailGo( m_pStgdb->m_MiniMd.GetInterfaceImplsForTypeDef(RidFromToken(tkParent), &ulStart, &ulEnd) );
+ if ( m_pStgdb->m_MiniMd.IsSorted( TBL_InterfaceImpl ) )
+ {
+ // These are index to InterfaceImpl table directly
+ HENUMInternal::InitSimpleEnum( mdtInterfaceImpl, ulStart, ulEnd, phEnum);
+ }
+ else
+ {
+ // These are index to VirtualSort table. Skip over one level direction.
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(m_pStgdb->m_MiniMd.GetInterfaceImplRid(index), mdtInterfaceImpl) ) );
+ }
+ }
+ break;
+
+ case mdtGenericParam:
+ //@todo: deal with non-sorted case.
+
+ if (TypeFromToken(tkParent) == mdtTypeDef)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getGenericParamsForTypeDef(
+ RidFromToken(tkParent),
+ &phEnum->u.m_ulEnd,
+ &(phEnum->u.m_ulStart)));
+ }
+ else
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getGenericParamsForMethodDef(
+ RidFromToken(tkParent),
+ &phEnum->u.m_ulEnd,
+ &(phEnum->u.m_ulStart)));
+ }
+ break;
+
+ case mdtGenericParamConstraint:
+ if ( !m_pStgdb->m_MiniMd.IsSorted(TBL_GenericParamConstraint) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_GenericParamConstraint))
+ {
+ // virtual sort table will be created!
+ //
+ CONVERT_READ_TO_WRITE_LOCK();
+ }
+
+ IfFailGo( m_pStgdb->m_MiniMd.GetGenericParamConstraintsForToken(RidFromToken(tkParent), &ulStart, &ulEnd) );
+ if ( m_pStgdb->m_MiniMd.IsSorted( TBL_GenericParamConstraint ) )
+ {
+ // These are index to GenericParamConstraint table directly
+ HENUMInternal::InitSimpleEnum( mdtGenericParamConstraint, ulStart, ulEnd, phEnum);
+ }
+ else
+ {
+ // These are index to VirtualSort table. Skip over one level direction.
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(m_pStgdb->m_MiniMd.GetGenericParamConstraintRid(index), mdtGenericParamConstraint) ) );
+ }
+ }
+ break;
+
+ case mdtProperty:
+ RID ridPropertyMap;
+ PropertyMapRec *pPropertyMapRec;
+
+ // get the starting/ending rid of properties of this typedef
+ IfFailGo(m_pStgdb->m_MiniMd.FindPropertyMapFor(RidFromToken(tkParent), &ridPropertyMap));
+ if (!InvalidRid(ridPropertyMap))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec));
+ ulStart = m_pStgdb->m_MiniMd.getPropertyListOfPropertyMap(pPropertyMapRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndPropertyListOfPropertyMap(ridPropertyMap, &ulEnd));
+ ulMax = m_pStgdb->m_MiniMd.getCountPropertys() + 1;
+ if(ulStart == 0) ulStart = 1;
+ if(ulEnd > ulMax) ulEnd = ulMax;
+ if(ulStart > ulEnd) ulStart = ulEnd;
+ if ( m_pStgdb->m_MiniMd.HasDelete() )
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ PropertyRec *pPropertyRec;
+ RID propertyRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRid(index, &propertyRid));
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(
+ propertyRid,
+ &pPropertyRec));
+ LPCSTR szPropertyName;
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfProperty(pPropertyRec, &szPropertyName));
+ if (IsPrRTSpecialName(pPropertyRec->GetPropFlags()) && IsDeletedName(szPropertyName))
+ {
+ continue;
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRid(index, &propertyRid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(propertyRid, mdtProperty)));
+ }
+ }
+ else if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Property))
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ RID propertyRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRid(index, &propertyRid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(propertyRid, mdtProperty)));
+ }
+ }
+ else
+ {
+ HENUMInternal::InitSimpleEnum( mdtProperty, ulStart, ulEnd, phEnum);
+ }
+ }
+ break;
+
+ case mdtEvent:
+ RID ridEventMap;
+ EventMapRec *pEventMapRec;
+
+ // get the starting/ending rid of events of this typedef
+ IfFailGo(m_pStgdb->m_MiniMd.FindEventMapFor(RidFromToken(tkParent), &ridEventMap));
+ if (!InvalidRid(ridEventMap))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventMapRecord(ridEventMap, &pEventMapRec));
+ ulStart = m_pStgdb->m_MiniMd.getEventListOfEventMap(pEventMapRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndEventListOfEventMap(ridEventMap, &ulEnd));
+ ulMax = m_pStgdb->m_MiniMd.getCountEvents() + 1;
+ if(ulStart == 0) ulStart = 1;
+ if(ulEnd > ulMax) ulEnd = ulMax;
+ if(ulStart > ulEnd) ulStart = ulEnd;
+ if ( m_pStgdb->m_MiniMd.HasDelete() )
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ EventRec *pEventRec;
+ RID eventRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRid(index, &eventRid));
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(eventRid, &pEventRec));
+ LPCSTR szEventName;
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfEvent(pEventRec, &szEventName));
+ if (IsEvRTSpecialName(pEventRec->GetEventFlags()) && IsDeletedName(szEventName))
+ {
+ continue;
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRid(index, &eventRid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(eventRid, mdtEvent)));
+ }
+ }
+ else if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Event))
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ RID eventRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRid(index, &eventRid));
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(eventRid, mdtEvent) ) );
+ }
+ }
+ else
+ {
+ HENUMInternal::InitSimpleEnum( mdtEvent, ulStart, ulEnd, phEnum);
+ }
+ }
+ break;
+
+ case mdtParamDef:
+ _ASSERTE(TypeFromToken(tkParent) == mdtMethodDef);
+
+ MethodRec *pMethodRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkParent), &pMethodRec));
+
+ // figure out the start rid and end rid of the parameter list of this methoddef
+ ulStart = m_pStgdb->m_MiniMd.getParamListOfMethod(pMethodRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndParamListOfMethod(RidFromToken(tkParent), &ulEnd));
+ if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Param))
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ RID paramRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetParamRid(index, &paramRid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(paramRid, mdtParamDef)));
+ }
+ }
+ else
+ {
+ HENUMInternal::InitSimpleEnum( mdtParamDef, ulStart, ulEnd, phEnum);
+ }
+ break;
+
+ case mdtCustomAttribute:
+ if (!m_pStgdb->m_MiniMd.IsSorted(TBL_CustomAttribute) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_CustomAttribute))
+ {
+ // CA's map table table will be sorted!
+ //
+ CONVERT_READ_TO_WRITE_LOCK();
+ }
+
+ IfFailGo( m_pStgdb->m_MiniMd.GetCustomAttributeForToken(tkParent, &ulStart, &ulEnd) );
+ if ( m_pStgdb->m_MiniMd.IsSorted( TBL_CustomAttribute ) )
+ {
+ // These are index to CustomAttribute table directly
+ HENUMInternal::InitSimpleEnum( mdtCustomAttribute, ulStart, ulEnd, phEnum);
+ }
+ else
+ {
+ // These are index to VirtualSort table. Skip over one level direction.
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(m_pStgdb->m_MiniMd.GetCustomAttributeRid(index), mdtCustomAttribute) ) );
+ }
+ }
+ break;
+ case mdtAssemblyRef:
+ _ASSERTE(IsNilToken(tkParent));
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountAssemblyRefs() + 1;
+ break;
+ case mdtFile:
+ _ASSERTE(IsNilToken(tkParent));
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountFiles() + 1;
+ break;
+ case mdtExportedType:
+ _ASSERTE(IsNilToken(tkParent));
+ if ( m_pStgdb->m_MiniMd.HasDelete() )
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+
+ phEnum->m_tkKind = mdtExportedType;
+ for (ULONG typeindex = 1; typeindex <= m_pStgdb->m_MiniMd.getCountExportedTypes(); typeindex ++ )
+ {
+ ExportedTypeRec *pExportedTypeRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(typeindex, &pExportedTypeRec));
+ LPCSTR szTypeName;
+ IfFailGo(m_pStgdb->m_MiniMd.getTypeNameOfExportedType(pExportedTypeRec, &szTypeName));
+ if (IsDeletedName(szTypeName))
+ {
+ continue;
+ }
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(typeindex, mdtExportedType) ) );
+ }
+ }
+ else
+ {
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountExportedTypes() + 1;
+ }
+ break;
+ case mdtManifestResource:
+ _ASSERTE(IsNilToken(tkParent));
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountManifestResources() + 1;
+ break;
+ case mdtModuleRef:
+ _ASSERTE(IsNilToken(tkParent));
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountModuleRefs() + 1;
+ break;
+ default:
+ _ASSERTE(!"ENUM INIT not implemented for the uncompressed format!");
+ IfFailGo(E_NOTIMPL);
+ break;
+ }
+
+ // If the count is negative, the metadata is corrupted somehow.
+ if (phEnum->u.m_ulEnd < phEnum->u.m_ulStart)
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+
+ phEnum->m_ulCount = phEnum->u.m_ulEnd - phEnum->u.m_ulStart;
+ phEnum->u.m_ulCur = phEnum->u.m_ulStart;
+ErrExit:
+ // we are done
+
+ return hr;
+} // MDInternalRW::EnumInit
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************
+// Enumerator initializer
+//*****************************************
+__checkReturn
+HRESULT MDInternalRW::EnumAllInit( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ HENUMInternal *phEnum) // [OUT] the enumerator to fill
+{
+ HRESULT hr = S_OK;
+ LOCKREAD();
+
+ // Vars for query.
+ _ASSERTE(phEnum);
+ memset(phEnum, 0, sizeof(HENUMInternal));
+
+ // cache the tkKind and the scope
+ phEnum->m_tkKind = TypeFromToken(tkKind);
+ phEnum->m_EnumType = MDSimpleEnum;
+
+ switch (TypeFromToken(tkKind))
+ {
+ case mdtTypeRef:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountTypeRefs();
+ break;
+
+ case mdtMemberRef:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountMemberRefs();
+ break;
+
+ case mdtSignature:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountStandAloneSigs();
+ break;
+
+ case mdtMethodDef:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountMethods();
+ break;
+
+ case mdtMethodSpec:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountMethodSpecs();
+ break;
+
+ case mdtFieldDef:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountFields();
+ break;
+
+ case mdtTypeSpec:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountTypeSpecs();
+ break;
+
+ case mdtAssemblyRef:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountAssemblyRefs();
+ break;
+
+ case mdtModuleRef:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountModuleRefs();
+ break;
+
+ case mdtTypeDef:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountTypeDefs();
+ break;
+
+ case mdtFile:
+ phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountFiles();
+ break;
+
+ default:
+ _ASSERTE(!"Bad token kind!");
+ break;
+ }
+ phEnum->u.m_ulStart = phEnum->u.m_ulCur = 1;
+ phEnum->u.m_ulEnd = phEnum->m_ulCount + 1;
+
+ErrExit:
+ // we are done
+
+ return hr;
+} // MDInternalRW::EnumAllInit
+
+
+//*****************************************
+// get the count
+//*****************************************
+ULONG MDInternalRW::EnumGetCount(
+ HENUMInternal *phEnum) // [IN] the enumerator to retrieve information
+{
+ _ASSERTE(phEnum);
+ return phEnum->m_ulCount;
+} // MDInternalRW::EnumGetCount
+
+//*****************************************
+// Get next value contained in the enumerator
+//*****************************************
+bool MDInternalRW::EnumNext(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk) // [OUT] token to scope the search
+{
+ _ASSERTE(phEnum && ptk);
+ if (phEnum->u.m_ulCur >= phEnum->u.m_ulEnd)
+ return false;
+
+ if ( phEnum->m_EnumType == MDSimpleEnum )
+ {
+ *ptk = phEnum->u.m_ulCur | phEnum->m_tkKind;
+ phEnum->u.m_ulCur++;
+ }
+ else
+ {
+ TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor);
+
+ _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum );
+ *ptk = *( pdalist->Get(phEnum->u.m_ulCur++) );
+ }
+ return true;
+} // MDInternalRW::EnumNext
+
+
+//*****************************************
+// Reset the enumerator to the beginning.
+//*****************************************
+void MDInternalRW::EnumReset(
+ HENUMInternal *phEnum) // [IN] the enumerator to be reset
+{
+ _ASSERTE(phEnum);
+ _ASSERTE( phEnum->m_EnumType == MDSimpleEnum || phEnum->m_EnumType == MDDynamicArrayEnum);
+
+ phEnum->u.m_ulCur = phEnum->u.m_ulStart;
+} // MDInternalRW::EnumReset
+
+
+//*****************************************
+// Close the enumerator. Only for read/write mode that we need to close the cursor.
+// Hopefully with readonly mode, it will be a no-op
+//*****************************************
+void MDInternalRW::EnumClose(
+ HENUMInternal *phEnum) // [IN] the enumerator to be closed
+{
+ _ASSERTE( phEnum->m_EnumType == MDSimpleEnum ||
+ phEnum->m_EnumType == MDDynamicArrayEnum ||
+ phEnum->m_EnumType == MDCustomEnum );
+ if (phEnum->m_EnumType == MDDynamicArrayEnum)
+ HENUMInternal::ClearEnum(phEnum);
+} // MDInternalRW::EnumClose
+
+
+#ifndef DACCESS_COMPILE
+//---------------------------------------------------------------------------------------
+//
+// Initialize enumerator of PermissionSets.
+//
+// Return Value:
+// CLDB_E_RECORD_NOTFOUND ... If record not found.
+// S_OK and empty enumeration ... If tkParent is nil token and Action is dclActionNil.
+//
+__checkReturn
+HRESULT
+MDInternalRW::EnumPermissionSetsInit(
+ mdToken tkParent, // [IN] Token to scope the search.
+ CorDeclSecurity Action, // [IN] Action to scope the search.
+ HENUMInternal *phEnum) // [OUT] Enumerator to fill.
+{
+ HRESULT hr = S_OK;
+ DeclSecurityRec *pDecl;
+ RID ridCur;
+ RID ridEnd;
+
+ LOCKREAD();
+
+ _ASSERTE(phEnum != NULL);
+
+ phEnum->m_EnumType = MDSimpleEnum;
+
+ if (!m_pStgdb->m_MiniMd.IsSorted(TBL_DeclSecurity) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_DeclSecurity))
+ {
+ // DeclSecurity lookup table might be created!
+ CONVERT_READ_TO_WRITE_LOCK();
+ }
+
+ // This call requires write-lock if the table is not sorted and doesn't have VirtualSort:
+ IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityForToken(tkParent, &ridCur, &ridEnd));
+ if (m_pStgdb->m_MiniMd.IsSorted(TBL_DeclSecurity))
+ {
+ // These are index to DeclSecurity table directly
+ if (Action != dclActionNil)
+ {
+ for (; ridCur < ridEnd; ridCur++)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(ridCur, &pDecl));
+ if (Action == m_pStgdb->m_MiniMd.getActionOfDeclSecurity(pDecl))
+ {
+ // found a match
+ HENUMInternal::InitSimpleEnum(mdtPermission, ridCur, ridCur + 1, phEnum);
+ goto ErrExit;
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ HENUMInternal::InitSimpleEnum(mdtPermission, ridCur, ridEnd, phEnum);
+ }
+ }
+ else
+ {
+ // These are index to VirtualSort table. Skip over one level direction.
+ if (Action != dclActionNil)
+ {
+ RID ridActual;
+
+ for (; ridCur < ridEnd; ridCur++)
+ {
+ ridActual = m_pStgdb->m_MiniMd.GetDeclSecurityRid(ridCur);
+ IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(ridActual, &pDecl));
+ if (Action == m_pStgdb->m_MiniMd.getActionOfDeclSecurity(pDecl))
+ {
+ // found a match
+ HENUMInternal::InitSimpleEnum(mdtPermission, ridActual, ridActual + 1, phEnum);
+ goto ErrExit;
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (; ridCur < ridEnd; ridCur++)
+ {
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(m_pStgdb->m_MiniMd.GetDeclSecurityRid(ridCur), mdtPermission)));
+ }
+ }
+ }
+
+ErrExit:
+ return hr;
+} // MDInternalRW::EnumPermissionSetsInit
+#endif //!DACCESS_COMPILE
+
+//*****************************************
+// Enumerator initializer for CustomAttributes
+//*****************************************
+__checkReturn
+HRESULT MDInternalRW::EnumCustomAttributeByNameInit(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum) // [OUT] the enumerator to fill
+{
+ return m_pStgdb->m_MiniMd.CommonEnumCustomAttributeByName(tkParent, szName, false, phEnum);
+} // MDInternalRW::EnumCustomAttributeByNameInit
+
+//*****************************************
+// Enumerator for CustomAttributes which doesn't
+// allocate any memory
+//*****************************************
+__checkReturn
+HRESULT MDInternalRW::SafeAndSlowEnumCustomAttributeByNameInit(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum) // [OUT] The enumerator
+{
+ _ASSERTE(phEnum);
+
+ HRESULT hr;
+ ULONG ridStart, ridEnd; // Loop start and endpoints.
+
+ // Get the list of custom values for the parent object.
+ if (m_pStgdb->m_MiniMd.IsSorted(TBL_CustomAttribute))
+ {
+ IfFailRet(m_pStgdb->m_MiniMd.getCustomAttributeForToken(tkParent, &ridEnd, &ridStart));
+ // If found none, done.
+ if (ridStart == 0)
+ goto NoMatch;
+ }
+ else
+ {
+ // linear scan of entire table.
+ ridEnd = m_pStgdb->m_MiniMd.getCountCustomAttributes() + 1;
+ if (ridEnd == 1)
+ goto NoMatch;
+
+ ridStart = 1;
+ }
+ phEnum->m_EnumType = MDCustomEnum;
+ phEnum->m_tkKind = mdtCustomAttribute;
+ phEnum->u.m_ulStart = ridStart;
+ phEnum->u.m_ulEnd = ridEnd;
+ phEnum->u.m_ulCur = ridStart;
+
+ return S_OK;
+
+NoMatch:
+ return S_FALSE;
+} // MDInternalRW::SafeAndSlowEnumCustomAttributeByNameInit
+
+__checkReturn
+HRESULT MDInternalRW::SafeAndSlowEnumCustomAttributeByNameNext(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum, // [IN] The enumerator
+ mdCustomAttribute *mdAttribute) // [OUT] The custom attribute that was found
+{
+ _ASSERTE(phEnum);
+ _ASSERTE(phEnum->m_EnumType == MDCustomEnum);
+ _ASSERTE(phEnum->m_tkKind == mdtCustomAttribute);
+
+ // Look for one with the given name.
+ for (; phEnum->u.m_ulCur < phEnum->u.m_ulEnd; ++phEnum->u.m_ulCur)
+ {
+ if (S_OK == m_pStgdb->m_MiniMd.CompareCustomAttribute( tkParent, szName, phEnum->u.m_ulCur))
+ {
+ // If here, found a match.
+ *mdAttribute = TokenFromRid(phEnum->u.m_ulCur, mdtCustomAttribute);
+ phEnum->u.m_ulCur++;
+ return S_OK;
+ }
+ }
+ // No match...
+ return S_FALSE;
+}// MDInternalRW::SafeAndSlowEnumCustomAttributeByNameNext
+
+//*****************************************
+// Nagivator helper to navigate back to the parent token given a token.
+// For example, given a memberdef token, it will return the containing typedef.
+//
+// the mapping is as following:
+// ---given child type---------parent type
+// mdMethodDef mdTypeDef
+// mdFieldDef mdTypeDef
+// mdInterfaceImpl mdTypeDef
+// mdParam mdMethodDef
+// mdProperty mdTypeDef
+// mdEvent mdTypeDef
+//
+//*****************************************
+__checkReturn
+HRESULT MDInternalRW::GetParentToken(
+ mdToken tkChild, // [IN] given child token
+ mdToken *ptkParent) // [OUT] returning parent
+{
+ HRESULT hr = NOERROR;
+ LOCKREAD();
+
+ _ASSERTE(ptkParent);
+
+ switch (TypeFromToken(tkChild))
+ {
+ case mdtTypeDef:
+ {
+ RID rid;
+ if (!m_pStgdb->m_MiniMd.IsSorted(TBL_NestedClass) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_NestedClass))
+ {
+ // NestedClass table is not sorted.
+ CONVERT_READ_TO_WRITE_LOCK();
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.FindNestedClassFor(RidFromToken(tkChild), &rid));
+
+ if (InvalidRid(rid))
+ {
+ // If not found, the *ptkParent has to be left unchanged! (callers depend on that)
+ hr = S_OK;
+ }
+ else
+ {
+ NestedClassRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetNestedClassRecord(rid, &pRecord));
+ *ptkParent = m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pRecord);
+ }
+ break;
+ }
+ case mdtMethodDef:
+ IfFailGo(m_pStgdb->m_MiniMd.FindParentOfMethodHelper(RidFromToken(tkChild), ptkParent));
+ RidToToken(*ptkParent, mdtTypeDef);
+ break;
+ case mdtMethodSpec:
+ {
+ MethodSpecRec *pRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodSpecRecord(RidFromToken(tkChild), &pRec));
+ *ptkParent = m_pStgdb->m_MiniMd.getMethodOfMethodSpec(pRec);
+ }
+ break;
+ case mdtFieldDef:
+ IfFailGo(m_pStgdb->m_MiniMd.FindParentOfFieldHelper(RidFromToken(tkChild), ptkParent));
+ RidToToken(*ptkParent, mdtTypeDef);
+ break;
+ case mdtParamDef:
+ IfFailGo(m_pStgdb->m_MiniMd.FindParentOfParamHelper(RidFromToken(tkChild), ptkParent));
+ RidToToken(*ptkParent, mdtMethodDef);
+ break;
+ case mdtMemberRef:
+ {
+ MemberRefRec *pRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(tkChild), &pRec));
+ *ptkParent = m_pStgdb->m_MiniMd.getClassOfMemberRef(pRec);
+ break;
+ }
+ case mdtCustomAttribute:
+ {
+ CustomAttributeRec *pRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(tkChild), &pRec));
+ *ptkParent = m_pStgdb->m_MiniMd.getParentOfCustomAttribute(pRec);
+ break;
+ }
+ case mdtEvent:
+ IfFailGo(m_pStgdb->m_MiniMd.FindParentOfEventHelper(tkChild, ptkParent));
+ break;
+ case mdtProperty:
+ IfFailGo(m_pStgdb->m_MiniMd.FindParentOfPropertyHelper(tkChild, ptkParent));
+ break;
+ default:
+ _ASSERTE(!"NYI: for compressed format!");
+ break;
+ }
+ErrExit:
+ return hr;
+} // MDInternalRW::GetParentToken
+
+//*****************************************************************************
+// Get information about a CustomAttribute.
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetCustomAttributeProps( // S_OK or error.
+ mdCustomAttribute at, // The attribute.
+ mdToken *pTkType) // Put attribute type here.
+{
+ HRESULT hr;
+ // Getting the custom value prop with a token, no need to lock!
+
+ _ASSERTE(TypeFromToken(at) == mdtCustomAttribute);
+
+ // Do a linear search on compressed version as we do not want to
+ // depend on ICR.
+ //
+ CustomAttributeRec *pCustomAttributeRec;
+
+ IfFailRet(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(at), &pCustomAttributeRec));
+ *pTkType = m_pStgdb->m_MiniMd.getTypeOfCustomAttribute(pCustomAttributeRec);
+ return S_OK;
+} // MDInternalRW::GetCustomAttributeProps
+
+
+//*****************************************************************************
+// return custom value
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetCustomAttributeAsBlob(
+ mdCustomAttribute cv, // [IN] given custom attribute token
+ void const **ppBlob, // [OUT] return the pointer to internal blob
+ ULONG *pcbSize) // [OUT] return the size of the blob
+{
+ // Getting the custom value prop with a token, no need to lock!
+ HRESULT hr;
+ _ASSERTE(ppBlob && pcbSize && TypeFromToken(cv) == mdtCustomAttribute);
+
+ CustomAttributeRec *pCustomAttributeRec;
+
+ IfFailRet(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(cv), &pCustomAttributeRec));
+
+ IfFailRet(m_pStgdb->m_MiniMd.getValueOfCustomAttribute(pCustomAttributeRec, reinterpret_cast<const BYTE **>(ppBlob), pcbSize));
+ return S_OK;
+} // MDInternalRW::GetCustomAttributeAsBlob
+
+//*****************************************************************************
+// Helper function to lookup and retrieve a CustomAttribute.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetCustomAttributeByName( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ __deref_out_bcount(*pcbData) const void **ppData, // [OUT] Put pointer to data here.
+ __out ULONG *pcbData) // [OUT] Put size of data here.
+{
+ HRESULT hr = S_OK;
+ LOCKREADIFFAILRET();
+ return m_pStgdb->m_MiniMd.CommonGetCustomAttributeByName(tkObj, szName, ppData, pcbData);
+} // MDInternalRW::GetCustomAttributeByName
+
+//*****************************************************************************
+// return the name of a custom attribute
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetNameOfCustomAttribute( // S_OK or error.
+ mdCustomAttribute mdAttribute, // [IN] The Custom Attribute
+ LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute.
+ LPCUTF8 *pszName) // [OUT] Name of Custom Attribute.
+{
+ HRESULT hr = S_OK;
+ LOCKREADIFFAILRET();
+ hr = m_pStgdb->m_MiniMd.CommonGetNameOfCustomAttribute(RidFromToken(mdAttribute), pszNamespace, pszName);
+ return (hr == S_FALSE) ? E_FAIL : hr;
+} // MDInternalRW::GetNameOfCustomAttribute
+
+//*****************************************************************************
+// return scope properties
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetScopeProps(
+ LPCSTR *pszName, // [OUT] scope name
+ GUID *pmvid) // [OUT] version id
+{
+ HRESULT hr = S_OK;
+ LOCKREAD();
+
+ ModuleRec *pModuleRec;
+
+ // there is only one module record
+ IfFailGo(m_pStgdb->m_MiniMd.GetModuleRecord(1, &pModuleRec));
+
+ if (pmvid != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getMvidOfModule(pModuleRec, pmvid));
+ }
+
+ if (pszName != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfModule(pModuleRec, pszName));
+ }
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetScopeProps
+
+//*****************************************************************************
+// This function gets the "built for" version of a metadata scope.
+// NOTE: if the scope has never been saved, it will not have a built-for
+// version, and an empty string will be returned.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetVersionString( // S_OK or error.
+ LPCSTR *pVer) // [OUT] Put version string here.
+{
+ HRESULT hr = NOERROR;
+
+ if (m_pStgdb->m_pvMd != NULL)
+ {
+ // For convenience, get a pointer to the version string.
+ // @todo: get from alternate locations when there is no STOREAGESIGNATURE.
+ *pVer = reinterpret_cast<const char*>(reinterpret_cast<const STORAGESIGNATURE*>(m_pStgdb->m_pvMd)->pVersion);
+ }
+ else
+ { // No string.
+ *pVer = NULL;
+ }
+
+ return hr;
+} // MDInternalRW::GetVersionString
+
+//*****************************************************************************
+// Find a given member in a TypeDef (typically a class).
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::FindMethodDef(// S_OK or error.
+ mdTypeDef classdef, // The owning class of the member.
+ LPCSTR szName, // Name of the member in utf8.
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmethoddef) // Put MemberDef token here.
+{
+ HRESULT hr = S_OK;
+ LOCKREAD();
+
+ _ASSERTE(szName && pmethoddef);
+
+ IfFailGo(ImportHelper::FindMethod(&(m_pStgdb->m_MiniMd),
+ classdef,
+ szName,
+ pvSigBlob,
+ cbSigBlob,
+ pmethoddef));
+
+ErrExit:
+ return hr;
+}
+
+//*****************************************************************************
+// Find a given member in a TypeDef (typically a class).
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::FindMethodDefUsingCompare(// S_OK or error.
+ mdTypeDef classdef, // The owning class of the member.
+ LPCSTR szName, // Name of the member in utf8.
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ PSIGCOMPARE pSignatureCompare, // [IN] Routine to compare signatures
+ void* pSignatureArgs, // [IN] Additional info to supply the compare function
+ mdMethodDef *pmethoddef) // Put MemberDef token here.
+{
+ HRESULT hr = S_OK;
+ LOCKREAD();
+
+ _ASSERTE(szName && pmethoddef);
+
+ IfFailGo(ImportHelper::FindMethod(&(m_pStgdb->m_MiniMd),
+ classdef,
+ szName,
+ pvSigBlob,
+ cbSigBlob,
+ pmethoddef,
+ 0,
+ pSignatureCompare,
+ pSignatureArgs));
+
+ErrExit:
+ return hr;
+}
+
+//*****************************************************************************
+// Find a given param of a Method.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::FindParamOfMethod(// S_OK or error.
+ mdMethodDef md, // [IN] The owning method of the param.
+ ULONG iSeq, // [IN] The sequence # of the param.
+ mdParamDef *pparamdef) // [OUT] Put ParamDef token here.
+{
+ ParamRec *pParamRec;
+ RID ridStart, ridEnd;
+ HRESULT hr = NOERROR;
+ MethodRec *pMethodRec = NULL;
+
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(md) == mdtMethodDef && pparamdef);
+
+ // get the methoddef record
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec));
+
+ // figure out the start rid and end rid of the parameter list of this methoddef
+ ridStart = m_pStgdb->m_MiniMd.getParamListOfMethod(pMethodRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndParamListOfMethod(RidFromToken(md), &ridEnd));
+
+ // loop through each param
+ //
+ for (; ridStart < ridEnd; ridStart++)
+ {
+ RID paramRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetParamRid(ridStart, &paramRid));
+ IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(paramRid, &pParamRec));
+ if (iSeq == m_pStgdb->m_MiniMd.getSequenceOfParam( pParamRec) )
+ {
+ // parameter has the sequence number matches what we are looking for
+ *pparamdef = TokenFromRid(paramRid, mdtParamDef );
+ goto ErrExit;
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+
+ return hr;
+} // MDInternalRW::FindParamOfMethod
+
+
+
+//*****************************************************************************
+// return a pointer which points to meta data's internal string
+// return the the type name in utf8
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetNameOfTypeDef( // return hresult
+ mdTypeDef classdef, // given typedef
+ LPCSTR* pszname, // pointer to an internal UTF8 string
+ LPCSTR* psznamespace) // pointer to the namespace.
+{
+ // No need to lock this method.
+ HRESULT hr;
+
+ if (pszname != NULL)
+ {
+ *pszname = NULL;
+ }
+ if (psznamespace != NULL)
+ {
+ *psznamespace = NULL;
+ }
+
+ if (TypeFromToken(classdef) == mdtTypeDef)
+ {
+ TypeDefRec *pTypeDefRec;
+ IfFailRet(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(classdef), &pTypeDefRec));
+
+ if (pszname != NULL)
+ {
+ IfFailRet(m_pStgdb->m_MiniMd.getNameOfTypeDef(pTypeDefRec, pszname));
+ }
+
+ if (psznamespace != NULL)
+ {
+ IfFailRet(m_pStgdb->m_MiniMd.getNamespaceOfTypeDef(pTypeDefRec, psznamespace));
+ }
+ return S_OK;
+ }
+
+ _ASSERTE(!"Invalid argument(s) of GetNameOfTypeDef");
+ return CLDB_E_INTERNALERROR;
+} // MDInternalRW::GetNameOfTypeDef
+
+//*****************************************************************************
+// return pDual indicating if the given TypeDef is marked as a Dual interface
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetIsDualOfTypeDef(// return hresult
+ mdTypeDef classdef, // given classdef
+ ULONG *pDual) // [OUT] return dual flag here.
+{
+ ULONG iFace=0; // Iface type.
+ HRESULT hr; // A result.
+
+ // no need to lock at this level
+
+ hr = GetIfaceTypeOfTypeDef(classdef, &iFace);
+ if (hr == S_OK)
+ *pDual = (iFace == ifDual);
+ else
+ *pDual = 1;
+
+ return hr;
+} // MDInternalRW::GetIsDualOfTypeDef
+
+__checkReturn
+HRESULT MDInternalRW::GetIfaceTypeOfTypeDef(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pIface) // [OUT] 0=dual, 1=vtable, 2=dispinterface
+{
+ HRESULT hr; // A result.
+ const BYTE *pVal; // The custom value.
+ ULONG cbVal; // Size of the custom value.
+ ULONG ItfType = DEFAULT_COM_INTERFACE_TYPE; // Set the interface type to the default.
+
+ // all of the public functions that it calls have proper locked
+
+ // If the value is not present, the class is assumed dual.
+ hr = GetCustomAttributeByName(classdef, INTEROP_INTERFACETYPE_TYPE, (const void**)&pVal, &cbVal);
+ if (hr == S_OK)
+ {
+ _ASSERTE("The ComInterfaceType custom attribute is invalid" && cbVal);
+ _ASSERTE("ComInterfaceType custom attribute does not have the right format" && (*pVal == 0x01) && (*(pVal + 1) == 0x00));
+ ItfType = *(pVal + 2);
+ if (ItfType >= ifLast)
+ ItfType = DEFAULT_COM_INTERFACE_TYPE;
+ }
+
+ // Set the return value.
+ *pIface = ItfType;
+
+ return hr;
+} // MDInternalRW::GetIfaceTypeOfTypeDef
+
+//*****************************************************************************
+// Given a methoddef, return a pointer to methoddef's name
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetNameOfMethodDef(
+ mdMethodDef md,
+ LPCSTR *pszMethodName)
+{
+ // name of method will not change. So no need to lock
+ HRESULT hr;
+ MethodRec *pMethodRec;
+ *pszMethodName = NULL;
+ IfFailRet(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec));
+ IfFailRet(m_pStgdb->m_MiniMd.getNameOfMethod(pMethodRec, pszMethodName));
+ return S_OK;
+} // MDInternalRW::GetNameOfMethodDef
+
+
+//*****************************************************************************
+// Given a methoddef, return a pointer to methoddef's signature and methoddef's name
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetNameAndSigOfMethodDef(
+ mdMethodDef methoddef, // [IN] given memberdef
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszMethodName)
+{
+ HRESULT hr;
+ // we don't need lock here because name and signature will not change
+
+ // Output parameter should not be NULL
+ _ASSERTE(ppvSigBlob && pcbSigBlob);
+ _ASSERTE(TypeFromToken(methoddef) == mdtMethodDef);
+
+ MethodRec *pMethodRec;
+ *pszMethodName = NULL;
+ *ppvSigBlob = NULL;
+ *ppvSigBlob = NULL;
+ IfFailRet(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(methoddef), &pMethodRec));
+ IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfMethod(pMethodRec, ppvSigBlob, pcbSigBlob));
+
+ return GetNameOfMethodDef(methoddef, pszMethodName);
+} // MDInternalRW::GetNameAndSigOfMethodDef
+
+
+//*****************************************************************************
+// Given a FieldDef, return a pointer to FieldDef's name in UTF8
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetNameOfFieldDef( // return hresult
+ mdFieldDef fd, // given field
+ LPCSTR *pszFieldName)
+{
+ // we don't need lock here because name of field will not change
+ HRESULT hr;
+
+ FieldRec *pFieldRec;
+ *pszFieldName = NULL;
+ IfFailRet(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec));
+ IfFailRet(m_pStgdb->m_MiniMd.getNameOfField(pFieldRec, pszFieldName));
+ return S_OK;
+} // MDInternalRW::GetNameOfFieldDef
+
+
+//*****************************************************************************
+// Given a classdef, return a pointer to classdef's name in UTF8
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetNameOfTypeRef( // return TypeDef's name
+ mdTypeRef classref, // [IN] given typeref
+ LPCSTR *psznamespace, // [OUT] return typeref name
+ LPCSTR *pszname) // [OUT] return typeref namespace
+{
+ _ASSERTE(TypeFromToken(classref) == mdtTypeRef);
+ HRESULT hr;
+
+ *psznamespace = NULL;
+ *pszname = NULL;
+
+ // we don't need lock here because name of a typeref will not change
+
+ TypeRefRec *pTypeRefRec;
+ IfFailRet(m_pStgdb->m_MiniMd.GetTypeRefRecord(RidFromToken(classref), &pTypeRefRec));
+ IfFailRet(m_pStgdb->m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, psznamespace));
+ IfFailRet(m_pStgdb->m_MiniMd.getNameOfTypeRef(pTypeRefRec, pszname));
+ return S_OK;
+} // MDInternalRW::GetNameOfTypeRef
+
+//*****************************************************************************
+// return the resolutionscope of typeref
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetResolutionScopeOfTypeRef(
+ mdTypeRef classref, // given classref
+ mdToken *ptkResolutionScope)
+{
+ HRESULT hr = S_OK;
+ TypeRefRec *pTypeRefRec = NULL;
+
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(classref) == mdtTypeRef && RidFromToken(classref));
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeRefRecord(RidFromToken(classref), &pTypeRefRec));
+ _ASSERTE(hr == S_OK);
+ *ptkResolutionScope = m_pStgdb->m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec);
+ return S_OK;
+
+ErrExit:
+ *ptkResolutionScope = mdTokenNil;
+ return hr;
+} // MDInternalRW::GetResolutionScopeOfTypeRef
+
+//*****************************************************************************
+// Given a name, find the corresponding TypeRef.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::FindTypeRefByName( // S_OK or error.
+ LPCSTR szNamespace, // [IN] Namespace for the TypeRef.
+ LPCSTR szName, // [IN] Name of the TypeRef.
+ mdToken tkResolutionScope, // [IN] Resolution Scope fo the TypeRef.
+ mdTypeRef *ptk) // [OUT] TypeRef token returned.
+{
+ HRESULT hr = NOERROR;
+ ULONG cTypeRefRecs;
+ TypeRefRec *pTypeRefRec;
+ LPCUTF8 szNamespaceTmp;
+ LPCUTF8 szNameTmp;
+ mdToken tkRes;
+
+ LOCKREAD();
+ _ASSERTE(ptk);
+
+ // initialize the output parameter
+ *ptk = mdTypeRefNil;
+
+ // Treat no namespace as empty string.
+ if (!szNamespace)
+ szNamespace = "";
+
+ // It is a linear search here. Do we want to instantiate the name hash?
+ cTypeRefRecs = m_pStgdb->m_MiniMd.getCountTypeRefs();
+
+ for (ULONG i = 1; i <= cTypeRefRecs; i++)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeRefRecord(i, &pTypeRefRec));
+
+ tkRes = m_pStgdb->m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec);
+ if (IsNilToken(tkRes))
+ {
+ if (!IsNilToken(tkResolutionScope))
+ continue;
+ }
+ else if (tkRes != tkResolutionScope)
+ continue;
+
+ IfFailGo(m_pStgdb->m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp));
+ if (strcmp(szNamespace, szNamespaceTmp))
+ continue;
+
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfTypeRef(pTypeRefRec, &szNameTmp));
+ if (!strcmp(szNameTmp, szName))
+ {
+ *ptk = TokenFromRid(i, mdtTypeRef);
+ goto ErrExit;
+ }
+ }
+
+ // cannot find the typedef
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+ return hr;
+} // MDInternalRW::FindTypeRefByName
+
+//*****************************************************************************
+// return flags for a given class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetTypeDefProps(
+ mdTypeDef td, // given classdef
+ DWORD *pdwAttr, // return flags on class
+ mdToken *ptkExtends) // [OUT] Put base class TypeDef/TypeRef here.
+{
+ HRESULT hr = S_OK;
+ TypeDefRec *pTypeDefRec = NULL;
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+
+ if (ptkExtends)
+ {
+ *ptkExtends = m_pStgdb->m_MiniMd.getExtendsOfTypeDef(pTypeDefRec);
+ }
+ if (pdwAttr)
+ {
+ *pdwAttr = m_pStgdb->m_MiniMd.getFlagsOfTypeDef(pTypeDefRec);
+ }
+
+ErrExit:
+
+ return hr;
+} // MDInternalRW::GetTypeDefProps
+
+
+//*****************************************************************************
+// return guid pointer to MetaData internal guid pool given a given class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetItemGuid( // return hresult
+ mdToken tkObj, // given item.
+ CLSID *pGuid) // [OUT] put guid here.
+{
+
+ HRESULT hr; // A result.
+ const BYTE *pBlob = NULL; // Blob with dispid.
+ ULONG cbBlob; // Length of blob.
+ WCHAR wzBlob[40]; // Wide char format of guid.
+ int ix; // Loop control.
+
+ // Get the GUID, if any.
+ hr = GetCustomAttributeByName(tkObj, INTEROP_GUID_TYPE, (const void**)&pBlob, &cbBlob);
+ if (hr != S_FALSE)
+ {
+ // Should be in format. Total length == 41
+ // <0x0001><0x24>01234567-0123-0123-0123-001122334455<0x0000>
+ if ((cbBlob != 41) || (GET_UNALIGNED_VAL16(pBlob) != 1))
+ IfFailGo(E_INVALIDARG);
+ for (ix=1; ix<=36; ++ix)
+ wzBlob[ix] = pBlob[ix+2];
+ wzBlob[0] = '{';
+ wzBlob[37] = '}';
+ wzBlob[38] = 0;
+ hr = IIDFromString(wzBlob, pGuid);
+ }
+ else
+ *pGuid = GUID_NULL;
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetItemGuid
+
+//*****************************************************************************
+// // get enclosing class of NestedClass
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetNestedClassProps(
+ mdTypeDef tkNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptkEnclosingClass) // [OUT] EnclosingClass token.
+{
+ HRESULT hr = NOERROR;
+ RID rid;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+
+ LOCKREAD();
+
+ if (!m_pStgdb->m_MiniMd.IsSorted(TBL_NestedClass) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_NestedClass))
+ {
+ // NestedClass table is not sorted.
+ CONVERT_READ_TO_WRITE_LOCK();
+ }
+
+ // This is a binary search thus we need to grap a read lock. Or this table
+ // might be sorted underneath our feet.
+
+ _ASSERTE(TypeFromToken(tkNestedClass) == mdtTypeDef && ptkEnclosingClass);
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindNestedClassFor(RidFromToken(tkNestedClass), &rid));
+
+ if (InvalidRid(rid))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ NestedClassRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetNestedClassRecord(rid, &pRecord));
+ *ptkEnclosingClass = m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pRecord);
+ }
+
+ErrExit:
+ END_SO_INTOLERANT_CODE;
+ return hr;
+} // MDInternalRW::GetNestedClassProps
+
+//*******************************************************************************
+// Get count of Nested classes given the enclosing class.
+//*******************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetCountNestedClasses( // return count of Nested classes.
+ mdTypeDef tkEnclosingClass, // [IN]Enclosing class.
+ ULONG *pcNestedClassesCount)
+{
+ HRESULT hr;
+ ULONG ulCount;
+ ULONG ulRetCount = 0;
+ NestedClassRec *pRecord;
+
+ _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef && !IsNilToken(tkEnclosingClass));
+
+ *pcNestedClassesCount = 0;
+
+ ulCount = m_pStgdb->m_MiniMd.getCountNestedClasss();
+
+ for (ULONG i = 1; i <= ulCount; i++)
+ {
+ IfFailRet(m_pStgdb->m_MiniMd.GetNestedClassRecord(i, &pRecord));
+ if (tkEnclosingClass == m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pRecord))
+ ulRetCount++;
+ }
+ *pcNestedClassesCount = ulRetCount;
+ return S_OK;
+} // MDInternalRW::GetCountNestedClasses
+
+//*******************************************************************************
+// Return array of Nested classes given the enclosing class.
+//*******************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetNestedClasses( // Return actual count.
+ mdTypeDef tkEnclosingClass, // [IN] Enclosing class.
+ mdTypeDef *rNestedClasses, // [OUT] Array of nested class tokens.
+ ULONG ulNestedClasses, // [IN] Size of array.
+ ULONG *pcNestedClasses)
+{
+ HRESULT hr;
+ ULONG ulCount;
+ ULONG ulRetCount = 0;
+ NestedClassRec *pRecord;
+
+ _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef &&
+ !IsNilToken(tkEnclosingClass));
+
+ *pcNestedClasses = 0;
+
+ ulCount = m_pStgdb->m_MiniMd.getCountNestedClasss();
+
+ for (ULONG i = 1; i <= ulCount; i++)
+ {
+ IfFailRet(m_pStgdb->m_MiniMd.GetNestedClassRecord(i, &pRecord));
+ if (tkEnclosingClass == m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pRecord))
+ {
+ if (ovadd_le(ulRetCount, 1, ulNestedClasses)) // ulRetCount is 0 based.
+ rNestedClasses[ulRetCount] = m_pStgdb->m_MiniMd.getNestedClassOfNestedClass(pRecord);
+ ulRetCount++;
+ }
+ }
+ *pcNestedClasses = ulRetCount;
+ return S_OK;
+} // MDInternalRW::GetNestedClasses
+
+//*******************************************************************************
+// return the ModuleRef properties
+//*******************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetModuleRefProps( // return hresult
+ mdModuleRef mur, // [IN] moduleref token
+ LPCSTR *pszName) // [OUT] buffer to fill with the moduleref name
+{
+ _ASSERTE(TypeFromToken(mur) == mdtModuleRef);
+ _ASSERTE(pszName);
+
+ HRESULT hr = S_OK;
+ ModuleRefRec *pModuleRefRec = NULL;
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetModuleRefRecord(RidFromToken(mur), &pModuleRefRec));
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfModuleRef(pModuleRefRec, pszName));
+
+ErrExit:
+
+ return hr;
+} // MDInternalRW::GetModuleRefProps
+
+
+
+//*****************************************************************************
+// Given a scope and a methoddef, return a pointer to methoddef's signature
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetSigOfMethodDef(
+ mdMethodDef methoddef, // given a methoddef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig)
+{
+ // Output parameter should not be NULL
+ _ASSERTE(pcbSigBlob);
+ _ASSERTE(TypeFromToken(methoddef) == mdtMethodDef);
+
+ HRESULT hr;
+ // We don't change MethodDef signature. No need to lock.
+
+ MethodRec *pMethodRec;
+ *ppSig = NULL;
+ *pcbSigBlob = 0;
+ IfFailRet(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(methoddef), &pMethodRec));
+ IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfMethod(pMethodRec, ppSig, pcbSigBlob));
+ return S_OK;
+} // MDInternalRW::GetSigOfMethodDef
+
+
+//*****************************************************************************
+// Given a scope and a fielddef, return a pointer to fielddef's signature
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetSigOfFieldDef(
+ mdFieldDef fielddef, // given a methoddef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig)
+{
+ _ASSERTE(pcbSigBlob);
+ _ASSERTE(TypeFromToken(fielddef) == mdtFieldDef);
+
+ HRESULT hr;
+ // We don't change Field's signature. No need to lock.
+
+ FieldRec *pFieldRec;
+ *ppSig = NULL;
+ *pcbSigBlob = 0;
+ IfFailRet(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fielddef), &pFieldRec));
+ IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfField(pFieldRec, ppSig, pcbSigBlob));
+ return S_OK;
+} // MDInternalRW::GetSigOfFieldDef
+
+
+//*****************************************************************************
+// Get signature for the token (FieldDef, MethodDef, Signature, or TypeSpec).
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetSigFromToken(
+ mdToken tk,
+ ULONG * pcbSig,
+ PCCOR_SIGNATURE * ppSig)
+{
+ HRESULT hr;
+ // We don't change token's signature. Thus no need to lock.
+
+ *ppSig = NULL;
+ *pcbSig = 0;
+ switch (TypeFromToken(tk))
+ {
+ case mdtSignature:
+ {
+ StandAloneSigRec *pRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetStandAloneSigRecord(RidFromToken(tk), &pRec));
+ IfFailGo(m_pStgdb->m_MiniMd.getSignatureOfStandAloneSig(pRec, ppSig, pcbSig));
+ return S_OK;
+ }
+ case mdtTypeSpec:
+ {
+ TypeSpecRec *pRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeSpecRecord(RidFromToken(tk), &pRec));
+ IfFailGo(m_pStgdb->m_MiniMd.getSignatureOfTypeSpec(pRec, ppSig, pcbSig));
+ return S_OK;
+ }
+ case mdtMethodDef:
+ {
+ IfFailGo(GetSigOfMethodDef(tk, pcbSig, ppSig));
+ return S_OK;
+ }
+ case mdtFieldDef:
+ {
+ IfFailGo(GetSigOfFieldDef(tk, pcbSig, ppSig));
+ return S_OK;
+ }
+ }
+
+ // not a known token type.
+#ifdef _DEBUG
+ if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1))
+ _ASSERTE(!"Unexpected token type");
+#endif
+ *pcbSig = 0;
+ hr = META_E_INVALID_TOKEN_TYPE;
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetSigFromToken
+
+
+//*****************************************************************************
+// Given methoddef, return the flags
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetMethodDefProps(
+ mdMethodDef md,
+ DWORD *pdwFlags) // return mdPublic, mdAbstract, etc
+{
+ HRESULT hr = S_OK;
+ MethodRec *pMethodRec = NULL;
+
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec));
+ _ASSERTE(hr == S_OK);
+ *pdwFlags = m_pStgdb->m_MiniMd.getFlagsOfMethod(pMethodRec);
+ return S_OK;
+
+ErrExit:
+ *pdwFlags = (DWORD)-1;
+ return hr;
+} // MDInternalRW::GetMethodDefProps
+
+//*****************************************************************************
+// Given a scope and a methoddef, return RVA and impl flags
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetMethodImplProps(
+ mdToken tk, // [IN] MethodDef
+ ULONG *pulCodeRVA, // [OUT] CodeRVA
+ DWORD *pdwImplFlags) // [OUT] Impl. Flags
+{
+ _ASSERTE(TypeFromToken(tk) == mdtMethodDef);
+ HRESULT hr = S_OK;
+ MethodRec *pMethodRec = NULL;
+
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethodRec));
+
+ if (pulCodeRVA)
+ {
+ *pulCodeRVA = m_pStgdb->m_MiniMd.getRVAOfMethod(pMethodRec);
+ }
+
+ if (pdwImplFlags)
+ {
+ *pdwImplFlags = m_pStgdb->m_MiniMd.getImplFlagsOfMethod(pMethodRec);
+ }
+
+ErrExit:
+
+ return hr;
+} // MDInternalRW::GetMethodImplProps
+
+
+//*****************************************************************************
+// return the field RVA
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetFieldRVA(
+ mdToken fd, // [IN] FieldDef
+ ULONG *pulCodeRVA) // [OUT] CodeRVA
+{
+ _ASSERTE(TypeFromToken(fd) == mdtFieldDef);
+ _ASSERTE(pulCodeRVA);
+ ULONG iRecord;
+ HRESULT hr = NOERROR;
+
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldRVAHelper(fd, &iRecord));
+ if (InvalidRid(iRecord))
+ {
+ if (pulCodeRVA)
+ *pulCodeRVA = 0;
+ hr = CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ FieldRVARec *pFieldRVARec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRVARecord(iRecord, &pFieldRVARec));
+
+ *pulCodeRVA = m_pStgdb->m_MiniMd.getRVAOfFieldRVA(pFieldRVARec);
+ }
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetFieldRVA
+
+
+//*****************************************************************************
+// Given a fielddef, return the flags. Such as fdPublic, fdStatic, etc
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetFieldDefProps(
+ mdFieldDef fd, // given memberdef
+ DWORD *pdwFlags) // [OUT] return fdPublic, fdPrive, etc flags
+{
+ _ASSERTE(TypeFromToken(fd) == mdtFieldDef);
+ HRESULT hr = S_OK;
+ FieldRec *pFieldRec = NULL;
+
+ LOCKREAD();
+
+ IfFailRet(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec));
+ _ASSERTE(hr == S_OK);
+ *pdwFlags = m_pStgdb->m_MiniMd.getFlagsOfField(pFieldRec);
+ return S_OK;
+
+ErrExit:
+ *pdwFlags = (DWORD)-1;
+ return hr;
+} // MDInternalRW::GetFieldDefProps
+
+//*****************************************************************************
+// return default value of a token(could be paramdef, fielddef, or property)
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetDefaultValue( // return hresult
+ mdToken tk, // [IN] given FieldDef, ParamDef, or Property
+ MDDefaultValue *pMDDefaultValue) // [OUT] default value
+{
+ _ASSERTE(pMDDefaultValue);
+
+ HRESULT hr;
+ BYTE bType;
+ const VOID *pValue;
+ ULONG cbValue;
+ RID rid;
+ ConstantRec *pConstantRec;
+
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindConstantHelper(tk, &rid));
+ if (InvalidRid(rid))
+ {
+ pMDDefaultValue->m_bType = ELEMENT_TYPE_VOID;
+ return S_OK;
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(rid, &pConstantRec));
+
+ // get the type of constant value
+ bType = m_pStgdb->m_MiniMd.getTypeOfConstant(pConstantRec);
+
+ // get the value blob
+ IfFailGo(m_pStgdb->m_MiniMd.getValueOfConstant(pConstantRec, reinterpret_cast<const BYTE **>(&pValue), &cbValue));
+
+ // convert it to our internal default value representation
+ hr = _FillMDDefaultValue(bType, pValue, cbValue, pMDDefaultValue);
+
+ErrExit:
+
+ return hr;
+} // MDInternalRW::GetDefaultValue
+
+
+//*****************************************************************************
+// Given a scope and a methoddef/fielddef, return the dispid
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetDispIdOfMemberDef( // return hresult
+ mdToken tk, // given methoddef or fielddef
+ ULONG *pDispid) // Put the dispid here.
+{
+#ifdef FEATURE_COMINTEROP
+ HRESULT hr; // A result.
+ const BYTE *pBlob; // Blob with dispid.
+ ULONG cbBlob; // Length of blob.
+
+ // No need to lock this function. All of the function that it is calling is already locked!
+
+ // Get the DISPID, if any.
+ _ASSERTE(pDispid);
+
+ *pDispid = DISPID_UNKNOWN;
+ hr = GetCustomAttributeByName(tk, INTEROP_DISPID_TYPE, (const void**)&pBlob, &cbBlob);
+ if (hr != S_FALSE)
+ {
+ // Check that this might be a dispid.
+ if (cbBlob >= (sizeof(*pDispid)+2))
+ *pDispid = GET_UNALIGNED_VAL32(pBlob+2);
+ else
+ IfFailGo(E_INVALIDARG);
+ }
+
+ErrExit:
+ return hr;
+#else // FEATURE_COMINTEROP
+ _ASSERTE(false);
+ return E_NOTIMPL;
+#endif // FEATURE_COMINTEROP
+} // MDInternalRW::GetDispIdOfMemberDef
+
+
+//*****************************************************************************
+// Given interfaceimpl, return the TypeRef/TypeDef and flags
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetTypeOfInterfaceImpl( // return hresult
+ mdInterfaceImpl iiImpl, // given a interfaceimpl
+ mdToken *ptkType)
+{
+ HRESULT hr;
+ // no need to lock this function.
+
+ _ASSERTE(TypeFromToken(iiImpl) == mdtInterfaceImpl);
+
+ *ptkType = mdTypeDefNil;
+
+ InterfaceImplRec *pIIRec;
+ IfFailRet(m_pStgdb->m_MiniMd.GetInterfaceImplRecord(RidFromToken(iiImpl), &pIIRec));
+ *ptkType = m_pStgdb->m_MiniMd.getInterfaceOfInterfaceImpl(pIIRec);
+ return S_OK;
+} // MDInternalRW::GetTypeOfInterfaceImpl
+
+//*****************************************************************************
+// This routine gets the properties for the given MethodSpec token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetMethodSpecProps( // S_OK or error.
+ mdMethodSpec mi, // [IN] The method instantiation
+ mdToken *tkParent, // [OUT] MethodDef or MemberRef
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob) // [OUT] actual size of signature blob
+{
+ HRESULT hr = NOERROR;
+ MethodSpecRec *pMethodSpecRec;
+
+ _ASSERTE(TypeFromToken(mi) == mdtMethodSpec);
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodSpecRecord(RidFromToken(mi), &pMethodSpecRec));
+
+ if (tkParent)
+ *tkParent = m_pStgdb->m_MiniMd.getMethodOfMethodSpec(pMethodSpecRec);
+
+ if (ppvSigBlob || pcbSigBlob)
+ {
+ // caller wants signature information
+ PCCOR_SIGNATURE pvSigTmp;
+ ULONG cbSig;
+ IfFailGo(m_pStgdb->m_MiniMd.getInstantiationOfMethodSpec(pMethodSpecRec, &pvSigTmp, &cbSig));
+ if ( ppvSigBlob )
+ *ppvSigBlob = pvSigTmp;
+ if ( pcbSigBlob)
+ *pcbSigBlob = cbSig;
+ }
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetMethodSpecProps
+
+//*****************************************************************************
+// Given a classname, return the typedef
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::FindTypeDef( // return hresult
+ LPCSTR szNamespace, // [IN] Namespace for the TypeDef.
+ LPCSTR szName, // [IN] Name of the TypeDef.
+ mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef of enclosing class.
+ mdTypeDef *ptypedef) // [OUT] return typedef
+{
+ HRESULT hr = S_OK;
+ LOCKREADIFFAILRET();
+
+ _ASSERTE(ptypedef);
+
+ // initialize the output parameter
+ *ptypedef = mdTypeDefNil;
+
+ return ImportHelper::FindTypeDefByName(&(m_pStgdb->m_MiniMd),
+ szNamespace,
+ szName,
+ tkEnclosingClass,
+ ptypedef);
+} // MDInternalRW::FindTypeDef
+
+//*****************************************************************************
+// Given a memberref, return a pointer to memberref's name and signature
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetNameAndSigOfMemberRef( // meberref's name
+ mdMemberRef memberref, // given a memberref
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszMemberRefName)
+{
+ HRESULT hr;
+
+ // MemberRef's name and sig won't change. Don't need to lock this.
+
+ _ASSERTE(TypeFromToken(memberref) == mdtMemberRef);
+
+ MemberRefRec *pMemberRefRec;
+ *pszMemberRefName = NULL;
+ if (ppvSigBlob != NULL)
+ {
+ _ASSERTE(pcbSigBlob != NULL);
+ *ppvSigBlob = NULL;
+ *pcbSigBlob = 0;
+ }
+ IfFailRet(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(memberref), &pMemberRefRec));
+ if (ppvSigBlob != NULL)
+ {
+ IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfMemberRef(pMemberRefRec, ppvSigBlob, pcbSigBlob));
+ }
+ IfFailRet(m_pStgdb->m_MiniMd.getNameOfMemberRef(pMemberRefRec, pszMemberRefName));
+ return S_OK;
+} // MDInternalRW::GetNameAndSigOfMemberRef
+
+
+
+//*****************************************************************************
+// Given a memberref, return parent token. It can be a TypeRef, ModuleRef, or a MethodDef
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetParentOfMemberRef( // return parent token
+ mdMemberRef memberref, // given a typedef
+ mdToken *ptkParent) // return the parent token
+{
+ HRESULT hr = S_OK;
+ MemberRefRec *pMemberRefRec = NULL;
+
+ LOCKREAD();
+
+ // parent for MemberRef can change. See SetParent.
+
+ _ASSERTE(TypeFromToken(memberref) == mdtMemberRef);
+
+ IfFailRet(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(memberref), &pMemberRefRec));
+ _ASSERTE(hr == S_OK);
+ *ptkParent = m_pStgdb->m_MiniMd.getClassOfMemberRef(pMemberRefRec);
+ return S_OK;
+
+ErrExit:
+ *ptkParent = mdTokenNil;
+ return hr;
+} // MDInternalRW::GetParentOfMemberRef
+
+//*****************************************************************************
+// return properties of a paramdef
+//*****************************************************************************/
+__checkReturn
+HRESULT
+MDInternalRW::GetParamDefProps (
+ mdParamDef paramdef, // given a paramdef
+ USHORT *pusSequence, // [OUT] slot number for this parameter
+ DWORD *pdwAttr, // [OUT] flags
+ LPCSTR *pszName) // [OUT] return the name of the parameter
+{
+ HRESULT hr = S_OK;
+ ParamRec *pParamRec = NULL;
+
+ LOCKREAD();
+
+ // parent for MemberRef can change. See SetParamProps.
+
+ _ASSERTE(TypeFromToken(paramdef) == mdtParamDef);
+ IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(paramdef), &pParamRec));
+ _ASSERTE(hr == S_OK);
+ if (pdwAttr != NULL)
+ {
+ *pdwAttr = m_pStgdb->m_MiniMd.getFlagsOfParam(pParamRec);
+ }
+ if (pusSequence != NULL)
+ {
+ *pusSequence = m_pStgdb->m_MiniMd.getSequenceOfParam(pParamRec);
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfParam(pParamRec, pszName));
+ _ASSERTE(hr == S_OK);
+ return S_OK;
+
+ErrExit:
+ *pszName = NULL;
+ return S_OK;
+} // MDInternalRW::GetParamDefProps
+
+//*****************************************************************************
+// Get property info for the method.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetPropertyInfoForMethodDef( // Result.
+ mdMethodDef md, // [IN] memberdef
+ mdProperty *ppd, // [OUT] put property token here
+ LPCSTR *pName, // [OUT] put pointer to name here
+ ULONG *pSemantic) // [OUT] put semantic here
+{
+ MethodSemanticsRec *pSemantics;
+ RID ridCur;
+ RID ridMax;
+ USHORT usSemantics;
+ HRESULT hr = S_OK;
+ LOCKREAD();
+
+ ridMax = m_pStgdb->m_MiniMd.getCountMethodSemantics();
+ for (ridCur = 1; ridCur <= ridMax; ridCur++)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics));
+ if (md == m_pStgdb->m_MiniMd.getMethodOfMethodSemantics(pSemantics))
+ {
+ // match the method
+ usSemantics = m_pStgdb->m_MiniMd.getSemanticOfMethodSemantics(pSemantics);
+ if (usSemantics == msGetter || usSemantics == msSetter)
+ {
+ // Make sure that it is not an invalid entry
+ if (m_pStgdb->m_MiniMd.getAssociationOfMethodSemantics(pSemantics) != mdPropertyNil)
+ {
+ // found a match. Fill out the output parameters
+ PropertyRec *pProperty;
+ mdProperty prop;
+ prop = m_pStgdb->m_MiniMd.getAssociationOfMethodSemantics(pSemantics);
+
+ if (ppd)
+ *ppd = prop;
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(prop), &pProperty));
+
+ if (pName)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfProperty(pProperty, pName));
+ }
+
+ if (pSemantic)
+ *pSemantic = usSemantics;
+ goto ErrExit;
+ }
+ }
+ }
+ }
+
+ hr = S_FALSE;
+ErrExit:
+ return hr;
+} // MDInternalRW::GetPropertyInfoForMethodDef
+
+//*****************************************************************************
+// return the pack size of a class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetClassPackSize(
+ mdTypeDef td, // [IN] give typedef
+ DWORD *pdwPackSize) // [OUT]
+{
+ HRESULT hr = NOERROR;
+ RID ridClassLayout = 0;
+
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && pdwPackSize);
+
+ ClassLayoutRec *pRec;
+ IfFailGo(m_pStgdb->m_MiniMd.FindClassLayoutHelper(td, &ridClassLayout));
+
+ if (InvalidRid(ridClassLayout))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec));
+ *pdwPackSize = m_pStgdb->m_MiniMd.getPackingSizeOfClassLayout(pRec);
+ErrExit:
+ return hr;
+} // MDInternalRW::GetClassPackSize
+
+
+//*****************************************************************************
+// return the total size of a value class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetClassTotalSize( // return error if a class does not have total size info
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pulClassSize) // [OUT] return the total size of the class
+{
+ CONTRACT_VIOLATION(ThrowsViolation);
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && pulClassSize);
+
+ ClassLayoutRec *pRec;
+ HRESULT hr = NOERROR;
+ RID ridClassLayout;
+
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindClassLayoutHelper(td, &ridClassLayout));
+ if (InvalidRid(ridClassLayout))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec));
+ *pulClassSize = m_pStgdb->m_MiniMd.getClassSizeOfClassLayout(pRec);
+ErrExit:
+ return hr;
+} // MDInternalRW::GetClassTotalSize
+
+
+//*****************************************************************************
+// init the layout enumerator of a class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetClassLayoutInit(
+ mdTypeDef td, // [IN] give typedef
+ MD_CLASS_LAYOUT *pmdLayout) // [OUT] set up the status of query here
+{
+ HRESULT hr = NOERROR;
+ LOCKREAD();
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+
+ // <TODO>Do we need to lock this function? Can clints add more Fields on a TypeDef?</TODO>
+
+ // initialize the output parameter
+ _ASSERTE(pmdLayout);
+ memset(pmdLayout, 0, sizeof(MD_CLASS_LAYOUT));
+
+ TypeDefRec *pTypeDefRec;
+
+ // record for this typedef in TypeDef Table
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+
+ // find the starting and end field for this typedef
+ pmdLayout->m_ridFieldCur = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pTypeDefRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(td), &(pmdLayout->m_ridFieldEnd)));
+
+ErrExit:
+
+ return hr;
+} // MDInternalRW::GetClassLayoutInit
+
+//*****************************************************************************
+// Get the field offset for a given field token
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetFieldOffset(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulOffset) // [OUT] FieldOffset
+{
+ HRESULT hr = S_OK;
+ FieldLayoutRec *pRec;
+
+ _ASSERTE(pulOffset);
+
+ RID iLayout;
+
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldLayoutHelper(fd, &iLayout));
+
+ if (InvalidRid(iLayout))
+ {
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldLayoutRecord(iLayout, &pRec));
+ *pulOffset = m_pStgdb->m_MiniMd.getOffSetOfFieldLayout(pRec);
+ _ASSERTE(*pulOffset != ULONG_MAX);
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetFieldOffset
+
+//*****************************************************************************
+// enum the next the field layout
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetClassLayoutNext(
+ MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here
+ mdFieldDef *pfd, // [OUT] field def
+ ULONG *pulOffset) // [OUT] field offset or sequence
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(pfd && pulOffset && pLayout);
+
+ RID iLayout2;
+ FieldLayoutRec *pRec;
+
+ LOCKREAD();
+
+ while (pLayout->m_ridFieldCur < pLayout->m_ridFieldEnd)
+ {
+ RID fieldRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(pLayout->m_ridFieldCur, &fieldRid));
+ mdFieldDef fd = TokenFromRid(fieldRid, mdtFieldDef);
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldLayoutHelper(fd, &iLayout2));
+ pLayout->m_ridFieldCur++;
+ if (!InvalidRid(iLayout2))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldLayoutRecord(iLayout2, &pRec));
+ *pulOffset = m_pStgdb->m_MiniMd.getOffSetOfFieldLayout(pRec);
+ _ASSERTE(*pulOffset != ULONG_MAX);
+ *pfd = fd;
+ goto ErrExit;
+ }
+ }
+
+ *pfd = mdFieldDefNil;
+ hr = S_FALSE;
+
+ // fall through
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetClassLayoutNext
+
+
+//*****************************************************************************
+// return the field's native type signature
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetFieldMarshal( // return error if no native type associate with the token
+ mdToken tk, // [IN] given fielddef or paramdef
+ PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature
+ ULONG *pcbNativeType) // [OUT] the count of bytes of *ppvNativeType
+{
+ // output parameters have to be supplied
+ _ASSERTE(pcbNativeType);
+
+ RID rid;
+ FieldMarshalRec *pFieldMarshalRec;
+ HRESULT hr = NOERROR;
+
+ LOCKREAD();
+
+ // find the row containing the marshal definition for tk
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldMarshalHelper(tk, &rid));
+ if (InvalidRid(rid))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldMarshalRecord(rid, &pFieldMarshalRec));
+
+ // get the native type
+ IfFailGo(m_pStgdb->m_MiniMd.getNativeTypeOfFieldMarshal(pFieldMarshalRec, pSigNativeType, pcbNativeType));
+ErrExit:
+ return hr;
+} // MDInternalRW::GetFieldMarshal
+
+
+
+//*****************************************
+// property APIs
+//*****************************************
+
+//*****************************************************************************
+// Find property by name
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::FindProperty(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szPropName, // [IN] property name
+ mdProperty *pProp) // [OUT] return property token
+{
+ HRESULT hr = NOERROR;
+ LOCKREAD();
+
+ // output parameters have to be supplied
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && pProp);
+
+ PropertyMapRec *pRec;
+ PropertyRec *pProperty;
+ RID ridPropertyMap;
+ RID ridCur;
+ RID ridEnd;
+ LPCUTF8 szName;
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindPropertyMapFor(RidFromToken(td), &ridPropertyMap));
+ if (InvalidRid(ridPropertyMap))
+ {
+ // not found!
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pRec));
+
+ // get the starting/ending rid of properties of this typedef
+ ridCur = m_pStgdb->m_MiniMd.getPropertyListOfPropertyMap(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd));
+
+ for ( ; ridCur < ridEnd; ridCur ++ )
+ {
+ RID propertyRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRid(ridCur, &propertyRid));
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(propertyRid, &pProperty));
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfProperty(pProperty, &szName));
+ if ( strcmp(szName, szPropName) ==0 )
+ {
+ // Found the match. Set the output parameter and we are done.
+ *pProp = TokenFromRid(propertyRid, mdtProperty);
+ goto ErrExit;
+ }
+ }
+
+ // not found
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+ return hr;
+} // MDInternalRW::FindProperty
+
+
+
+//*****************************************************************************
+// return the properties of a property
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetPropertyProps(
+ mdProperty prop, // [IN] property token
+ LPCSTR *pszProperty, // [OUT] property name
+ DWORD *pdwPropFlags, // [OUT] property flags.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob
+ ULONG *pcbSig) // [OUT] count of bytes in *ppvSig
+{
+ HRESULT hr = S_OK;
+ LOCKREAD();
+
+ // output parameters have to be supplied
+ _ASSERTE(TypeFromToken(prop) == mdtProperty);
+
+ PropertyRec *pProperty;
+ ULONG cbSig;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(prop), &pProperty));
+
+ // get name of the property
+ if (pszProperty)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfProperty(pProperty, pszProperty));
+ }
+
+ // get the flags of property
+ if (pdwPropFlags)
+ *pdwPropFlags = m_pStgdb->m_MiniMd.getPropFlagsOfProperty(pProperty);
+
+ // get the type of the property
+ if (ppvSig)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getTypeOfProperty(pProperty, ppvSig, &cbSig));
+ if (pcbSig)
+ {
+ *pcbSig = cbSig;
+ }
+ }
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetPropertyProps
+
+
+//**********************************
+//
+// Event APIs
+//
+//**********************************
+
+//*****************************************************************************
+// return an event by given the name
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::FindEvent(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szEventName, // [IN] event name
+ mdEvent *pEvent) // [OUT] return event token
+{
+ HRESULT hr = NOERROR;
+ LOCKREAD();
+
+ // output parameters have to be supplied
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && pEvent);
+
+ EventMapRec *pRec;
+ EventRec *pEventRec;
+ RID ridEventMap;
+ RID ridCur;
+ RID ridEnd;
+ LPCUTF8 szName;
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindEventMapFor(RidFromToken(td), &ridEventMap));
+ if (InvalidRid(ridEventMap))
+ {
+ // not found!
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventMapRecord(ridEventMap, &pRec));
+
+ // get the starting/ending rid of properties of this typedef
+ ridCur = m_pStgdb->m_MiniMd.getEventListOfEventMap(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndEventListOfEventMap(ridEventMap, &ridEnd));
+
+ for (; ridCur < ridEnd; ridCur ++)
+ {
+ RID eventRid;
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRid(ridCur, &eventRid));
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(eventRid, &pEventRec));
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfEvent(pEventRec, &szName));
+ if ( strcmp(szName, szEventName) ==0 )
+ {
+ // Found the match. Set the output parameter and we are done.
+ *pEvent = TokenFromRid(eventRid, mdtEvent);
+ goto ErrExit;
+ }
+ }
+
+ // not found
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+
+ return hr;
+} // MDInternalRW::FindEvent
+
+
+//*****************************************************************************
+// return the properties of an event
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetEventProps( // S_OK, S_FALSE, or error.
+ mdEvent ev, // [IN] event token
+ LPCSTR *pszEvent, // [OUT] Event name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType) // [OUT] EventType class
+{
+ HRESULT hr = S_OK;
+ LOCKREAD();
+ EventRec *pEvent;
+
+ // output parameters have to be supplied
+ _ASSERTE(TypeFromToken(ev) == mdtEvent);
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(ev), &pEvent));
+ if (pszEvent != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfEvent(pEvent, pszEvent));
+ }
+ if (pdwEventFlags)
+ *pdwEventFlags = m_pStgdb->m_MiniMd.getEventFlagsOfEvent(pEvent);
+ if (ptkEventType)
+ *ptkEventType = m_pStgdb->m_MiniMd.getEventTypeOfEvent(pEvent);
+
+ErrExit:
+
+ return hr;
+} // MDInternalRW::GetEventProps
+
+//*****************************************************************************
+// return the properties of a generic param
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetGenericParamProps( // S_OK or error.
+ mdGenericParam rd, // [IN] The type parameter
+ ULONG* pulSequence, // [OUT] Parameter sequence number
+ DWORD* pdwAttr, // [OUT] Type parameter flags (for future use)
+ mdToken *ptOwner, // [OUT] The owner (TypeDef or MethodDef)
+ DWORD *reserved, // [OUT] The kind (TypeDef/Ref/Spec, for future use)
+ LPCSTR *szName) // [OUT] The name
+{
+ HRESULT hr = NOERROR;
+ GenericParamRec *pGenericParamRec = NULL;
+
+ // See if this version of the metadata can do Generics
+ if (!m_pStgdb->m_MiniMd.SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+ _ASSERTE(TypeFromToken(rd) == mdtGenericParam);
+ if (TypeFromToken(rd) != mdtGenericParam)
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamRecord(RidFromToken(rd), &pGenericParamRec));
+
+ if (pulSequence)
+ *pulSequence = m_pStgdb->m_MiniMd.getNumberOfGenericParam(pGenericParamRec);
+ if (pdwAttr)
+ *pdwAttr = m_pStgdb->m_MiniMd.getFlagsOfGenericParam(pGenericParamRec);
+ if (ptOwner)
+ *ptOwner = m_pStgdb->m_MiniMd.getOwnerOfGenericParam(pGenericParamRec);
+ if (szName != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfGenericParam(pGenericParamRec, szName));
+ }
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetGenericParamProps
+
+
+//*****************************************************************************
+// This routine gets the properties for the given GenericParamConstraint token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetGenericParamConstraintProps( // S_OK or error.
+ mdGenericParamConstraint rd, // [IN] The constraint token
+ mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained
+ mdToken *ptkConstraintType) // [OUT] TypeDef/Ref/Spec constraint
+{
+ HRESULT hr = NOERROR;
+ GenericParamConstraintRec *pGPCRec;
+ RID ridRD = RidFromToken(rd);
+
+ // See if this version of the metadata can do Generics
+ if (!m_pStgdb->m_MiniMd.SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+ if((TypeFromToken(rd) == mdtGenericParamConstraint) && (ridRD != 0))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamConstraintRecord(ridRD, &pGPCRec));
+
+ if (ptGenericParam)
+ *ptGenericParam = TokenFromRid(m_pStgdb->m_MiniMd.getOwnerOfGenericParamConstraint(pGPCRec),mdtGenericParam);
+ if (ptkConstraintType)
+ *ptkConstraintType = m_pStgdb->m_MiniMd.getConstraintOfGenericParamConstraint(pGPCRec);
+ }
+ else
+ hr = META_E_BAD_INPUT_PARAMETER;
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetGenericParamConstraintProps
+
+//*****************************************************************************
+// Find methoddef of a particular associate with a property or an event
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::FindAssociate(
+ mdToken evprop, // [IN] given a property or event token
+ DWORD dwSemantics, // [IN] given a associate semantics(setter, getter, testdefault, reset)
+ mdMethodDef *pmd) // [OUT] return method def token
+{
+ HRESULT hr = NOERROR;
+ RID rid;
+ MethodSemanticsRec *pMethodSemantics;
+
+ // output parameters have to be supplied
+ _ASSERTE(pmd);
+ _ASSERTE(TypeFromToken(evprop) == mdtEvent || TypeFromToken(evprop) == mdtProperty);
+
+ LOCKREAD();
+
+ hr = m_pStgdb->m_MiniMd.FindAssociateHelper(evprop, dwSemantics, &rid);
+ if (SUCCEEDED(hr))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodSemanticsRecord(rid, &pMethodSemantics));
+ *pmd = m_pStgdb->m_MiniMd.getMethodOfMethodSemantics(pMethodSemantics);
+ }
+
+ErrExit:
+
+ return hr;
+} // MDInternalRW::FindAssociate
+
+
+//*****************************************************************************
+// get counts of methodsemantics associated with a particular property/event
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::EnumAssociateInit(
+ mdToken evprop, // [IN] given a property or an event token
+ HENUMInternal *phEnum) // [OUT] cursor to hold the query result
+{
+ HRESULT hr;
+
+ LOCKREAD();
+
+ // output parameters have to be supplied
+ _ASSERTE(phEnum);
+ _ASSERTE(TypeFromToken(evprop) == mdtEvent || TypeFromToken(evprop) == mdtProperty);
+
+ hr = m_pStgdb->m_MiniMd.FindMethodSemanticsHelper(evprop, phEnum);
+
+ErrExit:
+ return hr;
+} // MDInternalRW::EnumAssociateInit
+
+
+//*****************************************************************************
+// get all methodsemantics associated with a particular property/event
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetAllAssociates(
+ HENUMInternal *phEnum, // [OUT] cursor to hold the query result
+ ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output
+ ULONG cAssociateRec) // [IN] size of the buffer
+{
+ HRESULT hr = S_OK;
+ MethodSemanticsRec *pSemantics;
+ RID ridCur;
+ int index = 0;
+
+ LOCKREAD();
+
+ // <TODO>@FUTURE: rewrite the EnumAssociateInit and GetAllAssociates. Because we might add more properties and events.
+ // Then we might resort MethodSemantics table. So this can be totally out of sync.</TODO>
+
+ _ASSERTE(phEnum && pAssociateRec);
+ _ASSERTE(cAssociateRec == phEnum->m_ulCount);
+
+ // Convert from row pointers to RIDs.
+ while (HENUMInternal::EnumNext(phEnum, (mdToken *)&ridCur))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics));
+
+ pAssociateRec[index].m_memberdef = m_pStgdb->m_MiniMd.getMethodOfMethodSemantics(pSemantics);
+ pAssociateRec[index].m_dwSemantics = m_pStgdb->m_MiniMd.getSemanticOfMethodSemantics(pSemantics);
+ index++;
+ }
+
+ErrExit:
+
+ return hr;
+} // MDInternalRW::GetAllAssociates
+
+
+//*****************************************************************************
+// Get the Action and Permissions blob for a given PermissionSet.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetPermissionSetProps(
+ mdPermission pm, // [IN] the permission token.
+ DWORD *pdwAction, // [OUT] CorDeclSecurity.
+ void const **ppvPermission, // [OUT] permission blob.
+ ULONG *pcbPermission) // [OUT] count of bytes of pvPermission.
+{
+ HRESULT hr = S_OK;
+ _ASSERTE(TypeFromToken(pm) == mdtPermission);
+ _ASSERTE(pdwAction && ppvPermission && pcbPermission);
+
+ DeclSecurityRec *pPerm;
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(pm), &pPerm));
+ *pdwAction = m_pStgdb->m_MiniMd.getActionOfDeclSecurity(pPerm);
+ IfFailGo(m_pStgdb->m_MiniMd.getPermissionSetOfDeclSecurity(pPerm, reinterpret_cast<const BYTE **>(ppvPermission), pcbPermission));
+
+ErrExit:
+
+ return hr;
+} // MDInternalRW::GetPermissionSetProps
+
+
+//*****************************************************************************
+// Get the String given the String token.
+// Return a pointer to the string, or NULL in case of error.
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRW::GetUserString( // Offset into the string blob heap.
+ mdString stk, // [IN] the string token.
+ ULONG *pcchStringSize, // [OUT] count of characters in the string.
+ BOOL *pfIs80Plus, // [OUT] specifies where there are extended characters >= 0x80.
+ LPCWSTR *pwszUserString)
+{
+ HRESULT hr;
+ LPWSTR wszTmp;
+
+ // no need to lock this function.
+
+ if (pfIs80Plus != NULL)
+ {
+ *pfIs80Plus = FALSE;
+ }
+ *pwszUserString = NULL;
+ *pcchStringSize = 0;
+
+ _ASSERTE(pcchStringSize != NULL);
+ MetaData::DataBlob userString;
+ IfFailRet(m_pStgdb->m_MiniMd.GetUserString(RidFromToken(stk), &userString));
+
+ wszTmp = reinterpret_cast<LPWSTR>(userString.GetDataPointer());
+
+ *pcchStringSize = userString.GetSize() / sizeof(WCHAR);
+
+ if (userString.IsEmpty())
+ {
+ *pwszUserString = NULL;
+ return S_OK;
+ }
+
+ if (pfIs80Plus != NULL)
+ {
+ if (userString.GetSize() % sizeof(WCHAR) == 0)
+ {
+ *pfIs80Plus = TRUE; // no indicator, presume the worst
+ }
+ // Return the user string terminator (contains value fIs80Plus)
+ *pfIs80Plus = *(reinterpret_cast<PBYTE>(wszTmp + *pcchStringSize));
+ }
+
+ *pwszUserString = wszTmp;
+ return S_OK;
+} // MDInternalRW::GetUserString
+
+//*****************************************************************************
+// Get the properties for the given Assembly token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetAssemblyProps(
+ mdAssembly mda, // [IN] The Assembly for which to get the properties.
+ const void **ppbPublicKey, // [OUT] Pointer to the public key.
+ ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key.
+ ULONG *pulHashAlgId, // [OUT] Hash Algorithm.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData.
+ DWORD *pdwAssemblyFlags) // [OUT] Flags.
+{
+ AssemblyRec *pRecord;
+ HRESULT hr = S_OK;
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mda) == mdtAssembly && RidFromToken(mda));
+ IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRecord(RidFromToken(mda), &pRecord));
+
+ if (ppbPublicKey != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getPublicKeyOfAssembly(pRecord, reinterpret_cast<const BYTE **>(ppbPublicKey), pcbPublicKey));
+ }
+ if (pulHashAlgId)
+ *pulHashAlgId = m_pStgdb->m_MiniMd.getHashAlgIdOfAssembly(pRecord);
+ if (pszName != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfAssembly(pRecord, pszName));
+ }
+ if (pMetaData)
+ {
+ pMetaData->usMajorVersion = m_pStgdb->m_MiniMd.getMajorVersionOfAssembly(pRecord);
+ pMetaData->usMinorVersion = m_pStgdb->m_MiniMd.getMinorVersionOfAssembly(pRecord);
+ pMetaData->usBuildNumber = m_pStgdb->m_MiniMd.getBuildNumberOfAssembly(pRecord);
+ pMetaData->usRevisionNumber = m_pStgdb->m_MiniMd.getRevisionNumberOfAssembly(pRecord);
+ IfFailGo(m_pStgdb->m_MiniMd.getLocaleOfAssembly(pRecord, &pMetaData->szLocale));
+ pMetaData->ulProcessor = 0;
+ pMetaData->ulOS = 0;
+ }
+ if (pdwAssemblyFlags)
+ {
+ *pdwAssemblyFlags = m_pStgdb->m_MiniMd.getFlagsOfAssembly(pRecord);
+
+ // Turn on the afPublicKey if PublicKey blob is not empty
+ DWORD cbPublicKey;
+ const BYTE *pbPublicKey;
+ IfFailGo(m_pStgdb->m_MiniMd.getPublicKeyOfAssembly(pRecord, &pbPublicKey, &cbPublicKey));
+ if (cbPublicKey)
+ *pdwAssemblyFlags |= afPublicKey;
+ }
+
+ErrExit:
+ return hr;
+
+} // MDInternalRW::GetAssemblyProps
+
+//*****************************************************************************
+// Get the properties for the given AssemblyRef token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetAssemblyRefProps(
+ mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties.
+ const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token.
+ ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData.
+ const void **ppbHashValue, // [OUT] Hash blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ DWORD *pdwAssemblyRefFlags) // [OUT] Flags.
+{
+ AssemblyRefRec *pRecord;
+ HRESULT hr = S_OK;
+
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mdar) == mdtAssemblyRef && RidFromToken(mdar));
+ IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRefRecord(RidFromToken(mdar), &pRecord));
+
+ if (ppbPublicKeyOrToken != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getPublicKeyOrTokenOfAssemblyRef(pRecord, reinterpret_cast<const BYTE **>(ppbPublicKeyOrToken), pcbPublicKeyOrToken));
+ }
+ if (pszName != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfAssemblyRef(pRecord, pszName));
+ }
+ if (pMetaData)
+ {
+ pMetaData->usMajorVersion = m_pStgdb->m_MiniMd.getMajorVersionOfAssemblyRef(pRecord);
+ pMetaData->usMinorVersion = m_pStgdb->m_MiniMd.getMinorVersionOfAssemblyRef(pRecord);
+ pMetaData->usBuildNumber = m_pStgdb->m_MiniMd.getBuildNumberOfAssemblyRef(pRecord);
+ pMetaData->usRevisionNumber = m_pStgdb->m_MiniMd.getRevisionNumberOfAssemblyRef(pRecord);
+ IfFailGo(m_pStgdb->m_MiniMd.getLocaleOfAssemblyRef(pRecord, &pMetaData->szLocale));
+ pMetaData->ulProcessor = 0;
+ pMetaData->ulOS = 0;
+ }
+ if (ppbHashValue != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getHashValueOfAssemblyRef(pRecord, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue));
+ }
+ if (pdwAssemblyRefFlags)
+ *pdwAssemblyRefFlags = m_pStgdb->m_MiniMd.getFlagsOfAssemblyRef(pRecord);
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetAssemblyRefProps
+
+//*****************************************************************************
+// Get the properties for the given File token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetFileProps(
+ mdFile mdf, // [IN] The File for which to get the properties.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob.
+ DWORD *pdwFileFlags) // [OUT] Flags.
+{
+ FileRec *pRecord;
+ HRESULT hr = S_OK;
+
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mdf) == mdtFile && RidFromToken(mdf));
+ IfFailGo(m_pStgdb->m_MiniMd.GetFileRecord(RidFromToken(mdf), &pRecord));
+
+ if (pszName != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfFile(pRecord, pszName));
+ }
+ if (ppbHashValue != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getHashValueOfFile(pRecord, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue));
+ }
+ if (pdwFileFlags)
+ *pdwFileFlags = m_pStgdb->m_MiniMd.getFlagsOfFile(pRecord);
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetFileProps
+
+//*****************************************************************************
+// Get the properties for the given ExportedType token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetExportedTypeProps(
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ LPCSTR *pszNamespace, // [OUT] Buffer to fill with name.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags) // [OUT] Flags.
+{
+ ExportedTypeRec *pRecord;
+ HRESULT hr = S_OK;
+
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mdct) == mdtExportedType && RidFromToken(mdct));
+ IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(RidFromToken(mdct), &pRecord));
+
+ if (pszNamespace != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getTypeNamespaceOfExportedType(pRecord, pszNamespace));
+ }
+ if (pszName != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getTypeNameOfExportedType(pRecord, pszName));
+ }
+ if (ptkImplementation)
+ *ptkImplementation = m_pStgdb->m_MiniMd.getImplementationOfExportedType(pRecord);
+ if (ptkTypeDef)
+ *ptkTypeDef = m_pStgdb->m_MiniMd.getTypeDefIdOfExportedType(pRecord);
+ if (pdwExportedTypeFlags)
+ *pdwExportedTypeFlags = m_pStgdb->m_MiniMd.getFlagsOfExportedType(pRecord);
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetExportedTypeProps
+
+//*****************************************************************************
+// Get the properties for the given Resource token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetManifestResourceProps(
+ mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file.
+ DWORD *pdwResourceFlags) // [OUT] Flags.
+{
+ ManifestResourceRec *pRecord;
+ HRESULT hr = S_OK;
+
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mdmr) == mdtManifestResource && RidFromToken(mdmr));
+ IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(RidFromToken(mdmr), &pRecord));
+
+ if (pszName != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfManifestResource(pRecord, pszName));
+ }
+ if (ptkImplementation)
+ *ptkImplementation = m_pStgdb->m_MiniMd.getImplementationOfManifestResource(pRecord);
+ if (pdwOffset)
+ *pdwOffset = m_pStgdb->m_MiniMd.getOffsetOfManifestResource(pRecord);
+ if (pdwResourceFlags)
+ *pdwResourceFlags = m_pStgdb->m_MiniMd.getFlagsOfManifestResource(pRecord);
+
+ErrExit:
+ return hr;
+} // MDInternalRW::GetManifestResourceProps
+
+//*****************************************************************************
+// Find the ExportedType given the name.
+//*****************************************************************************
+__checkReturn
+STDMETHODIMP MDInternalRW::FindExportedTypeByName( // S_OK or error
+ LPCSTR szNamespace, // [IN] Namespace of the ExportedType.
+ LPCSTR szName, // [IN] Name of the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] Enclosing ExportedType.
+ mdExportedType *pmct) // [OUT] Put ExportedType token here.
+{
+ _ASSERTE(szName && pmct);
+ HRESULT hr = S_OK;
+ LOCKREADIFFAILRET();
+
+ IMetaModelCommon *pCommon = static_cast<IMetaModelCommon*>(&m_pStgdb->m_MiniMd);
+ return pCommon->CommonFindExportedType(szNamespace, szName, tkEnclosingType, pmct);
+} // MDInternalRW::FindExportedTypeByName
+
+//*****************************************************************************
+// Find the ManifestResource given the name.
+//*****************************************************************************
+__checkReturn
+STDMETHODIMP MDInternalRW::FindManifestResourceByName(// S_OK or error
+ LPCSTR szName, // [IN] Name of the resource.
+ mdManifestResource *pmmr) // [OUT] Put ManifestResource token here.
+{
+ _ASSERTE(szName && pmmr);
+
+ ManifestResourceRec *pRecord;
+ ULONG cRecords; // Count of records.
+ LPCUTF8 szNameTmp = 0; // Name obtained from the database.
+ ULONG i;
+ HRESULT hr = S_OK;
+
+ LOCKREAD();
+
+ cRecords = m_pStgdb->m_MiniMd.getCountManifestResources();
+
+ // Search for the ExportedType.
+ for (i = 1; i <= cRecords; i++)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(i, &pRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfManifestResource(pRecord, &szNameTmp));
+ if (! strcmp(szName, szNameTmp))
+ {
+ *pmmr = TokenFromRid(i, mdtManifestResource);
+ goto ErrExit;
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+
+ return hr;
+} // MDInternalRW::FindManifestResourceByName
+
+//*****************************************************************************
+// Get the Assembly token from the given scope.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetAssemblyFromScope( // S_OK or error
+ mdAssembly *ptkAssembly) // [OUT] Put token here.
+{
+ _ASSERTE(ptkAssembly);
+
+ if (m_pStgdb->m_MiniMd.getCountAssemblys())
+ {
+ *ptkAssembly = TokenFromRid(1, mdtAssembly);
+ return S_OK;
+ }
+ else
+ return CLDB_E_RECORD_NOTFOUND;
+} // MDInternalRW::GetAssemblyFromScope
+
+//*******************************************************************************
+// return properties regarding a TypeSpec
+//*******************************************************************************
+//*******************************************************************************
+// return properties regarding a TypeSpec
+//*******************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetTypeSpecFromToken( // S_OK or error.
+ mdTypeSpec typespec, // [IN] Signature token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token.
+ ULONG *pcbSig) // [OUT] return size of signature.
+{
+ HRESULT hr = NOERROR;
+
+ _ASSERTE(TypeFromToken(typespec) == mdtTypeSpec);
+ _ASSERTE(ppvSig && pcbSig);
+
+ if (!IsValidToken(typespec))
+ return E_INVALIDARG;
+
+ TypeSpecRec *pRec;
+ IfFailRet(m_pStgdb->m_MiniMd.GetTypeSpecRecord(RidFromToken(typespec), &pRec));
+
+ if (pRec == NULL)
+ return CLDB_E_FILE_CORRUPT;
+
+ IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfTypeSpec(pRec, ppvSig, pcbSig));
+
+ return hr;
+} // MDInternalRW::GetTypeSpecFromToken
+
+
+//*****************************************************************************
+// Return contents of Pinvoke given the forwarded member token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetPinvokeMap(
+ mdToken tk, // [IN] FieldDef, MethodDef or MethodImpl.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ LPCSTR *pszImportName, // [OUT] Import name.
+ mdModuleRef *pmrImportDLL) // [OUT] ModuleRef token for the target DLL.
+{
+ ImplMapRec *pRecord;
+ ULONG iRecord;
+ HRESULT hr = S_OK;
+
+ LOCKREAD();
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord));
+ if (InvalidRid(iRecord))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+ else
+ IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(iRecord, &pRecord));
+
+ if (pdwMappingFlags)
+ *pdwMappingFlags = m_pStgdb->m_MiniMd.getMappingFlagsOfImplMap(pRecord);
+ if (pszImportName != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.getImportNameOfImplMap(pRecord, pszImportName));
+ }
+ if (pmrImportDLL)
+ *pmrImportDLL = m_pStgdb->m_MiniMd.getImportScopeOfImplMap(pRecord);
+ErrExit:
+ return hr;
+} // MDInternalRW::GetPinvokeMap
+
+//*****************************************************************************
+// convert a text signature to com format
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::ConvertTextSigToComSig(// Return hresult.
+ BOOL fCreateTrIfNotFound, // create typeref if not found or not
+ LPCSTR pSignature, // class file format signature
+ CQuickBytes *pqbNewSig, // [OUT] place holder for COM+ signature
+ ULONG *pcbCount) // [OUT] the result size of signature
+{
+ return E_NOTIMPL;
+} // _ConvertTextSigToComSig
+
+//*****************************************************************************
+// This is a way for the EE to associate some data with this RW metadata to
+// be released when this RW goes away. This is useful when a RO metadata is
+// converted to RW, because arbitrary threads can be executing in the RO.
+// So, we hold onto the RO here, and when the module shuts down, we release it.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::SetUserContextData(// S_OK or E_NOTIMPL
+ IUnknown *pIUnk) // The user context.
+{
+ // Only one chance to do this.
+ if (m_pUserUnk)
+ return E_UNEXPECTED;
+ m_pUserUnk = pIUnk;
+ return S_OK;
+} // MDInternalRW::SetUserContextData
+
+//*****************************************************************************
+// determine if a token is valid or not
+//*****************************************************************************
+BOOL MDInternalRW::IsValidToken( // True or False.
+ mdToken tk) // [IN] Given token.
+{
+ RID rid = RidFromToken(tk);
+ // no need to lock on this function.
+ if (rid == 0)
+ {
+ return FALSE;
+ }
+ switch (TypeFromToken(tk))
+ {
+ case mdtModule:
+ // can have only one module record
+ return (rid <= m_pStgdb->m_MiniMd.getCountModules());
+ case mdtTypeRef:
+ return (rid <= m_pStgdb->m_MiniMd.getCountTypeRefs());
+ case mdtTypeDef:
+ return (rid <= m_pStgdb->m_MiniMd.getCountTypeDefs());
+ case mdtFieldDef:
+ return (rid <= m_pStgdb->m_MiniMd.getCountFields());
+ case mdtMethodDef:
+ return (rid <= m_pStgdb->m_MiniMd.getCountMethods());
+ case mdtParamDef:
+ return (rid <= m_pStgdb->m_MiniMd.getCountParams());
+ case mdtInterfaceImpl:
+ return (rid <= m_pStgdb->m_MiniMd.getCountInterfaceImpls());
+ case mdtMemberRef:
+ return (rid <= m_pStgdb->m_MiniMd.getCountMemberRefs());
+ case mdtCustomAttribute:
+ return (rid <= m_pStgdb->m_MiniMd.getCountCustomAttributes());
+ case mdtPermission:
+ return (rid <= m_pStgdb->m_MiniMd.getCountDeclSecuritys());
+ case mdtSignature:
+ return (rid <= m_pStgdb->m_MiniMd.getCountStandAloneSigs());
+ case mdtEvent:
+ return (rid <= m_pStgdb->m_MiniMd.getCountEvents());
+ case mdtProperty:
+ return (rid <= m_pStgdb->m_MiniMd.getCountPropertys());
+ case mdtModuleRef:
+ return (rid <= m_pStgdb->m_MiniMd.getCountModuleRefs());
+ case mdtTypeSpec:
+ return (rid <= m_pStgdb->m_MiniMd.getCountTypeSpecs());
+ case mdtAssembly:
+ return (rid <= m_pStgdb->m_MiniMd.getCountAssemblys());
+ case mdtAssemblyRef:
+ return (rid <= m_pStgdb->m_MiniMd.getCountAssemblyRefs());
+ case mdtFile:
+ return (rid <= m_pStgdb->m_MiniMd.getCountFiles());
+ case mdtExportedType:
+ return (rid <= m_pStgdb->m_MiniMd.getCountExportedTypes());
+ case mdtManifestResource:
+ return (rid <= m_pStgdb->m_MiniMd.getCountManifestResources());
+ case mdtMethodSpec:
+ return (rid <= m_pStgdb->m_MiniMd.getCountMethodSpecs());
+ case mdtString:
+ // need to check the user string heap
+ return m_pStgdb->m_MiniMd.m_UserStringHeap.IsValidIndex(rid);
+ }
+ return FALSE;
+} // MDInternalRW::IsValidToken
+
+mdModule MDInternalRW::GetModuleFromScope(void)
+{
+ return TokenFromRid(1, mdtModule);
+} // MDInternalRW::GetModuleFromScope
+
+//*****************************************************************************
+// Given a MetaData with ENC changes, apply those changes to this MetaData.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::ApplyEditAndContinue( // S_OK or error.
+ MDInternalRW *pDeltaMD) // Interface to MD with the ENC delta.
+{
+ HRESULT hr; // A result.
+ // Get the MiniMd on the delta.
+
+ LOCKWRITEIFFAILRET();
+
+ CMiniMdRW &mdDelta = pDeltaMD->m_pStgdb->m_MiniMd;
+ CMiniMdRW &mdBase = m_pStgdb->m_MiniMd;
+
+
+ IfFailGo(mdBase.ConvertToRW());
+ IfFailGo(mdBase.ApplyDelta(mdDelta));
+ErrExit:
+ return hr;
+} // MDInternalRW::ApplyEditAndContinue
+
+//*****************************************************************************
+// Given a MetaData with ENC changes, enumerate the changed tokens.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::EnumDeltaTokensInit( // return hresult
+ HENUMInternal *phEnum) // Enumerator to initialize.
+{
+ HRESULT hr = S_OK; // A result.
+ ULONG index; // Loop control.
+ ENCLogRec *pRec; // An ENCLog record.
+
+ // Vars for query.
+ _ASSERTE(phEnum);
+ memset(phEnum, 0, sizeof(HENUMInternal));
+
+ // cache the tkKind and the scope
+ phEnum->m_tkKind = 0;
+
+ phEnum->m_EnumType = MDSimpleEnum;
+
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = 1; index <= m_pStgdb->m_MiniMd.m_Schema.m_cRecs[TBL_ENCLog]; ++index)
+ {
+ // Get the token type; see if it is a real token.
+ IfFailGo(m_pStgdb->m_MiniMd.GetENCLogRecord(index, &pRec));
+ if (CMiniMdRW::IsRecId(pRec->GetToken()))
+ continue;
+ // If there is a function code, that means that this flags a child-record
+ // addition. The child record will generate its own token, so did the
+ // parent, so skip the record.
+ if (pRec->GetFuncCode())
+ continue;
+
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ phEnum,
+ pRec->GetToken()));
+ }
+
+ErrExit:
+ // we are done
+ return hr;
+} // MDInternalRW::EnumDeltaTokensInit
+
+
+//*****************************************************************************
+// Static function to apply a delta md. This is what the EE calls to apply
+// the metadata updates from an update PE to live metadata.
+// <TODO>MAY REPLACE THE IMDInternalImport POINTER!</TODO>
+//*****************************************************************************
+__checkReturn
+HRESULT MDApplyEditAndContinue( // S_OK or error.
+ IMDInternalImport **ppIMD, // [in, out] The metadata to be updated.
+ IMDInternalImportENC *pDeltaMD) // [in] The delta metadata.
+{
+ HRESULT hr; // A result.
+ IMDInternalImportENC *pENC; // ENC interface on the metadata.
+
+ // If the input metadata isn't RW, convert it.
+ hr = (*ppIMD)->QueryInterface(IID_IMDInternalImportENC, (void**)&pENC);
+ if (FAILED(hr))
+ {
+ IfFailGo(ConvertRO2RW(*ppIMD, IID_IMDInternalImportENC, (void**)&pENC));
+ // Replace the old interface pointer with the ENC one.
+ (*ppIMD)->Release();
+ IfFailGo(pENC->QueryInterface(IID_IMDInternalImport, (void**)ppIMD));
+ }
+
+ // Apply the delta to the input metadata.
+ hr = pENC->ApplyEditAndContinue(static_cast<MDInternalRW*>(pDeltaMD));
+
+ErrExit:
+ if (pENC)
+ pENC->Release();
+ return hr;
+} // MDApplyEditAndContinue
+
+//*****************************************************************************
+// Given a scope, return the table size and table ptr for a given index
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::GetTableInfoWithIndex( // return size
+ ULONG index, // [IN] pass in the index
+ void **pTable, // [OUT] pointer to table at index
+ void **pTableSize) // [OUT] size of table at index
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+//*****************************************************************************
+// Given a delta metadata byte stream, apply the changes to the current metadata
+// object returning the resulting metadata object in ppv
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRW::ApplyEditAndContinue(
+ void *pDeltaMD, // [IN] the delta metadata
+ ULONG cbDeltaMD, // [IN] length of pData
+ IMDInternalImport **ppv) // [OUT] the resulting metadata interface
+{
+ _ASSERTE(pDeltaMD);
+ _ASSERTE(ppv);
+
+ // debugging-specific usages don't need SO hardening
+ SO_NOT_MAINLINE_FUNCTION;
+
+ HRESULT hr = E_FAIL;
+ IMDInternalImportENC *pDeltaMDImport = NULL;
+
+ IfFailGo(GetInternalWithRWFormat(pDeltaMD, cbDeltaMD, 0, IID_IMDInternalImportENC, (void**)&pDeltaMDImport));
+
+ *ppv = this;
+ IfFailGo(MDApplyEditAndContinue(ppv, pDeltaMDImport));
+
+ErrExit:
+ if (pDeltaMDImport)
+ pDeltaMDImport->Release();
+
+ return hr;
+} // MDInternalRW::ApplyEditAndContinue
+
+#endif //FEATURE_METADATA_INTERNAL_APIS
diff --git a/src/md/enc/metamodelenc.cpp b/src/md/enc/metamodelenc.cpp
new file mode 100644
index 0000000000..1b3c733ae4
--- /dev/null
+++ b/src/md/enc/metamodelenc.cpp
@@ -0,0 +1,468 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// MetaModelENC.cpp
+//
+
+//
+// Implementation for applying ENC deltas to a MiniMd.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include <limits.h>
+#include <posterror.h>
+#include <metamodelrw.h>
+#include <stgio.h>
+#include <stgtiggerstorage.h>
+#include "mdlog.h"
+#include "rwutil.h"
+
+ULONG CMiniMdRW::m_SuppressedDeltaColumns[TBL_COUNT] = {0};
+
+//*****************************************************************************
+// Copy the data from one MiniMd to another.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyRecordDelta(
+ CMiniMdRW &mdDelta, // The delta MetaData.
+ ULONG ixTbl, // The table with the data.
+ void *pDelta, // The delta MetaData record.
+ void *pRecord) // The record to update.
+{
+ HRESULT hr = S_OK;
+ ULONG mask = m_SuppressedDeltaColumns[ixTbl];
+
+ for (ULONG ixCol = 0; ixCol<m_TableDefs[ixTbl].m_cCols; ++ixCol, mask >>= 1)
+ { // Skip certain pointer columns.
+ if (mask & 0x01)
+ continue;
+
+ ULONG val = mdDelta.GetCol(ixTbl, ixCol, pDelta);
+ IfFailRet(PutCol(ixTbl, ixCol, pRecord, val));
+ }
+ return hr;
+} // CMiniMdRW::ApplyRecordDelta
+
+//*****************************************************************************
+// Apply a delta record to a table, generically.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyTableDelta(
+ CMiniMdRW &mdDelta, // Interface to MD with the ENC delta.
+ ULONG ixTbl, // Table index to update.
+ RID iRid, // RID of the changed item.
+ int fc) // Function code of update.
+{
+ HRESULT hr = S_OK;
+ void *pRec; // Record in existing MetaData.
+ void *pDeltaRec; // Record if Delta MetaData.
+ RID newRid; // Rid of new record.
+
+ // Get the delta record.
+ IfFailGo(mdDelta.GetDeltaRecord(ixTbl, iRid, &pDeltaRec));
+ // Get the record from the base metadata.
+ if (iRid > m_Schema.m_cRecs[ixTbl])
+ { // Added record. Each addition is the next one.
+ _ASSERTE(iRid == m_Schema.m_cRecs[ixTbl] + 1);
+ switch (ixTbl)
+ {
+ case TBL_TypeDef:
+ IfFailGo(AddTypeDefRecord(reinterpret_cast<TypeDefRec **>(&pRec), &newRid));
+ break;
+ case TBL_Method:
+ IfFailGo(AddMethodRecord(reinterpret_cast<MethodRec **>(&pRec), &newRid));
+ break;
+ case TBL_EventMap:
+ IfFailGo(AddEventMapRecord(reinterpret_cast<EventMapRec **>(&pRec), &newRid));
+ break;
+ case TBL_PropertyMap:
+ IfFailGo(AddPropertyMapRecord(reinterpret_cast<PropertyMapRec **>(&pRec), &newRid));
+ break;
+ default:
+ IfFailGo(AddRecord(ixTbl, &pRec, &newRid));
+ break;
+ }
+ IfNullGo(pRec);
+ _ASSERTE(iRid == newRid);
+ }
+ else
+ { // Updated record.
+ IfFailGo(getRow(ixTbl, iRid, &pRec));
+ }
+
+ // Copy the record info.
+ IfFailGo(ApplyRecordDelta(mdDelta, ixTbl, pDeltaRec, pRec));
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::ApplyTableDelta
+
+//*****************************************************************************
+// Get the record from a Delta MetaData that corresponds to the actual record.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GetDeltaRecord(
+ ULONG ixTbl, // Table.
+ ULONG iRid, // Record in the table.
+ void **ppRecord)
+{
+ HRESULT hr;
+ ULONG iMap; // RID in map table.
+ ENCMapRec *pMap; // Row in map table.
+
+ *ppRecord = NULL;
+ // If no remap, just return record directly.
+ if ((m_Schema.m_cRecs[TBL_ENCMap] == 0) || (ixTbl == TBL_Module) || !IsMinimalDelta())
+ {
+ return getRow(ixTbl, iRid, ppRecord);
+ }
+
+ // Use the remap table to find the physical row containing this logical row.
+ iMap = (*m_rENCRecs)[ixTbl];
+ IfFailRet(GetENCMapRecord(iMap, &pMap));
+
+ // Search for desired record.
+ while ((TblFromRecId(pMap->GetToken()) == ixTbl) && (RidFromRecId(pMap->GetToken()) < iRid))
+ {
+ IfFailRet(GetENCMapRecord(++iMap, &pMap));
+ }
+
+ _ASSERTE((TblFromRecId(pMap->GetToken()) == ixTbl) && (RidFromRecId(pMap->GetToken()) == iRid));
+
+ // Relative position within table's group in map is physical rid.
+ iRid = iMap - (*m_rENCRecs)[ixTbl] + 1;
+
+ return getRow(ixTbl, iRid, ppRecord);
+} // CMiniMdRW::GetDeltaRecord
+
+//*****************************************************************************
+// Given a MetaData with ENC changes, apply those changes to this MetaData.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyHeapDeltas(
+ CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
+{
+ if (mdDelta.IsMinimalDelta())
+ {
+ return ApplyHeapDeltasWithMinimalDelta(mdDelta);
+ }
+ else
+ {
+ return ApplyHeapDeltasWithFullDelta(mdDelta);
+ }
+}// CMiniMdRW::ApplyHeapDeltas
+
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyHeapDeltasWithMinimalDelta(
+ CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
+{
+ HRESULT hr = S_OK;
+
+ // Extend the heaps with EnC minimal delta
+ IfFailGo(m_StringHeap.AddStringHeap(
+ &(mdDelta.m_StringHeap),
+ 0)); // Start offset in the mdDelta
+ IfFailGo(m_BlobHeap.AddBlobHeap(
+ &(mdDelta.m_BlobHeap),
+ 0)); // Start offset in the mdDelta
+ IfFailGo(m_UserStringHeap.AddBlobHeap(
+ &(mdDelta.m_UserStringHeap),
+ 0)); // Start offset in the mdDelta
+ // We never do a minimal delta with the guid heap
+ IfFailGo(m_GuidHeap.AddGuidHeap(
+ &(mdDelta.m_GuidHeap),
+ m_GuidHeap.GetSize())); // Starting offset in the full delta guid heap
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::ApplyHeapDeltasWithMinimalDelta
+
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyHeapDeltasWithFullDelta(
+ CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
+{
+ HRESULT hr = S_OK;
+
+ // Extend the heaps with EnC full delta
+ IfFailRet(m_StringHeap.AddStringHeap(
+ &(mdDelta.m_StringHeap),
+ m_StringHeap.GetUnalignedSize())); // Starting offset in the full delta string heap
+ IfFailRet(m_BlobHeap.AddBlobHeap(
+ &(mdDelta.m_BlobHeap),
+ m_BlobHeap.GetUnalignedSize())); // Starting offset in the full delta blob heap
+ IfFailRet(m_UserStringHeap.AddBlobHeap(
+ &(mdDelta.m_UserStringHeap),
+ m_UserStringHeap.GetUnalignedSize())); // Starting offset in the full delta user string heap
+ IfFailRet(m_GuidHeap.AddGuidHeap(
+ &(mdDelta.m_GuidHeap),
+ m_GuidHeap.GetSize())); // Starting offset in the full delta guid heap
+
+ return hr;
+} // CMiniMdRW::ApplyHeapDeltasWithFullDelta
+
+//*****************************************************************************
+// Driver for the delta process.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyDelta(
+ CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
+{
+ HRESULT hr = S_OK;
+ ULONG iENC; // Loop control.
+ ULONG iRid; // RID of some record.
+ ULONG iNew; // RID of a new record.
+ int i; // Loop control.
+ ULONG ixTbl; // A table.
+
+#ifdef _DEBUG
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_ApplyDeltaBreak))
+ {
+ _ASSERTE(!"CMiniMDRW::ApplyDelta()");
+ }
+#endif // _DEBUG
+
+ // Init the suppressed column table. We know this one isn't zero...
+ if (m_SuppressedDeltaColumns[TBL_TypeDef] == 0)
+ {
+ m_SuppressedDeltaColumns[TBL_EventMap] = (1 << EventMapRec::COL_EventList);
+ m_SuppressedDeltaColumns[TBL_PropertyMap] = (1 << PropertyMapRec::COL_PropertyList);
+ m_SuppressedDeltaColumns[TBL_EventMap] = (1 << EventMapRec::COL_EventList);
+ m_SuppressedDeltaColumns[TBL_Method] = (1 << MethodRec::COL_ParamList);
+ m_SuppressedDeltaColumns[TBL_TypeDef] = (1 << TypeDefRec::COL_FieldList)|(1<<TypeDefRec::COL_MethodList);
+ }
+
+ // Verify the version of the MD.
+ if (m_Schema.m_major != mdDelta.m_Schema.m_major ||
+ m_Schema.m_minor != mdDelta.m_Schema.m_minor)
+ {
+ _ASSERTE(!"Version of Delta MetaData is a incompatible with current MetaData.");
+ //<TODO>@FUTURE: unique error in the future since we are not shipping ENC.</TODO>
+ return E_INVALIDARG;
+ }
+
+ // verify MVIDs.
+ ModuleRec *pModDelta;
+ ModuleRec *pModBase;
+ IfFailGo(mdDelta.GetModuleRecord(1, &pModDelta));
+ IfFailGo(GetModuleRecord(1, &pModBase));
+ GUID GuidDelta;
+ GUID GuidBase;
+ IfFailGo(mdDelta.getMvidOfModule(pModDelta, &GuidDelta));
+ IfFailGo(getMvidOfModule(pModBase, &GuidBase));
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) && (GuidDelta != GuidBase))
+ {
+ _ASSERTE(!"Delta MetaData has different base than current MetaData.");
+ return E_INVALIDARG;
+ }
+
+ // Verify that the delta is based on the base.
+ IfFailGo(mdDelta.getEncBaseIdOfModule(pModDelta, &GuidDelta));
+ IfFailGo(getEncBaseIdOfModule(pModBase,&GuidBase));
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) && (GuidDelta != GuidBase))
+ {
+ _ASSERTE(!"The Delta MetaData is based on a different generation than the current MetaData.");
+ return E_INVALIDARG;
+ }
+
+ // Let the other md prepare for sparse records.
+ IfFailGo(mdDelta.StartENCMap());
+
+ // Fix the heaps.
+ IfFailGo(ApplyHeapDeltas(mdDelta));
+
+ // Truncate some tables in preparation to copy in new ENCLog data.
+ for (i = 0; (ixTbl = m_TruncatedEncTables[i]) != (ULONG)-1; ++i)
+ {
+ m_Tables[ixTbl].Delete();
+ IfFailGo(m_Tables[ixTbl].InitializeEmpty_WithRecordCount(
+ m_TableDefs[ixTbl].m_cbRec,
+ mdDelta.m_Schema.m_cRecs[ixTbl]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl));
+ m_Schema.m_cRecs[ixTbl] = 0;
+ }
+
+ // For each record in the ENC log...
+ for (iENC = 1; iENC <= mdDelta.m_Schema.m_cRecs[TBL_ENCLog]; ++iENC)
+ {
+ // Get the record, and the updated token.
+ ENCLogRec *pENC;
+ IfFailGo(mdDelta.GetENCLogRecord(iENC, &pENC));
+ ENCLogRec *pENC2;
+ IfFailGo(AddENCLogRecord(&pENC2, &iNew));
+ IfNullGo(pENC2);
+ ENCLogRec *pENC3;
+ _ASSERTE(iNew == iENC);
+ ULONG func = pENC->GetFuncCode();
+ pENC2->SetFuncCode(pENC->GetFuncCode());
+ pENC2->SetToken(pENC->GetToken());
+
+ // What kind of record is this?
+ if (IsRecId(pENC->GetToken()))
+ { // Non-token table
+ iRid = RidFromRecId(pENC->GetToken());
+ ixTbl = TblFromRecId(pENC->GetToken());
+ }
+ else
+ { // Token table.
+ iRid = RidFromToken(pENC->GetToken());
+ ixTbl = GetTableForToken(pENC->GetToken());
+ }
+
+ RID rid_Ignore;
+ // Switch based on the function code.
+ switch (func)
+ {
+ case eDeltaMethodCreate:
+ // Next ENC record will define the new Method.
+ MethodRec *pMethodRecord;
+ IfFailGo(AddMethodRecord(&pMethodRecord, &rid_Ignore));
+ IfFailGo(AddMethodToTypeDef(iRid, m_Schema.m_cRecs[TBL_Method]));
+ break;
+
+ case eDeltaParamCreate:
+ // Next ENC record will define the new Param. This record is
+ // tricky because params will be re-ordered based on their sequence,
+ // but the sequence isn't set until the NEXT record is applied.
+ // So, for ParamCreate only, apply the param record delta before
+ // adding the parent-child linkage.
+ ParamRec *pParamRecord;
+ IfFailGo(AddParamRecord(&pParamRecord, &rid_Ignore));
+
+ // Should have recorded a Param delta after the Param add.
+ _ASSERTE(iENC<mdDelta.m_Schema.m_cRecs[TBL_ENCLog]);
+ IfFailGo(mdDelta.GetENCLogRecord(iENC+1, &pENC3));
+ _ASSERTE(pENC3->GetFuncCode() == 0);
+ _ASSERTE(GetTableForToken(pENC3->GetToken()) == TBL_Param);
+ IfFailGo(ApplyTableDelta(mdDelta, TBL_Param, RidFromToken(pENC3->GetToken()), eDeltaFuncDefault));
+
+ // Now that Param record is OK, set up linkage.
+ IfFailGo(AddParamToMethod(iRid, m_Schema.m_cRecs[TBL_Param]));
+ break;
+
+ case eDeltaFieldCreate:
+ // Next ENC record will define the new Field.
+ FieldRec *pFieldRecord;
+ IfFailGo(AddFieldRecord(&pFieldRecord, &rid_Ignore));
+ IfFailGo(AddFieldToTypeDef(iRid, m_Schema.m_cRecs[TBL_Field]));
+ break;
+
+ case eDeltaPropertyCreate:
+ // Next ENC record will define the new Property.
+ PropertyRec *pPropertyRecord;
+ IfFailGo(AddPropertyRecord(&pPropertyRecord, &rid_Ignore));
+ IfFailGo(AddPropertyToPropertyMap(iRid, m_Schema.m_cRecs[TBL_Property]));
+ break;
+
+ case eDeltaEventCreate:
+ // Next ENC record will define the new Event.
+ EventRec *pEventRecord;
+ IfFailGo(AddEventRecord(&pEventRecord, &rid_Ignore));
+ IfFailGo(AddEventToEventMap(iRid, m_Schema.m_cRecs[TBL_Event]));
+ break;
+
+ case eDeltaFuncDefault:
+ IfFailGo(ApplyTableDelta(mdDelta, ixTbl, iRid, func));
+ break;
+
+ default:
+ _ASSERTE(!"Unexpected function in ApplyDelta");
+ IfFailGo(E_UNEXPECTED);
+ break;
+ }
+ }
+ m_Schema.m_cRecs[TBL_ENCLog] = mdDelta.m_Schema.m_cRecs[TBL_ENCLog];
+
+ErrExit:
+ // Store the result for returning (IfFailRet will modify hr)
+ HRESULT hrReturn = hr;
+ IfFailRet(mdDelta.EndENCMap());
+
+#ifndef FEATURE_CORECLR
+ if (SUCCEEDED(hrReturn) &&
+ CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) &&
+ CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_UseMinimalDeltas))
+ {
+ GUID GuidNewBase;
+
+ // We'll use the delta's 'delta guid' for our new base guid
+ IfFailRet(mdDelta.getEncIdOfModule(pModDelta, &GuidNewBase));
+
+ IfFailRet(PutGuid(TBL_Module, ModuleRec::COL_EncBaseId, pModBase, GuidNewBase));
+ }
+#endif //!FEATURE_CORECLR
+
+ return hrReturn;
+} // CMiniMdRW::ApplyDelta
+
+//*****************************************************************************
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::StartENCMap() // S_OK or error.
+{
+ HRESULT hr = S_OK;
+ ULONG iENC; // Loop control.
+ ULONG ixTbl; // A table.
+ int ixTblPrev = -1; // Table previously seen.
+
+ _ASSERTE(m_rENCRecs == 0);
+
+ if (m_Schema.m_cRecs[TBL_ENCMap] == 0)
+ return S_OK;
+
+ // Build an array of pointers into the ENCMap table for fast access to the ENCMap
+ // for each table.
+ m_rENCRecs = new (nothrow) ULONGARRAY;
+ IfNullGo(m_rENCRecs);
+ if (!m_rENCRecs->AllocateBlock(TBL_COUNT))
+ IfFailGo(E_OUTOFMEMORY);
+ for (iENC = 1; iENC <= m_Schema.m_cRecs[TBL_ENCMap]; ++iENC)
+ {
+ ENCMapRec *pMap;
+ IfFailGo(GetENCMapRecord(iENC, &pMap));
+ ixTbl = TblFromRecId(pMap->GetToken());
+ _ASSERTE((int)ixTbl >= ixTblPrev);
+ _ASSERTE(ixTbl < TBL_COUNT);
+ _ASSERTE(ixTbl != TBL_ENCMap);
+ _ASSERTE(ixTbl != TBL_ENCLog);
+ if ((int)ixTbl == ixTblPrev)
+ continue;
+ // Catch up on any skipped tables.
+ while (ixTblPrev < (int)ixTbl)
+ {
+ (*m_rENCRecs)[++ixTblPrev] = iENC;
+ }
+ }
+ while (ixTblPrev < TBL_COUNT-1)
+ {
+ (*m_rENCRecs)[++ixTblPrev] = iENC;
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::StartENCMap
+
+//*****************************************************************************
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::EndENCMap()
+{
+ if (m_rENCRecs != NULL)
+ {
+ delete m_rENCRecs;
+ m_rENCRecs = NULL;
+ }
+
+ return S_OK;
+} // CMiniMdRW::EndENCMap
diff --git a/src/md/enc/metamodelrw.cpp b/src/md/enc/metamodelrw.cpp
new file mode 100644
index 0000000000..70b8e72a44
--- /dev/null
+++ b/src/md/enc/metamodelrw.cpp
@@ -0,0 +1,8894 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// MetaModelRW.cpp
+//
+
+//
+// Implementation for the Read/Write MiniMD code.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include <limits.h>
+#include <posterror.h>
+#include <metamodelrw.h>
+#include <stgio.h>
+#include <stgtiggerstorage.h>
+#include "mdlog.h"
+#include "rwutil.h"
+#include "../compiler/importhelper.h"
+#include "metadata.h"
+#include "streamutil.h"
+
+#include "../hotdata/hotdataformat.h"
+
+#ifdef FEATURE_PREJIT
+#include "corcompile.h"
+#endif
+
+#ifdef _MSC_VER
+#pragma intrinsic(memcpy)
+#endif
+
+//********** RidMap ***********************************************************
+typedef CDynArray<RID> RIDMAP;
+
+
+//********** Types. ***********************************************************
+#define INDEX_ROW_COUNT_THRESHOLD 25
+
+
+//********** Locals. **********************************************************
+enum MetaDataSizeIndex
+{
+ // Standard MetaData sizes (from VBA library).
+ MDSizeIndex_Standard = 0,
+ // Minimal MetaData sizes used mainly by Reflection.Emit for small assemblies (emitting 1 type per
+ // assembly).
+ // Motivated by the performance requirement in collectible types.
+ MDSizeIndex_Minimal = 1,
+
+ MDSizeIndex_Count
+}; // enum MetaDataSizeIndex
+
+// Gets index of MetaData sizes used to access code:g_PoolSizeInfo, code:g_HashSize and code:g_TblSizeInfo.
+static
+enum MetaDataSizeIndex
+GetMetaDataSizeIndex(const OptionValue *pOptionValue)
+{
+ if (pOptionValue->m_InitialSize == MDInitialSizeMinimal)
+ {
+ return MDSizeIndex_Minimal;
+ }
+ _ASSERTE(pOptionValue->m_InitialSize == MDInitialSizeDefault);
+ return MDSizeIndex_Standard;
+} // GetSizeHint
+
+#define IX_STRING_POOL 0
+#define IX_US_BLOB_POOL 1
+#define IX_GUID_POOL 2
+#define IX_BLOB_POOL 3
+
+static
+const ULONG
+g_PoolSizeInfo[MDSizeIndex_Count][4][2] =
+{
+ { // Standard pool sizes { Size in bytes, Number of buckets in hash } (code:MDSizeIndex_Standard).
+ {20000, 449}, // Strings
+ {5000, 150}, // User literal string blobs
+ {256, 16}, // Guids
+ {20000, 449} // Blobs
+ },
+ { // Minimal pool sizes { Size in bytes, Number of buckets in hash } (code:MDSizeIndex_Minimal).
+ {300, 10}, // Strings
+ {50, 5}, // User literal string blobs
+ {16, 3}, // Guids
+ {200, 10} // Blobs
+ }
+};
+
+static
+const ULONG
+g_HashSize[MDSizeIndex_Count] =
+{
+ 257, // Standard MetaData size (code:MDSizeIndex_Standard).
+ 50 // Minimal MetaData size (code:MDSizeIndex_Minimal).
+};
+
+static
+const ULONG
+g_TblSizeInfo[MDSizeIndex_Count][TBL_COUNT] =
+{
+ // Standard table sizes (code:MDSizeIndex_Standard).
+ {
+ 1, // Module
+ 90, // TypeRef
+ 65, // TypeDef
+ 0, // FieldPtr
+ 400, // Field
+ 0, // MethodPtr
+ 625, // Method
+ 0, // ParamPtr
+ 1200, // Param
+ 6, // InterfaceImpl
+ 500, // MemberRef
+ 400, // Constant
+ 650, // CustomAttribute
+ 0, // FieldMarshal
+ 0, // DeclSecurity
+ 0, // ClassLayout
+ 0, // FieldLayout
+ 175, // StandAloneSig
+ 0, // EventMap
+ 0, // EventPtr
+ 0, // Event
+ 5, // PropertyMap
+ 0, // PropertyPtr
+ 25, // Property
+ 45, // MethodSemantics
+ 20, // MethodImpl
+ 0, // ModuleRef
+ 0, // TypeSpec
+ 0, // ImplMap
+ 0, // FieldRVA
+ 0, // ENCLog
+ 0, // ENCMap
+ 0, // Assembly
+ 0, // AssemblyProcessor
+ 0, // AssemblyOS
+ 0, // AssemblyRef
+ 0, // AssemblyRefProcessor
+ 0, // AssemblyRefOS
+ 0, // File
+ 0, // ExportedType
+ 0, // ManifestResource
+ 0, // NestedClass
+ 0, // GenericParam
+ 0, // MethodSpec
+ 0, // GenericParamConstraint
+ },
+ // Minimal table sizes (code:MDSizeIndex_Minimal).
+ {
+ 1, // Module
+ 2, // TypeRef
+ 2, // TypeDef
+ 0, // FieldPtr
+ 2, // Field
+ 0, // MethodPtr
+ 2, // Method
+ 0, // ParamPtr
+ 0, // Param
+ 0, // InterfaceImpl
+ 1, // MemberRef
+ 0, // Constant
+ 0, // CustomAttribute
+ 0, // FieldMarshal
+ 0, // DeclSecurity
+ 0, // ClassLayout
+ 0, // FieldLayout
+ 0, // StandAloneSig
+ 0, // EventMap
+ 0, // EventPtr
+ 0, // Event
+ 0, // PropertyMap
+ 0, // PropertyPtr
+ 0, // Property
+ 0, // MethodSemantics
+ 0, // MethodImpl
+ 0, // ModuleRef
+ 0, // TypeSpec
+ 0, // ImplMap
+ 0, // FieldRVA
+ 0, // ENCLog
+ 0, // ENCMap
+ 1, // Assembly
+ 0, // AssemblyProcessor
+ 0, // AssemblyOS
+ 1, // AssemblyRef
+ 0, // AssemblyRefProcessor
+ 0, // AssemblyRefOS
+ 0, // File
+ 0, // ExportedType
+ 0, // ManifestResource
+ 0, // NestedClass
+ 0, // GenericParam
+ 0, // MethodSpec
+ 0, // GenericParamConstraint
+ }
+}; // g_TblSizeInfo
+
+struct TblIndex
+{
+ ULONG m_iName; // Name column.
+ ULONG m_iParent; // Parent column, if any.
+ ULONG m_Token; // Token of the table.
+};
+
+// Table to drive generic named-item indexing.
+const TblIndex g_TblIndex[TBL_COUNT] =
+{
+ {(ULONG) -1, (ULONG) -1, mdtModule}, // Module
+ {TypeRefRec::COL_Name, (ULONG) -1, mdtTypeRef}, // TypeRef
+ {TypeDefRec::COL_Name, (ULONG) -1, mdtTypeDef}, // TypeDef
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // FieldPtr
+ {(ULONG) -1, (ULONG) -1, mdtFieldDef}, // Field
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // MethodPtr
+ {(ULONG) -1, (ULONG) -1, mdtMethodDef}, // Method
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ParamPtr
+ {(ULONG) -1, (ULONG) -1, mdtParamDef}, // Param
+ {(ULONG) -1, (ULONG) -1, mdtInterfaceImpl}, // InterfaceImpl
+ {MemberRefRec::COL_Name, MemberRefRec::COL_Class, mdtMemberRef}, // MemberRef
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // Constant
+ {(ULONG) -1, (ULONG) -1, mdtCustomAttribute},// CustomAttribute
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // FieldMarshal
+ {(ULONG) -1, (ULONG) -1, mdtPermission}, // DeclSecurity
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ClassLayout
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // FieldLayout
+ {(ULONG) -1, (ULONG) -1, mdtSignature}, // StandAloneSig
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // EventMap
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // EventPtr
+ {(ULONG) -1, (ULONG) -1, mdtEvent}, // Event
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // PropertyMap
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // PropertyPtr
+ {(ULONG) -1, (ULONG) -1, mdtProperty}, // Property
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // MethodSemantics
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // MethodImpl
+ {(ULONG) -1, (ULONG) -1, mdtModuleRef}, // ModuleRef
+ {(ULONG) -1, (ULONG) -1, mdtTypeSpec}, // TypeSpec
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ImplMap <TODO>@FUTURE: Check that these are the right entries here.</TODO>
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // FieldRVA <TODO>@FUTURE: Check that these are the right entries here.</TODO>
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ENCLog
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ENCMap
+ {(ULONG) -1, (ULONG) -1, mdtAssembly}, // Assembly <TODO>@FUTURE: Update with the right number.</TODO>
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // AssemblyProcessor <TODO>@FUTURE: Update with the right number.</TODO>
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // AssemblyOS <TODO>@FUTURE: Update with the right number.</TODO>
+ {(ULONG) -1, (ULONG) -1, mdtAssemblyRef}, // AssemblyRef <TODO>@FUTURE: Update with the right number.</TODO>
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // AssemblyRefProcessor <TODO>@FUTURE: Update with the right number.</TODO>
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // AssemblyRefOS <TODO>@FUTURE: Update with the right number.</TODO>
+ {(ULONG) -1, (ULONG) -1, mdtFile}, // File <TODO>@FUTURE: Update with the right number.</TODO>
+ {(ULONG) -1, (ULONG) -1, mdtExportedType}, // ExportedType <TODO>@FUTURE: Update with the right number.</TODO>
+ {(ULONG) -1, (ULONG) -1, mdtManifestResource},// ManifestResource <TODO>@FUTURE: Update with the right number.</TODO>
+ {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // NestedClass
+ {(ULONG) -1, (ULONG) -1, mdtGenericParam}, // GenericParam
+ {(ULONG) -1, (ULONG) -1, mdtMethodSpec}, // MethodSpec
+ {(ULONG) -1, (ULONG) -1, mdtGenericParamConstraint},// GenericParamConstraint
+};
+
+ULONG CMiniMdRW::m_TruncatedEncTables[] =
+{
+ TBL_ENCLog,
+ TBL_ENCMap,
+ (ULONG) -1
+};
+
+//*****************************************************************************
+// Given a token type, return the table index.
+//*****************************************************************************
+ULONG CMiniMdRW::GetTableForToken( // Table index, or -1.
+ mdToken tkn) // Token to find.
+{
+ ULONG ixTbl; // Loop control.
+ ULONG type = TypeFromToken(tkn);
+
+ // Get the type -- if a string, no associated table.
+ if (type >= mdtString)
+ return (ULONG) -1;
+ // Table number is same as high-byte of token.
+ ixTbl = type >> 24;
+ // Make sure.
+ _ASSERTE(g_TblIndex[ixTbl].m_Token == type);
+
+ return ixTbl;
+} // CMiniMdRW::GetTableForToken
+
+//*****************************************************************************
+// Given a Table index, return the Token type.
+//*****************************************************************************
+mdToken CMiniMdRW::GetTokenForTable( // Token type, or -1.
+ ULONG ixTbl) // Table index.
+{
+ _ASSERTE(g_TblIndex[ixTbl].m_Token == (ixTbl<<24) || g_TblIndex[ixTbl].m_Token == (ULONG) -1);
+ return g_TblIndex[ixTbl].m_Token;
+} // CMiniMdRW::GetTokenForTable
+
+//*****************************************************************************
+// Helper classes for sorting MiniMdRW tables.
+//*****************************************************************************
+class CQuickSortMiniMdRW
+{
+protected:
+ CMiniMdRW &m_MiniMd; // The MiniMd with the data.
+ ULONG m_ixTbl; // The table.
+ ULONG m_ixCol; // The column.
+ int m_iCount; // How many items in array.
+ int m_iElemSize; // Size of one element.
+ RIDMAP *m_pRidMap; // Rid map that need to be swapped as we swap data
+ bool m_bMapToken; // MapToken handling desired.
+
+ BYTE m_buf[128]; // For swapping.
+
+ HRESULT getRow(UINT32 nIndex, void **ppRecord)
+ {
+ return m_MiniMd.m_Tables[m_ixTbl].GetRecord(nIndex, reinterpret_cast<BYTE **>(ppRecord));
+ }
+ void SetSorted() { m_MiniMd.SetSorted(m_ixTbl, true); }
+
+ HRESULT PrepMapTokens()
+ {
+ HRESULT hr = S_OK;
+
+ // If remap notifications are desired, prepare to collect the info in a RIDMAP.
+ if (m_bMapToken)
+ {
+ _ASSERTE(m_pRidMap == NULL); // Don't call twice.
+ IfNullGo(m_pRidMap = new (nothrow) RIDMAP);
+ if (!m_pRidMap->AllocateBlock(m_iCount + 1))
+ {
+ delete m_pRidMap;
+ m_pRidMap = NULL;
+ IfFailGo(E_OUTOFMEMORY);
+ }
+ for (int i=0; i<= m_iCount; ++i)
+ *(m_pRidMap->Get(i)) = i;
+ }
+
+ ErrExit:
+ return hr;
+ } // CQuickSortMiniMdRW::PrepMapTokens
+
+ __checkReturn
+ HRESULT DoMapTokens()
+ {
+ HRESULT hr;
+ if (m_bMapToken)
+ {
+ mdToken typ = m_MiniMd.GetTokenForTable(m_ixTbl);
+ for (int i=1; i<=m_iCount; ++i)
+ {
+ IfFailRet(m_MiniMd.MapToken(*(m_pRidMap->Get(i)), i, typ));
+ }
+ }
+ return S_OK;
+ } // CQuickSortMiniMdRW::DoMapTokens
+
+public:
+ CQuickSortMiniMdRW(
+ CMiniMdRW &MiniMd, // MiniMd with the data.
+ ULONG ixTbl, // The table.
+ ULONG ixCol, // The column.
+ bool bMapToken) // If true, MapToken handling desired.
+ : m_MiniMd(MiniMd),
+ m_ixTbl(ixTbl),
+ m_ixCol(ixCol),
+ m_pRidMap(NULL),
+ m_bMapToken(bMapToken)
+ {
+ m_iElemSize = m_MiniMd.m_TableDefs[m_ixTbl].m_cbRec;
+ _ASSERTE(m_iElemSize <= (int) sizeof(m_buf));
+ }
+
+ ~CQuickSortMiniMdRW()
+ {
+ if (m_bMapToken)
+ {
+ if (m_pRidMap)
+ {
+ m_pRidMap->Clear();
+ delete m_pRidMap;
+ m_pRidMap = NULL;
+ }
+ m_bMapToken = false;
+ }
+ } // CQuickSortMiniMdRW::~CQuickSortMiniMdRW
+
+ // set the RidMap
+ void SetRidMap(RIDMAP *pRidMap) { m_pRidMap = pRidMap; }
+
+ //*****************************************************************************
+ // Call to sort the array.
+ //*****************************************************************************
+ HRESULT Sort()
+ {
+ HRESULT hr = S_OK;
+
+ INDEBUG(m_MiniMd.Debug_CheckIsLockedForWrite();)
+
+ _ASSERTE(m_MiniMd.IsSortable(m_ixTbl));
+ m_iCount = m_MiniMd.GetCountRecs(m_ixTbl);
+
+ // If remap notifications are desired, prepare to collect the info in a RIDMAP.
+ IfFailGo(PrepMapTokens());
+
+ // We are going to sort tables. Invalidate the hash tables
+ if ( m_MiniMd.m_pLookUpHashs[m_ixTbl] != NULL )
+ {
+ delete m_MiniMd.m_pLookUpHashs[m_ixTbl];
+ m_MiniMd.m_pLookUpHashs[m_ixTbl] = NULL;
+ }
+
+ IfFailGo(SortRange(1, m_iCount));
+
+ // The table is sorted until its next change.
+ SetSorted();
+
+ // If remap notifications were desired, send them.
+ IfFailGo(DoMapTokens());
+
+ ErrExit:
+ return hr;
+ } // CQuickSortMiniMdRW::Sort
+
+ //*****************************************************************************
+ // Call to check whether the array is sorted without altering it.
+ //*****************************************************************************
+ HRESULT CheckSortedWithNoDuplicates()
+ {
+ HRESULT hr = S_OK;
+ int iCount = m_MiniMd.GetCountRecs(m_ixTbl);
+ int nResult;
+
+ m_MiniMd.SetSorted(m_ixTbl, false);
+
+ for (int i = 1; i < iCount; i++)
+ {
+ IfFailGo(Compare(i, i+1, &nResult));
+
+ if (nResult >= 0)
+ {
+ return S_OK;
+ }
+ }
+
+ // The table is sorted until its next change.
+ SetSorted();
+
+ ErrExit:
+ return hr;
+ } // CQuickSortMiniMdRW::CheckSortedWithNoDuplicates
+
+ //*****************************************************************************
+ // Override this function to do the comparison.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT Compare(
+ int iLeft, // First item to compare.
+ int iRight, // Second item to compare.
+ int *pnResult) // -1, 0, or 1
+ {
+ HRESULT hr;
+ void *pLeft;
+ void *pRight;
+ IfFailRet(getRow(iLeft, &pLeft));
+ IfFailRet(getRow(iRight, &pRight));
+ ULONG ulLeft = m_MiniMd.GetCol(m_ixTbl, m_ixCol, pLeft);
+ ULONG ulRight = m_MiniMd.GetCol(m_ixTbl, m_ixCol, pRight);
+
+ if (ulLeft < ulRight)
+ {
+ *pnResult = -1;
+ return S_OK;
+ }
+ if (ulLeft == ulRight)
+ {
+ *pnResult = 0;
+ return S_OK;
+ }
+ *pnResult = 1;
+ return S_OK;
+ } // CQuickSortMiniMdRW::Compare
+
+private:
+ __checkReturn
+ HRESULT SortRange(
+ int iLeft,
+ int iRight)
+ {
+ HRESULT hr;
+ int iLast;
+ int nResult;
+
+ for (;;)
+ {
+ // if less than two elements you're done.
+ if (iLeft >= iRight)
+ {
+ return S_OK;
+ }
+
+ // The mid-element is the pivot, move it to the left.
+ IfFailRet(Compare(iLeft, (iLeft+iRight)/2, &nResult));
+ if (nResult != 0)
+ {
+ IfFailRet(Swap(iLeft, (iLeft+iRight)/2));
+ }
+ iLast = iLeft;
+
+ // move everything that is smaller than the pivot to the left.
+ for (int i = iLeft+1; i <= iRight; i++)
+ {
+ IfFailRet(Compare(i, iLeft, &nResult));
+ if (nResult < 0)
+ {
+ IfFailRet(Swap(i, ++iLast));
+ }
+ }
+
+ // Put the pivot to the point where it is in between smaller and larger elements.
+ IfFailRet(Compare(iLeft, iLast, &nResult));
+ if (nResult != 0)
+ {
+ IfFailRet(Swap(iLeft, iLast));
+ }
+
+ // Sort each partition.
+ int iLeftLast = iLast - 1;
+ int iRightFirst = iLast + 1;
+ if (iLeftLast - iLeft < iRight - iRightFirst)
+ { // Left partition is smaller, sort it recursively
+ IfFailRet(SortRange(iLeft, iLeftLast));
+ // Tail call to sort the right (bigger) partition
+ iLeft = iRightFirst;
+ //iRight = iRight;
+ continue;
+ }
+ else
+ { // Right partition is smaller, sort it recursively
+ IfFailRet(SortRange(iRightFirst, iRight));
+ // Tail call to sort the left (bigger) partition
+ //iLeft = iLeft;
+ iRight = iLeftLast;
+ continue;
+ }
+ }
+ } // CQuickSortMiniMdRW::SortRange
+
+protected:
+ __checkReturn
+ inline HRESULT Swap(
+ int iFirst,
+ int iSecond)
+ {
+ HRESULT hr;
+ void *pFirst;
+ void *pSecond;
+ if (iFirst == iSecond)
+ {
+ return S_OK;
+ }
+
+ PREFAST_ASSUME_MSG(m_iElemSize <= (int) sizeof(m_buf), "The MetaData table row has to fit into buffer for swapping.");
+
+ IfFailRet(getRow(iFirst, &pFirst));
+ IfFailRet(getRow(iSecond, &pSecond));
+ memcpy(m_buf, pFirst, m_iElemSize);
+ memcpy(pFirst, pSecond, m_iElemSize);
+ memcpy(pSecond, m_buf, m_iElemSize);
+ if (m_pRidMap != NULL)
+ {
+ RID ridTemp;
+ ridTemp = *(m_pRidMap->Get(iFirst));
+ *(m_pRidMap->Get(iFirst)) = *(m_pRidMap->Get(iSecond));
+ *(m_pRidMap->Get(iSecond)) = ridTemp;
+ }
+ return S_OK;
+ } // CQuickSortMiniMdRW::Swap
+
+}; // class CQuickSortMiniMdRW
+
+class CStableSortMiniMdRW : public CQuickSortMiniMdRW
+{
+public:
+ CStableSortMiniMdRW(
+ CMiniMdRW &MiniMd, // MiniMd with the data.
+ ULONG ixTbl, // The table.
+ ULONG ixCol, // The column.
+ bool bMapToken) // Is MapToken handling desired.
+ : CQuickSortMiniMdRW(MiniMd, ixTbl, ixCol, bMapToken)
+ {}
+
+ //*****************************************************************************
+ // Call to sort the array.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT Sort()
+ {
+ int i; // Outer loop counter.
+ int j; // Inner loop counter.
+ int bSwap; // Early out.
+ HRESULT hr = S_OK;
+ int nResult;
+
+ _ASSERTE(m_MiniMd.IsSortable(m_ixTbl));
+ m_iCount = m_MiniMd.GetCountRecs(m_ixTbl);
+
+ // If remap notifications are desired, prepare to collect the info in a RIDMAP.
+ IfFailGo(PrepMapTokens());
+
+ for (i=m_iCount; i>1; --i)
+ {
+ bSwap = 0;
+ for (j=1; j<i; ++j)
+ {
+ IfFailGo(Compare(j, j+1, &nResult));
+ if (nResult > 0)
+ {
+ IfFailGo(Swap(j, j+1));
+ bSwap = 1;
+ }
+ }
+ // If made a full pass w/o swaps, done.
+ if (!bSwap)
+ break;
+ }
+
+ // The table is sorted until its next change.
+ SetSorted();
+
+ // If remap notifications were desired, send them.
+ IfFailGo(DoMapTokens());
+
+ ErrExit:
+ return hr;
+ } // CStableSortMiniMdRW::Sort
+
+}; // class CStableSortMiniMdRW
+
+//-------------------------------------------------------------------------
+#define SORTER(tbl,key) CQuickSortMiniMdRW sort##tbl(*this, TBL_##tbl, tbl##Rec::COL_##key, false);
+#define SORTER_WITHREMAP(tbl,key) CQuickSortMiniMdRW sort##tbl(*this, TBL_##tbl, tbl##Rec::COL_##key, true);
+#define STABLESORTER(tbl,key) CStableSortMiniMdRW sort##tbl(*this, TBL_##tbl, tbl##Rec::COL_##key, false);
+#define STABLESORTER_WITHREMAP(tbl,key) CStableSortMiniMdRW sort##tbl(*this, TBL_##tbl, tbl##Rec::COL_##key, true);
+//-------------------------------------------------------------------------
+
+
+
+//********** Code. ************************************************************
+
+
+//*****************************************************************************
+// Ctor / dtor.
+//*****************************************************************************
+#ifdef _DEBUG
+static bool bENCDeltaOnly = false;
+#endif
+CMiniMdRW::CMiniMdRW()
+ : m_pMemberRefHash(0),
+ m_pMemberDefHash(0),
+ m_pNamedItemHash(0),
+ m_pHandler(0),
+ m_cbSaveSize(0),
+ m_fIsReadOnly(false),
+ m_bPreSaveDone(false),
+ m_bPostGSSMod(false),
+ m_pMethodMap(0),
+ m_pFieldMap(0),
+ m_pPropertyMap(0),
+ m_pEventMap(0),
+ m_pParamMap(0),
+ m_pFilterTable(0),
+ m_pHostFilter(0),
+ m_pTokenRemapManager(0),
+ m_fMinimalDelta(FALSE),
+ m_rENCRecs(0)
+{
+#ifdef _DEBUG
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_EncDelta))
+ {
+ bENCDeltaOnly = true;
+ }
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_MiniMDBreak))
+ {
+ _ASSERTE(!"CMiniMdRW::CMiniMdRW()");
+ }
+#endif // _DEBUG
+
+ ZeroMemory(&m_OptionValue, sizeof(OptionValue));
+
+ // initialize the embeded lookuptable struct. Further initialization, after constructor.
+ for (ULONG ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl)
+ {
+ m_pVS[ixTbl] = 0;
+ m_pLookUpHashs[ixTbl] = 0;
+ }
+
+ // Assume that we can sort tables as needed.
+ memset(m_bSortable, 1, sizeof(m_bSortable));
+
+ // Initialize the global array of Ptr table indices.
+ g_PtrTableIxs[TBL_Field].m_ixtbl = TBL_FieldPtr;
+ g_PtrTableIxs[TBL_Field].m_ixcol = FieldPtrRec::COL_Field;
+ g_PtrTableIxs[TBL_Method].m_ixtbl = TBL_MethodPtr;
+ g_PtrTableIxs[TBL_Method].m_ixcol = MethodPtrRec::COL_Method;
+ g_PtrTableIxs[TBL_Param].m_ixtbl = TBL_ParamPtr;
+ g_PtrTableIxs[TBL_Param].m_ixcol = ParamPtrRec::COL_Param;
+ g_PtrTableIxs[TBL_Property].m_ixtbl = TBL_PropertyPtr;
+ g_PtrTableIxs[TBL_Property].m_ixcol = PropertyPtrRec::COL_Property;
+ g_PtrTableIxs[TBL_Event].m_ixtbl = TBL_EventPtr;
+ g_PtrTableIxs[TBL_Event].m_ixcol = EventPtrRec::COL_Event;
+
+ // AUTO_GROW initialization
+ m_maxRid = m_maxIx = 0;
+ m_limIx = USHRT_MAX >> 1;
+ m_limRid = USHRT_MAX >> AUTO_GROW_CODED_TOKEN_PADDING;
+ m_eGrow = eg_ok;
+#ifdef _DEBUG
+ {
+ ULONG iMax, iCdTkn;
+ for (iMax=0, iCdTkn=0; iCdTkn<CDTKN_COUNT; ++iCdTkn)
+ {
+ CCodedTokenDef const *pCTD = &g_CodedTokens[iCdTkn];
+ if (pCTD->m_cTokens > iMax)
+ iMax = pCTD->m_cTokens;
+ }
+ // If assert fires, change define for AUTO_GROW_CODED_TOKEN_PADDING.
+ _ASSERTE(CMiniMdRW::m_cb[iMax] == AUTO_GROW_CODED_TOKEN_PADDING);
+ }
+ dbg_m_pLock = NULL;
+#endif //_DEBUG
+
+} // CMiniMdRW::CMiniMdRW
+
+CMiniMdRW::~CMiniMdRW()
+{
+ // Un-initialize the embeded lookuptable struct
+ for (ULONG ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl)
+ {
+ if (m_pVS[ixTbl])
+ {
+ m_pVS[ixTbl]->Uninit();
+ delete m_pVS[ixTbl];
+ }
+ if ( m_pLookUpHashs[ixTbl] != NULL )
+ delete m_pLookUpHashs[ixTbl];
+
+ }
+ if (m_pFilterTable)
+ delete m_pFilterTable;
+
+ if (m_rENCRecs)
+ delete [] m_rENCRecs;
+
+ if (m_pHandler)
+ m_pHandler->Release(), m_pHandler = 0;
+ if (m_pHostFilter)
+ m_pHostFilter->Release();
+ if (m_pMemberRefHash)
+ delete m_pMemberRefHash;
+ if (m_pMemberDefHash)
+ delete m_pMemberDefHash;
+ if (m_pNamedItemHash)
+ delete m_pNamedItemHash;
+ if (m_pMethodMap)
+ delete m_pMethodMap;
+ if (m_pFieldMap)
+ delete m_pFieldMap;
+ if (m_pPropertyMap)
+ delete m_pPropertyMap;
+ if (m_pEventMap)
+ delete m_pEventMap;
+ if (m_pParamMap)
+ delete m_pParamMap;
+ if (m_pTokenRemapManager)
+ delete m_pTokenRemapManager;
+} // CMiniMdRW::~CMiniMdRW
+
+
+//*****************************************************************************
+// return all found CAs in an enumerator
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::CommonEnumCustomAttributeByName(
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ bool fStopAtFirstFind, // [IN] just find the first one
+ HENUMInternal *phEnum) // enumerator to fill up
+{
+ HRESULT hr = S_OK;
+ HRESULT hrRet = S_FALSE; // Assume that we won't find any
+ ULONG ridStart, ridEnd; // Loop start and endpoints.
+ CLookUpHash *pHashTable = m_pLookUpHashs[TBL_CustomAttribute];
+
+ _ASSERTE(phEnum != NULL);
+
+ memset(phEnum, 0, sizeof(HENUMInternal));
+
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+
+ phEnum->m_tkKind = mdtCustomAttribute;
+
+ if (pHashTable)
+ {
+ // table is not sorted and hash is not built so we have to create a dynamic array
+ // create the dynamic enumerator.
+ TOKENHASHENTRY *p;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ iHash = HashCustomAttribute(tkObj);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pHashTable->FindFirst(iHash, pos);
+ p;
+ p = pHashTable->FindNext(pos))
+ {
+ IfFailGo(CompareCustomAttribute( tkObj, szName, RidFromToken(p->tok)));
+ if (hr == S_OK)
+ {
+ hrRet = S_OK;
+
+ // If here, found a match.
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(p->tok, mdtCustomAttribute)));
+ if (fStopAtFirstFind)
+ goto ErrExit;
+ }
+ }
+ }
+ else
+ {
+ // Get the list of custom values for the parent object.
+ if ( IsSorted(TBL_CustomAttribute) )
+ {
+ IfFailGo(getCustomAttributeForToken(tkObj, &ridEnd, &ridStart));
+ // If found none, done.
+ if (ridStart == 0)
+ goto ErrExit;
+ }
+ else
+ {
+ // linear scan of entire table.
+ ridStart = 1;
+ ridEnd = getCountCustomAttributes() + 1;
+ }
+
+ // Look for one with the given name.
+ for (; ridStart < ridEnd; ++ridStart)
+ {
+ IfFailGo(CompareCustomAttribute( tkObj, szName, ridStart));
+ if (hr == S_OK)
+ {
+ // If here, found a match.
+ hrRet = S_OK;
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ phEnum,
+ TokenFromRid(ridStart, mdtCustomAttribute)));
+ if (fStopAtFirstFind)
+ goto ErrExit;
+ }
+ }
+ }
+
+ErrExit:
+ if (FAILED(hr))
+ return hr;
+ return hrRet;
+} // CMiniMdRW::CommonEnumCustomAttributeByName
+
+
+
+//*****************************************************************************
+// return just the blob value of the first found CA matching the query.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::CommonGetCustomAttributeByNameEx( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ mdCustomAttribute *ptkCA, // [OUT] put custom attribute token here
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) // [OUT] Put size of data here.
+{
+ HRESULT hr;
+ const void *pData;
+ ULONG cbData;
+ HENUMInternal hEnum;
+ mdCustomAttribute ca;
+ CustomAttributeRec *pRec;
+
+ hr = CommonEnumCustomAttributeByName(tkObj, szName, true, &hEnum);
+ if (hr != S_OK)
+ goto ErrExit;
+
+ if (ppData != NULL || ptkCA != NULL)
+ {
+ // now get the record out.
+ if (ppData == 0)
+ ppData = &pData;
+ if (pcbData == 0)
+ pcbData = &cbData;
+
+
+ if (HENUMInternal::EnumNext(&hEnum, &ca))
+ {
+ IfFailGo(GetCustomAttributeRecord(RidFromToken(ca), &pRec));
+ IfFailGo(getValueOfCustomAttribute(pRec, reinterpret_cast<const BYTE **>(ppData), pcbData));
+ if (ptkCA)
+ *ptkCA = ca;
+ }
+ else
+ {
+ _ASSERTE(!"Enum returned no items after EnumInit returned S_OK");
+ hr = S_FALSE;
+ }
+ }
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+ return hr;
+} // CMiniMdRW::CommonGetCustomAttributeByName
+
+//*****************************************************************************
+// unmark everything in this module
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::UnmarkAll()
+{
+ HRESULT hr = NOERROR;
+ ULONG ulSize = 0;
+ ULONG ixTbl;
+ FilterTable *pFilter;
+
+ // find the max rec count with all tables
+ for (ixTbl = 0; ixTbl < TBL_COUNT; ++ixTbl)
+ {
+ if (GetCountRecs(ixTbl) > ulSize)
+ ulSize = GetCountRecs(ixTbl);
+ }
+ IfNullGo(pFilter = GetFilterTable());
+ IfFailGo(pFilter->UnmarkAll(this, ulSize));
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::UnmarkAll
+
+
+//*****************************************************************************
+// mark everything in this module
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::MarkAll()
+{
+ HRESULT hr = NOERROR;
+ ULONG ulSize = 0;
+ ULONG ixTbl;
+ FilterTable *pFilter;
+
+ // find the max rec count with all tables
+ for (ixTbl = 0; ixTbl < TBL_COUNT; ++ixTbl)
+ {
+ if (GetCountRecs(ixTbl) > ulSize)
+ ulSize = GetCountRecs(ixTbl);
+ }
+ IfNullGo(pFilter = GetFilterTable());
+ IfFailGo(pFilter->MarkAll(this, ulSize));
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::MarkAll
+
+//*****************************************************************************
+// This will trigger FilterTable to be created
+//*****************************************************************************
+FilterTable *CMiniMdRW::GetFilterTable()
+{
+ if (m_pFilterTable == NULL)
+ {
+ m_pFilterTable = new (nothrow) FilterTable;
+ }
+ return m_pFilterTable;
+}
+
+
+//*****************************************************************************
+// Calculate the map between TypeRef and TypeDef
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::CalculateTypeRefToTypeDefMap()
+{
+ HRESULT hr = NOERROR;
+ ULONG index;
+ TypeRefRec *pTypeRefRec;
+ LPCSTR szName;
+ LPCSTR szNamespace;
+ mdToken td;
+ mdToken tkResScope;
+
+ PREFIX_ASSUME(GetTypeRefToTypeDefMap() != NULL);
+
+ for (index = 1; index <= m_Schema.m_cRecs[TBL_TypeRef]; index++)
+ {
+ IfFailRet(GetTypeRefRecord(index, &pTypeRefRec));
+
+ // Get the name and namespace of the TypeRef.
+ IfFailRet(getNameOfTypeRef(pTypeRefRec, &szName));
+ IfFailRet(getNamespaceOfTypeRef(pTypeRefRec, &szNamespace));
+ tkResScope = getResolutionScopeOfTypeRef(pTypeRefRec);
+
+ // If the resolutionScope is an AssemblyRef, then the type is
+ // external, even if it has the same name as a type in this scope.
+ if (TypeFromToken(tkResScope) == mdtAssemblyRef)
+ continue;
+
+ // Iff the name is found in the typedef table, then use
+ // that value instead. Won't be found if typeref is trully external.
+ hr = ImportHelper::FindTypeDefByName(this, szNamespace, szName,
+ (TypeFromToken(tkResScope) == mdtTypeRef) ? tkResScope : mdTokenNil,
+ &td);
+ if (hr != S_OK)
+ {
+ // don't propagate the error in the Find
+ hr = NOERROR;
+ continue;
+ }
+ *(GetTypeRefToTypeDefMap()->Get(index)) = td;
+ }
+
+ return hr;
+} // CMiniMdRW::CalculateTypeRefToTypeDefMap
+
+
+//*****************************************************************************
+// Set a remap handler.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::SetHandler(
+ IUnknown *pIUnk)
+{
+ if (m_pHandler != NULL)
+ {
+ m_pHandler->Release();
+ m_pHandler = NULL;
+ }
+
+ if (pIUnk != NULL)
+ {
+ // ignore the error for QI the IHostFilter
+ pIUnk->QueryInterface(IID_IHostFilter, reinterpret_cast<void**>(&m_pHostFilter));
+
+ return pIUnk->QueryInterface(IID_IMapToken, reinterpret_cast<void**>(&m_pHandler));
+ }
+
+ return S_OK;
+} // CMiniMdRW::SetHandler
+
+//*****************************************************************************
+// Set a Options
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::SetOption(
+ OptionValue *pOptionValue)
+{
+ HRESULT hr = NOERROR;
+ ULONG ixTbl = 0;
+ int i;
+
+ m_OptionValue = *pOptionValue;
+
+ // Turn off delta metadata bit -- can't be used due to EE assumptions about delta PEs.
+ // Inspect ApplyEditAndContinue for details.
+ // To enable this, use the EnableDeltaMetadataGeneration/DisableDeltaMetadataGeneration accessors.
+ _ASSERTE((m_OptionValue.m_UpdateMode & MDUpdateDelta) != MDUpdateDelta);
+
+#ifdef _DEBUG
+ if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC &&
+ bENCDeltaOnly)
+ m_OptionValue.m_UpdateMode |= MDUpdateDelta;
+#endif
+
+ // if a scope is previously updated as incremental, then it should not be open again
+ // with full update for read/write.
+ //
+ if ((m_Schema.m_heaps & CMiniMdSchema::HAS_DELETE) &&
+ (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull &&
+ !m_fIsReadOnly)
+ {
+ IfFailGo( CLDB_E_BADUPDATEMODE );
+ }
+
+ if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateIncremental)
+ m_Schema.m_heaps |= CMiniMdSchema::HAS_DELETE;
+
+ // Set the value of sortable based on the options.
+ switch (m_OptionValue.m_UpdateMode & MDUpdateMask)
+ {
+ case MDUpdateFull:
+ // Always sortable.
+ for (ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl)
+ m_bSortable[ixTbl] = 1;
+ break;
+ case MDUpdateENC:
+ // Never sortable.
+ for (ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl)
+ m_bSortable[ixTbl] = 0;
+
+ // Truncate some tables.
+ for (i=0; (ixTbl = m_TruncatedEncTables[i]) != (ULONG) -1; ++i)
+ {
+ m_Tables[ixTbl].Delete();
+ IfFailGo(m_Tables[ixTbl].InitializeEmpty_WithRecordCount(
+ m_TableDefs[ixTbl].m_cbRec,
+ 0
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl));
+ m_Schema.m_cRecs[ixTbl] = 0;
+ }
+
+ // Out-of-order is expected in an ENC scenario, never an error.
+ m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderNone;
+
+ break;
+ case MDUpdateIncremental:
+ // Sortable if no external token.
+ for (ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl)
+ m_bSortable[ixTbl] = (GetTokenForTable(ixTbl) == (ULONG) -1);
+ break;
+ case MDUpdateExtension:
+ // Never sortable.
+ for (ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl)
+ m_bSortable[ixTbl] = 0;
+ break;
+ default:
+ _ASSERTE(!"Internal error -- unknown save mode");
+ return E_INVALIDARG;
+ }
+
+ // If this is an ENC session, track the generations.
+ if (!m_fIsReadOnly && (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC)
+ {
+#ifdef FEATURE_METADATA_EMIT
+ ModuleRec *pMod;
+ GUID encid;
+
+ // Get the module record.
+ IfFailGo(GetModuleRecord(1, &pMod));
+
+/* Do we really want to do this? This would reset the metadata each time we changed an option
+ // Copy EncId as BaseId.
+ uVal = GetCol(TBL_Module, ModuleRec::COL_EncId, pMod);
+ PutCol(TBL_Module, ModuleRec::COL_EncBaseId, pMod, uVal);
+*/
+ // Allocate a new GUID for EncId.
+ IfFailGo(CoCreateGuid(&encid));
+ IfFailGo(PutGuid(TBL_Module, ModuleRec::COL_EncId, pMod, encid));
+#else //!FEATURE_METADATA_EMIT
+ IfFailGo(E_INVALIDARG);
+#endif //!FEATURE_METADATA_EMIT
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::SetOption
+
+//*****************************************************************************
+// Get Options
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GetOption(
+ OptionValue *pOptionValue)
+{
+ *pOptionValue = m_OptionValue;
+ return S_OK;
+} // CMiniMdRW::GetOption
+
+//*****************************************************************************
+// Smart MapToken. Only calls client if token really changed.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::MapToken( // Return value from user callback.
+ RID from, // Old rid.
+ RID to, // New rid.
+ mdToken tkn) // Token type.
+{
+ HRESULT hr = S_OK;
+ TOKENREC *pTokenRec;
+ MDTOKENMAP *pMovementMap;
+ // If not change, done.
+ if (from == to)
+ return S_OK;
+
+ pMovementMap = GetTokenMovementMap();
+ _ASSERTE(GetTokenMovementMap() != NULL);
+ if (pMovementMap != NULL)
+ IfFailRet(pMovementMap->AppendRecord( TokenFromRid(from, tkn), false, TokenFromRid(to, tkn), &pTokenRec ));
+
+ // Notify client.
+ if (m_pHandler != NULL)
+ {
+ LOG((LOGMD, "CMiniMdRW::MapToken (remap): from 0x%08x to 0x%08x\n", TokenFromRid(from,tkn), TokenFromRid(to,tkn)));
+ return m_pHandler->Map(TokenFromRid(from,tkn), TokenFromRid(to,tkn));
+ }
+ else
+ {
+ return hr;
+ }
+} // CMiniMdCreate::MapToken
+
+//*****************************************************************************
+// Set max, lim, based on data.
+//*****************************************************************************
+void
+CMiniMdRW::ComputeGrowLimits(
+ int bSmall) // large or small tables?
+{
+ if (bSmall)
+ {
+ // Tables will need to grow if any value exceeds what a two-byte column can hold.
+ m_maxRid = m_maxIx = 0;
+ m_limIx = USHRT_MAX >> 1;
+ m_limRid = USHRT_MAX >> AUTO_GROW_CODED_TOKEN_PADDING;
+ m_eGrow = eg_ok;
+ }
+ else
+ {
+ // Tables are already large
+ m_maxRid = m_maxIx = ULONG_MAX;
+ m_limIx = USHRT_MAX << 1;
+ m_limRid = USHRT_MAX << 1;
+ m_eGrow = eg_grown;
+ }
+} // CMiniMdRW::ComputeGrowLimits
+
+//*****************************************************************************
+// Initialization of a new writable MiniMd's pools.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::InitPoolOnMem(
+ int iPool, // The pool to initialize.
+ void *pbData, // The data from which to init.
+ ULONG cbData, // Size of data.
+ int fIsReadOnly) // Is the memory read-only?
+{
+ HRESULT hr;
+
+ switch (iPool)
+ {
+ case MDPoolStrings:
+ if (pbData == NULL)
+ { // Creates new empty string heap with default empty string entry
+ IfFailRet(m_StringHeap.InitializeEmpty(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+ else
+ {
+ IfFailRet(m_StringHeap.Initialize(
+ MetaData::DataBlob((BYTE *)pbData, cbData),
+ !fIsReadOnly));
+ }
+ break;
+ case MDPoolGuids:
+ if (pbData == NULL)
+ { // Creates new empty guid heap
+ IfFailRet(m_GuidHeap.InitializeEmpty(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+ else
+ {
+ IfFailRet(m_GuidHeap.Initialize(
+ MetaData::DataBlob((BYTE *)pbData, cbData),
+ !fIsReadOnly));
+ }
+ break;
+ case MDPoolBlobs:
+ if (pbData == NULL)
+ {
+ if (IsMinimalDelta())
+ { // It's EnC minimal delta, don't include default empty blob
+ IfFailRet(m_BlobHeap.InitializeEmpty_WithoutDefaultEmptyBlob(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+ else
+ { // Creates new empty blob heap with default empty blob entry
+ IfFailRet(m_BlobHeap.InitializeEmpty(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+ }
+ else
+ {
+ IfFailRet(m_BlobHeap.Initialize(
+ MetaData::DataBlob((BYTE *)pbData, cbData),
+ !fIsReadOnly));
+ }
+ break;
+ case MDPoolUSBlobs:
+ if (pbData == NULL)
+ {
+ if (IsMinimalDelta())
+ { // It's EnC minimal delta, don't include default empty user string
+ IfFailRet(m_UserStringHeap.InitializeEmpty_WithoutDefaultEmptyBlob(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+ else
+ { // Creates new empty user string heap (with default empty !!!blob!!! entry)
+ // Note: backaward compatiblity: doesn't add default empty user string, but default empty
+ // blob entry
+ IfFailRet(m_UserStringHeap.InitializeEmpty(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+ }
+ else
+ {
+ IfFailRet(m_UserStringHeap.Initialize(
+ MetaData::DataBlob((BYTE *)pbData, cbData),
+ !fIsReadOnly));
+ }
+ break;
+ default:
+ hr = E_INVALIDARG;
+ }
+ return hr;
+} // CMiniMdRW::InitPoolOnMem
+
+//*****************************************************************************
+// Initialization of a new writable MiniMd
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::InitOnMem(
+ const void *pvBuf, // The data from which to init.
+ ULONG ulBufLen, // The data size
+ int fIsReadOnly) // Is the memory read-only?
+{
+ HRESULT hr = S_OK;
+ UINT32 cbSchemaSize; // Size of the schema structure.
+ S_UINT32 cbTotalSize; // Size of all data used.
+ BYTE *pBuf = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(pvBuf));
+ int i;
+
+ // post contruction initialize the embeded lookuptable struct
+ for (ULONG ixTbl = 0; ixTbl < m_TblCount; ++ixTbl)
+ {
+ if (m_TableDefs[ixTbl].m_iKey < m_TableDefs[ixTbl].m_cCols)
+ {
+ if (m_pVS[ixTbl] == NULL)
+ {
+ m_pVS[ixTbl] = new (nothrow) VirtualSort;
+ IfNullGo(m_pVS[ixTbl]);
+
+ m_pVS[ixTbl]->Init(ixTbl, m_TableDefs[ixTbl].m_iKey, this);
+ }
+ }
+ }
+
+ // Uncompress the schema from the buffer into our structures.
+ IfFailGo(SchemaPopulate(pvBuf, ulBufLen, (ULONG *)&cbSchemaSize));
+
+ if (m_fMinimalDelta)
+ IfFailGo(InitWithLargeTables());
+
+ // Initialize the pointers to the rest of the data.
+ pBuf += cbSchemaSize;
+ cbTotalSize = S_UINT32(cbSchemaSize);
+ for (i=0; i<(int)m_TblCount; ++i)
+ {
+ if (m_Schema.m_cRecs[i] > 0)
+ {
+ // Size of one table is rowsize * rowcount.
+ S_UINT32 cbTableSize =
+ S_UINT32(m_TableDefs[i].m_cbRec) *
+ S_UINT32(m_Schema.m_cRecs[i]);
+ if (cbTableSize.IsOverflow())
+ {
+ Debug_ReportError("Table is too big, its size overflows.");
+ IfFailGo(METADATA_E_INVALID_FORMAT);
+ }
+ cbTotalSize += cbTableSize;
+ if (cbTotalSize.IsOverflow())
+ {
+ Debug_ReportError("Total tables size is too big, their total size overflows.");
+ IfFailGo(METADATA_E_INVALID_FORMAT);
+ }
+ IfFailGo(m_Tables[i].Initialize(
+ m_TableDefs[i].m_cbRec,
+ MetaData::DataBlob(pBuf, cbTableSize.Value()),
+ !fIsReadOnly)); // fCopyData
+ INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i));
+ pBuf += cbTableSize.Value();
+ }
+ else
+ {
+ IfFailGo(m_Tables[i].InitializeEmpty_WithRecordCount(
+ m_TableDefs[i].m_cbRec,
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i));
+ }
+ }
+
+ // If the metadata is being opened for read/write, all the updateable columns
+ // need to be the same width.
+ if (!fIsReadOnly)
+ {
+ // variable to indicate if tables are large, small or mixed.
+ int fMixed = false;
+ int iSize = 0;
+ CMiniColDef *pCols; // The col defs to init.
+ int iCol;
+
+ // Look at all the tables, or until mixed sizes are discovered.
+ for (i=0; i<(int)m_TblCount && fMixed == false; i++)
+ { // Look at all the columns of the table.
+ pCols = m_TableDefs[i].m_pColDefs;
+ for (iCol = 0; iCol < m_TableDefs[i].m_cCols && !fMixed; iCol++)
+ { // If not a fixed size column...
+ if (!IsFixedType(m_TableDefs[i].m_pColDefs[iCol].m_Type))
+ { // If this is the first non-fixed size column...
+ if (iSize == 0)
+ { // remember it's size.
+ iSize = m_TableDefs[i].m_pColDefs[iCol].m_cbColumn;
+ }
+ else
+ { // Not first non-fixed size, so if a different size...
+ if (iSize != m_TableDefs[i].m_pColDefs[iCol].m_cbColumn)
+ { // ...the table has mixed column sizes.
+ fMixed = true;
+ }
+ }
+ }
+ }
+ }
+ if (fMixed)
+ {
+ // grow everything to large
+ IfFailGo(ExpandTables());
+ ComputeGrowLimits(FALSE /* ! small*/);
+ }
+ else
+ {
+ if (iSize == 2)
+ {
+ // small schema
+ ComputeGrowLimits(TRUE /* small */);
+ }
+ else
+ {
+ // large schema
+ ComputeGrowLimits(FALSE /* ! small */);
+ }
+ }
+ }
+ else
+ {
+ // Set the limits so we will know when to grow the database.
+ ComputeGrowLimits(TRUE /* small */);
+ }
+
+ // Track records that this MD started with.
+ m_StartupSchema = m_Schema;
+
+ m_fIsReadOnly = fIsReadOnly ? 1 : 0;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::InitOnMem
+
+//*****************************************************************************
+// Validate cross-stream consistency.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::PostInit(
+ int iLevel)
+{
+ return S_OK;
+} // CMiniMdRW::PostInit
+
+//*****************************************************************************
+// Init a CMiniMdRW from the data in a CMiniMd [RO].
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::InitOnRO(
+ CMiniMd *pMd, // The MiniMd to update from.
+ int fIsReadOnly) // Will updates be allowed?
+{
+ HRESULT hr = S_OK;
+ ULONG i; // Loop control.
+
+ // Init the schema.
+ IfFailGo(SchemaPopulate(*pMd));
+
+ // Allocate VS structs for tables with key columns.
+ for (ULONG ixTbl = 0; ixTbl < m_TblCount; ++ixTbl)
+ {
+ if (m_TableDefs[ixTbl].m_iKey < m_TableDefs[ixTbl].m_cCols)
+ {
+ m_pVS[ixTbl] = new (nothrow) VirtualSort;
+ IfNullGo(m_pVS[ixTbl]);
+
+ m_pVS[ixTbl]->Init(ixTbl, m_TableDefs[ixTbl].m_iKey, this);
+ }
+ }
+
+ // Copy over the column definitions.
+ for (i = 0; i < m_TblCount; ++i)
+ {
+ _ASSERTE(m_TableDefs[i].m_cCols == pMd->m_TableDefs[i].m_cCols);
+ m_TableDefs[i].m_cbRec = pMd->m_TableDefs[i].m_cbRec;
+ IfFailGo(SetNewColumnDefinition(&(m_TableDefs[i]), pMd->m_TableDefs[i].m_pColDefs, i));
+ }
+
+ // Initialize string heap
+ if (pMd->m_StringHeap.GetUnalignedSize() > 0)
+ {
+ IfFailGo(m_StringHeap.InitializeFromStringHeap(
+ &(pMd->m_StringHeap),
+ !fIsReadOnly));
+ }
+ else
+ {
+ IfFailGo(m_StringHeap.InitializeEmpty(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+
+ // Initialize user string heap
+ if (pMd->m_UserStringHeap.GetUnalignedSize() > 0)
+ {
+ IfFailGo(m_UserStringHeap.InitializeFromBlobHeap(
+ &(pMd->m_UserStringHeap),
+ !fIsReadOnly));
+ }
+ else
+ {
+ IfFailGo(m_UserStringHeap.InitializeEmpty(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+
+ // Initialize guid heap
+ if (pMd->m_GuidHeap.GetSize() > 0)
+ {
+ IfFailGo(m_GuidHeap.InitializeFromGuidHeap(
+ &(pMd->m_GuidHeap),
+ !fIsReadOnly));
+ }
+ else
+ {
+ IfFailGo(m_GuidHeap.InitializeEmpty(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+
+ // Initialize blob heap
+ if (pMd->m_BlobHeap.GetUnalignedSize() > 0)
+ {
+ IfFailGo(m_BlobHeap.InitializeFromBlobHeap(
+ &(pMd->m_BlobHeap),
+ !fIsReadOnly));
+ }
+ else
+ {
+ IfFailGo(m_BlobHeap.InitializeEmpty(
+ 0
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ }
+
+ // Init the record pools
+ for (i = 0; i < m_TblCount; ++i)
+ {
+ if (m_Schema.m_cRecs[i] > 0)
+ {
+ IfFailGo(m_Tables[i].InitializeFromTable(
+ &(pMd->m_Tables[i]),
+ m_TableDefs[i].m_cbRec,
+ m_Schema.m_cRecs[i],
+ !fIsReadOnly)); // fCopyData
+ INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i));
+
+ // We set this bit to indicate the compressed, read-only tables are always sorted
+ // <TODO>This is not true for all tables, so we should set it correctly and flush out resulting bugs</TODO>
+ SetSorted(i, true);
+ }
+ else
+ {
+ IfFailGo(m_Tables[i].InitializeEmpty_WithRecordCount(
+ m_TableDefs[i].m_cbRec,
+ 2
+ COMMA_INDEBUG_MD(!fIsReadOnly)));
+ INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i));
+ // An empty table can be considered unsorted.
+ SetSorted(i, false);
+ }
+ }
+
+ // Set the limits so we will know when to grow the database.
+ ComputeGrowLimits(TRUE /* small */);
+
+ // Track records that this MD started with.
+ m_StartupSchema = m_Schema;
+
+ m_fIsReadOnly = fIsReadOnly ? 1 : 0;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::InitOnRO
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+
+// This checks that column sizes are reasonable for their types
+// The sizes could still be too small to hold all values in the range, or larger
+// than they needed, but there must exist some scenario where this size is the
+// one we would use.
+// As long as this validation passes + we verify that the records actually
+// have space for columns of this size then the worst thing that malicious
+// data could do is be slightly inneficient, or be unable to address all their data
+HRESULT _ValidateColumnSize(BYTE trustedColumnType, BYTE untrustedColumnSize)
+{
+ // Is the field a RID into a table?
+ if (trustedColumnType <= iCodedTokenMax)
+ {
+ if (untrustedColumnSize != sizeof(USHORT) && untrustedColumnSize != sizeof(ULONG))
+ return CLDB_E_FILE_CORRUPT;
+ }
+ else
+ { // Fixed type.
+ switch (trustedColumnType)
+ {
+ case iBYTE:
+ if (untrustedColumnSize != 1)
+ return CLDB_E_FILE_CORRUPT;
+ break;
+ case iSHORT:
+ case iUSHORT:
+ if (untrustedColumnSize != 2)
+ return CLDB_E_FILE_CORRUPT;
+ break;
+ case iLONG:
+ case iULONG:
+ if (untrustedColumnSize != 4)
+ return CLDB_E_FILE_CORRUPT;
+ break;
+ case iSTRING:
+ case iGUID:
+ case iBLOB:
+ if (untrustedColumnSize != 2 && untrustedColumnSize != 4)
+ return CLDB_E_FILE_CORRUPT;
+ break;
+ default:
+ _ASSERTE(!"Unexpected schema type");
+ return CLDB_E_FILE_CORRUPT;
+ }
+ }
+ return S_OK;
+}
+
+__checkReturn
+HRESULT CMiniMdRW::InitOnCustomDataSource(IMDCustomDataSource* pDataSource)
+{
+ HRESULT hr = S_OK;
+ ULONG i; // Loop control.
+ ULONG key;
+ BOOL fIsReadOnly = TRUE;
+ MetaData::DataBlob stringPoolData;
+ MetaData::DataBlob userStringPoolData;
+ MetaData::DataBlob guidHeapData;
+ MetaData::DataBlob blobHeapData;
+ MetaData::DataBlob tableRecordData;
+ CMiniTableDef tableDef;
+ BOOL sortable = FALSE;
+
+
+ // the data source owns all the memory backing the storage pools, so we need to ensure it stays alive
+ // after this method returns. When the CMiniMdRW is destroyed the reference will be released.
+ pDataSource->AddRef();
+ m_pCustomDataSource = pDataSource;
+
+ // Copy over the schema.
+ IfFailGo(pDataSource->GetSchema(&m_Schema));
+
+ // Is this the "native" version of the metadata for this runtime?
+ if ((m_Schema.m_major != METAMODEL_MAJOR_VER) || (m_Schema.m_minor != METAMODEL_MINOR_VER))
+ {
+ // We don't support this version of the metadata
+ Debug_ReportError("Unsupported version of MetaData.");
+ return PostError(CLDB_E_FILE_OLDVER, m_Schema.m_major, m_Schema.m_minor);
+ }
+
+ // How big are the various pool inidices?
+ m_iStringsMask = (m_Schema.m_heaps & CMiniMdSchema::HEAP_STRING_4) ? 0xffffffff : 0xffff;
+ m_iGuidsMask = (m_Schema.m_heaps & CMiniMdSchema::HEAP_GUID_4) ? 0xffffffff : 0xffff;
+ m_iBlobsMask = (m_Schema.m_heaps & CMiniMdSchema::HEAP_BLOB_4) ? 0xffffffff : 0xffff;
+
+ // Copy over TableDefs, column definitions and allocate VS structs for tables with key columns.
+ for (ULONG ixTbl = 0; ixTbl < m_TblCount; ++ixTbl)
+ {
+ IfFailGo(pDataSource->GetTableDef(ixTbl, &tableDef));
+ const CMiniTableDef* pTemplate = GetTableDefTemplate(ixTbl);
+
+ // validate that the table def looks safe
+ // we only allow some very limited differences between the standard template and the data source
+ key = (pTemplate->m_iKey < pTemplate->m_cCols) ? pTemplate->m_iKey : 0xFF;
+ if (key != tableDef.m_iKey) { IfFailGo(CLDB_E_FILE_CORRUPT); }
+ if (pTemplate->m_cCols != tableDef.m_cCols) { IfFailGo(CLDB_E_FILE_CORRUPT); }
+ ULONG cbRec = 0;
+ for (ULONG i = 0; i < pTemplate->m_cCols; i++)
+ {
+ if (tableDef.m_pColDefs == NULL) { IfFailGo(CLDB_E_FILE_CORRUPT); }
+ if (pTemplate->m_pColDefs[i].m_Type != tableDef.m_pColDefs[i].m_Type) { IfFailGo(CLDB_E_FILE_CORRUPT); }
+ IfFailGo(_ValidateColumnSize(pTemplate->m_pColDefs[i].m_Type, tableDef.m_pColDefs[i].m_cbColumn));
+ // sometimes, but not always, it seems like columns get alignment padding
+ // we'll allow it if we see it
+ if (cbRec > tableDef.m_pColDefs[i].m_oColumn) { IfFailGo(CLDB_E_FILE_CORRUPT); }
+ if (tableDef.m_pColDefs[i].m_oColumn > AlignUp(cbRec, tableDef.m_pColDefs[i].m_cbColumn)) { IfFailGo(CLDB_E_FILE_CORRUPT); }
+ cbRec = tableDef.m_pColDefs[i].m_oColumn + tableDef.m_pColDefs[i].m_cbColumn;
+ }
+ if (tableDef.m_cbRec != cbRec) { IfFailGo(CLDB_E_FILE_CORRUPT); }
+
+ // tabledef passed validation, copy it in
+ m_TableDefs[ixTbl].m_iKey = tableDef.m_iKey;
+ m_TableDefs[ixTbl].m_cCols = tableDef.m_cCols;
+ m_TableDefs[ixTbl].m_cbRec = tableDef.m_cbRec;
+ IfFailGo(SetNewColumnDefinition(&(m_TableDefs[ixTbl]), tableDef.m_pColDefs, ixTbl));
+ if (m_TableDefs[ixTbl].m_iKey < m_TableDefs[ixTbl].m_cCols)
+ {
+ m_pVS[ixTbl] = new (nothrow)VirtualSort;
+ IfNullGo(m_pVS[ixTbl]);
+
+ m_pVS[ixTbl]->Init(ixTbl, m_TableDefs[ixTbl].m_iKey, this);
+ }
+ }
+
+ // Initialize string heap
+ IfFailGo(pDataSource->GetStringHeap(&stringPoolData));
+ m_StringHeap.Initialize(stringPoolData, !fIsReadOnly);
+
+ // Initialize user string heap
+ IfFailGo(pDataSource->GetUserStringHeap(&userStringPoolData));
+ m_UserStringHeap.Initialize(userStringPoolData, !fIsReadOnly);
+
+ // Initialize guid heap
+ IfFailGo(pDataSource->GetGuidHeap(&guidHeapData));
+ m_GuidHeap.Initialize(guidHeapData, !fIsReadOnly);
+
+ // Initialize blob heap
+ IfFailGo(pDataSource->GetBlobHeap(&blobHeapData));
+ m_BlobHeap.Initialize(blobHeapData, !fIsReadOnly);
+
+ // Init the record pools
+ for (i = 0; i < m_TblCount; ++i)
+ {
+ IfFailGo(pDataSource->GetTableRecords(i, &tableRecordData));
+ // sanity check record counts and table sizes, this also ensures that cbRec*m_cRecs[x] doesn't overflow
+ if (m_Schema.m_cRecs[i] > 1000000) { IfFailGo(CLDB_E_FILE_CORRUPT); }
+ if (tableRecordData.GetSize() < m_TableDefs[i].m_cbRec * m_Schema.m_cRecs[i]) { IfFailGo(CLDB_E_FILE_CORRUPT); }
+ m_Tables[i].Initialize(m_TableDefs[i].m_cbRec, tableRecordData, !fIsReadOnly);
+
+ IfFailGo(pDataSource->GetTableSortable(i, &sortable));
+ m_bSortable[i] = sortable;
+ }
+
+ // Set the limits so we will know when to grow the database.
+ ComputeGrowLimits(TRUE /* small */);
+
+ // Track records that this MD started with.
+ m_StartupSchema = m_Schema;
+
+ m_fIsReadOnly = fIsReadOnly;
+
+ErrExit:
+ return hr;
+}
+#endif
+
+//*****************************************************************************
+// Convert a read-only to read-write. Copies data.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ConvertToRW()
+{
+ HRESULT hr = S_OK;
+ int i; // Loop control.
+
+ // Check for already done.
+ if (!m_fIsReadOnly)
+ return hr;
+
+ // If this is a minimal delta, then we won't allow it to be RW
+ if (IsMinimalDelta())
+ return CLDB_E_INCOMPATIBLE;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+
+ IfFailGo(m_StringHeap.MakeWritable());
+ IfFailGo(m_GuidHeap.MakeWritable());
+ IfFailGo(m_UserStringHeap.MakeWritable());
+ IfFailGo(m_BlobHeap.MakeWritable());
+
+ // Init the record pools
+ for (i = 0; i < (int)m_TblCount; ++i)
+ {
+ IfFailGo(m_Tables[i].MakeWritable());
+ }
+
+ // Grow the tables.
+ IfFailGo(ExpandTables());
+
+ // Track records that this MD started with.
+ m_StartupSchema = m_Schema;
+
+ // No longer read-only.
+ m_fIsReadOnly = false;
+
+ErrExit:
+ ;
+ END_SO_INTOLERANT_CODE;
+ return hr;
+} // CMiniMdRW::ConvertToRW
+
+//*****************************************************************************
+// Initialization of a new writable MiniMd
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::InitNew()
+{
+ HRESULT hr = S_OK;
+ int i; // Loop control.
+
+ // Initialize the Schema.
+ IfFailGo(m_Schema.InitNew(m_OptionValue.m_MetadataVersion));
+
+ // Allocate VS structs for tables with key columns.
+ for (ULONG ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ {
+ if (m_TableDefs[ixTbl].m_iKey < m_TableDefs[ixTbl].m_cCols)
+ {
+ m_pVS[ixTbl] = new (nothrow) VirtualSort;
+ IfNullGo(m_pVS[ixTbl]);
+
+ m_pVS[ixTbl]->Init(ixTbl, m_TableDefs[ixTbl].m_iKey, this);
+ }
+ }
+
+ enum MetaDataSizeIndex sizeIndex;
+ sizeIndex = GetMetaDataSizeIndex(&m_OptionValue);
+ if ((sizeIndex == MDSizeIndex_Standard) || (sizeIndex == MDSizeIndex_Minimal))
+ {
+ // OutputDebugStringA("Default small tables enabled\n");
+ // How big are the various pool inidices?
+ m_Schema.m_heaps = 0;
+ // How many rows in various tables?
+ for (i = 0; i < (int)m_TblCount; ++i)
+ {
+ m_Schema.m_cRecs[i] = 0;
+ }
+
+ // Compute how many bits required to hold.
+ m_Schema.m_rid = 1;
+ m_maxRid = m_maxIx = 0;
+ m_limIx = USHRT_MAX >> 1;
+ m_limRid = USHRT_MAX >> AUTO_GROW_CODED_TOKEN_PADDING;
+ m_eGrow = eg_ok;
+ }
+
+ // Now call base class function to calculate the offsets, sizes.
+ IfFailGo(SchemaPopulate2(NULL));
+
+ // Initialize the record heaps.
+ for (i = 0; i < (int)m_TblCount; ++i)
+ { // Don't really have any records yet.
+ m_Schema.m_cRecs[i] = 0;
+ IfFailGo(m_Tables[i].InitializeEmpty_WithRecordCount(
+ m_TableDefs[i].m_cbRec,
+ g_TblSizeInfo[sizeIndex][i]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i));
+
+ // Create tables as un-sorted. We hope to add all records, then sort just once.
+ SetSorted(i, false);
+ }
+
+ // Initialize heaps
+ IfFailGo(m_StringHeap.InitializeEmpty_WithItemsCount(
+ g_PoolSizeInfo[sizeIndex][IX_STRING_POOL][0],
+ g_PoolSizeInfo[sizeIndex][IX_STRING_POOL][1]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ IfFailGo(m_BlobHeap.InitializeEmpty_WithItemsCount(
+ g_PoolSizeInfo[sizeIndex][IX_BLOB_POOL][0],
+ g_PoolSizeInfo[sizeIndex][IX_BLOB_POOL][1]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ IfFailGo(m_UserStringHeap.InitializeEmpty_WithItemsCount(
+ g_PoolSizeInfo[sizeIndex][IX_US_BLOB_POOL][0],
+ g_PoolSizeInfo[sizeIndex][IX_US_BLOB_POOL][1]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ IfFailGo(m_GuidHeap.InitializeEmpty_WithItemsCount(
+ g_PoolSizeInfo[sizeIndex][IX_GUID_POOL][0],
+ g_PoolSizeInfo[sizeIndex][IX_GUID_POOL][1]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+
+ // Track records that this MD started with.
+ m_StartupSchema = m_Schema;
+
+ // New db is never read-only.
+ m_fIsReadOnly = false;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::InitNew
+
+#ifdef FEATURE_PREJIT
+//*****************************************************************************
+// Helper function to determine the size of hot tables
+//*****************************************************************************
+static int ShiftCount(ULONG itemCount, ULONG hotItemCount)
+{
+ // figure out how many bits are needed to represent the highest rid
+ ULONG highestRid = itemCount;
+ int bitCount = 0;
+ while ((1UL<<bitCount) <= highestRid)
+ bitCount++;
+ int shiftCount = bitCount > 8 ? bitCount - 8 : 0;
+
+ // tune the shift count so that we don't need to search more than 4 hot entries on average.
+ while ((hotItemCount >> shiftCount) > 4)
+ shiftCount++;
+ if (shiftCount > 16)
+ shiftCount = 16;
+ return shiftCount;
+} // ShiftCount
+
+//*****************************************************************************
+// Helper function to qsort hot tokens
+//*****************************************************************************
+
+typedef struct _TokenIndexPair
+{
+ mdToken token;
+ WORD index;
+} TokenIndexPair;
+
+static WORD shiftCount;
+static int __cdecl TokenCmp(const void *a, const void *b)
+{
+ mdToken ta = ((const TokenIndexPair *)a)->token;
+ mdToken tb = ((const TokenIndexPair *)b)->token;
+ if (shiftCount > 0)
+ {
+ // shiftCount is the number of low order bits that are used to index
+ // into the first level table. The below swaps high and low order bits so
+ // the values with common low order bits end up together after the sort.
+ ta = (ta >> shiftCount) | ((ta & ((1<<shiftCount)-1)) << (32-shiftCount));
+ tb = (tb >> shiftCount) | ((tb & ((1<<shiftCount)-1)) << (32-shiftCount));
+ }
+ if (ta < tb)
+ return -1;
+ else if (ta > tb)
+ return 1;
+ else
+ return 0;
+}
+
+//*****************************************************************************
+// A wrapper for metadata's use of CorProfileData::GetHotTokens that recognizes tokens
+// flagged with ProfilingFlags_MetaDataSearch and reinterprets them into a corresponding
+// set of ProfilingFlags_MetaData tokens.
+//
+// If you are reading this because you are changing the implementation of one of the searches
+// in CMiniMdBase, it should be a mechanical process to copy the new search code below and
+// change the row accesses into setting values in the rowFlags array.
+//
+// Doing so allows us to fix the problem that incremental IBC is fundamentally not suited to
+// metadata searches.
+//
+// For instance, consider the following scenario:
+// - You gather IBC data on a scenario that does a metadata binary search
+// - The data comes from build X where the table is of size 100 and the target is in row 18
+// - This means the intermediate values touched are rows 50, 25, and 12
+// - You then apply this IBC data to build Y which has changed to include 20 new entries to start the table
+// - Naively, incremental IBC would have remapped these tokens and predicted accesses at rows 70, 35, 32, and 38
+// - But this is wrong! And very bad for working set. The search will actually touch 60, 30, and 45 on the way to 38
+//
+// The solution is to only store rows in IBC data that were touched intentionally, either as direct
+// accesses (with ProfilingFlags_MetaData) or as the result of searches (ProfilingFlags_MetaDataSearch).
+// We then expand these "search tokens" here into the set of accesses that would occur on the current
+// table as we do our various types of metadata search for them.
+//
+// Currently, we infer touches for the following types of access:
+// - Direct access (getRow)
+// - Binary search (CMiniMdBase::vSearchTable or CMiniMdBase::vSearchTableNotGreater)
+// - Bounds of a multi-element span (CMiniMdBase::SearchTableForMultipleRows)
+//
+// In theory, we could have different flags for each type of search (e.g. binary, multiple-row, etc) and
+// avoid any over-reporting of intermediate tokens, but in practice the IBC flag bits are scarce and
+// measurements show a minimal (<1%) amount of over-reporting.
+//
+//*****************************************************************************
+
+enum HotTokenFlags
+{
+ HotTokenFlags_Cold = 0x0,
+ HotTokenFlags_ProfiledAccess = 0x1,
+ HotTokenFlags_IntermediateInBinarySearch = 0x2,
+ HotTokenFlags_BoundingMultipleRowSearch = 0x4
+};
+
+__checkReturn
+HRESULT
+CMiniMdRW::GetHotMetadataTokensSearchAware(
+ CorProfileData *pProfileData,
+ ULONG ixTbl,
+ ULONG *pResultCount,
+ mdToken *tokenBuffer,
+ ULONG maxCount)
+{
+ HRESULT hr = S_OK;
+ ULONG resultCount = 0;
+
+ ULONG metadataAccessFlag = 1<<ProfilingFlags_MetaData;
+ ULONG metadataSearchFlag = 1<<ProfilingFlags_MetaDataSearch;
+
+ // Query the profile data to determine the number of hot search tokens
+ ULONG numSearchTokens = pProfileData->GetHotTokens(ixTbl, metadataSearchFlag, metadataSearchFlag, NULL, 0);
+ ULONG cRecs = GetCountRecs(ixTbl);
+
+ if (numSearchTokens == 0 || cRecs == 0)
+ {
+ // If there are none, we can simply return the hot access tokens without doing any interesting work
+ resultCount = pProfileData->GetHotTokens(ixTbl, metadataAccessFlag, metadataAccessFlag, tokenBuffer, maxCount);
+ }
+ else
+ {
+ // But if there are hot search tokens, we need to infer what intermediate rows will be touched by our various types of metadata searching.
+ // To do so, retrieve all hot tokens and allocate temporary storage to use to mark which rows should be considered hot and for what reason
+ // (i.e. an array of HotTokenFlags, one per entry in the table, indexed by RID).
+ ULONG numAccessTokens = pProfileData->GetHotTokens(ixTbl, metadataAccessFlag, metadataAccessFlag, NULL, 0);
+
+ NewArrayHolder<mdToken> searchTokens = new (nothrow) mdToken[numSearchTokens];
+ IfNullGo(searchTokens);
+ NewArrayHolder<mdToken> accessTokens = new (nothrow) mdToken[numAccessTokens];
+ IfNullGo(accessTokens);
+ NewArrayHolder<BYTE> rowFlags = new (nothrow) BYTE[cRecs + 1];
+ IfNullGo(rowFlags);
+
+ pProfileData->GetHotTokens(ixTbl, metadataSearchFlag, metadataSearchFlag, searchTokens, numSearchTokens);
+ pProfileData->GetHotTokens(ixTbl, metadataAccessFlag, metadataAccessFlag, accessTokens, numAccessTokens);
+
+ // Initially, consider all rows cold
+ memset(rowFlags, HotTokenFlags_Cold, cRecs + 1);
+
+ // Category 1: Rows may have been touched directly (getRow)
+ // Simply mark the corresponding entry to each access token
+ for (ULONG i = 0; i < numAccessTokens; ++i)
+ {
+ RID rid = RidFromToken(accessTokens[i]);
+
+ if (rid <= cRecs)
+ {
+ rowFlags[rid] |= HotTokenFlags_ProfiledAccess;
+ }
+ }
+
+ // Category 2: Rows may have been intermediate touches in a binary search (CMiniMdBase::vSearchTable or CMiniMdBase::vSearchTableNotGreater)
+ // A search token may indicate where a binary search stopped, so for each of them compute and mark the intermediate set of rows that would have been touched
+ for (ULONG i = 0; i < numSearchTokens; ++i)
+ {
+ RID rid = RidFromToken(searchTokens[i]);
+
+ ULONG lo = 1;
+ ULONG hi = cRecs;
+
+ while (lo <= hi)
+ {
+ ULONG mid = (lo + hi) / 2;
+
+ if (mid <= cRecs)
+ {
+ rowFlags[mid] |= HotTokenFlags_IntermediateInBinarySearch;
+ }
+
+ if (mid == rid)
+ {
+ break;
+ }
+
+ if (mid < rid)
+ lo = mid + 1;
+ else
+ hi = mid - 1;
+ }
+ }
+
+ // Category 3: Rows may have been touched to find the bounds of a multiple element span (CMiniMdBase::SearchTableForMultipleRows)
+ // A search token will indicate where the search stopped, so mark the first row before and after each that was not itself touched
+ for (ULONG i = 0; i < numSearchTokens; ++i)
+ {
+ RID rid = RidFromToken(searchTokens[i]);
+
+ for (RID r = rid - 1; r >= 1 && r <= cRecs; --r)
+ {
+ if ((rowFlags[r] & HotTokenFlags_ProfiledAccess) == 0)
+ {
+ rowFlags[r] |= HotTokenFlags_BoundingMultipleRowSearch;
+ break;
+ }
+ }
+
+ for (RID r = rid + 1; r <= cRecs; ++r)
+ {
+ if ((rowFlags[r] & HotTokenFlags_ProfiledAccess) == 0)
+ {
+ rowFlags[r] |= HotTokenFlags_BoundingMultipleRowSearch;
+ break;
+ }
+ }
+ }
+
+ // Now walk back over our temporary storage, counting and possibly returning the computed hot tokens
+ resultCount = 0;
+ for (ULONG i = 1; i <= cRecs; ++i)
+ {
+ if (rowFlags[i] != HotTokenFlags_Cold)
+ {
+ if (tokenBuffer != NULL && resultCount < maxCount)
+ tokenBuffer[resultCount] = TokenFromRid(i, ixTbl << 24);
+ resultCount++;
+ }
+ }
+ }
+
+ if (pResultCount)
+ *pResultCount = resultCount;
+
+ ErrExit:
+ return hr;
+} // CMiniMdRW::GetHotMetadataTokensSearchAware
+
+
+#endif //FEATURE_PREJIT
+
+//*****************************************************************************
+// Determine how big the tables would be when saved.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GetFullSaveSize(
+ CorSaveSize fSave, // [IN] cssAccurate or cssQuick.
+ UINT32 *pcbSaveSize, // [OUT] Put the size here.
+ DWORD *pbSaveCompressed, // [OUT] Will the saved data be fully compressed?
+ MetaDataReorderingOptions reorderingOptions, // [IN] Metadata reordering options
+ CorProfileData *pProfileData) // [IN] Optional IBC profile data for working set optimization
+{
+ HRESULT hr = S_OK;
+ CMiniTableDef sTempTable; // Definition for a temporary table.
+ CQuickArray<CMiniColDef> rTempCols; // Definition for a temp table's columns.
+ BYTE SchemaBuf[sizeof(CMiniMdSchema)]; //Buffer for compressed schema.
+ ULONG cbAlign; // Bytes needed for alignment.
+ UINT32 cbTable; // Bytes in a table.
+ UINT32 cbTotal; // Bytes written.
+ int i; // Loop control.
+
+ _ASSERTE(m_bPreSaveDone);
+#ifndef FEATURE_PREJIT
+ _ASSERTE(pProfileData == NULL);
+#endif //!FEATURE_PREJIT
+
+ // Determine if the stream is "fully compressed", ie no pointer tables.
+ *pbSaveCompressed = true;
+ for (i=0; i<(int)m_TblCount; ++i)
+ {
+ if (HasIndirectTable(i))
+ {
+ *pbSaveCompressed = false;
+ break;
+ }
+ }
+
+ // Build the header.
+ CMiniMdSchema Schema = m_Schema;
+ IfFailGo(m_StringHeap.GetAlignedSize(&cbTable));
+ if (cbTable > USHRT_MAX)
+ {
+ Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4;
+ }
+ else
+ {
+ Schema.m_heaps &= ~CMiniMdSchema::HEAP_STRING_4;
+ }
+
+ IfFailGo(m_BlobHeap.GetAlignedSize(&cbTable));
+ if (cbTable > USHRT_MAX)
+ {
+ Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4;
+ }
+ else
+ {
+ Schema.m_heaps &= ~CMiniMdSchema::HEAP_BLOB_4;
+ }
+
+ if (m_GuidHeap.GetSize() > USHRT_MAX)
+ {
+ Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4;
+ }
+ else
+ {
+ Schema.m_heaps &= ~CMiniMdSchema::HEAP_GUID_4;
+ }
+
+ cbTotal = 0;
+ // schema isn't saved for the hot metadata
+ if (pProfileData == NULL)
+ {
+ cbTotal = Schema.SaveTo(SchemaBuf);
+ if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0)
+ cbTotal += cbAlign;
+ }
+
+ // For each table...
+ ULONG ixTbl;
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ {
+ if (GetCountRecs(ixTbl))
+ {
+ // Determine how big the compressed table will be.
+
+ // Allocate a def for the temporary table.
+ sTempTable = m_TableDefs[ixTbl];
+ if (m_eGrow == eg_grown)
+ {
+ IfFailGo(rTempCols.ReSizeNoThrow(sTempTable.m_cCols));
+ sTempTable.m_pColDefs = rTempCols.Ptr();
+
+ // Initialize temp table col defs based on actual counts of data in the
+ // real tables.
+ IfFailGo(InitColsForTable(Schema, ixTbl, &sTempTable, 1, FALSE));
+ }
+
+ cbTable = sTempTable.m_cbRec * GetCountRecs(ixTbl);
+
+#ifdef FEATURE_PREJIT
+ if (pProfileData != NULL)
+ {
+ ULONG itemCount = GetCountRecs(ixTbl);
+
+ // determine number of rows touched in this table as indicated by IBC profile data
+ ULONG hotItemCount = 0;
+ IfFailGo(GetHotMetadataTokensSearchAware(pProfileData, ixTbl, &hotItemCount, NULL, 0));
+
+ // assume ManifestResource table is touched completely if touched at all or any hot metadata at all so far
+ // this is because it's searched linearly, and IBC data misses an unsuccessful search
+ // after module load
+ if (ixTbl == TBL_ManifestResource && (hotItemCount > 0 || cbTotal != 0))
+ hotItemCount = itemCount;
+
+ // if the hot subset of the rows along with their side lookup tables will occupy more space
+ // than the full table, keep the full table to both save space and access time.
+ if (hotItemCount <= USHRT_MAX && itemCount <= USHRT_MAX && m_TableDefs[ixTbl].m_cbRec <= SHRT_MAX)
+ {
+ ULONG estimatedSizeUsingSubsetCopy = hotItemCount * (sizeof(WORD) + sizeof(BYTE) + m_TableDefs[ixTbl].m_cbRec);
+ ULONG estimatedSizeUsingFullCopy = itemCount * m_TableDefs[ixTbl].m_cbRec;
+
+ if (estimatedSizeUsingSubsetCopy > estimatedSizeUsingFullCopy)
+ hotItemCount = itemCount;
+ }
+
+ // first level table is array of WORD, so we can't handle more than 2**16 hot items
+ if (hotItemCount > USHRT_MAX)
+ hotItemCount = 0;
+
+ cbTable = 0;
+ if (hotItemCount > 0)
+ {
+ cbTotal = Align4(cbTotal);
+ cbTable = 5*sizeof(DWORD) + sizeof(WORD); // header: count, 4 offsets, shift count
+ shiftCount = ShiftCount(itemCount, hotItemCount);
+ if (hotItemCount < itemCount)
+ {
+ cbTable += ((1<<shiftCount) + 1) * sizeof(WORD); // 1st level table
+ cbTable += hotItemCount*sizeof(BYTE); // 2nd level table
+ cbTable += hotItemCount*sizeof(WORD); // Index mapping table
+ }
+ cbTable = Align4(cbTable); // align hot metadata on 4-byte boundary
+ cbTable += sTempTable.m_cbRec * hotItemCount; // size of hot metadata
+
+ LOG((LOGMD, "CMiniMdRW::GetFullSaveSize: table %2d %5d items %3d hot items %2d shift count %4d total size\n", ixTbl, itemCount, hotItemCount, shiftCount, cbTable));
+ }
+ else
+ LOG((LOGMD, "CMiniMdRW::GetFullSaveSize: table %2d %5d items\n", ixTbl, itemCount));
+ }
+#endif //FEATURE_PREJIT
+ cbTotal += cbTable;
+ }
+ }
+
+ // Pad with at least 2 bytes and align on 4 bytes.
+ cbAlign = Align4(cbTotal) - cbTotal;
+ if (cbAlign < 2)
+ cbAlign += 4;
+ cbTotal += cbAlign;
+
+ if (pProfileData != NULL)
+ {
+#ifdef FEATURE_PREJIT
+ UINT32 cbHotHeapsSize = 0;
+
+ IfFailGo(GetHotPoolsSaveSize(&cbHotHeapsSize, reorderingOptions, pProfileData));
+ cbTotal += cbHotHeapsSize;
+
+ if (cbTotal <= 4)
+ cbTotal = 0;
+ else
+ cbTotal += sizeof(UINT32) + m_TblCount*sizeof(UINT32)
+ + 2 * sizeof(UINT32); // plus the size of hot metadata header
+#endif //FEATURE_PREJIT
+ }
+ else
+ {
+ m_cbSaveSize = cbTotal;
+ }
+
+ LOG((LOGMD, "CMiniMdRW::GetFullSaveSize: Total %ssize = %d\n", pProfileData ? "hot " : "", cbTotal));
+
+ *pcbSaveSize = cbTotal;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::GetFullSaveSize
+
+//*****************************************************************************
+// GetSaveSize for saving just the delta (ENC) data.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GetENCSaveSize( // S_OK or error.
+ UINT32 *pcbSaveSize) // [OUT] Put the size here.
+{
+ HRESULT hr = S_OK;
+ BYTE SchemaBuf[sizeof(CMiniMdSchema)]; //Buffer for compressed schema.
+ ULONG cbAlign; // Bytes needed for alignment.
+ UINT32 cbTable; // Bytes in a table.
+ UINT32 cbTotal; // Bytes written.
+ ULONG ixTbl; // Loop control.
+
+ // If not saving deltas, defer to full GetSaveSize.
+ if ((m_OptionValue.m_UpdateMode & MDUpdateDelta) != MDUpdateDelta)
+ {
+ DWORD bCompressed;
+ return GetFullSaveSize(cssAccurate, pcbSaveSize, &bCompressed);
+ }
+
+ // Make sure the minimal deltas have expanded tables
+ IfFailRet(ExpandTables());
+
+ // Build the header.
+ CMiniMdSchema Schema = m_Schema;
+
+ if (m_rENCRecs != NULL)
+ {
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ Schema.m_cRecs[ixTbl] = m_rENCRecs[ixTbl].Count();
+ }
+ else
+ {
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ Schema.m_cRecs[ixTbl] = 0;
+ }
+
+ Schema.m_cRecs[TBL_Module] = m_Schema.m_cRecs[TBL_Module];
+ Schema.m_cRecs[TBL_ENCLog] = m_Schema.m_cRecs[TBL_ENCLog];
+ Schema.m_cRecs[TBL_ENCMap] = m_Schema.m_cRecs[TBL_ENCMap];
+
+ cbTotal = Schema.SaveTo(SchemaBuf);
+ if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0)
+ cbTotal += cbAlign;
+
+ // Accumulate size of each table...
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ { // ENC tables are special.
+ if (ixTbl == TBL_ENCLog || ixTbl == TBL_ENCMap || ixTbl == TBL_Module)
+ cbTable = m_Schema.m_cRecs[ixTbl] * m_TableDefs[ixTbl].m_cbRec;
+ else
+ cbTable = Schema.m_cRecs[ixTbl] * m_TableDefs[ixTbl].m_cbRec;
+ cbTotal += cbTable;
+ }
+
+ // Pad with at least 2 bytes and align on 4 bytes.
+ cbAlign = Align4(cbTotal) - cbTotal;
+ if (cbAlign < 2)
+ cbAlign += 4;
+ cbTotal += cbAlign;
+
+ *pcbSaveSize = cbTotal;
+ m_cbSaveSize = cbTotal;
+
+//ErrExit:
+ return hr;
+} // CMiniMdRW::GetENCSaveSize
+
+
+#ifdef FEATURE_PREJIT
+
+// Determine the size of the hot blob data
+//
+__checkReturn
+HRESULT
+CMiniMdRW::GetHotPoolsSaveSize(
+ UINT32 *pcbSize,
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData)
+{
+ HRESULT hr = S_OK;
+ UINT32 cbSavedDirSize = 0;
+ UINT32 cbSavedHeapsSize = 0;
+
+ StreamUtil::NullStream stream;
+ IfFailGo(SaveHotPoolsToStream(
+ &stream,
+ reorderingOptions,
+ pProfileData,
+ &cbSavedDirSize,
+ &cbSavedHeapsSize));
+ *pcbSize = cbSavedDirSize + cbSavedHeapsSize;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::GetHotPoolsSaveSize
+
+#endif //FEATURE_PREJIT
+
+
+//*****************************************************************************
+// Determine how big the tables would be when saved.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GetSaveSize(
+ CorSaveSize fSave, // [IN] cssAccurate or cssQuick.
+ UINT32 *pcbSaveSize, // [OUT] Put the size here.
+ DWORD *pbSaveCompressed, // [OUT] Will the saved data be fully compressed?
+ MetaDataReorderingOptions reorderingOptions, // [IN] Optional metadata reordering options
+ CorProfileData *pProfileData) // [IN] Optional IBC profile data for working set optimization
+{
+ HRESULT hr;
+
+ // Prepare the data for save.
+ IfFailGo(PreSave());
+
+ switch (m_OptionValue.m_UpdateMode & MDUpdateMask)
+ {
+ case MDUpdateFull:
+ hr = GetFullSaveSize(fSave, pcbSaveSize, pbSaveCompressed, reorderingOptions, pProfileData);
+ break;
+ case MDUpdateIncremental:
+ case MDUpdateExtension:
+ case MDUpdateENC:
+ hr = GetFullSaveSize(fSave, pcbSaveSize, pbSaveCompressed, NoReordering, pProfileData);
+ // never save compressed if it is incremental compilation.
+ *pbSaveCompressed = false;
+ break;
+ case MDUpdateDelta:
+ *pbSaveCompressed = false;
+ hr = GetENCSaveSize(pcbSaveSize);
+ break;
+ default:
+ _ASSERTE(!"Internal error -- unknown save mode");
+ return E_INVALIDARG;
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::GetSaveSize
+
+//*****************************************************************************
+// Determine how big a pool would be when saved full size.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GetFullPoolSaveSize( // S_OK or error.
+ int iPool, // The pool of interest.
+ UINT32 *pcbSaveSize) // [OUT] Put the size here.
+{
+ HRESULT hr;
+
+ switch (iPool)
+ {
+ case MDPoolStrings:
+ hr = m_StringHeap.GetAlignedSize(pcbSaveSize);
+ break;
+ case MDPoolGuids:
+ *pcbSaveSize = m_GuidHeap.GetSize();
+ hr = S_OK;
+ break;
+ case MDPoolBlobs:
+ hr = m_BlobHeap.GetAlignedSize(pcbSaveSize);
+ break;
+ case MDPoolUSBlobs:
+ hr = m_UserStringHeap.GetAlignedSize(pcbSaveSize);
+ break;
+ default:
+ hr = E_INVALIDARG;
+ }
+
+ return hr;
+} // CMiniMdRW::GetFullPoolSaveSize
+
+//*****************************************************************************
+// Determine how big a pool would be when saved ENC size.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GetENCPoolSaveSize(
+ int iPool, // The pool of interest.
+ UINT32 *pcbSaveSize) // [OUT] Put the size here.
+{
+ HRESULT hr;
+
+ switch (iPool)
+ {
+ case MDPoolStrings:
+ IfFailRet(m_StringHeap.GetEnCSessionAddedHeapSize_Aligned(pcbSaveSize));
+ hr = S_OK;
+ break;
+ case MDPoolGuids:
+ // We never save delta guid heap, we save full guid heap everytime
+ *pcbSaveSize = m_GuidHeap.GetSize();
+ hr = S_OK;
+ break;
+ case MDPoolBlobs:
+ IfFailRet(m_BlobHeap.GetEnCSessionAddedHeapSize_Aligned(pcbSaveSize));
+ hr = S_OK;
+ break;
+ case MDPoolUSBlobs:
+ IfFailRet(m_UserStringHeap.GetEnCSessionAddedHeapSize_Aligned(pcbSaveSize));
+ hr = S_OK;
+ break;
+ default:
+ hr = E_INVALIDARG;
+ }
+
+ return hr;
+} // CMiniMdRW::GetENCPoolSaveSize
+
+//*****************************************************************************
+// Determine how big a pool would be when saved.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GetPoolSaveSize(
+ int iPool, // The pool of interest.
+ UINT32 *pcbSaveSize) // [OUT] Put the size here.
+{
+ HRESULT hr;
+
+ switch (m_OptionValue.m_UpdateMode & MDUpdateMask)
+ {
+ case MDUpdateFull:
+ case MDUpdateIncremental:
+ case MDUpdateExtension:
+ case MDUpdateENC:
+ hr = GetFullPoolSaveSize(iPool, pcbSaveSize);
+ break;
+ case MDUpdateDelta:
+ hr = GetENCPoolSaveSize(iPool, pcbSaveSize);
+ break;
+ default:
+ _ASSERTE(!"Internal error -- unknown save mode");
+ return E_INVALIDARG;
+ }
+
+ return hr;
+} // CMiniMdRW::GetPoolSaveSize
+
+//*****************************************************************************
+// Is the given pool empty?
+//*****************************************************************************
+int CMiniMdRW::IsPoolEmpty( // True or false.
+ int iPool) // The pool of interest.
+{
+ switch (iPool)
+ {
+ case MDPoolStrings:
+ return m_StringHeap.IsEmpty();
+ case MDPoolGuids:
+ return m_GuidHeap.IsEmpty();
+ case MDPoolBlobs:
+ return m_BlobHeap.IsEmpty();
+ case MDPoolUSBlobs:
+ return m_UserStringHeap.IsEmpty();
+ }
+ return true;
+} // CMiniMdRW::IsPoolEmpty
+
+// --------------------------------------------------------------------------------------
+//
+// Gets user string (*Data) at index (nIndex) and fills the index (*pnNextIndex) of the next user string
+// in the heap.
+// Returns S_OK and fills the string (*pData) and the next index (*pnNextIndex).
+// Returns S_FALSE if the index (nIndex) is not valid user string index.
+// Returns error code otherwise.
+// Clears *pData and sets *pnNextIndex to 0 on error or S_FALSE.
+//
+__checkReturn
+HRESULT
+CMiniMdRW::GetUserStringAndNextIndex(
+ UINT32 nIndex,
+ MetaData::DataBlob *pData,
+ UINT32 *pnNextIndex)
+{
+ HRESULT hr = S_OK;
+ MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+
+ // First check that the index is valid to avoid debug error reporting
+ // If this turns out to be slow, then we can add a new API to BlobHeap "GetBlobWithSizePrefix_DontFail"
+ // to merge this check with following GetBlobWithSizePrefix call
+ if (!m_UserStringHeap.IsValidIndex(nIndex))
+ {
+ return S_FALSE;
+ }
+
+ // Get user string at index nIndex (verifies that the user string is in the heap)
+ IfFailGo(m_UserStringHeap.GetBlobWithSizePrefix(
+ nIndex,
+ pData));
+ _ASSERTE(hr == S_OK);
+
+ // Get index behind the user string - doesn't overflow, because the user string is in the heap
+ *pnNextIndex = nIndex + pData->GetSize();
+
+ UINT32 cbUserStringSize_Ignore;
+ if (!pData->GetCompressedU(&cbUserStringSize_Ignore))
+ {
+ Debug_ReportInternalError("There's a bug, because previous call to GetBlobWithSizePrefix succeeded.");
+ IfFailGo(METADATA_E_INTERNAL_ERROR);
+ }
+ return S_OK;
+
+ErrExit:
+ // Fill output parameters on error
+ *pnNextIndex = 0;
+ pData->Clear();
+
+ return hr;
+} // CMiniMdRW::GetUserStringAndNextIndex
+
+//*****************************************************************************
+// Initialized TokenRemapManager
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::InitTokenRemapManager()
+{
+ HRESULT hr = NOERROR;
+
+ if (m_pTokenRemapManager == NULL)
+ {
+ // allocate TokenRemapManager
+ m_pTokenRemapManager = new (nothrow) TokenRemapManager;
+ IfNullGo(m_pTokenRemapManager);
+ }
+
+ // initialize the ref to def optimization map
+ IfFailGo( m_pTokenRemapManager->ClearAndEnsureCapacity(m_Schema.m_cRecs[TBL_TypeRef], m_Schema.m_cRecs[TBL_MemberRef]));
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::InitTokenRemapManager
+
+//*****************************************************************************
+// Debug code to check whether a table's objects can have custom attributes
+// attached.
+//*****************************************************************************
+#ifdef _DEBUG
+bool CMiniMdRW::CanHaveCustomAttribute( // Can a given table have a custom attribute token?
+ ULONG ixTbl) // Table in question.
+{
+ mdToken tk = GetTokenForTable(ixTbl);
+ size_t ix;
+ for (ix=0; ix<g_CodedTokens[CDTKN_HasCustomAttribute].m_cTokens; ++ix)
+ if (g_CodedTokens[CDTKN_HasCustomAttribute].m_pTokens[ix] == tk)
+ return true;
+ return false;
+} // CMiniMdRW::CanHaveCustomAttribute
+#endif //_DEBUG
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+//---------------------------------------------------------------------------------------
+//
+// Perform any available pre-save optimizations.
+//
+__checkReturn
+HRESULT
+CMiniMdRW::PreSaveFull()
+{
+ HRESULT hr = S_OK;
+ RID ridPtr; // A RID from a pointer table.
+
+ if (m_bPreSaveDone)
+ return hr;
+
+ // Don't yet know what the save size will be.
+ m_cbSaveSize = 0;
+ m_bSaveCompressed = false;
+
+ // Convert any END_OF_TABLE values for tables with child pointer tables.
+ IfFailGo(ConvertMarkerToEndOfTable(
+ TBL_TypeDef,
+ TypeDefRec::COL_MethodList,
+ m_Schema.m_cRecs[TBL_Method] + 1,
+ m_Schema.m_cRecs[TBL_TypeDef]));
+ IfFailGo(ConvertMarkerToEndOfTable(
+ TBL_TypeDef,
+ TypeDefRec::COL_FieldList,
+ m_Schema.m_cRecs[TBL_Field] + 1,
+ m_Schema.m_cRecs[TBL_TypeDef]));
+ IfFailGo(ConvertMarkerToEndOfTable(
+ TBL_Method,
+ MethodRec::COL_ParamList,
+ m_Schema.m_cRecs[TBL_Param]+1,
+ m_Schema.m_cRecs[TBL_Method]));
+ IfFailGo(ConvertMarkerToEndOfTable(
+ TBL_PropertyMap,
+ PropertyMapRec::COL_PropertyList,
+ m_Schema.m_cRecs[TBL_Property] + 1,
+ m_Schema.m_cRecs[TBL_PropertyMap]));
+ IfFailGo(ConvertMarkerToEndOfTable(
+ TBL_EventMap,
+ EventMapRec::COL_EventList,
+ m_Schema.m_cRecs[TBL_Event] + 1,
+ m_Schema.m_cRecs[TBL_EventMap]));
+
+ // If there is a handler and in "Full" mode, eliminate the intermediate tables.
+ if ((m_pHandler != NULL) && ((m_OptionValue.m_UpdateMode &MDUpdateMask) == MDUpdateFull))
+ {
+ // If there is a handler, and not in E&C, save as fully compressed.
+ m_bSaveCompressed = true;
+
+ // Temporary tables for new Fields, Methods, Params and FieldLayouts.
+ MetaData::TableRW newFields;
+ IfFailGo(newFields.InitializeEmpty_WithRecordCount(
+ m_TableDefs[TBL_Field].m_cbRec,
+ m_Schema.m_cRecs[TBL_Field]
+ COMMA_INDEBUG_MD(TRUE)));
+ INDEBUG_MD(newFields.Debug_SetTableInfo("TBL_Field", TBL_Field));
+
+ MetaData::TableRW newMethods;
+ IfFailGo(newMethods.InitializeEmpty_WithRecordCount(
+ m_TableDefs[TBL_Method].m_cbRec,
+ m_Schema.m_cRecs[TBL_Method]
+ COMMA_INDEBUG_MD(TRUE)));
+ INDEBUG_MD(newMethods.Debug_SetTableInfo("TBL_Method", TBL_Method));
+
+ MetaData::TableRW newParams;
+ IfFailGo(newParams.InitializeEmpty_WithRecordCount(
+ m_TableDefs[TBL_Param].m_cbRec,
+ m_Schema.m_cRecs[TBL_Param]
+ COMMA_INDEBUG_MD(TRUE)));
+ INDEBUG_MD(newParams.Debug_SetTableInfo("TBL_Param", TBL_Param));
+
+ MetaData::TableRW newEvents;
+ IfFailGo(newEvents.InitializeEmpty_WithRecordCount(
+ m_TableDefs[TBL_Event].m_cbRec,
+ m_Schema.m_cRecs[TBL_Event]
+ COMMA_INDEBUG_MD(TRUE)));
+ INDEBUG_MD(newEvents.Debug_SetTableInfo("TBL_Event", TBL_Event));
+
+ MetaData::TableRW newPropertys;
+ IfFailGo(newPropertys.InitializeEmpty_WithRecordCount(
+ m_TableDefs[TBL_Property].m_cbRec,
+ m_Schema.m_cRecs[TBL_Property]
+ COMMA_INDEBUG_MD(TRUE)));
+ INDEBUG_MD(newPropertys.Debug_SetTableInfo("TBL_Property", TBL_Property));
+
+ // If we have any indirect table for Field or Method and we are about to reorder these
+ // tables, the MemberDef hash table will be invalid after the token movement. So invalidate
+ // the hash.
+ if ((HasIndirectTable(TBL_Field) || HasIndirectTable(TBL_Method)) && (m_pMemberDefHash != NULL))
+ {
+ delete m_pMemberDefHash;
+ m_pMemberDefHash = NULL;
+ }
+
+ // Enumerate fields and copy.
+ if (HasIndirectTable(TBL_Field))
+ {
+ for (ridPtr = 1; ridPtr <= m_Schema.m_cRecs[TBL_Field]; ++ridPtr)
+ {
+ BYTE * pOldPtr;
+ IfFailGo(m_Tables[TBL_FieldPtr].GetRecord(ridPtr, &pOldPtr));
+ RID ridOld;
+ ridOld = GetCol(TBL_FieldPtr, FieldPtrRec::COL_Field, pOldPtr);
+ BYTE * pOld;
+ IfFailGo(m_Tables[TBL_Field].GetRecord(ridOld, &pOld));
+ RID ridNew;
+ BYTE * pNew;
+ IfFailGo(newFields.AddRecord(&pNew, (UINT32 *)&ridNew));
+ _ASSERTE(ridNew == ridPtr);
+ memcpy(pNew, pOld, m_TableDefs[TBL_Field].m_cbRec);
+
+ // Let the caller know of the token change.
+ IfFailGo(MapToken(ridOld, ridNew, mdtFieldDef));
+ }
+ }
+
+ // Enumerate methods and copy.
+ if (HasIndirectTable(TBL_Method) || HasIndirectTable(TBL_Param))
+ {
+ for (ridPtr = 1; ridPtr <= m_Schema.m_cRecs[TBL_Method]; ++ridPtr)
+ {
+ MethodRec * pOld;
+ RID ridOld;
+ BYTE * pNew = NULL;
+ if (HasIndirectTable(TBL_Method))
+ {
+ BYTE * pOldPtr;
+ IfFailGo(m_Tables[TBL_MethodPtr].GetRecord(ridPtr, &pOldPtr));
+ ridOld = GetCol(TBL_MethodPtr, MethodPtrRec::COL_Method, pOldPtr);
+ IfFailGo(GetMethodRecord(ridOld, &pOld));
+ RID ridNew;
+ IfFailGo(newMethods.AddRecord(&pNew, (UINT32 *)&ridNew));
+ _ASSERTE(ridNew == ridPtr);
+ memcpy(pNew, pOld, m_TableDefs[TBL_Method].m_cbRec);
+
+ // Let the caller know of the token change.
+ IfFailGo(MapToken(ridOld, ridNew, mdtMethodDef));
+ }
+ else
+ {
+ ridOld = ridPtr;
+ IfFailGo(GetMethodRecord(ridPtr, &pOld));
+ }
+
+ // Handle the params of the method.
+ if (HasIndirectTable(TBL_Method))
+ {
+ IfFailGo(PutCol(TBL_Method, MethodRec::COL_ParamList, pNew, newParams.GetRecordCount() + 1));
+ }
+ RID ixStart = getParamListOfMethod(pOld);
+ RID ixEnd;
+ IfFailGo(getEndParamListOfMethod(ridOld, &ixEnd));
+ for (; ixStart<ixEnd; ++ixStart)
+ {
+ RID ridParam;
+ if (HasIndirectTable(TBL_Param))
+ {
+ BYTE * pOldPtr;
+ IfFailGo(m_Tables[TBL_ParamPtr].GetRecord(ixStart, &pOldPtr));
+ ridParam = GetCol(TBL_ParamPtr, ParamPtrRec::COL_Param, pOldPtr);
+ }
+ else
+ {
+ ridParam = ixStart;
+ }
+ BYTE * pOldRecord;
+ IfFailGo(m_Tables[TBL_Param].GetRecord(ridParam, &pOldRecord));
+ RID ridNew;
+ BYTE * pNewRecord;
+ IfFailGo(newParams.AddRecord(&pNewRecord, (UINT32 *)&ridNew));
+ memcpy(pNewRecord, pOldRecord, m_TableDefs[TBL_Param].m_cbRec);
+
+ // Let the caller know of the token change.
+ IfFailGo(MapToken(ridParam, ridNew, mdtParamDef));
+ }
+ }
+ }
+
+ // Get rid of EventPtr and PropertyPtr table as well
+ // Enumerate fields and copy.
+ if (HasIndirectTable(TBL_Event))
+ {
+ for (ridPtr = 1; ridPtr <= m_Schema.m_cRecs[TBL_Event]; ++ridPtr)
+ {
+ BYTE * pOldPtr;
+ IfFailGo(m_Tables[TBL_EventPtr].GetRecord(ridPtr, &pOldPtr));
+ RID ridOld;
+ ridOld = GetCol(TBL_EventPtr, EventPtrRec::COL_Event, pOldPtr);
+ BYTE * pOld;
+ IfFailGo(m_Tables[TBL_Event].GetRecord(ridOld, &pOld));
+ RID ridNew;
+ BYTE * pNew;
+ IfFailGo(newEvents.AddRecord(&pNew, (UINT32 *)&ridNew));
+ _ASSERTE(ridNew == ridPtr);
+ memcpy(pNew, pOld, m_TableDefs[TBL_Event].m_cbRec);
+
+ // Let the caller know of the token change.
+ IfFailGo(MapToken(ridOld, ridNew, mdtEvent));
+ }
+ }
+
+ if (HasIndirectTable(TBL_Property))
+ {
+ for (ridPtr = 1; ridPtr <= m_Schema.m_cRecs[TBL_Property]; ++ridPtr)
+ {
+ BYTE * pOldPtr;
+ IfFailGo(m_Tables[TBL_PropertyPtr].GetRecord(ridPtr, &pOldPtr));
+ RID ridOld;
+ ridOld = GetCol(TBL_PropertyPtr, PropertyPtrRec::COL_Property, pOldPtr);
+ BYTE * pOld;
+ IfFailGo(m_Tables[TBL_Property].GetRecord(ridOld, &pOld));
+ RID ridNew;
+ BYTE * pNew;
+ IfFailGo(newPropertys.AddRecord(&pNew, (UINT32 *)&ridNew));
+ _ASSERTE(ridNew == ridPtr);
+ memcpy(pNew, pOld, m_TableDefs[TBL_Property].m_cbRec);
+
+ // Let the caller know of the token change.
+ IfFailGo(MapToken(ridOld, ridNew, mdtProperty));
+ }
+ }
+
+
+ // Replace the old tables with the new, sorted ones.
+ if (HasIndirectTable(TBL_Field))
+ {
+ m_Tables[TBL_Field].Delete();
+ IfFailGo(m_Tables[TBL_Field].InitializeFromTable(
+ &newFields,
+ TRUE)); // fCopyData
+ }
+ if (HasIndirectTable(TBL_Method))
+ {
+ m_Tables[TBL_Method].Delete();
+ IfFailGo(m_Tables[TBL_Method].InitializeFromTable(
+ &newMethods,
+ TRUE)); // fCopyData
+ }
+ if (HasIndirectTable(TBL_Method) || HasIndirectTable(TBL_Param))
+ {
+ m_Tables[TBL_Param].Delete();
+ IfFailGo(m_Tables[TBL_Param].InitializeFromTable(
+ &newParams,
+ TRUE)); // fCopyData
+ }
+ if (HasIndirectTable(TBL_Property))
+ {
+ m_Tables[TBL_Property].Delete();
+ IfFailGo(m_Tables[TBL_Property].InitializeFromTable(
+ &newPropertys,
+ TRUE)); // fCopyData
+ }
+ if (HasIndirectTable(TBL_Event))
+ {
+ m_Tables[TBL_Event].Delete();
+ IfFailGo(m_Tables[TBL_Event].InitializeFromTable(
+ &newEvents,
+ TRUE)); // fCopyData
+ }
+
+ // Empty the pointer tables table.
+ m_Schema.m_cRecs[TBL_FieldPtr] = 0;
+ m_Schema.m_cRecs[TBL_MethodPtr] = 0;
+ m_Schema.m_cRecs[TBL_ParamPtr] = 0;
+ m_Schema.m_cRecs[TBL_PropertyPtr] = 0;
+ m_Schema.m_cRecs[TBL_EventPtr] = 0;
+
+ // invalidated the parent look up tables
+ if (m_pMethodMap)
+ {
+ delete m_pMethodMap;
+ m_pMethodMap = NULL;
+ }
+ if (m_pFieldMap)
+ {
+ delete m_pFieldMap;
+ m_pFieldMap = NULL;
+ }
+ if (m_pPropertyMap)
+ {
+ delete m_pPropertyMap;
+ m_pPropertyMap = NULL;
+ }
+ if (m_pEventMap)
+ {
+ delete m_pEventMap;
+ m_pEventMap = NULL;
+ }
+ if (m_pParamMap)
+ {
+ delete m_pParamMap;
+ m_pParamMap = NULL;
+ }
+ }
+
+ // Do the ref to def fixup before fix up with token movement
+ IfFailGo(FixUpRefToDef());
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // We now need to do two kinds of fixups, and the two fixups interact with
+ // each other.
+ // 1) We need to sort several tables for binary searching.
+ // 2) We need to fixup any references to other tables, which may have
+ // changed due to ref-to-def, ptr-table elimination, or sorting.
+ //
+
+
+ // First do fixups. Some of these are then sorted based on fixed-up columns.
+
+ IfFailGo(FixUpTable(TBL_MemberRef));
+ IfFailGo(FixUpTable(TBL_MethodSemantics));
+ IfFailGo(FixUpTable(TBL_Constant));
+ IfFailGo(FixUpTable(TBL_FieldMarshal));
+ IfFailGo(FixUpTable(TBL_MethodImpl));
+ IfFailGo(FixUpTable(TBL_DeclSecurity));
+ IfFailGo(FixUpTable(TBL_ImplMap));
+ IfFailGo(FixUpTable(TBL_FieldRVA));
+ IfFailGo(FixUpTable(TBL_FieldLayout));
+
+ if (SupportsGenerics())
+ {
+ IfFailGo(FixUpTable(TBL_GenericParam));
+ IfFailGo(FixUpTable(TBL_MethodSpec));
+ }
+
+ // Now sort any tables that are allowed to have custom attributes.
+ // This block for tables sorted in full mode only -- basically
+ // tables for which we hand out tokens.
+ if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull)
+ {
+ if (SupportsGenerics())
+ {
+ // Sort the GenericParam table by the Owner.
+ // Don't disturb the sequence ordering within Owner
+ STABLESORTER_WITHREMAP(GenericParam, Owner);
+ IfFailGo(sortGenericParam.Sort());
+ }
+
+ // Sort the InterfaceImpl table by class.
+ STABLESORTER_WITHREMAP(InterfaceImpl, Class);
+ IfFailGo(sortInterfaceImpl.Sort());
+
+ // Sort the DeclSecurity table by parent.
+ SORTER_WITHREMAP(DeclSecurity, Parent);
+ IfFailGo(sortDeclSecurity.Sort());
+ }
+
+ // The GenericParamConstraint table is parented to the GenericParam table,
+ // so it needs fixup after sorting GenericParam table.
+ if (SupportsGenerics())
+ {
+ IfFailGo(FixUpTable(TBL_GenericParamConstraint));
+
+ // After fixing up the GenericParamConstraint table, we can then
+ // sort it.
+ if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull)
+ {
+ // Sort the GenericParamConstraint table by the Owner.
+ // Don't disturb the sequence ordering within Owner
+ STABLESORTER_WITHREMAP(GenericParamConstraint, Owner);
+ IfFailGo(sortGenericParamConstraint.Sort());
+ }
+ }
+ // Fixup the custom attribute table. After this, do not sort any table
+ // that is allowed to have a custom attribute.
+ IfFailGo(FixUpTable(TBL_CustomAttribute));
+
+ // Sort tables for binary searches.
+ if (((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull) ||
+ ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateIncremental))
+ {
+ // Sort tables as required
+ //-------------------------------------------------------------------------
+ // Module order is preserved
+ // TypeRef order is preserved
+ // TypeDef order is preserved
+ // Field grouped and pointed to by TypeDef
+ // Method grouped and pointed to by TypeDef
+ // Param grouped and pointed to by Method
+ // InterfaceImpl sorted here
+ // MemberRef order is preserved
+ // Constant sorted here
+ // CustomAttribute sorted INCORRECTLY!! here
+ // FieldMarshal sorted here
+ // DeclSecurity sorted here
+ // ClassLayout created in order with TypeDefs
+ // FieldLayout grouped and pointed to by ClassLayouts
+ // StandaloneSig order is preserved
+ // TypeSpec order is preserved
+ // EventMap created in order at conversion (by Event Parent)
+ // Event sorted by Parent at conversion
+ // PropertyMap created in order at conversion (by Property Parent)
+ // Property sorted by Parent at conversion
+ // MethodSemantics sorted by Association at conversion.
+ // MethodImpl sorted here.
+ // Sort the constant table by parent.
+ // Sort the nested class table by NestedClass.
+ // Sort the generic par table by Owner
+ // MethodSpec order is preserved
+
+ // Always sort Constant table
+ _ASSERTE(!CanHaveCustomAttribute(TBL_Constant));
+ SORTER(Constant, Parent);
+ sortConstant.Sort();
+
+ // Always sort the FieldMarshal table by Parent.
+ _ASSERTE(!CanHaveCustomAttribute(TBL_FieldMarshal));
+ SORTER(FieldMarshal, Parent);
+ sortFieldMarshal.Sort();
+
+ // Always sort the MethodSematics
+ _ASSERTE(!CanHaveCustomAttribute(TBL_MethodSemantics));
+ SORTER(MethodSemantics, Association);
+ sortMethodSemantics.Sort();
+
+ // Always Sort the ClassLayoutTable by parent.
+ _ASSERTE(!CanHaveCustomAttribute(TBL_ClassLayout));
+ SORTER(ClassLayout, Parent);
+ sortClassLayout.Sort();
+
+ // Always Sort the FieldLayoutTable by parent.
+ _ASSERTE(!CanHaveCustomAttribute(TBL_FieldLayout));
+ SORTER(FieldLayout, Field);
+ sortFieldLayout.Sort();
+
+ // Always Sort the ImplMap table by the parent.
+ _ASSERTE(!CanHaveCustomAttribute(TBL_ImplMap));
+ SORTER(ImplMap, MemberForwarded);
+ sortImplMap.Sort();
+
+ // Always Sort the FieldRVA table by the Field.
+ _ASSERTE(!CanHaveCustomAttribute(TBL_FieldRVA));
+ SORTER(FieldRVA, Field);
+ sortFieldRVA.Sort();
+
+ // Always Sort the NestedClass table by the NestedClass.
+ _ASSERTE(!CanHaveCustomAttribute(TBL_NestedClass));
+ SORTER(NestedClass, NestedClass);
+ sortNestedClass.Sort();
+
+ // Always Sort the MethodImpl table by the Class.
+ _ASSERTE(!CanHaveCustomAttribute(TBL_MethodImpl));
+ SORTER(MethodImpl, Class);
+ sortMethodImpl.Sort();
+
+ // Some tokens are not moved in ENC mode; only "full" mode.
+ if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull)
+ {
+ // Sort the CustomAttribute table by parent.
+ _ASSERTE(!CanHaveCustomAttribute(TBL_CustomAttribute));
+ SORTER_WITHREMAP(CustomAttribute, Parent);
+ IfFailGo(sortCustomAttribute.Sort());
+ }
+
+ // Determine if the PropertyMap and EventMap are already sorted, and set the flag appropriately
+ SORTER(PropertyMap, Parent);
+ sortPropertyMap.CheckSortedWithNoDuplicates();
+
+ SORTER(EventMap, Parent);
+ sortEventMap.CheckSortedWithNoDuplicates();
+
+ //-------------------------------------------------------------------------
+ } // enclosing scope required for initialization ("goto" above skips initialization).
+
+ m_bPreSaveDone = true;
+
+ // send the Ref->Def optmization notification to host
+ if (m_pHandler != NULL)
+ {
+ TOKENMAP * ptkmap = GetMemberRefToMemberDefMap();
+ PREFIX_ASSUME(ptkmap != NULL); // RegMeta always inits this.
+ MDTOKENMAP * ptkRemap = GetTokenMovementMap();
+ int iCount = m_Schema.m_cRecs[TBL_MemberRef];
+ mdToken tkTo;
+ mdToken tkDefTo;
+ int i;
+ MemberRefRec * pMemberRefRec; // A MemberRefRec.
+ const COR_SIGNATURE * pvSig; // Signature of the MemberRef.
+ ULONG cbSig; // Size of the signature blob.
+
+ // loop through all LocalVar
+ for (i = 1; i <= iCount; i++)
+ {
+ tkTo = *(ptkmap->Get(i));
+ if (RidFromToken(tkTo) != mdTokenNil)
+ {
+ // so far, the parent of memberref can be changed to only fielddef or methoddef
+ // or it will remain unchanged.
+ //
+ _ASSERTE((TypeFromToken(tkTo) == mdtFieldDef) || (TypeFromToken(tkTo) == mdtMethodDef));
+
+ IfFailGo(GetMemberRefRecord(i, &pMemberRefRec));
+ IfFailGo(getSignatureOfMemberRef(pMemberRefRec, &pvSig, &cbSig));
+
+ // Don't turn mr's with vararg's into defs, because the variable portion
+ // of the call is kept in the mr signature.
+ if ((pvSig != NULL) && isCallConv(*pvSig, IMAGE_CEE_CS_CALLCONV_VARARG))
+ continue;
+
+ // ref is optimized to the def
+
+ // now remap the def since def could be moved again.
+ tkDefTo = ptkRemap->SafeRemap(tkTo);
+
+ // when Def token moves, it will not change type!!
+ _ASSERTE(TypeFromToken(tkTo) == TypeFromToken(tkDefTo));
+ LOG((LOGMD, "MapToken (remap): from 0x%08x to 0x%08x\n", TokenFromRid(i, mdtMemberRef), tkDefTo));
+ m_pHandler->Map(TokenFromRid(i, mdtMemberRef), tkDefTo);
+ }
+ }
+ }
+
+ // Ok, we've applied all of the token remaps. Make sure we don't apply them again in the future
+ if (GetTokenMovementMap() != NULL)
+ IfFailGo(GetTokenMovementMap()->EmptyMap());
+
+ErrExit:
+
+ return hr;
+} // CMiniMdRW::PreSaveFull
+
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//---------------------------------------------------------------------------------------
+//
+// ENC-specific pre-safe work.
+//
+__checkReturn
+HRESULT
+CMiniMdRW::PreSaveEnc()
+{
+ HRESULT hr;
+ int iNew; // Insertion point for new tokens.
+ ULONG *pul; // Found token.
+ ULONG iRid; // RID from a token.
+ ULONG ixTbl; // Table from an ENC record.
+ ULONG cRecs; // Count of records in a table.
+
+ IfFailGo(PreSaveFull());
+
+ // Turn off pre-save bit so that we can add ENC map records.
+ m_bPreSaveDone = false;
+
+ if (m_Schema.m_cRecs[TBL_ENCLog])
+ { // Keep track of ENC recs we've seen.
+ _ASSERTE(m_rENCRecs == 0);
+ m_rENCRecs = new (nothrow) ULONGARRAY[m_TblCount];
+ IfNullGo(m_rENCRecs);
+
+ // Create the temporary table.
+ MetaData::TableRW tempTable;
+ IfFailGo(tempTable.InitializeEmpty_WithRecordCount(
+ m_TableDefs[TBL_ENCLog].m_cbRec,
+ m_Schema.m_cRecs[TBL_ENCLog]
+ COMMA_INDEBUG_MD(TRUE)));
+ INDEBUG_MD(tempTable.Debug_SetTableInfo("TBL_ENCLog", TBL_ENCLog));
+
+ // For each row in the data.
+ RID rid;
+ ULONG iKept=0;
+ for (rid=1; rid<=m_Schema.m_cRecs[TBL_ENCLog]; ++rid)
+ {
+ ENCLogRec *pFrom;
+ IfFailGo(m_Tables[TBL_ENCLog].GetRecord(rid, reinterpret_cast<BYTE **>(&pFrom)));
+
+ // Keep this record?
+ if (pFrom->GetFuncCode() == 0)
+ { // No func code. Skip if we've seen this token before.
+
+ // What kind of record is this?
+ if (IsRecId(pFrom->GetToken()))
+ { // Non-token table
+ iRid = RidFromRecId(pFrom->GetToken());
+ ixTbl = TblFromRecId(pFrom->GetToken());
+ }
+ else
+ { // Token table.
+ iRid = RidFromToken(pFrom->GetToken());
+ ixTbl = GetTableForToken(pFrom->GetToken());
+
+ }
+
+ RIDBinarySearch searcher((UINT32 *)m_rENCRecs[ixTbl].Ptr(), m_rENCRecs[ixTbl].Count());
+ pul = (ULONG *)(searcher.Find((UINT32 *)&iRid, &iNew));
+ // If we found the token, don't keep the record.
+ if (pul != 0)
+ {
+ LOG((LOGMD, "PreSave ENCLog skipping duplicate token %d", pFrom->GetToken()));
+ continue;
+ }
+ // First time token was seen, so keep track of it.
+ IfNullGo(pul = m_rENCRecs[ixTbl].Insert(iNew));
+ *pul = iRid;
+ }
+
+ // Keeping the record, so allocate the new record to hold it.
+ ++iKept;
+ RID ridNew;
+ ENCLogRec *pTo;
+ IfFailGo(tempTable.AddRecord(reinterpret_cast<BYTE **>(&pTo), (UINT32 *)&ridNew));
+ _ASSERTE(ridNew == iKept);
+
+ // copy the data.
+ *pTo = *pFrom;
+ }
+
+ // Keep the expanded table.
+ m_Tables[TBL_ENCLog].Delete();
+ IfFailGo(m_Tables[TBL_ENCLog].InitializeFromTable(
+ &tempTable,
+ TRUE)); // fCopyData
+ INDEBUG_MD(m_Tables[TBL_ENCLog].Debug_SetTableInfo("TBL_ENCLog", TBL_ENCLog));
+ m_Schema.m_cRecs[TBL_ENCLog] = iKept;
+
+ // If saving only deltas, build the ENC Map table.
+ if (((m_OptionValue.m_UpdateMode & MDUpdateDelta)) == MDUpdateDelta)
+ {
+ cRecs = 0;
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ {
+ cRecs += m_rENCRecs[ixTbl].Count();
+ }
+ m_Tables[TBL_ENCMap].Delete();
+
+ m_Schema.m_cRecs[TBL_ENCMap] = 0;
+
+ IfFailGo(m_Tables[TBL_ENCMap].InitializeEmpty_WithRecordCount(
+ m_TableDefs[TBL_ENCMap].m_cbRec,
+ cRecs
+ COMMA_INDEBUG_MD(TRUE)));
+ INDEBUG_MD(m_Tables[TBL_ENCMap].Debug_SetTableInfo("TBL_ENCMap", TBL_ENCMap));
+ cRecs = 0;
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ {
+ ENCMapRec *pNew;
+ ULONG nNew;
+ for (int i=0; i<m_rENCRecs[ixTbl].Count(); ++i)
+ {
+ IfFailGo(AddENCMapRecord(&pNew, &nNew)); // pre-allocated for all rows.
+ _ASSERTE(nNew == ++cRecs);
+ _ASSERTE(TblFromRecId(RecIdFromRid(m_rENCRecs[ixTbl][i], ixTbl)) < m_TblCount);
+ pNew->SetToken(RecIdFromRid(m_rENCRecs[ixTbl][i], ixTbl));
+ }
+ }
+ }
+ }
+
+ // Turn pre-save bit back on.
+ m_bPreSaveDone = true;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::PreSaveEnc
+
+//*****************************************************************************
+// Perform any appropriate pre-save optimization or reorganization.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::PreSave(
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData)
+{
+ HRESULT hr = S_OK;
+
+#ifdef _DEBUG
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_PreSaveBreak))
+ {
+ _ASSERTE(!"CMiniMdRW::PreSave()");
+ }
+#endif //_DEBUG
+
+ if (m_bPreSaveDone)
+ return hr;
+
+#ifdef FEATURE_PREJIT
+ // Reorganization should be done at ngen time only
+ if( reorderingOptions & ReArrangeStringPool )
+ {
+ EX_TRY
+ {
+ OrganizeStringPool(pProfileData);
+ }
+ EX_CATCH
+ {
+ hr = GET_EXCEPTION()->GetHR();
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ IfFailRet(hr);
+ }
+#endif // FEATURE_PREJIT
+
+ switch (m_OptionValue.m_UpdateMode & MDUpdateMask)
+ {
+ case MDUpdateFull:
+ case MDUpdateIncremental:
+ case MDUpdateExtension:
+ hr = PreSaveFull();
+ break;
+ // PreSaveEnc removes duplicate entries in the ENCLog table,
+ // which we need to do regardless if we're saving a full MD
+ // or a minimal delta.
+ case MDUpdateDelta:
+ case MDUpdateENC:
+ hr = PreSaveEnc();
+ break;
+ default:
+ _ASSERTE(!"Internal error -- unknown save mode");
+ return E_INVALIDARG;
+ }
+
+ return hr;
+} // CMiniMdRW::PreSave
+
+//*****************************************************************************
+// Perform any necessary post-save cleanup.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::PostSave()
+{
+ if (m_rENCRecs)
+ {
+ delete [] m_rENCRecs;
+ m_rENCRecs = 0;
+ }
+
+ m_bPreSaveDone = false;
+
+ return S_OK;
+} // CMiniMdRW::PostSave
+
+//*****************************************************************************
+// Save the tables to the stream.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::SaveFullTablesToStream(
+ IStream *pIStream,
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData)
+{
+ HRESULT hr;
+ CMiniTableDef sTempTable; // Definition for a temporary table.
+ CQuickArray<CMiniColDef> rTempCols; // Definition for a temp table's columns.
+ BYTE SchemaBuf[sizeof(CMiniMdSchema)]; //Buffer for compressed schema.
+ ULONG cbAlign; // Bytes needed for alignment.
+ UINT32 cbTable; // Bytes in a table.
+ UINT32 cbTotal; // Bytes written.
+ static const unsigned char zeros[8] = {0}; // For padding and alignment.
+
+#ifndef FEATURE_PREJIT
+ _ASSERTE(pProfileData == NULL);
+#endif //!FEATURE_PREJIT
+
+ // Write the header.
+ CMiniMdSchema Schema = m_Schema;
+ IfFailGo(m_StringHeap.GetAlignedSize(&cbTable));
+ if (cbTable > USHRT_MAX)
+ {
+ Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4;
+ }
+ else
+ {
+ Schema.m_heaps &= ~CMiniMdSchema::HEAP_STRING_4;
+ }
+
+ if (m_GuidHeap.GetSize() > USHRT_MAX)
+ {
+ Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4;
+ }
+ else
+ {
+ Schema.m_heaps &= ~CMiniMdSchema::HEAP_GUID_4;
+ }
+
+ IfFailGo(m_BlobHeap.GetAlignedSize(&cbTable));
+ if (cbTable > USHRT_MAX)
+ {
+ Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4;
+ }
+ else
+ {
+ Schema.m_heaps &= ~CMiniMdSchema::HEAP_BLOB_4;
+ }
+
+ cbTotal = 0;
+ if (pProfileData == NULL)
+ {
+ cbTotal = Schema.SaveTo(SchemaBuf);
+ IfFailGo(pIStream->Write(SchemaBuf, cbTotal, 0));
+ if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0)
+ IfFailGo(pIStream->Write(&hr, cbAlign, 0));
+ cbTotal += cbAlign;
+ }
+
+ ULONG headerOffset[TBL_COUNT];
+ _ASSERTE(m_TblCount <= TBL_COUNT);
+
+ ULONG ixTbl;
+ // For each table...
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ {
+ headerOffset[ixTbl] = ~0UL;
+
+ ULONG itemCount = GetCountRecs(ixTbl);
+ if (itemCount)
+ {
+#ifdef FEATURE_PREJIT
+ ULONG hotItemCount = 0;
+
+ NewArrayHolder<mdToken> hotItemList = NULL;
+ NewArrayHolder<TokenIndexPair> indexMapping = NULL;
+
+ // check if we were asked to generate the hot tables
+ if (pProfileData != NULL)
+ {
+ // obtain the number of tokens in this table whose metadata was touched
+ IfFailGo(GetHotMetadataTokensSearchAware(pProfileData, ixTbl, &hotItemCount, NULL, 0));
+
+ // assume ManifestResource table is touched completely if touched at all or any hot metadata at all so far
+ // this is because it's searched linearly, and IBC data misses an unsuccessful search
+ // after module load
+ if (ixTbl == TBL_ManifestResource && (hotItemCount > 0 || cbTotal != 0))
+ hotItemCount = itemCount;
+
+ // if the hot subset of the rows along with their side lookup tables will occupy more space
+ // than the full table, keep the full table to save both space and access time.
+ if (hotItemCount <= USHRT_MAX && itemCount <= USHRT_MAX && m_TableDefs[ixTbl].m_cbRec <= SHRT_MAX)
+ {
+ ULONG estimatedSizeUsingSubsetCopy = hotItemCount * (sizeof(WORD) + sizeof(BYTE) + m_TableDefs[ixTbl].m_cbRec);
+ ULONG estimatedSizeUsingFullCopy = itemCount * m_TableDefs[ixTbl].m_cbRec;
+
+ if (estimatedSizeUsingSubsetCopy > estimatedSizeUsingFullCopy)
+ hotItemCount = itemCount;
+ }
+
+ // first level table is array of WORD, so we can't handle more than 2**16 hot items
+ if (hotItemCount > USHRT_MAX)
+ hotItemCount = 0;
+
+ // only generate additional table if any hot items at all
+ if (hotItemCount > 0)
+ {
+ if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0)
+ IfFailGo(pIStream->Write(&hr, cbAlign, 0));
+ cbTotal += cbAlign;
+
+ headerOffset[ixTbl] = cbTotal;
+
+ // write first part of header: hot item count
+ IfFailGo(pIStream->Write(&hotItemCount, sizeof(hotItemCount), 0));
+ cbTotal += sizeof(hotItemCount);
+
+ ULONG offset = 0;
+ if (hotItemCount < itemCount)
+ {
+ // obtain the tokens whose metadata was touched
+ hotItemList = new (nothrow) mdToken[hotItemCount];
+ IfNullGo(hotItemList);
+ IfFailGo(GetHotMetadataTokensSearchAware(pProfileData, ixTbl, NULL, hotItemList, hotItemCount));
+
+ // construct an array of token-index pairs and save the original order of the tokens in pProfileData->GetHotTokens
+ // we want to write hot rows in this order to preserve the ordering optimizations done by IbcMerge
+ indexMapping = new (nothrow) TokenIndexPair[hotItemCount];
+ IfNullGo(indexMapping);
+
+ for (DWORD i = 0; i < hotItemCount; i++)
+ {
+ indexMapping[i].token = hotItemList[i];
+ indexMapping[i].index = (WORD)i;
+ }
+
+ // figure out how big the first level table should be
+ // and sort tokens accordingly
+ shiftCount = ShiftCount(itemCount, hotItemCount);
+ qsort(indexMapping, hotItemCount, sizeof(indexMapping[0]), TokenCmp);
+
+ // each table has a header that consists of the hotItemCount, offsets to
+ // the first and second level tables, an offset to the actual data, and the
+ // shiftCount that determines the size of the first level table.
+ // see class HotTableHeader in metamodelro.h
+
+ // we have already written the hotItemCount above.
+
+ // so now write the offset of the first level table (just after the header)
+ offset = sizeof(hotItemCount) + 4*sizeof(offset) + sizeof(shiftCount);
+ IfFailGo(pIStream->Write(&offset, sizeof(offset), 0));
+ cbTotal += sizeof(offset);
+
+ // figure out first level table size (1 extra entry at the end)
+ ULONG firstLevelCount = (1<<shiftCount)+1;
+ offset += firstLevelCount*sizeof(WORD);
+
+ // write offset of second level table.
+ IfFailGo(pIStream->Write(&offset, sizeof(offset), 0));
+ cbTotal += sizeof(offset);
+
+ // second level table has a byte-sized entry for each hot item
+ offset += hotItemCount*sizeof(BYTE);
+
+ // write offset of index mapping table.
+ IfFailGo(pIStream->Write(&offset, sizeof(offset), 0));
+ cbTotal += sizeof(offset);
+
+ // index mapping table has a word-sized entry for each hot item
+ offset += hotItemCount*sizeof(WORD);
+
+ // actual data is just behind it, but 4-byte aligned
+ offset = Align4(offset);
+
+ // write offset of actual hot metadata
+ IfFailGo(pIStream->Write(&offset, sizeof(offset), 0));
+ cbTotal += sizeof(offset);
+
+ // write shiftCount
+ IfFailGo(pIStream->Write(&shiftCount, sizeof(shiftCount), 0));
+ cbTotal += sizeof(shiftCount);
+
+ // allocate tables
+ NewArrayHolder<WORD> firstLevelTable = new (nothrow) WORD[firstLevelCount];
+ IfNullGo(firstLevelTable);
+ NewArrayHolder<BYTE> secondLevelTable = new (nothrow) BYTE[hotItemCount];
+ IfNullGo(secondLevelTable);
+ NewArrayHolder<WORD> indexMappingTable = new (nothrow) WORD[hotItemCount];
+ IfNullGo(indexMappingTable);
+
+ // fill out the tables
+ ULONG nextFirstLevelIndex = 0;
+ for (DWORD i = 0; i < hotItemCount; i++)
+ {
+ // second level table contains the high order bits for each hot rid
+ secondLevelTable[i] = (BYTE)(RidFromToken(indexMapping[i].token) >> shiftCount);
+
+ // the index into the first level table is the low order bits.
+ ULONG firstLevelIndex = indexMapping[i].token & ((1<<shiftCount)-1);
+
+ // first level indicates where to start searching in the second level table
+ while (nextFirstLevelIndex <= firstLevelIndex)
+ firstLevelTable[nextFirstLevelIndex++] = (WORD)i;
+
+ // index mapping table converts the index of this hot rid in the second level table
+ // to the index of the hot data in the cached rows
+ indexMappingTable[i] = indexMapping[i].index;
+ }
+ // fill remaining entries
+ while (nextFirstLevelIndex < firstLevelCount)
+ firstLevelTable[nextFirstLevelIndex++] = (WORD)hotItemCount;
+
+ // write first level table
+ IfFailGo(pIStream->Write(firstLevelTable, sizeof(firstLevelTable[0])*firstLevelCount, 0));
+ cbTotal += sizeof(firstLevelTable[0])*firstLevelCount;
+
+ // write second level table
+ IfFailGo(pIStream->Write(secondLevelTable, sizeof(secondLevelTable[0])*hotItemCount, 0));
+ cbTotal += sizeof(secondLevelTable[0])*hotItemCount;
+
+ // write index mapping table
+ IfFailGo(pIStream->Write(indexMappingTable, sizeof(indexMappingTable[0])*hotItemCount, 0));
+ cbTotal += sizeof(indexMappingTable[0])*hotItemCount;
+
+ // NewArrayHolder for firstLevelTable and secondLevelTable going out of scope - no delete[] necessary
+ }
+ else
+ {
+ // in case the whole table is touched, omit the tables
+ // we still have a full header though with zero offsets for these tables.
+ IfFailGo(pIStream->Write(&offset, sizeof(offset), 0));
+ cbTotal += sizeof(offset);
+ IfFailGo(pIStream->Write(&offset, sizeof(offset), 0));
+ cbTotal += sizeof(offset);
+ IfFailGo(pIStream->Write(&offset, sizeof(offset), 0));
+ cbTotal += sizeof(offset);
+
+ // offset for actual data points immediately after the header
+ offset += sizeof(hotItemCount) + 4*sizeof(offset) + sizeof(shiftCount);
+ offset = Align4(offset);
+ IfFailGo(pIStream->Write(&offset, sizeof(offset), 0));
+ cbTotal += sizeof(offset);
+ shiftCount = 0;
+
+ // write shift count
+ IfFailGo(pIStream->Write(&shiftCount, sizeof(shiftCount), 0));
+ cbTotal += sizeof(shiftCount);
+ }
+ if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0)
+ IfFailGo(pIStream->Write(&hr, cbAlign, 0));
+ cbTotal += cbAlign;
+ _ASSERTE(cbTotal == headerOffset[ixTbl] + offset);
+ }
+ }
+#endif //FEATURE_PREJIT
+
+ // Compress the records by allocating a new, temporary, table and
+ // copying the rows from the one to the new.
+
+ // If the table was grown, shrink it as much as possible.
+ if (m_eGrow == eg_grown)
+ {
+
+ // Allocate a def for the temporary table.
+ sTempTable = m_TableDefs[ixTbl];
+ IfFailGo(rTempCols.ReSizeNoThrow(sTempTable.m_cCols));
+ sTempTable.m_pColDefs = rTempCols.Ptr();
+
+ // Initialize temp table col defs based on actual counts of data in the
+ // real tables.
+ IfFailGo(InitColsForTable(Schema, ixTbl, &sTempTable, 1, FALSE));
+
+ // Create the temporary table.
+ MetaData::TableRW tempTable;
+ IfFailGo(tempTable.InitializeEmpty_WithRecordCount(
+ sTempTable.m_cbRec,
+ m_Schema.m_cRecs[ixTbl]
+ COMMA_INDEBUG_MD(TRUE)));
+ INDEBUG_MD(tempTable.Debug_SetTableInfo(NULL, ixTbl));
+
+ // For each row in the data.
+ RID rid;
+ for (rid=1; rid<=m_Schema.m_cRecs[ixTbl]; ++rid)
+ {
+ RID ridNew;
+ BYTE *pRow;
+ IfFailGo(m_Tables[ixTbl].GetRecord(rid, &pRow));
+ BYTE *pNew;
+ IfFailGo(tempTable.AddRecord(&pNew, (UINT32 *)&ridNew));
+ _ASSERTE(rid == ridNew);
+
+ // For each column.
+ for (ULONG ixCol=0; ixCol<sTempTable.m_cCols; ++ixCol)
+ {
+ // Copy the data to the temp table.
+ ULONG ulVal = GetCol(ixTbl, ixCol, pRow);
+ IfFailGo(PutCol(rTempCols[ixCol], pNew, ulVal));
+ }
+ } // Persist the temp table to the stream.
+#ifdef FEATURE_PREJIT
+ if (pProfileData != NULL)
+ {
+ // only write out the hot rows as indicated by profile data
+ for (DWORD i = 0; i < hotItemCount; i++)
+ {
+ BYTE *pRow;
+ IfFailGo(tempTable.GetRecord(
+ hotItemList != NULL ? RidFromToken(hotItemList[i]) : i + 1,
+ &pRow));
+ IfFailGo(pIStream->Write(pRow, sTempTable.m_cbRec, 0));
+ }
+ cbTable = sTempTable.m_cbRec*hotItemCount;
+ }
+ else
+#endif //FEATURE_PREJIT
+ {
+ IfFailGo(tempTable.GetRecordsDataSize(&cbTable));
+ _ASSERTE(cbTable == sTempTable.m_cbRec * GetCountRecs(ixTbl));
+ IfFailGo(tempTable.SaveToStream(
+ pIStream));
+ }
+ cbTotal += cbTable;
+ }
+ else
+ { // Didn't grow, so just persist directly to stream.
+#ifdef FEATURE_PREJIT
+ if (pProfileData != NULL)
+ {
+ // only write out the hot rows as indicated by profile data
+ for (DWORD i = 0; i < hotItemCount; i++)
+ {
+ BYTE *pRow;
+ IfFailGo(m_Tables[ixTbl].GetRecord(
+ hotItemList != NULL ? RidFromToken(hotItemList[i]) : i + 1,
+ &pRow));
+ IfFailGo(pIStream->Write(pRow, m_TableDefs[ixTbl].m_cbRec, 0));
+ }
+ cbTable = m_TableDefs[ixTbl].m_cbRec*hotItemCount;
+ }
+ else
+#endif //FEATURE_PREJIT
+ {
+ IfFailGo(m_Tables[ixTbl].GetRecordsDataSize(&cbTable));
+ _ASSERTE(cbTable == m_TableDefs[ixTbl].m_cbRec * GetCountRecs(ixTbl));
+ IfFailGo(m_Tables[ixTbl].SaveToStream(
+ pIStream));
+ }
+ cbTotal += cbTable;
+ }
+ // NewArrayHolder hotItemList going out of scope - no delete [] necessary
+ }
+ }
+
+ // Pad with at least 2 bytes and align on 4 bytes.
+ cbAlign = Align4(cbTotal) - cbTotal;
+ if (cbAlign < 2)
+ cbAlign += 4;
+ IfFailGo(pIStream->Write(zeros, cbAlign, 0));
+ cbTotal += cbAlign;
+ _ASSERTE((m_cbSaveSize == 0) || (m_cbSaveSize == cbTotal) || (pProfileData != NULL));
+
+#ifdef FEATURE_PREJIT
+ if (pProfileData != NULL)
+ {
+ // #WritingHotMetaData write hot table directory (HotTableDirectory in MetaModelRO.h)
+
+ // first write magic
+ ULONG magic = 0x484f4e44;
+ IfFailGo(pIStream->Write(&magic, sizeof(magic), 0));
+
+ // compute offsets to table headers
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ if (headerOffset[ixTbl] != ~0u)
+ {
+ headerOffset[ixTbl] -= cbTotal;
+ }
+ else
+ {
+ headerOffset[ixTbl] = 0;
+ }
+
+ // write the offsets to the table headers
+ IfFailGo(pIStream->Write(headerOffset, sizeof(headerOffset), 0));
+ cbTotal += sizeof(magic) + sizeof(headerOffset);
+
+ UINT32 cbPoolDirSize = 0;
+ UINT32 cbSavedHeapsSize = 0;
+
+ IfFailGo(SaveHotPoolsToStream(
+ pIStream,
+ reorderingOptions,
+ pProfileData,
+ &cbPoolDirSize,
+ &cbSavedHeapsSize));
+
+ // write hot metadata (including pools) header
+ IfFailGo(StreamUtil::WriteToStream(pIStream, (DWORD)(cbSavedHeapsSize + cbPoolDirSize)));
+ IfFailGo(StreamUtil::WriteToStream(pIStream, (DWORD)cbPoolDirSize));
+ }
+#endif //FEATURE_PREJIT
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::SaveFullTablesToStream
+
+//*****************************************************************************
+// Check to see if it is safe to reorder the string pool
+// The existing implementation of metadata tables is such that string offsets in different tables
+// may have different sizes.
+// Since we are going to reorder the string pool, offsets of strings would change and that may
+// cause overflows if tables have string offsets with different sizes
+//*****************************************************************************
+BOOL CMiniMdRW::IsSafeToReorderStringPool()
+{
+#ifdef FEATURE_PREJIT
+ BYTE lastColumnSize=0;
+ ULONG ixTbl=0, ixCol=0;
+ for (ixTbl=0; ixTbl<m_TblCount; ixTbl++)
+ {
+ // for every column in this row
+ for (ixCol=0; ixCol<m_TableDefs[ixTbl].m_cCols; ixCol++)
+ {
+ // proceed only when the column type is iSTRING
+ if(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING)
+ {
+ if(lastColumnSize == 0)
+ {
+ lastColumnSize = m_TableDefs[ixTbl].m_pColDefs[ixCol].m_cbColumn;
+ }
+ else if(lastColumnSize != m_TableDefs[ixTbl].m_pColDefs[ixCol].m_cbColumn)
+ {
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+#else
+ return FALSE;
+#endif // FEATURE_PREJIT
+} // CMiniMdRW::IsSafeToReorderStringPool
+
+//*****************************************************************************
+// Function to mark hot strings in the marks array based on the token information
+// in profile data
+//*****************************************************************************
+VOID CMiniMdRW::MarkHotStrings(CorProfileData *pProfileData, BYTE * pMarks, ULONG poolSize)
+{
+#ifdef FEATURE_PREJIT
+ if(pProfileData != NULL)
+ {
+ ULONG hotItemCount = pProfileData->GetHotTokens( TBL_COUNT + MDPoolStrings, 1 << ProfilingFlags_MetaData, 1 << ProfilingFlags_MetaData, NULL, 0 );
+ if(hotItemCount > 0)
+ {
+ NewArrayHolder< ULONG > hotItemList = new ULONG[hotItemCount];
+
+ // get hot tokens
+ pProfileData->GetHotTokens( TBL_COUNT + MDPoolStrings, 1 << ProfilingFlags_MetaData, 1 << ProfilingFlags_MetaData, reinterpret_cast<mdToken *>(&hotItemList[0]), hotItemCount );
+
+ for ( ULONG i=0; i<hotItemCount; ++i )
+ {
+ // convert tokens to rids
+ ULONG ulStringOffset = RidFromToken(hotItemList[i]);
+
+ if (ulStringOffset >= poolSize)
+ ThrowHR(E_UNEXPECTED);
+
+ pMarks[ulStringOffset] = ReorderData::ProfileData;
+ }
+ }
+ }
+#endif // FEATURE_PREJIT
+} // CMiniMdRW::MarkHotStrings
+
+//*******************************************************************************
+// Function to mark hot strings referenced by hot tables based on token information in profile data
+//*******************************************************************************
+VOID CMiniMdRW::MarkStringsInHotTables(CorProfileData *pProfileData, BYTE * pMarks, ULONG poolSize)
+{
+#ifdef FEATURE_PREJIT
+ ULONG ixTbl=0, ixCol=0;
+ ULONG hotItemCount=0;
+ RID hotRID=0;
+ BYTE *pHotRow=NULL;
+
+ if(pProfileData != NULL)
+ {
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ {
+ NewArrayHolder<mdToken> hotItemList = NULL;
+ // obtain the number of tokens in this table whose metadata was touched
+ hotItemCount = pProfileData->GetHotTokens(ixTbl, 1<<ProfilingFlags_MetaData, 1<<ProfilingFlags_MetaData, NULL, 0);
+
+ // obtain the tokens whose metadata was touched
+ if(hotItemCount > 0)
+ {
+ hotItemList = new mdToken[hotItemCount];
+ pProfileData->GetHotTokens(ixTbl, 1<<ProfilingFlags_MetaData, 1<<ProfilingFlags_MetaData, hotItemList, hotItemCount);
+ }
+
+ // for every column in this hot row
+ for (ixCol=0; ixCol<m_TableDefs[ixTbl].m_cCols; ++ixCol)
+ {
+ // add the string to the string pool only if it hasn't been added yet
+ if(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING)
+ {
+ // for every hot token in the list
+ for(ULONG item=0; item<hotItemCount; item++)
+ {
+ // get the rid from the token
+ hotRID = RidFromToken(hotItemList[item]);
+ IfFailThrow(m_Tables[ixTbl].GetRecord(hotRID, &pHotRow));
+ _ASSERTE(pHotRow != NULL);
+
+ // get column for string; this will get me the current string offset
+ ULONG ulStringOffset = GetCol(ixTbl, ixCol, pHotRow);
+
+ if (ulStringOffset >= poolSize)
+ ThrowHR(E_UNEXPECTED);
+
+ pMarks[ulStringOffset] = ReorderData::ProfileData;
+ }
+ }
+ }
+ }
+ }
+#endif // FEATURE_PREJIT
+} // CMiniMdRW::MarkStringsInHotTables
+
+//*****************************************************************************
+// Function to mark strings referenced by the different metadata tables
+//*****************************************************************************
+VOID CMiniMdRW::MarkStringsInTables(BYTE * pMarks, ULONG poolSize)
+{
+#ifdef FEATURE_PREJIT
+ for (ULONG ixTbl=0; ixTbl<m_TblCount; ixTbl++)
+ {
+ // for every row in the table
+ for (RID ridOld=1; ridOld<=m_Schema.m_cRecs[ixTbl]; ridOld++)
+ {
+ // lets assume we do not have any references to the stringpool
+ BOOL fHasStringData = FALSE;
+
+ // for every column in this row
+ for (ULONG ixCol=0; ixCol<m_TableDefs[ixTbl].m_cCols; ixCol++)
+ {
+ // proceed only when the column type is iSTRING
+ if(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING)
+ {
+ fHasStringData = TRUE;
+ // get the current record
+ BYTE *pOldRow;
+ IfFailThrow(m_Tables[ixTbl].GetRecord(ridOld, &pOldRow));
+
+ // get column for string; this will get me the current string offset
+ ULONG ulStringOffset = GetCol(ixTbl, ixCol, pOldRow);
+
+ // ignore empty strings, they are not moving anywhere
+ if(ulStringOffset == 0)
+ continue;
+
+ if (ulStringOffset >= poolSize)
+ ThrowHR(E_UNEXPECTED);
+
+ BYTE ulBucketType=0;
+
+ switch(ixTbl)
+ {
+ case TBL_Method:
+ ulBucketType = IsMdPublic(GetCol(TBL_Method, MethodRec::COL_Flags, pOldRow))
+ ? ReorderData::PublicData
+ : ReorderData::NonPublicData;
+ break;
+ case TBL_Field:
+ ulBucketType = IsFdPublic(GetCol(TBL_Field, FieldRec::COL_Flags, pOldRow))
+ ? ReorderData::PublicData
+ : ReorderData::NonPublicData;
+ break;
+ case TBL_TypeDef:
+ ulBucketType = IsTdPublic(GetCol(TBL_TypeDef, TypeDefRec::COL_Flags, pOldRow))
+ ? ReorderData::PublicData
+ : ReorderData::NonPublicData;
+ break;
+ case TBL_ManifestResource:
+ ulBucketType = IsMrPublic(GetCol(TBL_ManifestResource, ManifestResourceRec::COL_Flags, pOldRow))
+ ? ReorderData::PublicData
+ : ReorderData::NonPublicData;
+ break;
+ default:
+ ulBucketType = ReorderData::OtherData;
+ break;
+ }
+
+ if (pMarks[ulStringOffset] == ReorderData::Undefined || pMarks[ulStringOffset] > ulBucketType)
+ pMarks[ulStringOffset] = ulBucketType;
+ }
+ }
+ if (!fHasStringData)
+ break;
+ }
+ }
+#endif // FEATURE_PREJIT
+} // CMiniMdRW::MarkStringsInTables
+
+// --------------------------------------------------------------------------------------
+//
+// Function to mark duplicate strings in the mark array. This step is basically to take care of
+// strings that have the same tail.
+// Throws on error.
+//
+VOID CMiniMdRW::MarkDuplicateStrings(BYTE * pMarks, ULONG poolSize)
+{
+#ifdef FEATURE_PREJIT
+ ULONG offset=1;
+ while (offset<poolSize)
+ {
+ if (pMarks[offset] == ReorderData::Undefined)
+ {
+ offset++;
+ continue;
+ }
+
+ LPCSTR pszString;
+ IfFailThrow(m_StringHeap.GetString(offset, &pszString));
+
+ ULONG start = offset;
+ ULONG end = offset + (ULONG)strlen(pszString);
+
+ BYTE tag = pMarks[offset];
+ offset++;
+
+ while (offset <= end)
+ {
+ if (pMarks[offset] != ReorderData::Undefined)
+ {
+ tag = min(pMarks[offset], tag);
+ pMarks[offset] = ReorderData::Duplicate;
+ }
+ offset++;
+ }
+ pMarks[start] = tag;
+ }
+#endif // FEATURE_PREJIT
+} // CMiniMdRW::MarkDuplicateStrings
+
+//*****************************************************************************
+// Function to update the tables with the modified string offsets
+//*****************************************************************************
+VOID CMiniMdRW::FixStringsInTables()
+{
+#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE)
+ for (ULONG ixTbl=0; ixTbl<m_TblCount; ixTbl++)
+ {
+ // for every row in the table
+ for (RID ridOld=1; ridOld<=m_Schema.m_cRecs[ixTbl]; ridOld++)
+ {
+ // lets assume we do not have any references to the stringpool
+ BOOL fHasStringData = FALSE;
+
+ // for every column in this row
+ for (ULONG ixCol=0; ixCol<m_TableDefs[ixTbl].m_cCols; ixCol++)
+ {
+ // proceed only when the column type is iSTRING
+ if(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING)
+ {
+ fHasStringData = TRUE;
+ // get the current record
+ BYTE *pOldRow;
+ IfFailThrow(m_Tables[ixTbl].GetRecord(ridOld, &pOldRow));
+ _ASSERTE(pOldRow != NULL);
+
+ // get column for string; this will get me the current string offset
+ UINT32 nOldStringOffset = GetCol(ixTbl, ixCol, pOldRow);
+
+ // ignore empty strings, they are not moving anywhere
+ if (nOldStringOffset == 0)
+ continue;
+
+ UINT32 nNewStringOffset;
+ if (!m_StringPoolOffsetHash.Lookup(nOldStringOffset, &nNewStringOffset))
+ ThrowHR(E_UNEXPECTED);
+
+ IfFailThrow(PutCol(ixTbl, ixCol, pOldRow, nNewStringOffset));
+ }
+ }
+ if (!fHasStringData)
+ break;
+ }
+ }
+#endif // FEATURE_PREJIT
+} // CMiniMdRW::FixStringsInTables
+
+// --------------------------------------------------------------------------------------
+//
+// Function to fill the given string pool with strings from the existing string pool using the mark array.
+// Throws on error.
+//
+VOID
+CMiniMdRW::CreateReorderedStringPool(
+ MetaData::StringHeapRW *pStringHeap,
+ BYTE *pMarks,
+ ULONG cbHeapSize,
+ CorProfileData *pProfileData)
+{
+#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE)
+ ULONG lastOldOffset = 0;
+ ULONG lastNewOffset = 0;
+
+ // special handling of profile data so as to maintain the same order
+ // as the hot tokens in the CorProfileData object
+ if (pProfileData != NULL)
+ {
+ ULONG hotItems = pProfileData->GetHotTokens(
+ TBL_COUNT + MDPoolStrings,
+ 1 << ProfilingFlags_MetaData,
+ 1 << ProfilingFlags_MetaData,
+ NULL,
+ 0);
+ if ( hotItems )
+ {
+ NewArrayHolder< ULONG > hotItemArr = new ULONG[ hotItems ];
+ pProfileData->GetHotTokens(
+ TBL_COUNT + MDPoolStrings,
+ 1 << ProfilingFlags_MetaData,
+ 1 << ProfilingFlags_MetaData,
+ reinterpret_cast<mdToken *>(&hotItemArr[0]),
+ hotItems);
+
+ // convert tokens to rids
+ for ( ULONG i = 0; i < hotItems ; ++i )
+ {
+ UINT32 newOffset=0, start=0, end=0;
+ hotItemArr[i] = RidFromToken(hotItemArr[i]);
+
+ for (UINT32 offset = hotItemArr[i]; offset >= 1; offset--)
+ {
+ if(pMarks[offset] == ReorderData::ProfileData)
+ {
+ LPCSTR szString;
+ IfFailThrow(m_StringHeap.GetString(offset, &szString));
+ IfFailThrow(pStringHeap->AddString(szString, &newOffset));
+ start = offset;
+ end = start + (UINT32)strlen(szString);
+ break;
+ }
+ }
+
+ for (UINT32 offset = start; offset <end; offset++)
+ {
+ if(pMarks[offset] == ReorderData::ProfileData || pMarks[offset] == ReorderData::Duplicate)
+ {
+ m_StringPoolOffsetHash.Add(offset, newOffset);
+ }
+ newOffset++;
+ }
+ }
+ }
+ }
+
+ for (BYTE priority = ReorderData::ProfileData; priority <= ReorderData::NonPublicData; priority++)
+ {
+ for (UINT32 offset = 1; offset < cbHeapSize; offset++)
+ {
+ // Since MinReorderBucketType is 0 and MaxReorderBucketType is 255, checking an unsigned BYTE against that gives a "comparison
+ // is always true" warning. Logically, the assert is:
+ // _ASSERTE(pMarks[offset] >= ReorderData::MinReorderBucketType && pMarks[offset] <= ReorderData::MaxReorderBucketType);
+ _ASSERTE(0 == ReorderData::MinReorderBucketType);
+ _ASSERTE(255 == ReorderData::MaxReorderBucketType);
+ _ASSERTE(sizeof(pMarks[0]) == 1);
+
+ if (pMarks[offset] == priority)
+ {
+ UINT32 newOffset;
+
+ if(!m_StringPoolOffsetHash.Lookup(offset, &newOffset))
+ {
+ LPCSTR szString;
+ IfFailThrow(m_StringHeap.GetString(offset, &szString));
+ IfFailThrow(pStringHeap->AddString(szString, &newOffset));
+ m_StringPoolOffsetHash.Add(offset, newOffset);
+
+ lastOldOffset = offset;
+ lastNewOffset = newOffset;
+ }
+ }
+ else
+ if (pMarks[offset] == ReorderData::Duplicate)
+ {
+ UINT32 newOffset;
+ if (lastNewOffset != 0 && !m_StringPoolOffsetHash.Lookup(offset, &newOffset))
+ m_StringPoolOffsetHash.Add(offset, lastNewOffset + (offset - lastOldOffset));
+ }
+ else
+ if (pMarks[offset] != ReorderData::Undefined)
+ {
+ lastNewOffset = 0;
+ }
+ }
+ }
+#endif // FEATURE_PREJIT
+} // CMiniMdRW::CreateReorderedStringPool
+
+// --------------------------------------------------------------------------------------
+//
+// Function to reorganize the string pool based on IBC profile data (if available) and static analysis
+// Throws on error.
+//
+VOID CMiniMdRW::OrganizeStringPool(CorProfileData *pProfileData)
+{
+#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE)
+ if(!IsSafeToReorderStringPool())
+ {
+ return;
+ }
+
+ UINT32 cbStringHeapSize = m_StringHeap.GetUnalignedSize();
+
+ NewArrayHolder<BYTE> stringMarks = new BYTE[cbStringHeapSize];
+ ZeroMemory(stringMarks, cbStringHeapSize);
+
+ // Each string will be assigned a value based on its hotness in the Mark*() functions
+ // This list will be later traversed to place the strings in the right order in the string pool and also
+ // to update the references in the metadata tables
+
+ // Mark all hot strings
+ MarkHotStrings(pProfileData, stringMarks, cbStringHeapSize);
+
+ // Mark all strings in hot rows
+ MarkStringsInHotTables(pProfileData, stringMarks, cbStringHeapSize);
+
+ // Mark all remaining strings
+ MarkStringsInTables(stringMarks, cbStringHeapSize);
+
+ // Mark duplicates for interned strings
+ MarkDuplicateStrings(stringMarks, cbStringHeapSize);
+
+ // Initalize the temporary string heap
+ MetaData::StringHeapRW tempStringHeap;
+
+ IfFailThrow(tempStringHeap.InitializeEmpty(
+ cbStringHeapSize
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+
+ // We will use this hash for fixing the string references in the profile data
+ m_StringPoolOffsetHash.Reallocate(cbStringHeapSize);
+
+ // Create the temporary string pool using the mark arrays
+ CreateReorderedStringPool(&tempStringHeap, stringMarks, cbStringHeapSize, pProfileData);
+
+ // Update the tables with string offsets into the temporary string pool
+ FixStringsInTables();
+
+ // Replace the existing string pool with the modified version
+ m_StringHeap.Delete();
+ IfFailThrow(m_StringHeap.InitializeFromStringHeap(
+ &tempStringHeap,
+ TRUE)); // fCopyData
+#endif // FEATURE_PREJIT
+} // CMiniMdRW::OrganizeStringPool
+
+#ifdef FEATURE_PREJIT
+
+// write hot data of the pools
+//
+__checkReturn
+HRESULT
+CMiniMdRW::SaveHotPoolsToStream(
+ IStream *pStream,
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData,
+ UINT32 *pnPoolDirSize,
+ UINT32 *pnHeapsSavedSize)
+{
+// @todo: Triton workaround: FEATURE_METADATA_STANDALNE_WINRT_RO is supposed to disable FEATURE_PREJIT - remove this #if once we figure out how to get that working in the CoreClr build.
+#ifdef FEATURE_METADATA_STANDALONE_WINRT_RO
+ _ASSERTE(!"SaveHotPoolsToStream: This method not supported in RoMetadata.dll");
+ return E_NOTIMPL;
+#else // FEATURE_METADATA_STANDALONE_WINRT_RO
+ HRESULT hr = S_OK;
+ UINT32 rgHeapSavedSize[MDPoolCount] = { 0, 0, 0, 0 };
+
+ // save pools in the order they are described in MDPools enum
+ //
+ // we skip the hot string pool when we reorganize the string pool
+ if (!(reorderingOptions & ReArrangeStringPool))
+ {
+ MetaData::HotHeapWriter stringHotHeapWriter(&m_StringHeap);
+ IfFailRet(SaveHotPoolToStream(
+ pStream,
+ pProfileData,
+ &stringHotHeapWriter,
+ &rgHeapSavedSize[MDPoolStrings]));
+ }
+
+ // Save guid heap hot data
+ MetaData::HotHeapWriter guidsHotHeapWriter(&m_GuidHeap);
+ IfFailRet(SaveHotPoolToStream(
+ pStream,
+ pProfileData,
+ &guidsHotHeapWriter,
+ &rgHeapSavedSize[MDPoolGuids]));
+
+ // Save blob heap hot data
+ MetaData::HotHeapWriter blobsHotHeapWriter(
+ &m_BlobHeap,
+ FALSE); // fUserStringHeap
+ IfFailRet(SaveHotPoolToStream(
+ pStream,
+ pProfileData,
+ &blobsHotHeapWriter,
+ &rgHeapSavedSize[MDPoolBlobs]));
+
+ // Save user string heap hot data
+ MetaData::HotHeapWriter userStringsHotHeapWriter(
+ &m_UserStringHeap,
+ TRUE); // fUserStringHeap
+ IfFailRet(SaveHotPoolToStream(
+ pStream,
+ pProfileData,
+ &userStringsHotHeapWriter,
+ &rgHeapSavedSize[MDPoolUSBlobs]));
+
+ // fix pool offsets, they need to point to the header of each saved pool
+ UINT32 nHeapEndOffset = 0;
+ for (int i = MDPoolCount; i-- > 0; )
+ {
+ if (rgHeapSavedSize[i] != 0)
+ {
+ UINT32 nHeapSavedSize = rgHeapSavedSize[i];
+ // Change size of the heap to the (negative) offset of its header
+ rgHeapSavedSize[i] = sizeof(struct MetaData::HotHeapHeader) + nHeapEndOffset;
+ nHeapEndOffset += nHeapSavedSize;
+ }
+ }
+ // Store size of all heaps
+ *pnHeapsSavedSize = nHeapEndOffset;
+
+ // save hot pool dirs
+ *pnPoolDirSize = 0;
+ for (int i = 0; i < MDPoolCount; i++)
+ {
+ if (rgHeapSavedSize[i] != 0)
+ {
+ IfFailRet(StreamUtil::WriteToStream(pStream, i, pnPoolDirSize));
+ IfFailRet(StreamUtil::WriteToStream(pStream, (ULONG)rgHeapSavedSize[i], pnPoolDirSize));
+ }
+ }
+
+ return S_OK;
+#endif //FEATURE_METADATA_STANDALONE_WINRT_RO
+} // CMiniMdRW::SaveHotPoolsToStream
+
+// write hot data of specific blob
+//
+__checkReturn
+HRESULT
+CMiniMdRW::SaveHotPoolToStream(
+ IStream *pStream,
+ CorProfileData *pProfileData,
+ MetaData::HotHeapWriter *pHotHeapWriter,
+ UINT32 *pnSavedSize)
+{
+// @todo: Triton workaround: FEATURE_METADATA_STANDALNE_WINRT_RO is supposed to disable FEATURE_PREJIT - remove this #if once we figure out how to get that working in the CoreClr build.
+#ifdef FEATURE_METADATA_STANDALONE_WINRT_RO
+ _ASSERTE(!"SaveHotPoolToStream: This method not supported in RoMetadata.dll");
+ return E_NOTIMPL;
+#else //FEATURE_METADATA_STANDALONE_WINRT_RO
+
+ _ASSERTE(pProfileData != NULL);
+
+ HRESULT hr = S_OK;
+ // #CallToGetHotTokens
+ // see code:CMiniMdRW.SaveFullTablesToStream#WritingHotMetaData for the main caller of this.
+ if (pProfileData->GetHotTokens(
+ pHotHeapWriter->GetTableIndex(),
+ 1 << ProfilingFlags_MetaData,
+ 1 << ProfilingFlags_MetaData,
+ NULL,
+ 0) != 0)
+ {
+ IfFailRet(pHotHeapWriter->SaveToStream(
+ pStream,
+ pProfileData,
+ pnSavedSize));
+ }
+ else
+ {
+ *pnSavedSize = 0;
+ }
+
+ return S_OK;
+#endif // FEATURE_METADATA_STANDALONE_WINRT_RO
+} // CMiniMdRW::SaveHotPoolToStream
+
+#endif //FEATURE_PREJIT
+
+//*****************************************************************************
+// Save the tables to the stream.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::SaveENCTablesToStream(
+ IStream *pIStream)
+{
+ HRESULT hr;
+ BYTE SchemaBuf[sizeof(CMiniMdSchema)]; //Buffer for compressed schema.
+ ULONG cbAlign; // Bytes needed for alignment.
+ ULONG cbTable; // Bytes in a table.
+ ULONG cbTotal; // Bytes written.
+ ULONG ixTbl; // Table counter.
+ static const unsigned char zeros[8] = {0}; // For padding and alignment.
+
+ // Make sure the minimal delta has a fully expanded table
+ IfFailRet(ExpandTables());
+
+ // Write the header.
+ CMiniMdSchema Schema = m_Schema;
+ Schema.m_heaps |= CMiniMdSchema::DELTA_ONLY;
+
+ if (m_rENCRecs != NULL)
+ {
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ Schema.m_cRecs[ixTbl] = m_rENCRecs[ixTbl].Count();
+ }
+ else
+ {
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ Schema.m_cRecs[ixTbl] = 0;
+ }
+
+ Schema.m_cRecs[TBL_Module] = m_Schema.m_cRecs[TBL_Module];
+ Schema.m_cRecs[TBL_ENCLog] = m_Schema.m_cRecs[TBL_ENCLog];
+ Schema.m_cRecs[TBL_ENCMap] = m_Schema.m_cRecs[TBL_ENCMap];
+
+ cbTotal = Schema.SaveTo(SchemaBuf);
+ IfFailGo(pIStream->Write(SchemaBuf, cbTotal, 0));
+ if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0)
+ IfFailGo(pIStream->Write(&hr, cbAlign, 0));
+ cbTotal += cbAlign;
+
+ // For each table...
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ {
+ if (ixTbl == TBL_ENCLog || ixTbl == TBL_ENCMap || ixTbl == TBL_Module)
+ {
+ if (m_Schema.m_cRecs[ixTbl] == 0)
+ continue; // pretty strange if ENC has no enc data.
+ // Persist the ENC table.
+ IfFailGo(m_Tables[ixTbl].GetRecordsDataSize((UINT32 *)&cbTable));
+ _ASSERTE(cbTable == m_TableDefs[ixTbl].m_cbRec * m_Schema.m_cRecs[ixTbl]);
+ cbTotal += cbTable;
+ IfFailGo(m_Tables[ixTbl].SaveToStream(
+ pIStream));
+ }
+ else
+ if (Schema.m_cRecs[ixTbl])
+ {
+ // Copy just the delta records.
+
+ // Create the temporary table.
+ MetaData::TableRW tempTable;
+ IfFailGo(tempTable.InitializeEmpty_WithRecordCount(
+ m_TableDefs[ixTbl].m_cbRec,
+ Schema.m_cRecs[ixTbl]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ INDEBUG_MD(tempTable.Debug_SetTableInfo(NULL, ixTbl));
+
+ // For each row in the data.
+ RID rid;
+ for (ULONG iDelta=0; iDelta<Schema.m_cRecs[ixTbl]; ++iDelta)
+ {
+ RID ridNew;
+ rid = m_rENCRecs[ixTbl][iDelta];
+ BYTE *pRow;
+ IfFailGo(m_Tables[ixTbl].GetRecord(rid, &pRow));
+ BYTE *pNew;
+ IfFailGo(tempTable.AddRecord(&pNew, (UINT32 *)&ridNew));
+ _ASSERTE(iDelta+1 == ridNew);
+
+ memcpy(pNew, pRow, m_TableDefs[ixTbl].m_cbRec);
+ }
+ // Persist the temp table to the stream.
+ IfFailGo(tempTable.GetRecordsDataSize((UINT32 *)&cbTable));
+ _ASSERTE(cbTable == m_TableDefs[ixTbl].m_cbRec * Schema.m_cRecs[ixTbl]);
+ cbTotal += cbTable;
+ IfFailGo(tempTable.SaveToStream(
+ pIStream));
+ }
+ }
+
+ // Pad with at least 2 bytes and align on 4 bytes.
+ cbAlign = Align4(cbTotal) - cbTotal;
+ if (cbAlign < 2)
+ cbAlign += 4;
+ IfFailGo(pIStream->Write(zeros, cbAlign, 0));
+ cbTotal += cbAlign;
+ _ASSERTE(m_cbSaveSize == 0 || m_cbSaveSize == cbTotal);
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::SaveENCTablesToStream
+
+//*****************************************************************************
+// Save the tables to the stream.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::SaveTablesToStream(
+ IStream *pIStream, // The stream.
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData)
+{
+ HRESULT hr;
+
+ // Prepare the data for save.
+ IfFailGo(PreSave());
+
+ switch (m_OptionValue.m_UpdateMode & MDUpdateMask)
+ {
+ case MDUpdateFull:
+ case MDUpdateIncremental:
+ case MDUpdateExtension:
+ case MDUpdateENC:
+ hr = SaveFullTablesToStream(pIStream, reorderingOptions, pProfileData);
+ break;
+ case MDUpdateDelta:
+ hr = SaveENCTablesToStream(pIStream);
+ break;
+ default:
+ _ASSERTE(!"Internal error -- unknown save mode");
+ return E_INVALIDARG;
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::SaveTablesToStream
+
+//*****************************************************************************
+// Save a full pool to the stream.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::SaveFullPoolToStream(
+ int iPool, // The pool.
+ IStream *pStream) // The stream.
+{
+ HRESULT hr;
+
+ switch (iPool)
+ {
+ case MDPoolStrings:
+ hr = m_StringHeap.SaveToStream_Aligned(
+ 0, // Start offset of the data to be stored
+ pStream);
+ break;
+ case MDPoolGuids:
+ hr = m_GuidHeap.SaveToStream(
+ pStream);
+ break;
+ case MDPoolBlobs:
+ hr = m_BlobHeap.SaveToStream_Aligned(
+ 0, // Start offset of the data to be stored
+ pStream);
+ break;
+ case MDPoolUSBlobs:
+ hr = m_UserStringHeap.SaveToStream_Aligned(
+ 0, // Start offset of the data to be stored
+ pStream);
+ break;
+ default:
+ hr = E_INVALIDARG;
+ }
+
+ return hr;
+} // CMiniMdRW::SaveFullPoolToStream
+
+//*****************************************************************************
+// Save a ENC pool to the stream.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::SaveENCPoolToStream(
+ int iPool, // The pool.
+ IStream *pIStream) // The stream.
+{
+ HRESULT hr;
+
+ switch (iPool)
+ {
+ case MDPoolStrings:
+ {
+ UINT32 nEnCDeltaStartOffset = m_StringHeap.GetEnCSessionStartHeapSize();
+ hr = m_StringHeap.SaveToStream_Aligned(
+ nEnCDeltaStartOffset, // Start offset of the data to be stored
+ pIStream);
+ break;
+ }
+ case MDPoolGuids:
+ {
+ // Save full Guid heap (we never save EnC delta)
+ hr = m_GuidHeap.SaveToStream(
+ pIStream);
+ break;
+ }
+ case MDPoolBlobs:
+ {
+ UINT32 nEnCDeltaStartOffset = m_BlobHeap.GetEnCSessionStartHeapSize();
+ hr = m_BlobHeap.SaveToStream_Aligned(
+ nEnCDeltaStartOffset, // Start offset of the data to be stored
+ pIStream);
+ break;
+ }
+ case MDPoolUSBlobs:
+ {
+ UINT32 nEnCDeltaStartOffset = m_UserStringHeap.GetEnCSessionStartHeapSize();
+ hr = m_UserStringHeap.SaveToStream_Aligned(
+ nEnCDeltaStartOffset, // Start offset of the data to be stored
+ pIStream);
+ break;
+ }
+ default:
+ hr = E_INVALIDARG;
+ }
+
+ return hr;
+} // CMiniMdRW::SaveENCPoolToStream
+
+//*****************************************************************************
+// Save a pool to the stream.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::SavePoolToStream(
+ int iPool, // The pool.
+ IStream *pIStream) // The stream.
+{
+ HRESULT hr;
+ switch (m_OptionValue.m_UpdateMode & MDUpdateMask)
+ {
+ case MDUpdateFull:
+ case MDUpdateIncremental:
+ case MDUpdateExtension:
+ case MDUpdateENC:
+ hr = SaveFullPoolToStream(iPool, pIStream);
+ break;
+ case MDUpdateDelta:
+ hr = SaveENCPoolToStream(iPool, pIStream);
+ break;
+ default:
+ _ASSERTE(!"Internal error -- unknown save mode");
+ return E_INVALIDARG;
+ }
+
+ return hr;
+} // CMiniMdRW::SavePoolToStream
+
+//*****************************************************************************
+// Expand a table from the initial (hopeful) 2-byte column sizes to the large
+// (but always adequate) 4-byte column sizes.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ExpandTables()
+{
+ HRESULT hr = S_OK;
+ CMiniMdSchema Schema; // Temp schema by which to build tables.
+ ULONG ixTbl; // Table counter.
+
+ // Allow function to be called many times.
+ if (m_eGrow == eg_grown)
+ return (S_OK);
+
+ // OutputDebugStringA("Growing tables to large size.\n");
+
+ // Make pool indices the large size.
+ Schema.m_heaps = 0;
+ Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4;
+ Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4;
+ Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4;
+
+ // Make Row counts the large size.
+ memset(Schema.m_cRecs, 0, sizeof(Schema.m_cRecs));
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ Schema.m_cRecs[ixTbl] = USHRT_MAX+1;
+
+ // Compute how many bits required to hold a rid.
+ Schema.m_rid = 16;
+
+ for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl)
+ {
+ IfFailGo(ExpandTableColumns(Schema, ixTbl));
+ }
+
+ // Things are bigger now.
+ m_Schema.m_rid = 16;
+ m_Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4;
+ m_Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4;
+ m_Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4;
+ m_iStringsMask = 0xffffffff;
+ m_iGuidsMask = 0xffffffff;
+ m_iBlobsMask = 0xffffffff;
+
+ // Remember that we've grown.
+ m_eGrow = eg_grown;
+ m_maxRid = m_maxIx = ULONG_MAX;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::ExpandTables
+
+
+__checkReturn
+HRESULT
+CMiniMdRW::InitWithLargeTables()
+{
+ CMiniMdSchema Schema; // Temp schema by which to build tables.
+ HRESULT hr = S_OK;
+
+ // Make pool indices the large size.
+ Schema.m_heaps = 0;
+ Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4;
+ Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4;
+ Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4;
+
+ // Make Row counts the large size.
+ memset(Schema.m_cRecs, 0, sizeof(Schema.m_cRecs));
+ for (int ixTbl=0; ixTbl<(int)m_TblCount; ++ixTbl)
+ Schema.m_cRecs[ixTbl] = USHRT_MAX+1;
+
+ // Compute how many bits required to hold a rid.
+ Schema.m_rid = 16;
+
+ // For each table...
+ for (int ixTbl=0; ixTbl<(int)m_TblCount; ++ixTbl)
+ {
+ IfFailRet(InitColsForTable(Schema, ixTbl, &m_TableDefs[ixTbl], 0, TRUE));
+ }
+
+
+ // Things are bigger now.
+ m_Schema.m_rid = 16;
+ m_Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4;
+ m_Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4;
+ m_Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4;
+ m_iStringsMask = 0xffffffff;
+ m_iGuidsMask = 0xffffffff;
+
+ return hr;
+}// CMiniMdRW::InitWithLargeTables
+
+//*****************************************************************************
+// Expand the sizes of a tables columns according to a new schema. When this
+// happens, all RID and Pool index columns expand from 2 to 4 bytes.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ExpandTableColumns(
+ CMiniMdSchema &Schema,
+ ULONG ixTbl)
+{
+ HRESULT hr;
+ CMiniTableDef sTempTable; // Definition for a temporary table.
+ CQuickBytes qbTempCols;
+ ULONG ixCol; // Column counter.
+ ULONG cbFixed; // Count of bytes that don't move.
+ CMiniColDef *pFromCols; // Definitions of "from" columns.
+ CMiniColDef *pToCols; // Definitions of "To" columns.
+ ULONG cMoveCols; // Count of columns to move.
+ ULONG cFixedCols; // Count of columns to move.
+
+ // Allocate a def for the temporary table.
+ sTempTable = m_TableDefs[ixTbl];
+ IfFailGo(qbTempCols.ReSizeNoThrow(sTempTable.m_cCols * sizeof(CMiniColDef) + 1));
+ // Mark the array of columns as not allocated (not ALLOCATED_MEMORY_MARKER) for SetNewColumnDefinition
+ // call bellow (code:#SetNewColumnDefinition_call)
+ *(BYTE *)(qbTempCols.Ptr()) = 0;
+ sTempTable.m_pColDefs = (CMiniColDef *)((BYTE *)(qbTempCols.Ptr()) + 1);
+
+ // Initialize temp table col defs based on counts of data in the tables.
+ IfFailGo(InitColsForTable(Schema, ixTbl, &sTempTable, 1, FALSE));
+
+ if (GetCountRecs(ixTbl) > 0)
+ {
+ // Analyze the column definitions to determine the unchanged vs changed parts.
+ cbFixed = 0;
+ for (ixCol = 0; ixCol < sTempTable.m_cCols; ++ixCol)
+ {
+ if (sTempTable.m_pColDefs[ixCol].m_oColumn != m_TableDefs[ixTbl].m_pColDefs[ixCol].m_oColumn ||
+ sTempTable.m_pColDefs[ixCol].m_cbColumn != m_TableDefs[ixTbl].m_pColDefs[ixCol].m_cbColumn)
+ break;
+ cbFixed += sTempTable.m_pColDefs[ixCol].m_cbColumn;
+ }
+ if (ixCol == sTempTable.m_cCols)
+ {
+ // no column is changing. We are done.
+ goto ErrExit;
+ }
+ cFixedCols = ixCol;
+ pFromCols = &m_TableDefs[ixTbl].m_pColDefs[ixCol];
+ pToCols = &sTempTable.m_pColDefs[ixCol];
+ cMoveCols = sTempTable.m_cCols - ixCol;
+ for (; ixCol < sTempTable.m_cCols; ++ixCol)
+ {
+ _ASSERTE(sTempTable.m_pColDefs[ixCol].m_cbColumn == 4);
+ }
+
+ // Create the temporary table.
+ MetaData::TableRW tempTable;
+ IfFailGo(tempTable.InitializeEmpty_WithRecordCount(
+ sTempTable.m_cbRec,
+ m_Schema.m_cRecs[ixTbl]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ INDEBUG_MD(tempTable.Debug_SetTableInfo(NULL, ixTbl));
+
+ // For each row in the data.
+ RID rid; // Row iterator.
+
+ for (rid = 1; rid <= m_Schema.m_cRecs[ixTbl]; ++rid)
+ {
+ RID ridNew;
+ BYTE *pFrom;
+ BYTE *pTo;
+
+ IfFailGo(m_Tables[ixTbl].GetRecord(rid, &pFrom));
+ IfFailGo(tempTable.AddRecord(&pTo, (UINT32 *)&ridNew));
+ _ASSERTE(rid == ridNew);
+
+ // Move the fixed part.
+ memcpy(pTo, pFrom, cbFixed);
+
+ // Expand the expanded parts.
+ for (ixCol = 0; ixCol < cMoveCols; ++ixCol)
+ {
+ if (m_TableDefs[ixTbl].m_pColDefs[cFixedCols + ixCol].m_cbColumn == sizeof(USHORT))
+ {
+ // The places that access expect the int16 to be in the high bytes so we need to the extra swap
+ SET_UNALIGNED_VAL32((pTo + pToCols[ixCol].m_oColumn), VAL16(*(USHORT*)(pFrom + pFromCols[ixCol].m_oColumn)));
+ }
+ else
+ {
+ // In this case we're just copying the data over
+ memcpy(pTo + pToCols[ixCol].m_oColumn, pFrom + pFromCols[ixCol].m_oColumn, sizeof(ULONG));
+ }
+ }
+ }
+
+ // Keep the expanded table.
+ m_Tables[ixTbl].Delete();
+ IfFailGo(m_Tables[ixTbl].InitializeFromTable(
+ &tempTable,
+ TRUE)); // fCopyData
+ INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl));
+ }
+ else
+ { // No data, so just reinitialize.
+ m_Tables[ixTbl].Delete();
+ IfFailGo(m_Tables[ixTbl].InitializeEmpty_WithRecordCount(
+ sTempTable.m_cbRec,
+ g_TblSizeInfo[0][ixTbl]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl));
+ }
+
+ //#SetNewColumnDefinition_call
+ // Keep the new column defs.
+ IfFailGo(SetNewColumnDefinition(&(m_TableDefs[ixTbl]), sTempTable.m_pColDefs, ixTbl));
+ m_TableDefs[ixTbl].m_cbRec = sTempTable.m_cbRec;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::ExpandTableColumns
+
+
+//*****************************************************************************
+// Used by caller to let us know save is completed.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::SaveDone()
+{
+ return PostSave();
+} // CMiniMdRW::SaveDone
+
+//*****************************************************************************
+// General post-token-move table fixup.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FixUpTable(
+ ULONG ixTbl) // Index of table to fix.
+{
+ HRESULT hr = S_OK;
+ ULONG i, j; // Loop control.
+ ULONG cRows; // Count of rows in table.
+ void *pRec; // Pointer to row data.
+ mdToken tk; // A token.
+ ULONG rCols[16]; // List of columns with token data.
+ ULONG cCols; // Count of columns with token data.
+
+ // If no remaps, nothing to do.
+ if (GetTokenMovementMap() == NULL)
+ return S_OK;
+
+ // Find the columns with token data.
+ cCols = 0;
+ _ASSERTE(m_TableDefs[ixTbl].m_cCols <= 16);
+ for (i=0; i<m_TableDefs[ixTbl].m_cCols; ++i)
+ {
+ if (m_TableDefs[ixTbl].m_pColDefs[i].m_Type <= iCodedTokenMax)
+ rCols[cCols++] = i;
+ }
+ _ASSERTE(cCols);
+ if (cCols == 0)
+ return S_OK;
+
+ cRows = m_Schema.m_cRecs[ixTbl];
+
+ // loop through all Rows
+ for (i = 1; i<=cRows; ++i)
+ {
+ IfFailGo(getRow(ixTbl, i, &pRec));
+ for (j=0; j<cCols; ++j)
+ {
+ tk = GetToken(ixTbl, rCols[j], pRec);
+ tk = GetTokenMovementMap()->SafeRemap(tk);
+ IfFailGo(PutToken(ixTbl, rCols[j], pRec, tk));
+ }
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::FixUpTable
+
+
+//*****************************************************************************
+// Fixup all the embedded ref to corresponding def before we remap tokens movement.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FixUpRefToDef()
+{
+ return NOERROR;
+} // CMiniMdRW::FixUpRefToDef
+
+//*****************************************************************************
+// Given a table with a pointer (index) to a sequence of rows in another
+// table, get the RID of the end row. This is the STL-ish end; the first row
+// not in the list. Thus, for a list of 0 elements, the start and end will
+// be the same.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::Impl_GetEndRidForColumn( // The End rid.
+ UINT32 nTableIndex,
+ RID nRowIndex,
+ CMiniColDef &def, // Column containing the RID into other table.
+ UINT32 nTargetTableIndex, // The other table.
+ RID *pEndRid)
+{
+ HRESULT hr;
+ ULONG ixEnd;
+ void *pRow;
+
+ // Last rid in range from NEXT record, or count of table, if last record.
+ _ASSERTE(nRowIndex <= m_Schema.m_cRecs[nTableIndex]);
+ if (nRowIndex < m_Schema.m_cRecs[nTableIndex])
+ {
+ IfFailRet(getRow(nTableIndex, nRowIndex + 1, &pRow));
+ ixEnd = getIX(pRow, def);
+ // We use a special value, 'END_OF_TABLE' (currently 0), to indicate
+ // end-of-table. If we find the special value we'll have to compute
+ // the value to return. If we don't find the special value, then
+ // the value is correct.
+ if (ixEnd != END_OF_TABLE)
+ {
+ *pEndRid = ixEnd;
+ return S_OK;
+ }
+ }
+
+ // Either the child pointer value in the next row was END_OF_TABLE, or
+ // the row is the last row of the table. In either case, we must return
+ // a value which will work out to the END of the child table. That
+ // value depends on the value in the row itself -- if the row contains
+ // END_OF_TABLE, there are no children, and to make the subtraction
+ // work out, we return END_OF_TABLE for the END value. If the row
+ // contains some value, then we return the actual END count.
+ IfFailRet(getRow(nTableIndex, nRowIndex, &pRow));
+ if (getIX(pRow, def) == END_OF_TABLE)
+ {
+ ixEnd = END_OF_TABLE;
+ }
+ else
+ {
+ ixEnd = m_Schema.m_cRecs[nTargetTableIndex] + 1;
+ }
+
+ *pEndRid = ixEnd;
+ return S_OK;
+} // CMiniMd::Impl_GetEndRidForColumn
+
+//*****************************************************************************
+// Add a row to any table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddRecord( // S_OK or error.
+ UINT32 nTableIndex, // The table to expand.
+ void **ppRow,
+ RID *pRid) // Put RID here.
+{
+ HRESULT hr;
+
+ _ASSERTE(nTableIndex < m_TblCount);
+ _ASSERTE(!m_bPreSaveDone && "Cannot add records after PreSave and before Save.");
+ IfFailRet(m_Tables[nTableIndex].AddRecord(
+ reinterpret_cast<BYTE **>(ppRow),
+ reinterpret_cast<UINT32 *>(pRid)));
+ if (*pRid > m_maxRid)
+ {
+ m_maxRid = *pRid;
+ if (m_maxRid > m_limRid && m_eGrow == eg_ok)
+ {
+ // OutputDebugStringA("Growing tables due to Record overflow.\n");
+ m_eGrow = eg_grow, m_maxRid = m_maxIx = ULONG_MAX;
+ }
+ }
+ ++m_Schema.m_cRecs[nTableIndex];
+ SetSorted(nTableIndex, false);
+ if (m_pVS[nTableIndex] != NULL)
+ {
+ m_pVS[nTableIndex]->m_isMapValid = false;
+ }
+
+ return S_OK;
+} // CMiniMdRW::AddRecord
+
+//*****************************************************************************
+// Add a row to the TypeDef table, and initialize the pointers to other tables.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddTypeDefRecord(
+ TypeDefRec **ppRow,
+ RID *pnRowIndex)
+{
+ HRESULT hr;
+ IfFailRet(AddRecord(TBL_TypeDef, (void **)ppRow, pnRowIndex));
+
+ IfFailRet(PutCol(TBL_TypeDef, TypeDefRec::COL_MethodList, *ppRow, NewRecordPointerEndValue(TBL_Method)));
+ IfFailRet(PutCol(TBL_TypeDef, TypeDefRec::COL_FieldList, *ppRow, NewRecordPointerEndValue(TBL_Field)));
+
+ return S_OK;
+} // CMiniMdRW::AddTypeDefRecord
+
+//*****************************************************************************
+// Add a row to the Method table, and initialize the pointers to other tables.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddMethodRecord(
+ MethodRec **ppRow,
+ RID *pnRowIndex)
+{
+ HRESULT hr;
+ IfFailRet(AddRecord(TBL_Method, (void **)ppRow, pnRowIndex));
+
+ IfFailRet(PutCol(TBL_Method, MethodRec::COL_ParamList, *ppRow, NewRecordPointerEndValue(TBL_Param)));
+
+ return S_OK;
+} // CMiniMdRW::AddMethodRecord
+
+//*****************************************************************************
+// Add a row to the EventMap table, and initialize the pointers to other tables.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddEventMapRecord(
+ EventMapRec **ppRow,
+ RID *pnRowIndex)
+{
+ HRESULT hr;
+ IfFailRet(AddRecord(TBL_EventMap, (void **)ppRow, pnRowIndex));
+
+ IfFailRet(PutCol(TBL_EventMap, EventMapRec::COL_EventList, *ppRow, NewRecordPointerEndValue(TBL_Event)));
+
+ SetSorted(TBL_EventMap, false);
+
+ return S_OK;
+} // CMiniMdRW::AddEventMapRecord
+
+//*********************************************************************************
+// Add a row to the PropertyMap table, and initialize the pointers to other tables.
+//*********************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddPropertyMapRecord(
+ PropertyMapRec **ppRow,
+ RID *pnRowIndex)
+{
+ HRESULT hr;
+ IfFailRet(AddRecord(TBL_PropertyMap, (void **)ppRow, pnRowIndex));
+
+ IfFailRet(PutCol(TBL_PropertyMap, PropertyMapRec::COL_PropertyList, *ppRow, NewRecordPointerEndValue(TBL_Property)));
+
+ SetSorted(TBL_PropertyMap, false);
+
+ return S_OK;
+} // CMiniMdRW::AddPropertyMapRecord
+
+//*****************************************************************************
+// converting a ANSI heap string to unicode string to an output buffer
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::Impl_GetStringW(
+ ULONG ix,
+ __out_ecount (cchBuffer) LPWSTR szOut,
+ ULONG cchBuffer,
+ ULONG *pcchBuffer)
+{
+ LPCSTR szString; // Single byte version.
+ int iSize; // Size of resulting string, in wide chars.
+ HRESULT hr = NOERROR;
+
+ IfFailGo(getString(ix, &szString));
+
+ if (*szString == 0)
+ {
+ // If emtpy string "", return pccBuffer 0
+ if ( szOut && cchBuffer )
+ szOut[0] = W('\0');
+ if ( pcchBuffer )
+ *pcchBuffer = 0;
+ goto ErrExit;
+ }
+ if (!(iSize=::WszMultiByteToWideChar(CP_UTF8, 0, szString, -1, szOut, cchBuffer)))
+ {
+ // What was the problem?
+ DWORD dwNT = GetLastError();
+
+ // Not truncation?
+ if (dwNT != ERROR_INSUFFICIENT_BUFFER)
+ IfFailGo(HRESULT_FROM_NT(dwNT));
+
+ // Truncation error; get the size required.
+ if (pcchBuffer)
+ *pcchBuffer = ::WszMultiByteToWideChar(CP_UTF8, 0, szString, -1, NULL, 0);
+
+ if ((szOut != NULL) && (cchBuffer > 0))
+ { // null-terminate the truncated output string
+ szOut[cchBuffer - 1] = W('\0');
+ }
+
+ hr = CLDB_S_TRUNCATION;
+ goto ErrExit;
+ }
+ if (pcchBuffer)
+ *pcchBuffer = iSize;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::Impl_GetStringW
+
+//*****************************************************************************
+// Get a column value from a row. Signed types are sign-extended to the full
+// ULONG; unsigned types are 0-extended.
+//*****************************************************************************
+ULONG CMiniMdRW::GetCol( // Column data.
+ ULONG ixTbl, // Index of the table.
+ ULONG ixCol, // Index of the column.
+ void *pvRecord) // Record with the data.
+{
+ BYTE *pRecord; // The row.
+ BYTE *pData; // The item in the row.
+ ULONG val; // The return value.
+ // Valid Table, Column, Row?
+ _ASSERTE(ixTbl < m_TblCount);
+ _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols);
+
+ // Column size, offset
+ CMiniColDef *pColDef = &m_TableDefs[ixTbl].m_pColDefs[ixCol];
+
+ pRecord = reinterpret_cast<BYTE*>(pvRecord);
+ pData = pRecord + pColDef->m_oColumn;
+
+ switch (pColDef->m_cbColumn)
+ {
+ case 1:
+ val = *pData;
+ break;
+ case 2:
+ if (pColDef->m_Type == iSHORT)
+ val = static_cast<LONG>((INT16)GET_UNALIGNED_VAL16(pData));
+ else
+ val = GET_UNALIGNED_VAL16(pData);
+ break;
+ case 4:
+ val = GET_UNALIGNED_VAL32(pData);
+ break;
+ default:
+ _ASSERTE(!"Unexpected column size");
+ return 0;
+ }
+
+ return val;
+} // CMiniMdRW::GetCol
+
+//*****************************************************************************
+// General token column fetcher.
+//*****************************************************************************
+mdToken CMiniMdRW::GetToken(
+ ULONG ixTbl, // Index of the table.
+ ULONG ixCol, // Index of the column.
+ void *pvRecord) // Record with the data.
+{
+ ULONG tkn; // Token from the table.
+
+ // Valid Table, Column, Row?
+ _ASSERTE(ixTbl < m_TblCount);
+ _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols);
+
+ // Column description.
+ CMiniColDef *pColDef = &m_TableDefs[ixTbl].m_pColDefs[ixCol];
+
+ // Is the column just a RID?
+ if (pColDef->m_Type <= iRidMax)
+ {
+ tkn = GetCol(ixTbl, ixCol, pvRecord); //pColDef, pvRecord, RidFromToken(tk));
+ tkn = TokenFromRid(tkn, GetTokenForTable(pColDef->m_Type));
+ }
+ else // Is it a coded token?
+ if (pColDef->m_Type <= iCodedTokenMax)
+ {
+ ULONG indexCodedToken = pColDef->m_Type - iCodedToken;
+ if (indexCodedToken < COUNTOF(g_CodedTokens))
+ {
+ const CCodedTokenDef *pCdTkn = &g_CodedTokens[indexCodedToken];
+ tkn = decodeToken(GetCol(ixTbl, ixCol, pvRecord), pCdTkn->m_pTokens, pCdTkn->m_cTokens);
+ }
+ else
+ {
+ _ASSERTE(!"GetToken called on unexpected coded token type");
+ tkn = 0;
+ }
+ }
+ else // It is an error.
+ {
+ _ASSERTE(!"GetToken called on unexpected column type");
+ tkn = 0;
+ }
+
+ return tkn;
+} // CMiniMdRW::GetToken
+
+//*****************************************************************************
+// Put a column value into a row. The value is passed as a ULONG; 1, 2, or 4
+// bytes are stored into the column. No table is specified, and the coldef
+// is passed directly. This allows putting data into other buffers, such as
+// the temporary table used for saving.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::PutCol( // S_OK or E_UNEXPECTED.
+ CMiniColDef ColDef, // The col def.
+ void *pvRecord, // The row.
+ ULONG uVal) // Value to put.
+{
+ HRESULT hr = S_OK;
+ BYTE *pRecord; // The row.
+ BYTE *pData; // The item in the row.
+
+ pRecord = reinterpret_cast<BYTE*>(pvRecord);
+ pData = pRecord + ColDef.m_oColumn;
+
+ switch (ColDef.m_cbColumn)
+ {
+ case 1:
+ // Don't store a value that would overflow.
+ if (uVal > UCHAR_MAX)
+ return E_INVALIDARG;
+ *pData = static_cast<BYTE>(uVal);
+ break;
+ case 2:
+ if (uVal > USHRT_MAX)
+ return E_INVALIDARG;
+ SET_UNALIGNED_VAL16(pData, uVal);
+ break;
+ case 4:
+ SET_UNALIGNED_VAL32(pData, uVal);
+ break;
+ default:
+ _ASSERTE(!"Unexpected column size");
+ return E_UNEXPECTED;
+ }
+
+ return hr;
+} // CMiniMdRW::PutCol
+
+//*****************************************************************************
+// Put a column value into a row. The value is passed as a ULONG; 1, 2, or 4
+// bytes are stored into the column.
+//*****************************************************************************
+
+//*****************************************************************************
+// Add a string to the string pool, and store the offset in the cell.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::PutString( // S_OK or E_UNEXPECTED.
+ ULONG ixTbl, // The table.
+ ULONG ixCol, // The column.
+ void *pvRecord, // The row.
+ LPCSTR szString) // Value to put.
+{
+ _ASSERTE(szString != NULL);
+
+ HRESULT hr = S_OK;
+ UINT32 nStringIndex = 0;
+
+ // Valid Table, Column, Row?
+ _ASSERTE(ixTbl < m_TblCount);
+ _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols);
+
+ // Column description.
+ _ASSERTE(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING);
+
+ // <TODO>@FUTURE: Set iOffset to 0 for empty string. Work around the bug in
+ // StringPool that does not handle empty strings correctly.</TODO>
+ if (szString[0] == 0)
+ { // It's empty string
+ nStringIndex = 0;
+ }
+ else
+ { // It's non-empty string
+ IfFailGo(m_StringHeap.AddString(
+ szString,
+ &nStringIndex));
+ }
+
+ hr = PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pvRecord, nStringIndex);
+
+ if (m_maxIx != ULONG_MAX)
+ {
+ IfFailGo(m_StringHeap.GetAlignedSize(&nStringIndex));
+ }
+ if (nStringIndex > m_maxIx)
+ {
+ m_maxIx = nStringIndex;
+ if (m_maxIx > m_limIx && m_eGrow == eg_ok)
+ {
+ // OutputDebugStringA("Growing tables due to String overflow.\n");
+ m_eGrow = eg_grow, m_maxRid = m_maxIx = ULONG_MAX;
+ }
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::PutString
+
+//*****************************************************************************
+// Add a string to the string pool, and store the offset in the cell.
+// Returns: S_OK or E_UNEXPECTED.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::PutStringW(
+ ULONG ixTbl, // The table.
+ ULONG ixCol, // The column.
+ void *pvRecord, // The row.
+ LPCWSTR wszString) // Value to put.
+{
+ _ASSERTE(wszString != NULL);
+
+ HRESULT hr = S_OK;
+ UINT32 nStringIndex = 0; // The new string.
+
+ // Valid Table, Column, Row?
+ _ASSERTE(ixTbl < m_TblCount);
+ _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols);
+
+ // Column description.
+ _ASSERTE(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING);
+
+ // Special case for empty string for StringPool
+ if (wszString[0] == 0)
+ { // It's empty string
+ // TODO: Is it OK that index 0 contains empty blob (00) and not empty string (00 01)?
+ nStringIndex = 0;
+ }
+ else
+ { // It's non-empty string
+ IfFailGo(m_StringHeap.AddStringW(
+ wszString,
+ &nStringIndex));
+ }
+
+ hr = PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pvRecord, nStringIndex);
+
+ if (m_maxIx != ULONG_MAX)
+ {
+ IfFailGo(m_StringHeap.GetAlignedSize(&nStringIndex));
+ }
+ if (nStringIndex > m_maxIx)
+ {
+ m_maxIx = nStringIndex;
+ if (m_maxIx > m_limIx && m_eGrow == eg_ok)
+ {
+ // OutputDebugStringA("Growing tables due to String overflow.\n");
+ m_eGrow = eg_grow, m_maxRid = m_maxIx = ULONG_MAX;
+ }
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::PutStringW
+
+//*****************************************************************************
+// Add a guid to the guid pool, and store the index in the cell.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::PutGuid( // S_OK or E_UNEXPECTED.
+ ULONG ixTbl, // The table.
+ ULONG ixCol, // The column.
+ void *pvRecord, // The row.
+ REFGUID guid) // Value to put.
+{
+ HRESULT hr = S_OK;
+ UINT32 nIndex;
+ UINT32 cbSize = 0;
+
+ // Valid Table, Column, Row?
+ _ASSERTE(ixTbl < m_TblCount);
+ _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols);
+
+ // Column description.
+ _ASSERTE(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iGUID);
+
+ IfFailGo(AddGuid(guid, &nIndex));
+
+ hr = PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pvRecord, nIndex);
+
+ if (m_maxIx != ULONG_MAX)
+ {
+ cbSize = m_GuidHeap.GetSize();
+ }
+ if (cbSize > m_maxIx)
+ {
+ m_maxIx = cbSize;
+ if (m_maxIx > m_limIx && m_eGrow == eg_ok)
+ {
+ // OutputDebugStringA("Growing tables due to GUID overflow.\n");
+ m_eGrow = eg_grow, m_maxRid = m_maxIx = ULONG_MAX;
+ }
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::PutGuid
+
+//*****************************************************************************
+// Normally, an MVID is randomly generated for every metadata.
+// ChangeMvid() can be used to explicitly set it.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ChangeMvid( // S_OK or E_UNEXPECTED.
+ REFGUID newMvid)
+{
+ HRESULT hr = S_OK;
+
+ ModuleRec *pModuleRec;
+ IfFailRet(GetModuleRecord(1, &pModuleRec));
+ UINT32 nGuidIndex = GetCol(TBL_Module, ModuleRec::COL_Mvid, pModuleRec);
+
+ GUID UNALIGNED *pMvid;
+ IfFailRet(m_GuidHeap.GetGuid(
+ nGuidIndex,
+ &pMvid));
+
+ // Replace the GUID with new MVID.
+ *pMvid = newMvid;
+ // This was missing (probably because we don't test on platform with different bitness):
+ //SwapGuid(pMvid);
+
+ return hr;
+} // CMiniMdRW::ChangeMvid
+
+//*****************************************************************************
+// Put a token into a cell. If the column is a coded token, perform the
+// encoding first.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::PutToken( // S_OK or E_UNEXPECTED.
+ ULONG ixTbl, // The table.
+ ULONG ixCol, // The column.
+ void *pvRecord, // The row.
+ mdToken tk) // Value to put.
+{
+ HRESULT hr = S_OK;
+ ULONG cdTkn; // The new coded token.
+
+ // Valid Table, Column, Row?
+ _ASSERTE(ixTbl < m_TblCount);
+ _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols);
+
+ // Column description.
+ CMiniColDef ColDef = m_TableDefs[ixTbl].m_pColDefs[ixCol];
+
+ // Is the column just a RID?
+ if (ColDef.m_Type <= iRidMax)
+ hr = PutCol(ColDef, pvRecord, RidFromToken(tk));
+ else // Is it a coded token?
+ if (ColDef.m_Type <= iCodedTokenMax)
+ {
+ ULONG indexCodedToken = ColDef.m_Type - iCodedToken;
+ if (indexCodedToken < COUNTOF(g_CodedTokens))
+ {
+ const CCodedTokenDef *pCdTkn = &g_CodedTokens[indexCodedToken];
+ cdTkn = encodeToken(RidFromToken(tk), TypeFromToken(tk), pCdTkn->m_pTokens, pCdTkn->m_cTokens);
+ hr = PutCol(ColDef, pvRecord, cdTkn);
+ }
+ else
+ {
+ _ASSERTE(!"PutToken called on unexpected coded token type");
+ hr = E_FAIL;
+ }
+ }
+ else // It is an error.
+ {
+ _ASSERTE(!"PutToken called on unexpected column type");
+ }
+
+ return hr;
+} // CMiniMdRW::PutToken
+
+//*****************************************************************************
+// Add a blob to the blob pool, and store the offset in the cell.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::PutBlob(
+ ULONG ixTbl, // Table with the row.
+ ULONG ixCol, // Column to set.
+ void *pvRecord, // The row.
+ const void *pvData, // Blob data.
+ ULONG cbData) // Size of the blob data.
+{
+ HRESULT hr = S_OK;
+ UINT32 nBlobIndex;
+
+ // Valid Table, Column, Row?
+ _ASSERTE(ixTbl < m_TblCount);
+ _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols);
+
+ // Column description.
+ _ASSERTE(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iBLOB);
+
+ IfFailGo(m_BlobHeap.AddBlob(
+ MetaData::DataBlob((BYTE *)pvData, cbData),
+ &nBlobIndex));
+
+ hr = PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pvRecord, nBlobIndex);
+
+ if (m_maxIx != ULONG_MAX)
+ {
+ IfFailGo(m_BlobHeap.GetAlignedSize(&nBlobIndex));
+ }
+ if (nBlobIndex > m_maxIx)
+ {
+ m_maxIx = nBlobIndex;
+ if (m_maxIx > m_limIx && m_eGrow == eg_ok)
+ {
+ // OutputDebugStringA("Growing tables due to Blob overflow.\n");
+ m_eGrow = eg_grow, m_maxRid = m_maxIx = ULONG_MAX;
+ }
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::PutBlob
+
+//*****************************************************************************
+// Given a table with a pointer to another table, add a row in the second table
+// at the end of the range of rows belonging to some parent.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddChildRowIndirectForParent(
+ ULONG tblParent, // Parent table.
+ ULONG colParent, // Column in parent table.
+ ULONG tblChild, // Child table, pointed to by parent cell.
+ RID ridParent, // Rid of parent row.
+ void **ppRow)
+{
+ HRESULT hr;
+ ULONG ixInsert; // Index of new row.
+ ULONG i; // Loop control.
+ void *pRow; // A parent row.
+ ULONG ixChild; // Some child record RID.
+
+ // If the row in the parent table is the last row, just append.
+ if (ridParent == GetCountRecs(tblParent))
+ {
+ RID nRowIndex_Ignore;
+ return AddRecord(tblChild, ppRow, &nRowIndex_Ignore);
+ }
+
+ // Determine the index at which to insert a row.
+ IfFailRet(getRow(tblParent, ridParent+1, &pRow));
+ ixInsert = GetCol(tblParent, colParent, pRow);
+
+ // Insert the row.
+ IfFailRet(m_Tables[tblChild].InsertRecord(ixInsert, reinterpret_cast<BYTE **>(ppRow)));
+ // Count the inserted record.
+ ++m_Schema.m_cRecs[tblChild];
+
+ if (m_Schema.m_cRecs[tblChild] > m_maxRid)
+ {
+ m_maxRid = m_Schema.m_cRecs[tblChild];
+ if (m_maxRid > m_limRid && m_eGrow == eg_ok)
+ m_eGrow = eg_grow, m_maxIx = m_maxRid = ULONG_MAX;
+ }
+
+ // Adjust the rest of the rows in the table.
+ for (i=GetCountRecs(tblParent); i>ridParent; --i)
+ {
+ IfFailRet(getRow(tblParent, i, &pRow));
+ ixChild = GetCol(tblParent, colParent, pRow);
+ ++ixChild;
+ IfFailRet(PutCol(tblParent, colParent, pRow, ixChild));
+ }
+
+ return S_OK;
+} // CMiniMdRW::AddChildRowIndirectForParent
+
+//*****************************************************************************
+// Given a Parent and a Child, this routine figures if there needs to be an
+// indirect table and creates it if needed. Else it just update the pointers
+// in the entries contained in the parent table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddChildRowDirectForParent(
+ ULONG tblParent, // Parent table.
+ ULONG colParent, // Column in parent table.
+ ULONG tblChild, // Child table, pointed to by parent cell.
+ RID ridParent) // Rid of parent row.
+{
+ HRESULT hr = S_OK;
+ void *pRow; // A row in the parent table.
+ RID ixChild; // Rid of a child record.
+
+ if (m_Schema.m_cRecs[tblChild-1] != 0)
+ {
+ // If there already exists an indirect table, just return.
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+ // If the parent record has subsequent parent records with children,
+ // we will now need to build a pointer table.
+ //
+ // The canonical form of a child pointer in a parent record is to point to
+ // the start of the child list. A record with no children will point
+ // to the same location as its subsequent record (that is, if A and B *could*
+ // have a child record, but only B *does*, both A and B will point to the
+ // same place. If the last record in the parent table has no child records,
+ // it will point one past the end of the child table. This is patterned
+ // after the STL's inclusive-BEGIN and exclusive-END.
+ // This has the unfortunate side effect that if a child record is added to
+ // a parent not at the end of its table, *all* of the subsequent parent records
+ // will have to be updated to point to the new "1 past end of child table"
+ // location.
+ // Therefore, as an optimization, we will also recognize a special marker,
+ // END_OF_TABLE (currently 0), to mean "past eot".
+ //
+ // If the child pointer of the record getting the new child is END_OF_TABLE,
+ // then there is no subsequent child pointer. We need to fix up this parent
+ // record, and any previous parent records with END_OF_TABLE to point to the
+ // new child record.
+ // If the child pointer of this parent record is not END_OF_TABLE, but the
+ // child pointer of the next parent record is, then there is nothing at
+ // all that needs to be done.
+ // If the child pointer of the next parent record is not END_OF_TABLE, then
+ // we will have to build a pointer table.
+
+ // Get the parent record, and see if its child pointer is END_OF_TABLE. If so,
+ // fix the parent, and all previous END_OF_TABLE valued parent records.
+ IfFailGo(getRow(tblParent, ridParent, &pRow));
+ ixChild = GetCol(tblParent, colParent, pRow);
+ if (ixChild == END_OF_TABLE)
+ {
+ IfFailGo(ConvertMarkerToEndOfTable(tblParent, colParent, m_Schema.m_cRecs[tblChild], ridParent));
+ goto ErrExit;
+ }
+
+ // The parent did not have END_OF_TABLE for its child pointer. If it was the last
+ // record in the table, there is nothing more to do.
+ if (ridParent == m_Schema.m_cRecs[tblParent])
+ goto ErrExit;
+
+ // The parent didn't have END_OF_TABLE, and there are more rows in parent table.
+ // If the next parent record's child pointer is END_OF_TABLE, then all of the
+ // remaining records are OK.
+ IfFailGo(getRow(tblParent, ridParent+1, &pRow));
+ ixChild = GetCol(tblParent, colParent, pRow);
+ if (ixChild == END_OF_TABLE)
+ goto ErrExit;
+
+ // The next record was not END_OF_TABLE, so some adjustment will be required.
+ // If it points to the actual END of the table, there are no more child records
+ // and the child pointers can be adjusted to the new END of the table.
+ if (ixChild == m_Schema.m_cRecs[tblChild])
+ {
+ for (ULONG i=m_Schema.m_cRecs[tblParent]; i>ridParent; --i)
+ {
+ IfFailGo(getRow(tblParent, i, &pRow));
+ IfFailGo(PutCol(tblParent, colParent, pRow, ixChild+1));
+ }
+ goto ErrExit;
+ }
+
+ // The next record contained a pointer to some actual child data. That means that
+ // this is an out-of-order insertion. We must create an indirect table.
+ // Convert any END_OF_TABLE to actual END of table value. Note that a record has
+ // just been added to the child table, and not yet to the parent table, so the END
+ // should currently point to the last valid record (instead of the usual first invalid
+ // rid).
+ IfFailGo(ConvertMarkerToEndOfTable(tblParent, colParent, m_Schema.m_cRecs[tblChild], m_Schema.m_cRecs[tblParent]));
+ // Create the indirect table.
+ IfFailGo(CreateIndirectTable(tblChild));
+ hr = S_FALSE;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddChildRowDirectForParent
+
+//*****************************************************************************
+// Starting with some location, convert special END_OF_TABLE values into
+// actual end of table values (count of records + 1).
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ConvertMarkerToEndOfTable(
+ ULONG tblParent, // Parent table to convert.
+ ULONG colParent, // Column in parent table.
+ ULONG ixEnd, // Value to store to child pointer.
+ RID ridParent) // Rid of parent row to start with (work down).
+{
+ HRESULT hr;
+ void *pRow; // A row in the parent table.
+ RID ixChild; // Rid of a child record.
+
+ for (; ridParent > 0; --ridParent)
+ {
+ IfFailGo(getRow(tblParent, ridParent, &pRow));
+ ixChild = GetCol(tblParent, colParent, pRow);
+ // Finished when rows no longer have special value.
+ if (ixChild != END_OF_TABLE)
+ break;
+ IfFailGo(PutCol(tblParent, colParent, pRow, ixEnd));
+ }
+ // Success.
+ hr = S_OK;
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::ConvertMarkerToEndOfTable
+
+//*****************************************************************************
+// Given a Table ID this routine creates the corresponding pointer table with
+// the entries in the given Table ID less one. It doesn't create the last
+// entry by default, since its the last entry that caused the Indirect table to
+// be required in most cases and will need to inserted at the appropriate location
+// with AddChildRowIndirectForParent() function. So, be VERY CAREFUL when using this function!
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::CreateIndirectTable(
+ ULONG ixTbl, // Given Table.
+ BOOL bOneLess /* = true */) // if true, create one entry less.
+{
+ void *pRecord;
+ ULONG cRecords;
+ HRESULT hr = S_OK;
+
+ if (m_OptionValue.m_ErrorIfEmitOutOfOrder)
+ {
+ //<TODO> Can we use some bit fields and reduce the code size here??
+ //</TODO>
+ if (ixTbl == TBL_Field && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDFieldOutOfOrder ) )
+ {
+ _ASSERTE(!"Out of order emit of field token!");
+ return CLDB_E_RECORD_OUTOFORDER;
+ }
+ else if (ixTbl == TBL_Method && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDMethodOutOfOrder ) )
+ {
+ _ASSERTE(!"Out of order emit of method token!");
+ return CLDB_E_RECORD_OUTOFORDER;
+ }
+ else if (ixTbl == TBL_Param && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDParamOutOfOrder ) )
+ {
+ _ASSERTE(!"Out of order emit of param token!");
+ return CLDB_E_RECORD_OUTOFORDER;
+ }
+ else if (ixTbl == TBL_Property && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDPropertyOutOfOrder ) )
+ {
+ _ASSERTE(!"Out of order emit of property token!");
+ return CLDB_E_RECORD_OUTOFORDER;
+ }
+ else if (ixTbl == TBL_Event && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDEventOutOfOrder ) )
+ {
+ _ASSERTE(!"Out of order emit of event token!");
+ return CLDB_E_RECORD_OUTOFORDER;
+ }
+ }
+
+ _ASSERTE(! HasIndirectTable(ixTbl));
+
+ cRecords = GetCountRecs(ixTbl);
+ if (bOneLess)
+ cRecords--;
+
+ // Create one less than the number of records in the given table.
+ for (ULONG i = 1; i <= cRecords ; i++)
+ {
+ RID nRowIndex_Ignore;
+ IfFailGo(AddRecord(g_PtrTableIxs[ixTbl].m_ixtbl, &pRecord, &nRowIndex_Ignore));
+ IfFailGo(PutCol(g_PtrTableIxs[ixTbl].m_ixtbl, g_PtrTableIxs[ixTbl].m_ixcol, pRecord, i));
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::CreateIndirectTable
+
+//---------------------------------------------------------------------------------------
+//
+// The new paramter may not have been emitted in sequence order. So
+// check the current parameter and move it up in the indirect table until
+// we find the right home.
+//
+__checkReturn
+HRESULT
+CMiniMdRW::FixParamSequence(
+ RID md) // Rid of method with new parameter.
+{
+ HRESULT hr;
+ MethodRec * pMethod;
+ IfFailRet(GetMethodRecord(md, &pMethod));
+ RID ixStart = getParamListOfMethod(pMethod);
+ RID ixEnd;
+ IfFailRet(getEndParamListOfMethod(md, &ixEnd));
+ int iSlots = 0;
+
+ // Param table should not be empty at this point.
+ _ASSERTE(ixEnd > ixStart);
+
+ // Get a pointer to the new guy.
+ RID ridNew;
+ ParamPtrRec * pNewParamPtr = NULL;
+ if (HasIndirectTable(TBL_Param))
+ {
+ IfFailRet(GetParamPtrRecord(--ixEnd, &pNewParamPtr));
+ ridNew = GetCol(TBL_ParamPtr, ParamPtrRec::COL_Param, pNewParamPtr);
+ }
+ else
+ {
+ ridNew = --ixEnd;
+ }
+
+ ParamRec * pNewParam;
+ IfFailRet(GetParamRecord(ridNew, &pNewParam));
+
+ // Walk the list forward looking for the insert point.
+ for (; ixStart < ixEnd; --ixEnd)
+ {
+ // Get the current parameter record.
+ RID ridOld;
+ if (HasIndirectTable(TBL_Param))
+ {
+ ParamPtrRec * pParamPtr;
+ IfFailRet(GetParamPtrRecord(ixEnd - 1, &pParamPtr));
+ ridOld = GetCol(TBL_ParamPtr, ParamPtrRec::COL_Param, pParamPtr);
+ }
+ else
+ {
+ ridOld = ixEnd - 1;
+ }
+
+ ParamRec * pParamRec;
+ IfFailRet(GetParamRecord(ridOld, &pParamRec));
+
+ // If the new record belongs before this existing record, slide
+ // all of the old stuff down.
+ if (pNewParam->GetSequence() < pParamRec->GetSequence())
+ {
+ ++iSlots;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // If the item is out of order, move everything down one slot and
+ // copy the new guy into the new location. Because the heap can be
+ // split, this must be done carefully.
+ //<TODO>@Future: one could write a more complicated but faster routine that
+ // copies blocks within heaps.</TODO>
+ if (iSlots)
+ {
+ RID endRid;
+ // Create an indirect table if there isn't one already. This is because
+ // we can't change tokens that have been handed out, in this case the
+ // param tokens.
+ if (!HasIndirectTable(TBL_Param))
+ {
+ IfFailRet(CreateIndirectTable(TBL_Param, false));
+ IfFailRet(getEndParamListOfMethod(md, &endRid));
+ IfFailRet(GetParamPtrRecord(endRid - 1, &pNewParamPtr));
+ }
+ int cbCopy = m_TableDefs[TBL_ParamPtr].m_cbRec;
+ void * pbBackup = _alloca(cbCopy);
+ memcpy(pbBackup, pNewParamPtr, cbCopy);
+
+ IfFailRet(getEndParamListOfMethod(md, &endRid));
+ for (ixEnd = endRid - 1; iSlots; iSlots--, --ixEnd)
+ {
+ ParamPtrRec * pTo;
+ IfFailRet(GetParamPtrRecord(ixEnd, &pTo));
+ ParamPtrRec * pFrom;
+ IfFailRet(GetParamPtrRecord(ixEnd - 1, &pFrom));
+ memcpy(pTo, pFrom, cbCopy);
+ }
+
+ ParamPtrRec * pTo;
+ IfFailRet(GetParamPtrRecord(ixEnd, &pTo));
+ memcpy(pTo, pbBackup, cbCopy);
+ }
+ return S_OK;
+} // CMiniMdRW::FixParamSequence
+
+//---------------------------------------------------------------------------------------
+//
+// Given a MethodDef and its parent TypeDef, add the MethodDef to the parent,
+// adjusting the MethodPtr table if it exists or if it needs to be created.
+//
+__checkReturn
+HRESULT
+CMiniMdRW::AddMethodToTypeDef(
+ RID td, // The TypeDef to which to add the Method.
+ RID md) // MethodDef to add to TypeDef.
+{
+ HRESULT hr;
+ void * pPtr;
+
+ // Add direct if possible.
+ IfFailGo(AddChildRowDirectForParent(TBL_TypeDef, TypeDefRec::COL_MethodList, TBL_Method, td));
+
+ // If couldn't add direct...
+ if (hr == S_FALSE)
+ { // Add indirect.
+ IfFailGo(AddChildRowIndirectForParent(TBL_TypeDef, TypeDefRec::COL_MethodList, TBL_MethodPtr, td, &pPtr));
+ hr = PutCol(TBL_MethodPtr, MethodPtrRec::COL_Method, pPtr, md);
+
+ // Add the <md, td> to the method parent lookup table.
+ IfFailGo(AddMethodToLookUpTable(TokenFromRid(md, mdtMethodDef), td) );
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddMethodToTypeDef
+
+//*****************************************************************************
+// Given a FieldDef and its parent TypeDef, add the FieldDef to the parent,
+// adjusting the FieldPtr table if it exists or if it needs to be created.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddFieldToTypeDef(
+ RID td, // The TypeDef to which to add the Field.
+ RID md) // FieldDef to add to TypeDef.
+{
+ HRESULT hr;
+ void *pPtr;
+
+ // Add direct if possible.
+ IfFailGo(AddChildRowDirectForParent(TBL_TypeDef, TypeDefRec::COL_FieldList, TBL_Field, td));
+
+ // If couldn't add direct...
+ if (hr == S_FALSE)
+ { // Add indirect.
+ IfFailGo(AddChildRowIndirectForParent(TBL_TypeDef, TypeDefRec::COL_FieldList, TBL_FieldPtr, td, &pPtr));
+ hr = PutCol(TBL_FieldPtr, FieldPtrRec::COL_Field, pPtr, md);
+
+ // Add the <md, td> to the field parent lookup table.
+ IfFailGo(AddFieldToLookUpTable(TokenFromRid(md, mdtFieldDef), td));
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddFieldToTypeDef
+
+//*****************************************************************************
+// Given a Param and its parent Method, add the Param to the parent,
+// adjusting the ParamPtr table if there is an indirect table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddParamToMethod(
+ RID md, // The MethodDef to which to add the Param.
+ RID pd) // Param to add to MethodDef.
+{
+ HRESULT hr;
+ void *pPtr;
+
+ IfFailGo(AddChildRowDirectForParent(TBL_Method, MethodRec::COL_ParamList, TBL_Param, md));
+ if (hr == S_FALSE)
+ {
+ IfFailGo(AddChildRowIndirectForParent(TBL_Method, MethodRec::COL_ParamList, TBL_ParamPtr, md, &pPtr));
+ IfFailGo(PutCol(TBL_ParamPtr, ParamPtrRec::COL_Param, pPtr, pd));
+
+ // Add the <pd, md> to the field parent lookup table.
+ IfFailGo(AddParamToLookUpTable(TokenFromRid(pd, mdtParamDef), md));
+ }
+ IfFailGo(FixParamSequence(md));
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddParamToMethod
+
+//*****************************************************************************
+// Given a Property and its parent PropertyMap, add the Property to the parent,
+// adjusting the PropertyPtr table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddPropertyToPropertyMap(
+ RID pmd, // The PropertyMap to which to add the Property.
+ RID pd) // Property to add to PropertyMap.
+{
+ HRESULT hr;
+ void *pPtr;
+
+ IfFailGo(AddChildRowDirectForParent(TBL_PropertyMap, PropertyMapRec::COL_PropertyList,
+ TBL_Property, pmd));
+ if (hr == S_FALSE)
+ {
+ IfFailGo(AddChildRowIndirectForParent(TBL_PropertyMap, PropertyMapRec::COL_PropertyList,
+ TBL_PropertyPtr, pmd, &pPtr));
+ hr = PutCol(TBL_PropertyPtr, PropertyPtrRec::COL_Property, pPtr, pd);
+ }
+
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddPropertyToPropertyMap
+
+//*****************************************************************************
+// Given a Event and its parent EventMap, add the Event to the parent,
+// adjusting the EventPtr table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddEventToEventMap(
+ ULONG emd, // The EventMap to which to add the Event.
+ RID ed) // Event to add to EventMap.
+{
+ HRESULT hr;
+ void *pPtr;
+
+ IfFailGo(AddChildRowDirectForParent(TBL_EventMap, EventMapRec::COL_EventList,
+ TBL_Event, emd));
+ if (hr == S_FALSE)
+ {
+ IfFailGo(AddChildRowIndirectForParent(TBL_EventMap, EventMapRec::COL_EventList,
+ TBL_EventPtr, emd, &pPtr));
+ hr = PutCol(TBL_EventPtr, EventPtrRec::COL_Event, pPtr, ed);
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddEventToEventMap
+
+//*****************************************************************************
+// Find helper for a constant. This will trigger constant table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindConstantHelper( // return index to the constant table
+ mdToken tkParent, // Parent token.
+ RID *pFoundRid)
+{
+ _ASSERTE(TypeFromToken(tkParent) != 0);
+
+ // If sorted, use the faster lookup
+ if (IsSorted(TBL_Constant))
+ {
+ return FindConstantFor(RidFromToken(tkParent), TypeFromToken(tkParent), pFoundRid);
+ }
+ return GenericFindWithHash(TBL_Constant, ConstantRec::COL_Parent, tkParent, pFoundRid);
+} // CMiniMdRW::FindConstantHelper
+
+//*****************************************************************************
+// Find helper for a FieldMarshal. This will trigger FieldMarshal table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindFieldMarshalHelper( // return index to the field marshal table
+ mdToken tkParent, // Parent token. Can be a FieldDef or ParamDef.
+ RID *pFoundRid)
+{
+ _ASSERTE(TypeFromToken(tkParent) != 0);
+
+ // If sorted, use the faster lookup
+ if (IsSorted(TBL_FieldMarshal))
+ {
+ return FindFieldMarshalFor(RidFromToken(tkParent), TypeFromToken(tkParent), pFoundRid);
+ }
+ return GenericFindWithHash(TBL_FieldMarshal, FieldMarshalRec::COL_Parent, tkParent, pFoundRid);
+} // CMiniMdRW::FindFieldMarshalHelper
+
+
+//*****************************************************************************
+// Find helper for a method semantics.
+// This will look up methodsemantics based on its status!
+// Can return out of memory error because of the enumerator.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindMethodSemanticsHelper(
+ mdToken tkAssociate, // Event or property token
+ HENUMInternal *phEnum) // fill in the enum
+{
+ ULONG ridStart, ridEnd;
+ ULONG index;
+ MethodSemanticsRec *pMethodSemantics;
+ HRESULT hr = NOERROR;
+ CLookUpHash *pHashTable = m_pLookUpHashs[TBL_MethodSemantics];
+
+ _ASSERTE(TypeFromToken(tkAssociate) != 0);
+
+ if (IsSorted(TBL_MethodSemantics))
+ {
+ IfFailGo(getAssociatesForToken(tkAssociate, &ridEnd, &ridStart));
+ HENUMInternal::InitSimpleEnum(0, ridStart, ridEnd, phEnum);
+ }
+ else if (pHashTable)
+ {
+ TOKENHASHENTRY *p;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ iHash = HashToken(tkAssociate);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pHashTable->FindFirst(iHash, pos);
+ p;
+ p = pHashTable->FindNext(pos))
+ {
+ IfFailGo(GetMethodSemanticsRecord(p->tok, &pMethodSemantics));
+ if (getAssociationOfMethodSemantics(pMethodSemantics) == tkAssociate)
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnum, p->tok) );
+ }
+ }
+ }
+ else
+ {
+ // linear search
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = 1; index <= getCountMethodSemantics(); index++)
+ {
+ IfFailGo(GetMethodSemanticsRecord(index, &pMethodSemantics));
+ if (getAssociationOfMethodSemantics(pMethodSemantics) == tkAssociate)
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnum, index) );
+ }
+ }
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindMethodSemanticsHelper
+
+
+//*****************************************************************************
+// Find helper for a method semantics given a associate and semantics.
+// This will look up methodsemantics based on its status!
+// Return CLDB_E_RECORD_NOTFOUND if cannot find the matching one
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindAssociateHelper(
+ mdToken tkAssociate, // Event or property token
+ DWORD dwSemantics, // [IN] given a associate semantics(setter, getter, testdefault, reset)
+ RID *pRid) // [OUT] return matching row index here
+{
+ ULONG ridStart, ridEnd;
+ ULONG index;
+ MethodSemanticsRec *pMethodSemantics;
+ HRESULT hr = NOERROR;
+ CLookUpHash *pHashTable = m_pLookUpHashs[TBL_MethodSemantics];
+
+ _ASSERTE(TypeFromToken(tkAssociate) != 0);
+
+ if (pHashTable)
+ {
+ TOKENHASHENTRY *p;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ iHash = HashToken(tkAssociate);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pHashTable->FindFirst(iHash, pos);
+ p;
+ p = pHashTable->FindNext(pos))
+ {
+ IfFailGo(GetMethodSemanticsRecord(p->tok, &pMethodSemantics));
+ if (pMethodSemantics->GetSemantic() == dwSemantics && getAssociationOfMethodSemantics(pMethodSemantics) == tkAssociate)
+ {
+ *pRid = p->tok;
+ goto ErrExit;
+ }
+ }
+ }
+ else
+ {
+ if (IsSorted(TBL_MethodSemantics))
+ {
+ IfFailGo(getAssociatesForToken(tkAssociate, &ridEnd, &ridStart));
+ }
+ else
+ {
+ ridStart = 1;
+ ridEnd = getCountMethodSemantics() + 1;
+ }
+
+ for (index = ridStart; index < ridEnd ; index++)
+ {
+ IfFailGo(GetMethodSemanticsRecord(index, &pMethodSemantics));
+ if (pMethodSemantics->GetSemantic() == dwSemantics && getAssociationOfMethodSemantics(pMethodSemantics) == tkAssociate)
+ {
+ *pRid = index;
+ goto ErrExit;
+ }
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindAssociateHelper
+
+
+//*****************************************************************************
+// Find helper for a MethodImpl.
+// This will trigger MethodImpl table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindMethodImplHelper(
+ mdTypeDef td, // TypeDef token for the Class.
+ HENUMInternal *phEnum) // fill in the enum
+{
+ ULONG ridStart, ridEnd;
+ ULONG index;
+ MethodImplRec *pMethodImpl;
+ HRESULT hr = NOERROR;
+ CLookUpHash *pHashTable = m_pLookUpHashs[TBL_MethodImpl];
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+
+ if (IsSorted(TBL_MethodImpl))
+ {
+ IfFailGo(getMethodImplsForClass(RidFromToken(td), &ridEnd, &ridStart));
+ HENUMInternal::InitSimpleEnum(0, ridStart, ridEnd, phEnum);
+ }
+ else if (pHashTable)
+ {
+ TOKENHASHENTRY *p;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ iHash = HashToken(td);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pHashTable->FindFirst(iHash, pos);
+ p;
+ p = pHashTable->FindNext(pos))
+ {
+ IfFailGo(GetMethodImplRecord(p->tok, &pMethodImpl));
+ if (getClassOfMethodImpl(pMethodImpl) == td)
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnum, p->tok) );
+ }
+ }
+ }
+ else
+ {
+ // linear search
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = 1; index <= getCountMethodImpls(); index++)
+ {
+ IfFailGo(GetMethodImplRecord(index, &pMethodImpl));
+ if (getClassOfMethodImpl(pMethodImpl) == td)
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnum, index) );
+ }
+ }
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindMethodImplHelper
+
+
+//*****************************************************************************
+// Find helper for a GenericParam.
+// This will trigger GenericParam table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindGenericParamHelper(
+ mdToken tkOwner, // Token for the GenericParams' owner.
+ HENUMInternal *phEnum) // fill in the enum
+{
+ HRESULT hr = NOERROR;
+ ULONG ridStart, ridEnd; // Start, end of range of tokens.
+ ULONG index; // A loop counter.
+ GenericParamRec *pGenericParam;
+ CLookUpHash *pHashTable = m_pLookUpHashs[TBL_GenericParam];
+
+ if (IsSorted(TBL_GenericParam))
+ {
+ mdToken tk;
+ tk = encodeToken(RidFromToken(tkOwner), TypeFromToken(tkOwner), mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef));
+ IfFailGo(SearchTableForMultipleRows(TBL_GenericParam,
+ _COLDEF(GenericParam,Owner),
+ tk,
+ &ridEnd,
+ &ridStart));
+ HENUMInternal::InitSimpleEnum(mdtGenericParam, ridStart, ridEnd, phEnum);
+ }
+ else if (pHashTable)
+ {
+ TOKENHASHENTRY *p;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ iHash = HashToken(tkOwner);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pHashTable->FindFirst(iHash, pos);
+ p;
+ p = pHashTable->FindNext(pos))
+ {
+ IfFailGo(GetGenericParamRecord(p->tok, &pGenericParam));
+ if (getOwnerOfGenericParam(pGenericParam) == tkOwner)
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnum, TokenFromRid(p->tok, mdtGenericParam)) );
+ }
+ }
+ }
+ else
+ {
+ // linear search
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = 1; index <= getCountGenericParams(); index++)
+ {
+ IfFailGo(GetGenericParamRecord(index, &pGenericParam));
+ if (getOwnerOfGenericParam(pGenericParam) == tkOwner)
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnum, TokenFromRid(index, mdtGenericParam)) );
+ }
+ }
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindGenericParamHelper
+
+
+//*****************************************************************************
+// Find helper for a GenericParamConstraint.
+// This will trigger GenericParamConstraint table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindGenericParamConstraintHelper(
+ mdGenericParam tkParam, // Token for the GenericParam
+ HENUMInternal *phEnum) // fill in the enum
+{
+ HRESULT hr = NOERROR;
+ ULONG ridStart, ridEnd; // Start, end of range of tokens.
+ ULONG index; // A loop counter.
+ GenericParamConstraintRec *pConstraint;
+ CLookUpHash *pHashTable = m_pLookUpHashs[TBL_GenericParamConstraint];
+ RID ridParam = RidFromToken(tkParam);
+ _ASSERTE(TypeFromToken(tkParam) == mdtGenericParam);
+
+ // Extract the rid part of the token for comparison below. Be sure
+ // that the column is a RID column, so that getGPCFGP() returns a RID.
+ _ASSERTE(IsRidType(m_TableDefs[TBL_GenericParamConstraint].m_pColDefs[GenericParamConstraintRec::COL_Owner].m_Type));
+
+ if (IsSorted(TBL_GenericParamConstraint))
+ {
+ IfFailGo(getGenericParamConstraintsForGenericParam(ridParam, &ridEnd, &ridStart));
+ HENUMInternal::InitSimpleEnum(mdtGenericParamConstraint, ridStart, ridEnd, phEnum);
+ }
+ else if (pHashTable)
+ {
+ TOKENHASHENTRY *p;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ iHash = HashToken(tkParam);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pHashTable->FindFirst(iHash, pos);
+ p;
+ p = pHashTable->FindNext(pos))
+ {
+ IfFailGo(GetGenericParamConstraintRecord(p->tok, &pConstraint));
+ if (getOwnerOfGenericParamConstraint(pConstraint) == tkParam)
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnum, TokenFromRid(p->tok, mdtGenericParamConstraint)) );
+ }
+ }
+ }
+ else
+ {
+ // linear search
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+ for (index = 1; index <= getCountGenericParamConstraints(); index++)
+ {
+ IfFailGo(GetGenericParamConstraintRecord(index, &pConstraint));
+ if (getOwnerOfGenericParamConstraint(pConstraint) == tkParam)
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(phEnum, TokenFromRid(index, mdtGenericParamConstraint)) );
+ }
+ }
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindGenericParamConstraintHelper
+
+
+//*****************************************************************************
+// Find helper for a ClassLayout. This will trigger ClassLayout table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindClassLayoutHelper( // return index to the ClassLayout table
+ mdTypeDef tkParent, // Parent token.
+ RID *pFoundRid)
+{
+ _ASSERTE(TypeFromToken(tkParent) == mdtTypeDef);
+
+ // If sorted, use the faster lookup
+ if (IsSorted(TBL_ClassLayout))
+ {
+ return FindClassLayoutFor(RidFromToken(tkParent), pFoundRid);
+ }
+ return GenericFindWithHash(TBL_ClassLayout, ClassLayoutRec::COL_Parent, tkParent, pFoundRid);
+} // CMiniMdRW::FindClassLayoutHelper
+
+//*****************************************************************************
+// Find helper for a FieldLayout. This will trigger FieldLayout table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindFieldLayoutHelper( // return index to the FieldLayout table
+ mdFieldDef tkField, // Field RID.
+ RID *pFoundRid)
+{
+ _ASSERTE(TypeFromToken(tkField) == mdtFieldDef);
+
+ // If sorted, use the faster lookup
+ if (IsSorted(TBL_FieldLayout))
+ {
+ return FindFieldLayoutFor(RidFromToken(tkField), pFoundRid);
+ }
+ return GenericFindWithHash(TBL_FieldLayout, FieldLayoutRec::COL_Field, tkField, pFoundRid);
+} // CMiniMdRW::FindFieldLayoutHelper
+
+//*****************************************************************************
+// Find helper for a ImplMap. This will trigger ImplMap table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindImplMapHelper( // return index to the ImplMap table
+ mdToken tk, // Member forwarded token.
+ RID *pFoundRid)
+{
+ _ASSERTE(TypeFromToken(tk) != 0);
+
+ // If sorted, use the faster lookup
+ if (IsSorted(TBL_ImplMap))
+ {
+ return FindImplMapFor(RidFromToken(tk), TypeFromToken(tk), pFoundRid);
+ }
+ return GenericFindWithHash(TBL_ImplMap, ImplMapRec::COL_MemberForwarded, tk, pFoundRid);
+} // CMiniMdRW::FindImplMapHelper
+
+
+//*****************************************************************************
+// Find helper for a FieldRVA. This will trigger FieldRVA table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindFieldRVAHelper( // return index to the FieldRVA table
+ mdFieldDef tkField, // Field token.
+ RID *pFoundRid)
+{
+ _ASSERTE(TypeFromToken(tkField) == mdtFieldDef);
+
+ // If sorted, use the faster lookup
+ if (IsSorted(TBL_FieldRVA))
+ {
+ return FindFieldRVAFor(RidFromToken(tkField), pFoundRid);
+ }
+ return GenericFindWithHash(TBL_FieldRVA, FieldRVARec::COL_Field, tkField, pFoundRid);
+} // CMiniMdRW::FindFieldRVAHelper
+
+//*****************************************************************************
+// Find helper for a NestedClass. This will trigger NestedClass table to be sorted if it is not.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindNestedClassHelper( // return index to the NestedClass table
+ mdTypeDef tkClass, // NestedClass RID.
+ RID *pFoundRid)
+{
+ // If sorted, use the faster lookup
+ if (IsSorted(TBL_NestedClass))
+ {
+ return FindNestedClassFor(RidFromToken(tkClass), pFoundRid);
+ }
+ return GenericFindWithHash(TBL_NestedClass, NestedClassRec::COL_NestedClass, tkClass, pFoundRid);
+} // CMiniMdRW::FindNestedClassHelper
+
+
+//*************************************************************************
+// generic find helper with hash table
+//*************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GenericFindWithHash( // Return code.
+ ULONG ixTbl, // Table with hash
+ ULONG ixCol, // col that we hash.
+ mdToken tkTarget, // token to be find in the hash
+ RID *pFoundRid)
+{
+ HRESULT hr;
+ ULONG index;
+ mdToken tkHash;
+ BYTE * pRec;
+
+ // Partial check -- only one rid for table 0, so if type is 0, rid should be 1.
+ _ASSERTE(TypeFromToken(tkTarget) != 0 || RidFromToken(tkTarget) == 1);
+
+ if (m_pLookUpHashs[ixTbl] == NULL)
+ {
+ // Just ignore the returned error - the hash is either created or not
+ (void)GenericBuildHashTable(ixTbl, ixCol);
+ }
+
+ CLookUpHash * pHashTable = m_pLookUpHashs[ixTbl];
+ if (pHashTable != NULL)
+ {
+ TOKENHASHENTRY *p;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ iHash = HashToken(tkTarget);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pHashTable->FindFirst(iHash, pos);
+ p;
+ p = pHashTable->FindNext(pos))
+ {
+ IfFailRet(m_Tables[ixTbl].GetRecord(p->tok, &pRec));
+
+ // get the column value that we will hash
+ tkHash = GetToken(ixTbl, ixCol, pRec);
+ if (tkHash == tkTarget)
+ {
+ // found the match
+ *pFoundRid = p->tok;
+ return S_OK;
+ }
+ }
+ }
+ else
+ {
+ // linear search
+ for (index = 1; index <= GetCountRecs(ixTbl); index++)
+ {
+ IfFailRet(m_Tables[ixTbl].GetRecord(index, &pRec));
+ tkHash = GetToken(ixTbl, ixCol, pRec);
+ if (tkHash == tkTarget)
+ {
+ // found the match
+ *pFoundRid = index;
+ return S_OK;
+ }
+ }
+ }
+ *pFoundRid = 0;
+ return S_OK;
+} // CMiniMdRW::GenericFindWithHash
+
+//*************************************************************************
+// Build a hash table for the specified table if the size exceed the thresholds.
+//*************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GenericBuildHashTable(
+ ULONG ixTbl, // Table with hash.
+ ULONG ixCol) // Column that we hash.
+{
+ HRESULT hr = S_OK;
+ BYTE *pRec;
+ mdToken tkHash;
+ ULONG iHash;
+ TOKENHASHENTRY *pEntry;
+
+ // If the hash table hasn't been built it, see if it should get faulted in.
+ if (m_pLookUpHashs[ixTbl] == NULL)
+ {
+ ULONG ridEnd = GetCountRecs(ixTbl);
+
+ //<TODO>@FUTURE: we need to init the size of the hash table corresponding to the current
+ // size of table in E&C's case.
+ //</TODO>
+ // Avoid prefast warning with "if (ridEnd + 1 > INDEX_ROW_COUNT_THRESHOLD)"
+ if (ridEnd > INDEX_ROW_COUNT_THRESHOLD - 1)
+ {
+ // Create a new hash.
+ NewHolder<CLookUpHash> pHashTable = new (nothrow) CLookUpHash;
+ IfNullGo(pHashTable);
+ IfFailGo(pHashTable->NewInit(
+ g_HashSize[GetMetaDataSizeIndex(&m_OptionValue)]));
+
+ // Scan every entry already in the table, add it to the hash.
+ for (ULONG index = 1; index <= ridEnd; index++)
+ {
+ IfFailGo(m_Tables[ixTbl].GetRecord(index, &pRec));
+
+ // get the column value that we will hash
+ tkHash = GetToken(ixTbl, ixCol, pRec);
+
+ // hash the value
+ iHash = HashToken(tkHash);
+
+ pEntry = pHashTable->Add(iHash);
+ IfNullGo(pEntry);
+ pEntry->tok = index;
+
+ }
+
+ if (InterlockedCompareExchangeT<CLookUpHash *>(
+ &m_pLookUpHashs[ixTbl],
+ pHashTable,
+ NULL) == NULL)
+ { // We won the initializaion race
+ pHashTable.SuppressRelease();
+ }
+ }
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::GenericBuildHashTable
+
+//*************************************************************************
+// Add a rid from a table into a hash. We will hash on the ixCol of the ixTbl.
+//*************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GenericAddToHash(
+ ULONG ixTbl, // Table with hash
+ ULONG ixCol, // column that we hash by calling HashToken.
+ RID rid) // Token of new guy into the ixTbl.
+{
+ HRESULT hr = S_OK;
+ CLookUpHash *pHashTable = m_pLookUpHashs[ixTbl];
+ void *pRec;
+ mdToken tkHash;
+ ULONG iHash;
+ TOKENHASHENTRY *pEntry;
+
+ // If the hash table hasn't been built it, see if it should get faulted in.
+ if (pHashTable == NULL)
+ {
+ IfFailGo(GenericBuildHashTable(ixTbl, ixCol));
+ }
+ else
+ {
+ // Adding into hash table has to be protected by write-lock
+ INDEBUG(Debug_CheckIsLockedForWrite();)
+
+ IfFailGo(m_Tables[ixTbl].GetRecord(rid, reinterpret_cast<BYTE **>(&pRec)));
+
+ tkHash = GetToken(ixTbl, ixCol, pRec);
+ iHash = HashToken(tkHash);
+ pEntry = pHashTable->Add(iHash);
+ IfNullGo(pEntry);
+ pEntry->tok = rid;
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::GenericAddToHash
+
+
+//*****************************************************************************
+// look up a table by a col given col value is ulVal.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::LookUpTableByCol(
+ ULONG ulVal, // Value for which to search.
+ VirtualSort *pVSTable, // A VirtualSort on the table, if any.
+ RID *pRidStart, // Put RID of first match here.
+ RID *pRidEnd) // [OPTIONAL] Put RID of end match here.
+{
+ HRESULT hr = NOERROR;
+ ULONG ixTbl;
+ ULONG ixCol;
+
+ _ASSERTE(pVSTable != NULL);
+ ixTbl = pVSTable->m_ixTbl;
+ ixCol = pVSTable->m_ixCol;
+ if (IsSorted(ixTbl))
+ {
+ // Table itself is sorted so we don't need to build a virtual sort table.
+ // Binary search on the table directly.
+ //
+ IfFailGo(SearchTableForMultipleRows(
+ ixTbl,
+ m_TableDefs[ixTbl].m_pColDefs[ixCol],
+ ulVal,
+ pRidEnd,
+ pRidStart));
+ }
+ else
+ {
+ if (!pVSTable->m_isMapValid)
+ {
+ INDEBUG(Debug_CheckIsLockedForWrite();)
+
+ int iCount;
+
+ // build the parallel VirtualSort table
+ if (pVSTable->m_pMap == NULL)
+ {
+ // the first time that we build the VS table. We need to allocate the TOKENMAP
+ pVSTable->m_pMap = new (nothrow) TOKENMAP;
+ IfNullGo(pVSTable->m_pMap);
+ }
+
+ // ensure the look up table is big enough
+ iCount = pVSTable->m_pMap->Count();
+ if (pVSTable->m_pMap->AllocateBlock(m_Schema.m_cRecs[ixTbl] + 1 - iCount) == 0)
+ {
+ IfFailGo(E_OUTOFMEMORY);
+ }
+
+ // now build the table
+ // Element 0 of m_pMap will never be used, its just being initialized anyway.
+ for (ULONG i = 0; i <= m_Schema.m_cRecs[ixTbl]; i++)
+ {
+ *(pVSTable->m_pMap->Get(i)) = i;
+ }
+ // sort the table
+ IfFailGo(pVSTable->Sort());
+ }
+ // binary search on the LookUp
+ {
+ void *pRow; // Row from a table.
+ ULONG val; // Value from a row.
+ CMiniColDef *pCol;
+ int lo,hi,mid=0; // binary search indices.
+ RID ridEnd, ridBegin;
+
+ pCol = m_TableDefs[ixTbl].m_pColDefs;
+
+ // Start with entire table.
+ lo = 1;
+ hi = GetCountRecs( ixTbl );
+ // While there are rows in the range...
+ while ( lo <= hi )
+ { // Look at the one in the middle.
+ mid = (lo + hi) / 2;
+ IfFailGo(getRow(
+ ixTbl,
+ (UINT32)*(pVSTable->m_pMap->Get(mid)),
+ &pRow));
+ val = getIX( pRow, pCol[ixCol] );
+
+ // If equal to the target, done.
+ if ( val == ulVal )
+ break;
+ // If middle item is too small, search the top half.
+ if ( val < ulVal )
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ if ( lo > hi )
+ {
+ // Didn't find anything that matched.
+ *pRidStart = 0;
+ if (pRidEnd) *pRidEnd = 0;
+ goto ErrExit;
+ }
+
+
+ // Now mid is pointing to one of the several records that match the search.
+ // Find the beginning and find the end.
+ ridBegin = mid;
+
+ // End will be at least one larger than found record.
+ ridEnd = ridBegin + 1;
+
+ // Search back to start of group.
+ for (;;)
+ {
+ if (ridBegin <= 1)
+ {
+ break;
+ }
+ IfFailGo(getRow(
+ ixTbl,
+ (UINT32)*(pVSTable->m_pMap->Get(ridBegin-1)),
+ &pRow));
+ if (getIX(pRow, pCol[ixCol]) != ulVal)
+ {
+ break;
+ }
+ --ridBegin;
+ }
+
+ // If desired, search forward to end of group.
+ if (pRidEnd != NULL)
+ {
+ for (;;)
+ {
+ if (ridEnd > GetCountRecs(ixTbl))
+ {
+ break;
+ }
+ IfFailGo(getRow(
+ ixTbl,
+ (UINT32)*(pVSTable->m_pMap->Get(ridEnd)),
+ &pRow));
+ if (getIX(pRow, pCol[ixCol]) != ulVal)
+ {
+ break;
+ }
+ ++ridEnd;
+ }
+ *pRidEnd = ridEnd;
+ }
+ *pRidStart = ridBegin;
+ }
+ }
+
+ // fall through
+ErrExit:
+ return hr;
+} // CMiniMdRW::LookUpTableByCol
+
+__checkReturn
+HRESULT
+CMiniMdRW::Impl_SearchTableRW(
+ ULONG ixTbl, // Table to search.
+ ULONG ixCol, // Column to search.
+ ULONG ulTarget, // Value to search for.
+ RID *pFoundRid)
+{
+ HRESULT hr = S_OK;
+ RID iRid; // The resulting RID.
+ RID iRidEnd; // Unused.
+
+ // Look up.
+ hr = LookUpTableByCol(ulTarget, m_pVS[ixTbl], &iRid, &iRidEnd);
+ if (FAILED(hr))
+ {
+ iRid = 0;
+ }
+ else // Convert to real RID.
+ {
+ iRid = GetRidFromVirtualSort(ixTbl, iRid);
+ }
+
+ *pFoundRid = iRid;
+ return S_OK;
+} // CMiniMdRW::Impl_SearchTableRW
+
+//*****************************************************************************
+// Search a table for the row containing the given key value.
+// EG. Constant table has pointer back to Param or Field.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::vSearchTable( // RID of matching row, or 0.
+ ULONG ixTbl, // Table to search.
+ CMiniColDef sColumn, // Sorted key column, containing search value.
+ ULONG ulTarget, // Target for search.
+ RID *pRid)
+{
+ HRESULT hr;
+ void *pRow; // Row from a table.
+ ULONG val; // Value from a row.
+
+ int lo,mid,hi; // binary search indices.
+
+ // Binary search requires sorted table.
+ // @todo GENERICS: why is IsSorted not true for mdtGenericParam?
+ // _ASSERTE(IsSorted(ixTbl));
+
+ // Start with entire table.
+ lo = 1;
+ hi = GetCountRecs(ixTbl);
+ // While there are rows in the range...
+ while (lo <= hi)
+ { // Look at the one in the middle.
+ mid = (lo + hi) / 2;
+ IfFailRet(getRow(ixTbl, mid, &pRow));
+ val = getIX(pRow, sColumn);
+ // If equal to the target, done.
+ if (val == ulTarget)
+ {
+ *pRid = mid;
+ return S_OK;
+ }
+ // If middle item is too small, search the top half.
+ if (val < ulTarget || val == END_OF_TABLE)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ // Didn't find anything that matched.
+
+ // @todo GENERICS: Work around for refEmit feature. Remove once table is sorted.
+ if (ixTbl == TBL_GenericParam && !IsSorted(ixTbl))
+ {
+ for (int i = 1; i <= (int)GetCountRecs(ixTbl); i ++)
+ {
+ IfFailRet(getRow(ixTbl, i, &pRow));
+ if (getIX(pRow, sColumn) == ulTarget)
+ {
+ *pRid = i;
+ return S_OK;
+ }
+ }
+ }
+
+ *pRid = 0;
+ return S_OK;
+} // CMiniMdRW::vSearchTable
+
+//*****************************************************************************
+// Search a table for the highest-RID row containing a value that is less than
+// or equal to the target value. EG. TypeDef points to first Field, but if
+// a TypeDef has no fields, it points to first field of next TypeDef.
+// This is complicated by the possible presence of columns containing
+// END_OF_TABLE values, which are not necessarily in greater than
+// other values. However, this invalid-rid value will occur only at the
+// end of the table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::vSearchTableNotGreater( // RID of matching row, or 0.
+ ULONG ixTbl, // Table to search.
+ CMiniColDef sColumn, // the column def containing search value
+ ULONG ulTarget, // target for search
+ RID *pRid)
+{
+ HRESULT hr;
+ void *pRow; // Row from a table.
+ ULONG cRecs; // Rows in the table.
+ ULONG val = 0; // Value from a table.
+ ULONG lo,mid=0,hi; // binary search indices.
+
+ cRecs = GetCountRecs(ixTbl);
+
+ // Start with entire table.
+ lo = 1;
+ hi = cRecs;
+ // If no recs, return.
+ if (lo > hi)
+ {
+ *pRid = 0;
+ return S_OK;
+ }
+ // While there are rows in the range...
+ while (lo <= hi)
+ { // Look at the one in the middle.
+ mid = (lo + hi) / 2;
+ IfFailRet(getRow(ixTbl, mid, &pRow));
+ val = getIX(pRow, sColumn);
+ // If equal to the target, done searching.
+ if (val == ulTarget)
+ break;
+ // If middle item is too small, search the top half.
+ if (val < ulTarget && val != END_OF_TABLE)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ // May or may not have found anything that matched. Mid will be close, but may
+ // be to high or too low. It should point to the highest acceptable
+ // record.
+
+ // If the value is greater than the target, back up just until the value is
+ // less than or equal to the target. SHOULD only be one step.
+ if (val > ulTarget || val == END_OF_TABLE)
+ {
+ while (val > ulTarget || val == END_OF_TABLE)
+ {
+ _ASSERTE(mid > 1);
+ // If no recs match, return.
+ if (mid == 1)
+ {
+ *pRid = 0;
+ return S_OK;
+ }
+ --mid;
+ IfFailRet(getRow(ixTbl, mid, &pRow));
+ val = getIX(pRow, sColumn);
+ }
+ }
+ else
+ {
+ // Value is less than or equal to the target. As long as the next
+ // record is also acceptable, move forward.
+ while (mid < cRecs)
+ {
+ // There is another record. Get its value.
+ IfFailRet(getRow(ixTbl, mid+1, &pRow));
+ val = getIX(pRow, sColumn);
+ // If that record is too high, stop.
+ if (val > ulTarget || val == END_OF_TABLE)
+ break;
+ mid++;
+ }
+ }
+
+ // Return the value that's just less than the target.
+ *pRid = mid;
+ return S_OK;
+} // CMiniMdRW::vSearchTableNotGreater
+
+//---------------------------------------------------------------------------------------
+//
+// Create MemberRef hash table.
+//
+__checkReturn
+HRESULT
+CMiniMdRW::CreateMemberRefHash()
+{
+ HRESULT hr = S_OK;
+
+ if (m_pMemberRefHash == NULL)
+ {
+ ULONG ridEnd = getCountMemberRefs();
+ if (ridEnd + 1 > INDEX_ROW_COUNT_THRESHOLD)
+ {
+ // Create a new hash.
+ NewHolder<CMemberRefHash> pMemberRefHash = new (nothrow) CMemberRefHash();
+ IfNullGo(pMemberRefHash);
+ IfFailGo(pMemberRefHash->NewInit(
+ g_HashSize[GetMetaDataSizeIndex(&m_OptionValue)]));
+
+ // Scan every entry already in the table, add it to the hash.
+ for (ULONG index = 1; index <= ridEnd; index++)
+ {
+ MemberRefRec * pMemberRef;
+ IfFailGo(GetMemberRefRecord(index, &pMemberRef));
+
+ LPCSTR szMemberRefName;
+ IfFailGo(getNameOfMemberRef(pMemberRef, &szMemberRefName));
+ ULONG iHash = HashMemberRef(
+ getClassOfMemberRef(pMemberRef),
+ szMemberRefName);
+
+ TOKENHASHENTRY * pEntry = pMemberRefHash->Add(iHash);
+ IfNullGo(pEntry);
+ pEntry->tok = TokenFromRid(index, mdtMemberRef);
+ }
+
+ if (InterlockedCompareExchangeT<CMemberRefHash *>(&m_pMemberRefHash, pMemberRefHash, NULL) == NULL)
+ { // We won the initialization race
+ pMemberRefHash.SuppressRelease();
+ }
+ }
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::CreateMemberRefHash
+
+//---------------------------------------------------------------------------------------
+//
+// Add a new MemberRef to the hash table.
+//
+__checkReturn
+HRESULT
+CMiniMdRW::AddMemberRefToHash(
+ mdMemberRef mr) // Token of new guy.
+{
+ HRESULT hr = S_OK;
+
+ // If the hash exists, we will add to it - requires write-lock
+ INDEBUG(Debug_CheckIsLockedForWrite();)
+
+ // If the hash table hasn't been built it, see if it should get faulted in.
+ if (m_pMemberRefHash == NULL)
+ {
+ IfFailGo(CreateMemberRefHash());
+ }
+ else
+ {
+ MemberRefRec * pMemberRef;
+ IfFailGo(GetMemberRefRecord(RidFromToken(mr), &pMemberRef));
+
+ LPCSTR szMemberRefName;
+ IfFailGo(getNameOfMemberRef(pMemberRef, &szMemberRefName));
+ ULONG iHash = HashMemberRef(
+ getClassOfMemberRef(pMemberRef),
+ szMemberRefName);
+
+ TOKENHASHENTRY * pEntry = m_pMemberRefHash->Add(iHash);
+ IfNullGo(pEntry);
+ pEntry->tok = TokenFromRid(RidFromToken(mr), mdtMemberRef);
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddMemberRefToHash
+
+//---------------------------------------------------------------------------------------
+//
+// If the hash is built, search for the item. Ignore token *ptkMemberRef.
+//
+CMiniMdRW::HashSearchResult
+CMiniMdRW::FindMemberRefFromHash(
+ mdToken tkParent, // Parent token.
+ LPCUTF8 szName, // Name of item.
+ PCCOR_SIGNATURE pvSigBlob, // Signature.
+ ULONG cbSigBlob, // Size of signature.
+ mdMemberRef * ptkMemberRef) // IN: Ignored token. OUT: Return if found.
+{
+ // If the table is there, look for the item in the chain of items.
+ if (m_pMemberRefHash != NULL)
+ {
+ TOKENHASHENTRY * p;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ iHash = HashMemberRef(tkParent, szName);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = m_pMemberRefHash->FindFirst(iHash, pos);
+ p != NULL;
+ p = m_pMemberRefHash->FindNext(pos))
+ {
+ if ((CompareMemberRefs(p->tok, tkParent, szName, pvSigBlob, cbSigBlob) == S_OK)
+ && (*ptkMemberRef != p->tok))
+ {
+ *ptkMemberRef = p->tok;
+ return Found;
+ }
+ }
+
+ return NotFound;
+ }
+ else
+ {
+ return NoTable;
+ }
+} // CMiniMdRW::FindMemberRefFromHash
+
+//*****************************************************************************
+// Check a given mr token to see if this one is a match.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::CompareMemberRefs( // S_OK match, S_FALSE no match.
+ mdMemberRef mr, // Token to check.
+ mdToken tkPar, // Parent token.
+ LPCUTF8 szNameUtf8, // Name of item.
+ PCCOR_SIGNATURE pvSigBlob, // Signature.
+ ULONG cbSigBlob) // Size of signature.
+{
+ HRESULT hr;
+ MemberRefRec *pMemberRef;
+ LPCUTF8 szNameUtf8Tmp;
+ PCCOR_SIGNATURE pvSigBlobTmp;
+ ULONG cbSigBlobTmp;
+
+ IfFailRet(GetMemberRefRecord(RidFromToken(mr), &pMemberRef));
+ if (!IsNilToken(tkPar))
+ {
+ // If caller specifies the tkPar and tkPar doesn't match,
+ // try the next memberref.
+ //
+ if (tkPar != getClassOfMemberRef(pMemberRef))
+ return S_FALSE;
+ }
+
+ IfFailRet(getNameOfMemberRef(pMemberRef, &szNameUtf8Tmp));
+ if (strcmp(szNameUtf8Tmp, szNameUtf8) == 0)
+ {
+ if (pvSigBlob == NULL)
+ {
+ return S_OK;
+ }
+
+ // Name matched. Now check the signature if caller supplies signature
+ //
+ if ((cbSigBlob != 0) && (pvSigBlob != NULL))
+ {
+ IfFailRet(getSignatureOfMemberRef(pMemberRef, &pvSigBlobTmp, &cbSigBlobTmp));
+ if ((cbSigBlobTmp == cbSigBlob) &&
+ (memcmp(pvSigBlob, pvSigBlobTmp, cbSigBlob) == 0))
+ {
+ return S_OK;
+ }
+ }
+ }
+ return S_FALSE;
+} // CMiniMdRW::CompareMemberRefs
+
+
+//*****************************************************************************
+// Add a new memberdef to the hash table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddMemberDefToHash(
+ mdToken tkMember, // Token of new guy. It can be MethodDef or FieldDef
+ mdToken tkParent) // Parent token.
+{
+ HRESULT hr = S_OK;
+ ULONG iHash;
+ MEMBERDEFHASHENTRY * pEntry;
+
+ // If the hash exists, we will add to it - requires write-lock
+ INDEBUG(Debug_CheckIsLockedForWrite();)
+
+ // If the hash table hasn't been built it, see if it should get faulted in.
+ if (m_pMemberDefHash == NULL)
+ {
+ IfFailGo(CreateMemberDefHash());
+ }
+ else
+ {
+ LPCSTR szName;
+ if (TypeFromToken(tkMember) == mdtMethodDef)
+ {
+ MethodRec * pMethodRecord;
+ IfFailGo(GetMethodRecord(RidFromToken(tkMember), &pMethodRecord));
+ IfFailGo(getNameOfMethod(pMethodRecord, &szName));
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(tkMember) == mdtFieldDef);
+ FieldRec * pFieldRecord;
+ IfFailGo(GetFieldRecord(RidFromToken(tkMember), &pFieldRecord));
+ IfFailGo(getNameOfField(pFieldRecord, &szName));
+ }
+
+ iHash = HashMemberDef(tkParent, szName);
+
+ pEntry = m_pMemberDefHash->Add(iHash);
+ IfNullGo(pEntry);
+ pEntry->tok = tkMember;
+ pEntry->tkParent = tkParent;
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddMemberDefToHash
+
+
+//*****************************************************************************
+// Create MemberDef Hash
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::CreateMemberDefHash()
+{
+ HRESULT hr = S_OK;
+ ULONG iHash;
+ MEMBERDEFHASHENTRY * pEntry;
+
+ // If the hash table hasn't been built it, see if it should get faulted in.
+ if (m_pMemberDefHash == NULL)
+ {
+ ULONG ridMethod = getCountMethods();
+ ULONG ridField = getCountFields();
+ ULONG iType;
+ ULONG ridStart;
+ ULONG ridEnd;
+ TypeDefRec * pRec;
+ MethodRec * pMethod;
+ FieldRec * pField;
+
+ if ((ridMethod + ridField + 1) > INDEX_ROW_COUNT_THRESHOLD)
+ {
+ // Create a new hash.
+ NewHolder<CMemberDefHash> pMemberDefHash = new (nothrow) CMemberDefHash();
+ IfNullGo(pMemberDefHash);
+ IfFailGo(pMemberDefHash->NewInit(
+ g_HashSize[GetMetaDataSizeIndex(&m_OptionValue)]));
+
+ for (iType = 1; iType <= getCountTypeDefs(); iType++)
+ {
+ IfFailGo(GetTypeDefRecord(iType, &pRec));
+ ridStart = getMethodListOfTypeDef(pRec);
+ IfFailGo(getEndMethodListOfTypeDef(iType, &ridEnd));
+
+ // add all of the methods of this typedef into hash table
+ for (; ridStart < ridEnd; ridStart++)
+ {
+ RID methodRid;
+ IfFailGo(GetMethodRid(ridStart, &methodRid));
+ IfFailGo(GetMethodRecord(methodRid, &pMethod));
+ LPCSTR szMethodName;
+ IfFailGo(getNameOfMethod(pMethod, &szMethodName));
+ iHash = HashMemberDef(TokenFromRid(iType, mdtTypeDef), szMethodName);
+
+ pEntry = pMemberDefHash->Add(iHash);
+ if (pEntry == NULL)
+ IfFailGo(OutOfMemory());
+ pEntry->tok = TokenFromRid(methodRid, mdtMethodDef);
+ pEntry->tkParent = TokenFromRid(iType, mdtTypeDef);
+ }
+
+ // add all of the fields of this typedef into hash table
+ ridStart = getFieldListOfTypeDef(pRec);
+ IfFailGo(getEndFieldListOfTypeDef(iType, &ridEnd));
+
+ // Scan every entry already in the Method table, add it to the hash.
+ for (; ridStart < ridEnd; ridStart++)
+ {
+ RID fieldRid;
+ IfFailGo(GetFieldRid(ridStart, &fieldRid));
+ IfFailGo(GetFieldRecord(fieldRid, &pField));
+ LPCSTR szFieldName;
+ IfFailGo(getNameOfField(pField, &szFieldName));
+ iHash = HashMemberDef(TokenFromRid(iType, mdtTypeDef), szFieldName);
+
+ pEntry = pMemberDefHash->Add(iHash);
+ IfNullGo(pEntry);
+ pEntry->tok = TokenFromRid(fieldRid, mdtFieldDef);
+ pEntry->tkParent = TokenFromRid(iType, mdtTypeDef);
+ }
+ }
+
+ if (InterlockedCompareExchangeT<CMemberDefHash *>(&m_pMemberDefHash, pMemberDefHash, NULL) == NULL)
+ { // We won the initialization race
+ pMemberDefHash.SuppressRelease();
+ }
+ }
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::CreateMemberDefHash
+
+//---------------------------------------------------------------------------------------
+//
+// If the hash is built, search for the item. Ignore token *ptkMember.
+//
+CMiniMdRW::HashSearchResult
+CMiniMdRW::FindMemberDefFromHash(
+ mdToken tkParent, // Parent token.
+ LPCUTF8 szName, // Name of item.
+ PCCOR_SIGNATURE pvSigBlob, // Signature.
+ ULONG cbSigBlob, // Size of signature.
+ mdToken * ptkMember) // IN: Ignored token. OUT: Return if found. It can be MethodDef or FieldDef
+{
+ // check to see if we need to create hash table
+ if (m_pMemberDefHash == NULL)
+ {
+ // Ignore the failure - the hash won't be created in the worst case
+ (void)CreateMemberDefHash();
+ }
+
+ // If the table is there, look for the item in the chain of items.
+ if (m_pMemberDefHash != NULL)
+ {
+ MEMBERDEFHASHENTRY * pEntry;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ iHash = HashMemberDef(tkParent, szName);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (pEntry = m_pMemberDefHash->FindFirst(iHash, pos);
+ pEntry != NULL;
+ pEntry = m_pMemberDefHash->FindNext(pos))
+ {
+ if ((CompareMemberDefs(pEntry->tok, pEntry->tkParent, tkParent, szName, pvSigBlob, cbSigBlob) == S_OK)
+ && (pEntry->tok != *ptkMember))
+ {
+ *ptkMember = pEntry->tok;
+ return Found;
+ }
+ }
+
+ return NotFound;
+ }
+ else
+ {
+ return NoTable;
+ }
+} // CMiniMdRW::FindMemberDefFromHash
+
+
+//*****************************************************************************
+// Check a given memberDef token to see if this one is a match.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::CompareMemberDefs( // S_OK match, S_FALSE no match.
+ mdToken tkMember, // Token to check. It can be MethodDef or FieldDef
+ mdToken tkParent, // Parent token recorded in the hash entry
+ mdToken tkPar, // Parent token.
+ LPCUTF8 szNameUtf8, // Name of item.
+ PCCOR_SIGNATURE pvSigBlob, // Signature.
+ ULONG cbSigBlob) // Size of signature.
+{
+ HRESULT hr;
+ MethodRec *pMethod;
+ FieldRec *pField;
+ LPCUTF8 szNameUtf8Tmp;
+ PCCOR_SIGNATURE pvSigBlobTmp;
+ ULONG cbSigBlobTmp;
+ bool bPrivateScope;
+
+ if (TypeFromToken(tkMember) == mdtMethodDef)
+ {
+ IfFailGo(GetMethodRecord(RidFromToken(tkMember), &pMethod));
+ IfFailGo(getNameOfMethod(pMethod, &szNameUtf8Tmp));
+ IfFailGo(getSignatureOfMethod(pMethod, &pvSigBlobTmp, &cbSigBlobTmp));
+ bPrivateScope = IsMdPrivateScope(getFlagsOfMethod(pMethod));
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(tkMember) == mdtFieldDef);
+ IfFailGo(GetFieldRecord(RidFromToken(tkMember), &pField));
+ IfFailGo(getNameOfField(pField, &szNameUtf8Tmp));
+ IfFailGo(getSignatureOfField(pField, &pvSigBlobTmp, &cbSigBlobTmp));
+ bPrivateScope = IsFdPrivateScope(getFlagsOfField(pField));
+ }
+ if (bPrivateScope || (tkPar != tkParent))
+ {
+ return S_FALSE;
+ }
+
+ if (strcmp(szNameUtf8Tmp, szNameUtf8) == 0)
+ {
+ if (pvSigBlob == NULL)
+ {
+ return S_OK;
+ }
+
+ // Name matched. Now check the signature if caller supplies signature
+ //
+ if ((cbSigBlob != 0) && (pvSigBlob != NULL))
+ {
+ if ((cbSigBlobTmp == cbSigBlob) &&
+ (memcmp(pvSigBlob, pvSigBlobTmp, cbSigBlob) == 0))
+ {
+ return S_OK;
+ }
+ }
+ }
+ hr = S_FALSE;
+ErrExit:
+ return hr;
+} // CMiniMdRW::CompareMemberDefs
+
+//*****************************************************************************
+// Add a new NamedItem to the hash table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddNamedItemToHash(
+ ULONG ixTbl, // Table with the new item.
+ mdToken tk, // Token of new guy.
+ LPCUTF8 szName, // Name of item.
+ mdToken tkParent) // Token of parent, if any.
+{
+ HRESULT hr = S_OK;
+ BYTE *pNamedItem; // A named item record.
+ LPCUTF8 szItem; // Name of the item.
+ mdToken tkPar = 0; // Parent token of the item.
+ ULONG iHash; // A named item's hash value.
+ TOKENHASHENTRY *pEntry; // New hash entry.
+
+ // If the hash table hasn't been built it, see if it should get faulted in.
+ if (m_pNamedItemHash == NULL)
+ {
+ ULONG ridEnd = GetCountRecs(ixTbl);
+ // Range check avoiding prefast warning with: "if (ridEnd + 1 > INDEX_ROW_COUNT_THRESHOLD)"
+ if (ridEnd > (INDEX_ROW_COUNT_THRESHOLD - 1))
+ {
+ // This assert causes Dev11 #65887, turn it on when the bug is fixed
+ //INDEBUG(Debug_CheckIsLockedForWrite();)
+
+ // OutputDebugStringA("Creating TypeRef hash\n");
+ // Create a new hash.
+ m_pNamedItemHash = new (nothrow) CMetaDataHashBase;
+ IfNullGo(m_pNamedItemHash);
+ IfFailGo(m_pNamedItemHash->NewInit(
+ g_HashSize[GetMetaDataSizeIndex(&m_OptionValue)]));
+
+ // Scan every entry already in the table, add it to the hash.
+ for (ULONG index = 1; index <= ridEnd; index++)
+ {
+ IfFailGo(m_Tables[ixTbl].GetRecord(index, &pNamedItem));
+ IfFailGo(getString(GetCol(ixTbl, g_TblIndex[ixTbl].m_iName, pNamedItem), &szItem));
+ if (g_TblIndex[ixTbl].m_iParent != (ULONG) -1)
+ tkPar = GetToken(ixTbl, g_TblIndex[ixTbl].m_iParent, pNamedItem);
+
+ iHash = HashNamedItem(tkPar, szItem);
+
+ pEntry = m_pNamedItemHash->Add(iHash);
+ IfNullGo(pEntry);
+ pEntry->tok = TokenFromRid(index, g_TblIndex[ixTbl].m_Token);
+ }
+ }
+ }
+ else
+ {
+ tk = RidFromToken(tk);
+ IfFailGo(m_Tables[ixTbl].GetRecord(tk, &pNamedItem));
+ IfFailGo(getString(GetCol(ixTbl, g_TblIndex[ixTbl].m_iName, pNamedItem), &szItem));
+ if (g_TblIndex[ixTbl].m_iParent != (ULONG)-1)
+ tkPar = GetToken(ixTbl, g_TblIndex[ixTbl].m_iParent, pNamedItem);
+
+ iHash = HashNamedItem(tkPar, szItem);
+
+ pEntry = m_pNamedItemHash->Add(iHash);
+ IfNullGo(pEntry);
+ pEntry->tok = TokenFromRid(tk, g_TblIndex[ixTbl].m_Token);
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddNamedItemToHash
+
+//*****************************************************************************
+// If the hash is built, search for the item.
+//*****************************************************************************
+CMiniMdRW::HashSearchResult
+CMiniMdRW::FindNamedItemFromHash(
+ ULONG ixTbl, // Table with the item.
+ LPCUTF8 szName, // Name of item.
+ mdToken tkParent, // Token of parent, if any.
+ mdToken * ptk) // Return if found.
+{
+ // If the table is there, look for the item in the chain of items.
+ if (m_pNamedItemHash != NULL)
+ {
+ TOKENHASHENTRY *p; // Hash entry from chain.
+ ULONG iHash; // Item's hash value.
+ int pos; // Position in hash chain.
+ mdToken type; // Type of the item being sought.
+
+ type = g_TblIndex[ixTbl].m_Token;
+
+ // Hash the data.
+ iHash = HashNamedItem(tkParent, szName);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = m_pNamedItemHash->FindFirst(iHash, pos);
+ p != NULL;
+ p = m_pNamedItemHash->FindNext(pos))
+ { // Check that the item is from the right table.
+ if (TypeFromToken(p->tok) != (ULONG)type)
+ {
+ //<TODO>@FUTURE: if using the named item hash for multiple tables, remove
+ // this check. Until then, debugging aid.</TODO>
+ _ASSERTE(!"Table mismatch in hash chain");
+ continue;
+ }
+ // Item is in the right table, do the deeper check.
+ if (CompareNamedItems(ixTbl, p->tok, szName, tkParent) == S_OK)
+ {
+ *ptk = p->tok;
+ return Found;
+ }
+ }
+
+ return NotFound;
+ }
+ else
+ {
+ return NoTable;
+ }
+} // CMiniMdRW::FindNamedItemFromHash
+
+//*****************************************************************************
+// Check a given mr token to see if this one is a match.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::CompareNamedItems( // S_OK match, S_FALSE no match.
+ ULONG ixTbl, // Table with the item.
+ mdToken tk, // Token to check.
+ LPCUTF8 szName, // Name of item.
+ mdToken tkParent) // Token of parent, if any.
+{
+ HRESULT hr;
+ BYTE *pNamedItem; // Item to check.
+ LPCUTF8 szNameUtf8Tmp; // Name of item to check.
+
+ // Get the record.
+ IfFailRet(m_Tables[ixTbl].GetRecord(RidFromToken(tk), &pNamedItem));
+
+ // Name is cheaper to get than coded token parent, and fails pretty quickly.
+ IfFailRet(getString(GetCol(ixTbl, g_TblIndex[ixTbl].m_iName, pNamedItem), &szNameUtf8Tmp));
+ if (strcmp(szNameUtf8Tmp, szName) != 0)
+ return S_FALSE;
+
+ // Name matched, try parent, if any.
+ if (g_TblIndex[ixTbl].m_iParent != (ULONG)-1)
+ {
+ mdToken tkPar = GetToken(ixTbl, g_TblIndex[ixTbl].m_iParent, pNamedItem);
+ if (tkPar != tkParent)
+ return S_FALSE;
+ }
+
+ // Made it to here, so everything matched.
+ return S_OK;
+} // CMiniMdRW::CompareNamedItems
+
+//*****************************************************************************
+// Add <md, td> entry to the MethodDef map look up table
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddMethodToLookUpTable(
+ mdMethodDef md,
+ mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ mdToken *ptk;
+ _ASSERTE((TypeFromToken(md) == mdtMethodDef) && HasIndirectTable(TBL_Method));
+
+ if (m_pMethodMap != NULL)
+ {
+ // Only add to the lookup table if it has been built already by demand.
+ //
+ // The first entry in the map is a dummy entry.
+ // The i'th index entry of the map is the td for methoddef of i.
+ // We do expect the methoddef tokens are all added when the map exist.
+ //
+ _ASSERTE(RidFromToken(md) == (ULONG)m_pMethodMap->Count());
+ INDEBUG(Debug_CheckIsLockedForWrite();)
+ ptk = m_pMethodMap->Append();
+ IfNullGo(ptk);
+ *ptk = td;
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddMethodToLookUpTable
+
+//*****************************************************************************
+// Add <fd, td> entry to the FieldDef map look up table
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddFieldToLookUpTable(
+ mdFieldDef fd,
+ mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ mdToken *ptk;
+ _ASSERTE((TypeFromToken(fd) == mdtFieldDef) && HasIndirectTable(TBL_Field));
+ if (m_pFieldMap != NULL)
+ {
+ // Only add to the lookup table if it has been built already by demand.
+ //
+ // The first entry in the map is a dummy entry.
+ // The i'th index entry of the map is the td for fielddef of i.
+ // We do expect the fielddef tokens are all added when the map exist.
+ //
+ _ASSERTE(RidFromToken(fd) == (ULONG)m_pFieldMap->Count());
+ ptk = m_pFieldMap->Append();
+ IfNullGo(ptk);
+ *ptk = td;
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddFieldToLookUpTable
+
+//*****************************************************************************
+// Add <pr, td> entry to the Property map look up table
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddPropertyToLookUpTable(
+ mdProperty pr,
+ mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ mdToken *ptk;
+ _ASSERTE((TypeFromToken(pr) == mdtProperty) && HasIndirectTable(TBL_Property));
+
+ if (m_pPropertyMap != NULL)
+ {
+ // Only add to the lookup table if it has been built already by demand.
+ //
+ // The first entry in the map is a dummy entry.
+ // The i'th index entry of the map is the td for property of i.
+ // We do expect the property tokens are all added when the map exist.
+ //
+ _ASSERTE(RidFromToken(pr) == (ULONG)m_pPropertyMap->Count());
+ ptk = m_pPropertyMap->Append();
+ IfNullGo(ptk);
+ *ptk = td;
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddPropertyToLookUpTable
+
+//*****************************************************************************
+// Add <ev, td> entry to the Event map look up table
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddEventToLookUpTable(
+ mdEvent ev,
+ mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ mdToken *ptk;
+ _ASSERTE((TypeFromToken(ev) == mdtEvent) && HasIndirectTable(TBL_Event));
+
+ if (m_pEventMap != NULL)
+ {
+ // Only add to the lookup table if it has been built already by demand.
+ //
+ // now add to the EventMap table
+ _ASSERTE(RidFromToken(ev) == (ULONG)m_pEventMap->Count());
+ ptk = m_pEventMap->Append();
+ IfNullGo(ptk);
+ *ptk = td;
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddEventToLookUpTable
+
+//*****************************************************************************
+// Add <pd, md> entry to the Param map look up table
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::AddParamToLookUpTable(
+ mdParamDef pd,
+ mdMethodDef md)
+{
+ HRESULT hr = NOERROR;
+ mdToken *ptk;
+ _ASSERTE((TypeFromToken(pd) == mdtParamDef) && HasIndirectTable(TBL_Param));
+
+ if (m_pParamMap != NULL)
+ {
+ // Only add to the lookup table if it has been built already by demand.
+ //
+ // now add to the EventMap table
+ _ASSERTE(RidFromToken(pd) == (ULONG)m_pParamMap->Count());
+ ptk = m_pParamMap->Append();
+ IfNullGo(ptk);
+ *ptk = md;
+ }
+ErrExit:
+ return hr;
+} // CMiniMdRW::AddParamToLookUpTable
+
+//*****************************************************************************
+// Find parent for a method token. This will use the lookup table if there is an
+// intermediate table. Or it will use FindMethodOfParent helper
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindParentOfMethodHelper(
+ mdMethodDef md, // [IN] the methoddef token
+ mdTypeDef *ptd) // [OUT] the parent token
+{
+ HRESULT hr = NOERROR;
+ if (HasIndirectTable(TBL_Method))
+ {
+ if (m_pMethodMap == NULL)
+ {
+ ULONG indexTd;
+ ULONG indexMd;
+ ULONG ridStart;
+ ULONG ridEnd;
+ TypeDefRec * pTypeDefRec;
+ MethodPtrRec * pMethodPtrRec;
+
+ // build the MethodMap table
+ NewHolder<TOKENMAP> pMethodMap = new (nothrow) TOKENMAP;
+ IfNullGo(pMethodMap);
+ ULONG nAllocateSize;
+ if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Method], 1, nAllocateSize))
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ if (pMethodMap->AllocateBlock(nAllocateSize) == 0)
+ IfFailGo(E_OUTOFMEMORY);
+ for (indexTd = 1; indexTd <= m_Schema.m_cRecs[TBL_TypeDef]; indexTd++)
+ {
+ IfFailGo(GetTypeDefRecord(indexTd, &pTypeDefRec));
+ ridStart = getMethodListOfTypeDef(pTypeDefRec);
+ IfFailGo(getEndMethodListOfTypeDef(indexTd, &ridEnd));
+
+ for (indexMd = ridStart; indexMd < ridEnd; indexMd++)
+ {
+ IfFailGo(GetMethodPtrRecord(indexMd, &pMethodPtrRec));
+ PREFIX_ASSUME(pMethodMap->Get(getMethodOfMethodPtr(pMethodPtrRec)) != NULL);
+ *(pMethodMap->Get(getMethodOfMethodPtr(pMethodPtrRec))) = indexTd;
+ }
+ }
+ if (InterlockedCompareExchangeT<TOKENMAP *>(
+ &m_pMethodMap,
+ pMethodMap,
+ NULL) == NULL)
+ { // We won the initializaion race
+ pMethodMap.SuppressRelease();
+ }
+ }
+ *ptd = *(m_pMethodMap->Get(RidFromToken(md)));
+ }
+ else
+ {
+ IfFailGo(FindParentOfMethod(RidFromToken(md), (RID *)ptd));
+ }
+ RidToToken(*ptd, mdtTypeDef);
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindParentOfMethodHelper
+
+//*****************************************************************************
+// Find parent for a field token. This will use the lookup table if there is an
+// intermediate table. Or it will use FindFieldOfParent helper
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindParentOfFieldHelper(
+ mdFieldDef fd, // [IN] fielddef token
+ mdTypeDef *ptd) // [OUT] parent token
+{
+ HRESULT hr = NOERROR;
+ if (HasIndirectTable(TBL_Field))
+ {
+ if (m_pFieldMap == NULL)
+ {
+ ULONG indexTd;
+ ULONG indexFd;
+ ULONG ridStart, ridEnd;
+ TypeDefRec *pTypeDefRec;
+ FieldPtrRec *pFieldPtrRec;
+
+ // build the FieldMap table
+ NewHolder<TOKENMAP> pFieldMap = new (nothrow) TOKENMAP;
+ IfNullGo(pFieldMap);
+ ULONG nAllocateSize;
+ if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Field], 1, nAllocateSize))
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ if (pFieldMap->AllocateBlock(nAllocateSize) == 0)
+ IfFailGo(E_OUTOFMEMORY);
+ for (indexTd = 1; indexTd<= m_Schema.m_cRecs[TBL_TypeDef]; indexTd++)
+ {
+ IfFailGo(GetTypeDefRecord(indexTd, &pTypeDefRec));
+ ridStart = getFieldListOfTypeDef(pTypeDefRec);
+ IfFailGo(getEndFieldListOfTypeDef(indexTd, &ridEnd));
+
+ for (indexFd = ridStart; indexFd < ridEnd; indexFd++)
+ {
+ IfFailGo(GetFieldPtrRecord(indexFd, &pFieldPtrRec));
+ PREFIX_ASSUME(pFieldMap->Get(getFieldOfFieldPtr(pFieldPtrRec)) != NULL);
+ *(pFieldMap->Get(getFieldOfFieldPtr(pFieldPtrRec))) = indexTd;
+ }
+ }
+ if (InterlockedCompareExchangeT<TOKENMAP *>(
+ &m_pFieldMap,
+ pFieldMap,
+ NULL) == NULL)
+ { // We won the initializaion race
+ pFieldMap.SuppressRelease();
+ }
+ }
+ *ptd = *(m_pFieldMap->Get(RidFromToken(fd)));
+ }
+ else
+ {
+ IfFailGo(FindParentOfField(RidFromToken(fd), (RID *)ptd));
+ }
+ RidToToken(*ptd, mdtTypeDef);
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindParentOfFieldHelper
+
+//*****************************************************************************
+// Find parent for a property token. This will use the lookup table if there is an
+// intermediate table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindParentOfPropertyHelper(
+ mdProperty pr,
+ mdTypeDef *ptd)
+{
+ HRESULT hr = NOERROR;
+ if (HasIndirectTable(TBL_Property))
+ {
+ if (m_pPropertyMap == NULL)
+ {
+ ULONG indexMap;
+ ULONG indexPr;
+ ULONG ridStart, ridEnd;
+ PropertyMapRec *pPropertyMapRec;
+ PropertyPtrRec *pPropertyPtrRec;
+
+ // build the PropertyMap table
+ NewHolder<TOKENMAP> pPropertyMap = new (nothrow) TOKENMAP;
+ IfNullGo(pPropertyMap);
+ ULONG nAllocateSize;
+ if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Property], 1, nAllocateSize))
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ if (pPropertyMap->AllocateBlock(nAllocateSize) == 0)
+ IfFailGo( E_OUTOFMEMORY );
+ for (indexMap = 1; indexMap<= m_Schema.m_cRecs[TBL_PropertyMap]; indexMap++)
+ {
+ IfFailGo(GetPropertyMapRecord(indexMap, &pPropertyMapRec));
+ ridStart = getPropertyListOfPropertyMap(pPropertyMapRec);
+ IfFailGo(getEndPropertyListOfPropertyMap(indexMap, &ridEnd));
+
+ for (indexPr = ridStart; indexPr < ridEnd; indexPr++)
+ {
+ IfFailGo(GetPropertyPtrRecord(indexPr, &pPropertyPtrRec));
+ mdToken *tok = pPropertyMap->Get(getPropertyOfPropertyPtr(pPropertyPtrRec));
+ PREFIX_ASSUME(tok != NULL);
+ *tok = getParentOfPropertyMap(pPropertyMapRec);
+ }
+ }
+ if (InterlockedCompareExchangeT<TOKENMAP *>(
+ &m_pPropertyMap,
+ pPropertyMap,
+ NULL) == NULL)
+ { // We won the initializaion race
+ pPropertyMap.SuppressRelease();
+ }
+ }
+ *ptd = *(m_pPropertyMap->Get(RidFromToken(pr)));
+ }
+ else
+ {
+ RID ridPropertyMap;
+ PropertyMapRec *pRec;
+
+ IfFailGo(FindPropertyMapParentOfProperty(RidFromToken(pr), &ridPropertyMap));
+ IfFailGo(GetPropertyMapRecord(ridPropertyMap, &pRec));
+ *ptd = getParentOfPropertyMap(pRec);
+ }
+ RidToToken(*ptd, mdtTypeDef);
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindParentOfPropertyHelper
+
+//*****************************************************************************
+// Find parent for an Event token. This will use the lookup table if there is an
+// intermediate table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindParentOfEventHelper(
+ mdEvent ev,
+ mdTypeDef *ptd)
+{
+ HRESULT hr = NOERROR;
+ if (HasIndirectTable(TBL_Event))
+ {
+ if (m_pEventMap == NULL)
+ {
+ ULONG indexMap;
+ ULONG indexEv;
+ ULONG ridStart, ridEnd;
+ EventMapRec *pEventMapRec;
+ EventPtrRec *pEventPtrRec;
+
+ // build the EventMap table
+ NewHolder<TOKENMAP> pEventMap = new (nothrow) TOKENMAP;
+ IfNullGo(pEventMap);
+ ULONG nAllocateSize;
+ if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Event], 1, nAllocateSize))
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ if (pEventMap->AllocateBlock(nAllocateSize) == 0)
+ IfFailGo(E_OUTOFMEMORY);
+ for (indexMap = 1; indexMap<= m_Schema.m_cRecs[TBL_EventMap]; indexMap++)
+ {
+ IfFailGo(GetEventMapRecord(indexMap, &pEventMapRec));
+ ridStart = getEventListOfEventMap(pEventMapRec);
+ IfFailGo(getEndEventListOfEventMap(indexMap, &ridEnd));
+
+ for (indexEv = ridStart; indexEv < ridEnd; indexEv++)
+ {
+ IfFailGo(GetEventPtrRecord(indexEv, &pEventPtrRec));
+ mdToken* tok = pEventMap->Get(getEventOfEventPtr(pEventPtrRec));
+ PREFIX_ASSUME(tok != NULL);
+ *tok = getParentOfEventMap(pEventMapRec);
+ }
+ }
+ if (InterlockedCompareExchangeT<TOKENMAP *>(
+ &m_pEventMap,
+ pEventMap,
+ NULL) == NULL)
+ { // We won the initializaion race
+ pEventMap.SuppressRelease();
+ }
+ }
+ *ptd = *(m_pEventMap->Get(RidFromToken(ev)));
+ }
+ else
+ {
+ RID ridEventMap;
+ EventMapRec *pRec;
+
+ IfFailGo(FindEventMapParentOfEvent(RidFromToken(ev), &ridEventMap));
+ IfFailGo(GetEventMapRecord(ridEventMap, &pRec));
+ *ptd = getParentOfEventMap(pRec);
+ }
+ RidToToken(*ptd, mdtTypeDef);
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindParentOfEventHelper
+
+//*****************************************************************************
+// Find parent for a ParamDef token. This will use the lookup table if there is an
+// intermediate table.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::FindParentOfParamHelper(
+ mdParamDef pd,
+ mdMethodDef *pmd)
+{
+ HRESULT hr = NOERROR;
+ if (HasIndirectTable(TBL_Param))
+ {
+ if (m_pParamMap == NULL)
+ {
+ ULONG indexMd;
+ ULONG indexPd;
+ ULONG ridStart, ridEnd;
+ MethodRec *pMethodRec;
+ ParamPtrRec *pParamPtrRec;
+
+ // build the ParamMap table
+ NewHolder<TOKENMAP> pParamMap = new (nothrow) TOKENMAP;
+ IfNullGo(pParamMap);
+ ULONG nAllocateSize;
+ if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Param], 1, nAllocateSize))
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ if (pParamMap->AllocateBlock(nAllocateSize) == 0)
+ IfFailGo(E_OUTOFMEMORY);
+ for (indexMd = 1; indexMd<= m_Schema.m_cRecs[TBL_Method]; indexMd++)
+ {
+ IfFailGo(GetMethodRecord(indexMd, &pMethodRec));
+ ridStart = getParamListOfMethod(pMethodRec);
+ IfFailGo(getEndParamListOfMethod(indexMd, &ridEnd));
+
+ for (indexPd = ridStart; indexPd < ridEnd; indexPd++)
+ {
+ IfFailGo(GetParamPtrRecord(indexPd, &pParamPtrRec));
+ PREFIX_ASSUME(pParamMap->Get(getParamOfParamPtr(pParamPtrRec)) != NULL);
+ *(pParamMap->Get(getParamOfParamPtr(pParamPtrRec))) = indexMd;
+ }
+ }
+ if (InterlockedCompareExchangeT<TOKENMAP *>(
+ &m_pParamMap,
+ pParamMap,
+ NULL) == NULL)
+ { // We won the initializaion race
+ pParamMap.SuppressRelease();
+ }
+ }
+ *pmd = *(m_pParamMap->Get(RidFromToken(pd)));
+ }
+ else
+ {
+ IfFailGo(FindParentOfParam(RidFromToken(pd), (RID *)pmd));
+ }
+ RidToToken(*pmd, mdtMethodDef);
+ErrExit:
+ return hr;
+} // CMiniMdRW::FindParentOfParamHelper
+
+
+//******************************************************************************
+// Add an entry in the ENC Log table.
+//******************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::UpdateENCLogHelper(
+ mdToken tk, // Token to be added to the ENCLog table.
+ CMiniMdRW::eDeltaFuncs funccode) // Specifies the optional function code..
+{
+ ENCLogRec *pRecord;
+ RID iRecord;
+ HRESULT hr = S_OK;
+
+ // @todo - MD can't handle anything other than functions right now
+ /* if (TypeFromToken(tk) != mdtMethodDef)
+ {
+ _ASSERTE(!"Trying to do something that we can't do");
+ return S_OK;
+ }
+ */
+ IfFailGo(AddENCLogRecord(&pRecord, &iRecord));
+ pRecord->SetToken(tk);
+ pRecord->SetFuncCode(funccode);
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::UpdateENCLogHelper
+
+__checkReturn
+HRESULT
+CMiniMdRW::UpdateENCLogHelper2(
+ ULONG ixTbl, // Table being updated.
+ ULONG iRid, // Record within table.
+ CMiniMdRW::eDeltaFuncs funccode) // Specifies the optional function code..
+{
+ ENCLogRec *pRecord;
+ RID iRecord;
+ HRESULT hr = S_OK;
+
+ IfFailGo(AddENCLogRecord(&pRecord, &iRecord));
+ pRecord->SetToken(RecIdFromRid(iRid, ixTbl));
+ pRecord->SetFuncCode(funccode);
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::UpdateENCLogHelper2
+
+__checkReturn
+HRESULT
+CMiniMdRW::ResetENCLog()
+{
+#ifdef FEATURE_METADATA_EMIT
+ HRESULT hr = S_OK;
+ ModuleRec * pMod;
+
+ // Get the module record.
+ IfFailGo(GetModuleRecord(1, &pMod));
+
+#ifndef FEATURE_CORECLR
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_UseMinimalDeltas))
+ { // Update the ENC Guids
+ GUID encid;
+ // Copy EncId as BaseId.
+ ULONG uVal = GetCol(TBL_Module, ModuleRec::COL_EncId, pMod);
+ IfFailGo(PutCol(TBL_Module, ModuleRec::COL_EncBaseId, pMod, uVal));
+
+ // Allocate a new GUID for EncId.
+ IfFailGo(CoCreateGuid(&encid));
+ IfFailGo(PutGuid(TBL_Module, ModuleRec::COL_EncId, pMod, encid));
+ }
+#endif //!FEATURE_CORECLR
+
+ // Reset the pool deltas
+ m_StringHeap.StartNewEnCSession();
+ m_BlobHeap.StartNewEnCSession();
+ m_UserStringHeap.StartNewEnCSession();
+
+ // Clear the ENCLog
+ m_Tables[TBL_ENCLog].Delete();
+ m_Schema.m_cRecs[TBL_ENCLog] = 0;
+
+ErrExit:
+ return hr;
+#else //!FEATURE_METADATA_EMIT
+ return S_OK;
+#endif //!FEATURE_METADATA_EMIT
+} // CMiniMdRW::ResetENCLog
+
+// ----------------------------------------------------------------------------
+// Workaround for compiler performance issue VSW 584653 for 2.0 RTM.
+// Get the table's VirtualSort validity state.
+bool
+CMiniMdRW::IsTableVirtualSorted(ULONG ixTbl)
+{
+ _ASSERTE(ixTbl < m_TblCount);
+
+ if (m_pVS[ixTbl] == NULL)
+ {
+ return false;
+ }
+ return m_pVS[ixTbl]->m_isMapValid;
+} // CMiniMdRW::IsTableVirtualSorted
+
+// ----------------------------------------------------------------------------
+// Workaround for compiler performance issue VSW 584653 for 2.0 RTM.
+//
+// Validate table's VirtualSort after adding one record into the table.
+// Returns new VirtualSort validity state in *pfIsTableVirtualSortValid.
+// Assumptions:
+// Table's VirtualSort was valid before adding the record to the table.
+// The caller must ensure validity of VirtualSort by calling to
+// IsTableVirtualSorted or by using the returned state from previous
+// call to this method.
+__checkReturn
+HRESULT
+CMiniMdRW::ValidateVirtualSortAfterAddRecord(
+ ULONG ixTbl,
+ bool *pfIsTableVirtualSortValid)
+{
+ _ASSERTE(ixTbl < m_TblCount);
+
+ HRESULT hr;
+ VirtualSort *pVS = m_pVS[ixTbl];
+
+ // VirtualSort was valid (had to exist)
+ _ASSERTE(pVS != NULL);
+ // Adding record invalidated VirtualSort
+ _ASSERTE(!pVS->m_isMapValid);
+ // Only 1 record was added into table (VirtualSort has 1 bogus element)
+ _ASSERTE(m_Schema.m_cRecs[ixTbl] == (ULONG)pVS->m_pMap->Count());
+
+ // Append 1 element into VirtualSort
+ mdToken *pAddedVSToken = pVS->m_pMap->Append();
+ if (pAddedVSToken == NULL)
+ { // There's not enough memory
+ // Do not handle OOM now, just leave the VirtualSort invalidated, the
+ // next allocation will take care of OOM or the VirtualSort will be
+ // resorted when needed (as it was before this performance workaround)
+ *pfIsTableVirtualSortValid = false;
+ return S_OK;
+ }
+
+ // Initialize added element
+ int iLastElementIndex = pVS->m_pMap->Count() - 1;
+ *pAddedVSToken = iLastElementIndex;
+ // Check if the added element extends the VirtualSort (keeps sorting)
+ if (iLastElementIndex > 2)
+ {
+ int nCompareResult;
+ IfFailRet(pVS->Compare(
+ iLastElementIndex - 1,
+ iLastElementIndex,
+ &nCompareResult));
+ if (nCompareResult < 0)
+ { // VirtualSort was extended - the added element is bigger than
+ // previously last element in VirtualSort
+
+ // Validate VirtualSort as it is still sorted and covers all elements
+ // of the MetaData table
+ pVS->m_isMapValid = true;
+ *pfIsTableVirtualSortValid = true;
+ return S_OK;
+ }
+ }
+ // The added element doesn't extend VirtualSort - it is not sorted
+
+ // Keep the VirtualSort invalidated, therefore next binary search will
+ // force its recreation and resorting (as it did before this performance
+ // workaround)
+ *pfIsTableVirtualSortValid = false;
+ return S_OK;
+} // CMiniMdRW::ValidateVirtualSortAfterAddRecord
+
+#ifdef _DEBUG
+
+// ----------------------------------------------------------------------------
+void
+CMiniMdRW::Debug_CheckIsLockedForWrite()
+{
+ // If this assert fires, then we are trying to modify MetaData that is not locked for write
+ _ASSERTE((dbg_m_pLock == NULL) || dbg_m_pLock->Debug_IsLockedForWrite());
+}
+
+#endif //_DEBUG
+
+//*****************************************************************************
+//
+// Sort the whole RID table
+//
+//*****************************************************************************
+__checkReturn
+HRESULT
+VirtualSort::Sort()
+{
+ m_isMapValid = true;
+ // Note that m_pMap stores an additional bogus element at count 0. This is
+ // just so we can align the index in m_pMap with the Rids which are 1 based.
+ return SortRange(1, m_pMap->Count() - 1);
+} // VirtualSort::Sort
+
+//*****************************************************************************
+//
+// Sort the range from iLeft to iRight
+//
+//*****************************************************************************
+__checkReturn
+HRESULT
+VirtualSort::SortRange(
+ int iLeft,
+ int iRight)
+{
+ HRESULT hr;
+ int iLast;
+
+ for (;;)
+ {
+ // if less than two elements you're done.
+ if (iLeft >= iRight)
+ {
+ return S_OK;
+ }
+
+ // The mid-element is the pivot, move it to the left.
+ Swap(iLeft, (iLeft+iRight)/2);
+ iLast = iLeft;
+
+ // move everything that is smaller than the pivot to the left.
+ for (int i = iLeft+1; i <= iRight; i++)
+ {
+ int nCompareResult;
+ IfFailRet(Compare(i, iLeft, &nCompareResult));
+ if (nCompareResult < 0)
+ {
+ Swap(i, ++iLast);
+ }
+ }
+
+ // Put the pivot to the point where it is in between smaller and larger elements.
+ Swap(iLeft, iLast);
+
+ // Sort each partition.
+ int iLeftLast = iLast - 1;
+ int iRightFirst = iLast + 1;
+ if (iLeftLast - iLeft < iRight - iRightFirst)
+ { // Left partition is smaller, sort it recursively
+ IfFailRet(SortRange(iLeft, iLeftLast));
+ // Tail call to sort the right (bigger) partition
+ iLeft = iRightFirst;
+ //iRight = iRight;
+ continue;
+ }
+ else
+ { // Right partition is smaller, sort it recursively
+ IfFailRet(SortRange(iRightFirst, iRight));
+ // Tail call to sort the left (bigger) partition
+ //iLeft = iLeft;
+ iRight = iLeftLast;
+ continue;
+ }
+ }
+} // VirtualSort::SortRange
+
+//*****************************************************************************
+//
+// Compare two RID base on the m_ixTbl's m_ixCol
+//
+//*****************************************************************************
+__checkReturn
+HRESULT
+VirtualSort::Compare(
+ RID iLeft, // First item to compare.
+ RID iRight, // Second item to compare.
+ int *pnResult) // -1, 0, or 1
+{
+ HRESULT hr;
+ RID ridLeft = *(m_pMap->Get(iLeft));
+ RID ridRight = *(m_pMap->Get(iRight));
+ void *pRow; // Row from a table.
+ ULONG valRight, valLeft; // Value from a row.
+
+ IfFailRet(m_pMiniMd->getRow(m_ixTbl, ridLeft, &pRow));
+ valLeft = m_pMiniMd->getIX(pRow, m_pMiniMd->m_TableDefs[m_ixTbl].m_pColDefs[m_ixCol]);
+ IfFailRet(m_pMiniMd->getRow(m_ixTbl, ridRight, &pRow));
+ valRight = m_pMiniMd->getIX(pRow, m_pMiniMd->m_TableDefs[m_ixTbl].m_pColDefs[m_ixCol]);
+
+ if (valLeft < valRight)
+ {
+ *pnResult = -1;
+ return S_OK;
+ }
+ if (valLeft > valRight)
+ {
+ *pnResult = 1;
+ return S_OK;
+ }
+ // Values are equal -- preserve existing ordering.
+ if (ridLeft < ridRight)
+ {
+ *pnResult = -1;
+ return S_OK;
+ }
+ if (ridLeft > ridRight)
+ {
+ *pnResult = 1;
+ return S_OK;
+ }
+ // Comparing an item to itself?
+ _ASSERTE(!"Comparing an item to itself in sort");
+
+ *pnResult = 0;
+ return S_OK;
+} // VirtualSort::Compare
+
+//*****************************************************************************
+//
+// Initialization function
+//
+//*****************************************************************************
+void VirtualSort::Init( //
+ ULONG ixTbl, // Table index.
+ ULONG ixCol, // Column index.
+ CMiniMdRW *pMiniMd) // MiniMD with data.
+{
+ m_pMap = NULL;
+ m_isMapValid = false;
+ m_ixTbl = ixTbl;
+ m_ixCol = ixCol;
+ m_pMiniMd = pMiniMd;
+} // VirtualSort::Init
+
+
+//*****************************************************************************
+//
+// Uninitialization function
+//
+//*****************************************************************************
+void VirtualSort::Uninit()
+{
+ if ( m_pMap )
+ delete m_pMap;
+ m_pMap = NULL;
+ m_isMapValid = false;
+} // VirtualSort::Uninit
+
+
+//*****************************************************************************
+//
+// Mark a token
+//
+//*****************************************************************************
+HRESULT FilterTable::MarkToken(
+ mdToken tk, // token to be marked as to keep
+ DWORD bitToMark) // bit flag to set in the keep table
+{
+ HRESULT hr = NOERROR;
+ RID rid = RidFromToken(tk);
+
+ if ( (Count() == 0) || ((RID)(Count() -1)) < rid )
+ {
+ // grow table
+ IfFailGo( AllocateBlock( rid + 1 - Count() ) );
+ }
+
+#ifdef _DEBUG
+ if ( (*Get(rid)) & bitToMark )
+ {
+ // global TypeDef could be marked more than once so don't assert if token is mdtTypeDef
+ if (TypeFromToken(tk) != mdtTypeDef)
+ _ASSERTE(!"Token has been Marked");
+ }
+#endif //_DEBUG
+
+ // set the keep bit
+ *Get(rid) = (*Get(rid)) | bitToMark;
+ErrExit:
+ return hr;
+} // FilterTable::MarkToken
+
+
+//*****************************************************************************
+//
+// Unmark a token
+//
+//*****************************************************************************
+HRESULT FilterTable::UnmarkToken(
+ mdToken tk, // token to be unmarked as deleted.
+ DWORD bitToMark) // bit flag to unset in the keep table
+{
+ RID rid = RidFromToken(tk);
+
+ if ( (Count() == 0) || ((RID)(Count() -1)) < rid )
+ {
+ // unmarking should not have grown table. It currently only support dropping the transient CAs.
+ _ASSERTE(!"BAD state!");
+ }
+
+#ifdef _DEBUG
+ if ( (*Get(rid)) & bitToMark )
+ {
+ // global TypeDef could be marked more than once so don't assert if token is mdtTypeDef
+ if (TypeFromToken(tk) != mdtTypeDef)
+ _ASSERTE(!"Token has been Marked");
+ }
+#endif //_DEBUG
+
+ // unset the keep bit
+ *Get(rid) = (*Get(rid)) & ~bitToMark;
+ return NOERROR;
+} // FilterTable::MarkToken
+
+
+//*****************************************************************************
+//
+// Mark an UserString token
+//
+//*****************************************************************************
+HRESULT FilterTable::MarkUserString(
+ mdString str)
+{
+ int high, low, mid;
+
+ low = 0;
+ high = m_daUserStringMarker->Count() - 1;
+ while (low <= high)
+ {
+ mid = (high + low) / 2;
+ if ((m_daUserStringMarker->Get(mid))->m_tkString > (DWORD) str)
+ {
+ high = mid - 1;
+ }
+ else if ((m_daUserStringMarker->Get(mid))->m_tkString < (DWORD) str)
+ {
+ low = mid + 1;
+ }
+ else
+ {
+ (m_daUserStringMarker->Get(mid))->m_fMarked = true;
+ return NOERROR;
+ }
+ }
+ _ASSERTE(!"Bad Token!");
+ return NOERROR;
+} // FilterTable::MarkUserString
+
+//*****************************************************************************
+//
+// Mark a UserString token that was added since our last MarkAll/UnMarkAll
+//
+//*****************************************************************************
+HRESULT FilterTable::MarkNewUserString(mdString str)
+{
+ FilterUserStringEntry *pItem = m_daUserStringMarker->Append();
+
+ if (pItem == NULL)
+ return E_OUTOFMEMORY;
+
+ pItem->m_tkString = str;
+ pItem->m_fMarked = true;
+
+ return S_OK;
+} // FilterTable::MarkNewUserString
+
+//*****************************************************************************
+//
+// Unmarking from 1 to ulSize for all tokens.
+//
+//*****************************************************************************
+HRESULT FilterTable::UnmarkAll(
+ CMiniMdRW *pMiniMd,
+ ULONG ulSize)
+{
+ HRESULT hr;
+
+ S_UINT32 nAllocateSize = S_UINT32(ulSize) + S_UINT32(1);
+ if (nAllocateSize.IsOverflow())
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ if (!AllocateBlock(nAllocateSize.Value()))
+ {
+ IfFailGo(E_OUTOFMEMORY);
+ }
+ memset(Get(0), 0, nAllocateSize.Value() * sizeof(DWORD));
+
+ // unmark all of the user string
+ m_daUserStringMarker = new (nothrow) CDynArray<FilterUserStringEntry>();
+ IfNullGo(m_daUserStringMarker);
+
+ for (UINT32 nIndex = 0; ;)
+ {
+ MetaData::DataBlob userString;
+ UINT32 nNextIndex;
+ hr = pMiniMd->GetUserStringAndNextIndex(
+ nIndex,
+ &userString,
+ &nNextIndex);
+ IfFailGo(hr);
+ if (hr == S_FALSE)
+ { // We reached the last user string
+ hr = S_OK;
+ break;
+ }
+ _ASSERTE(hr == S_OK);
+
+ // Skip empty strings
+ if (userString.IsEmpty())
+ {
+ nIndex = nNextIndex;
+ continue;
+ }
+ FilterUserStringEntry *pItem = m_daUserStringMarker->Append();
+ pItem->m_tkString = TokenFromRid(nIndex, mdtString);
+ pItem->m_fMarked = false;
+
+ // Process next user string in the heap
+ nIndex = nNextIndex;
+ }
+
+ErrExit:
+ return hr;
+} // FilterTable::UnmarkAll
+
+
+
+//*****************************************************************************
+//
+// Marking from 1 to ulSize for all tokens.
+//
+//*****************************************************************************
+HRESULT FilterTable::MarkAll(
+ CMiniMdRW *pMiniMd,
+ ULONG ulSize)
+{
+ HRESULT hr = S_OK;
+
+ S_UINT32 nAllocateSize = S_UINT32(ulSize) + S_UINT32(1);
+ if (nAllocateSize.IsOverflow())
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ if (!AllocateBlock(nAllocateSize.Value()))
+ {
+ IfFailGo(E_OUTOFMEMORY);
+ }
+ memset(Get(0), 0xFFFFFFFF, nAllocateSize.Value() * sizeof(DWORD));
+
+ // mark all of the user string
+ m_daUserStringMarker = new (nothrow) CDynArray<FilterUserStringEntry>();
+ IfNullGo(m_daUserStringMarker);
+
+ for (UINT32 nIndex = 0; ;)
+ {
+ MetaData::DataBlob userString;
+ UINT32 nNextIndex;
+ hr = pMiniMd->GetUserStringAndNextIndex(
+ nIndex,
+ &userString,
+ &nNextIndex);
+ IfFailGo(hr);
+ if (hr == S_FALSE)
+ { // We reached the last user string
+ hr = S_OK;
+ break;
+ }
+ _ASSERTE(hr == S_OK);
+
+ // Skip empty strings
+ if (userString.IsEmpty())
+ {
+ nIndex = nNextIndex;
+ continue;
+ }
+ FilterUserStringEntry *pItem = m_daUserStringMarker->Append();
+ pItem->m_tkString = TokenFromRid(nIndex, mdtString);
+ pItem->m_fMarked = true;
+
+ // Process next user string in the heap
+ nIndex = nNextIndex;
+ }
+
+ErrExit:
+ return hr;
+} // FilterTable::MarkAll
+
+//*****************************************************************************
+//
+// return true if a token is marked. Otherwise return false.
+//
+//*****************************************************************************
+bool FilterTable::IsTokenMarked(
+ mdToken tk, // Token to inquiry
+ DWORD bitMarked) // bit flag to check in the deletion table
+{
+ RID rid = RidFromToken(tk);
+
+ //<TODO>@FUTURE: inconsistency!!!
+ // If caller unmarked everything while the module has 2 typedef and 10 methodef.
+ // We will have 11 rows in the FilterTable. Then user add the 3 typedef, it is
+ // considered unmarked unless we mark it when we do DefineTypeDef. However, if user
+ // add another MethodDef, it will be considered marked unless we unmarked.....
+ // Maybe the solution is not to support DefineXXXX if you use the filter interface??</TODO>
+
+ if ( (Count() == 0) || ((RID)(Count() - 1)) < rid )
+ {
+ // If UnmarkAll has never been called or tk is added after UnmarkAll,
+ // tk is considered marked.
+ //
+ return true;
+ }
+ return ( (*Get(rid)) & bitMarked ? true : false);
+} // FilterTable::IsTokenMarked
+
+
+//*****************************************************************************
+//
+// return true if a token is marked. Otherwise return false.
+//
+//*****************************************************************************
+bool FilterTable::IsTokenMarked(
+ mdToken tk) // Token to inquiry
+{
+
+ switch ( TypeFromToken(tk) )
+ {
+ case mdtTypeRef:
+ return IsTypeRefMarked(tk);
+ case mdtTypeDef:
+ return IsTypeDefMarked(tk);
+ case mdtFieldDef:
+ return IsFieldMarked(tk);
+ case mdtMethodDef:
+ return IsMethodMarked(tk);
+ case mdtParamDef:
+ return IsParamMarked(tk);
+ case mdtMemberRef:
+ return IsMemberRefMarked(tk);
+ case mdtCustomAttribute:
+ return IsCustomAttributeMarked(tk);
+ case mdtPermission:
+ return IsDeclSecurityMarked(tk);
+ case mdtSignature:
+ return IsSignatureMarked(tk);
+ case mdtEvent:
+ return IsEventMarked(tk);
+ case mdtProperty:
+ return IsPropertyMarked(tk);
+ case mdtModuleRef:
+ return IsModuleRefMarked(tk);
+ case mdtTypeSpec:
+ return IsTypeSpecMarked(tk);
+ case mdtInterfaceImpl:
+ return IsInterfaceImplMarked(tk);
+ case mdtMethodSpec:
+ return IsMethodSpecMarked(tk);
+ case mdtString:
+ return IsUserStringMarked(tk);
+ default:
+ _ASSERTE(!"Bad token type!");
+ break;
+ }
+ return false;
+} // FilterTable::IsTokenMarked
+
+//*****************************************************************************
+//
+// return true if an UserString is marked.
+//
+//*****************************************************************************
+bool FilterTable::IsUserStringMarked(mdString str)
+{
+ int low, mid, high, count;
+
+ // if m_daUserStringMarker is not created, UnmarkAll has never been called
+ if (m_daUserStringMarker == NULL)
+ return true;
+
+ low = 0;
+ count = m_daUserStringMarker->Count();
+
+ if (count == 0)
+ {
+ // No strings are marked.
+ return false;
+ }
+
+ high = m_daUserStringMarker->Count() - 1;
+
+ while (low <= high)
+ {
+ mid = (high + low) / 2;
+ if ((m_daUserStringMarker->Get(mid))->m_tkString > (DWORD) str)
+ {
+ high = mid - 1;
+ }
+ else if ((m_daUserStringMarker->Get(mid))->m_tkString < (DWORD) str)
+ {
+ low = mid + 1;
+ }
+ else
+ {
+ return (m_daUserStringMarker->Get(mid))->m_fMarked;
+ }
+ }
+ _ASSERTE(!"Bad Token!");
+ return false;
+} // FilterTable::IsUserStringMarked
+
+
+
+//*****************************************************************************
+//
+// destructor
+//
+//*****************************************************************************
+FilterTable::~FilterTable()
+{
+ if (m_daUserStringMarker)
+ delete m_daUserStringMarker;
+ Clear();
+} // FilterTable::~FilterTable
+
diff --git a/src/md/enc/peparse.cpp b/src/md/enc/peparse.cpp
new file mode 100644
index 0000000000..583ad32d2f
--- /dev/null
+++ b/src/md/enc/peparse.cpp
@@ -0,0 +1,149 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "stdafx.h"
+
+#include <windows.h>
+#include <corhdr.h>
+#include "corerror.h"
+#include "pedecoder.h"
+
+
+static const char g_szCORMETA[] = ".cormeta";
+
+
+HRESULT CLiteWeightStgdbRW::FindImageMetaData(PVOID pImage, DWORD dwFileLength, BOOL bMappedImage, PVOID *ppMetaData, ULONG *pcbMetaData)
+{
+#ifndef DACCESS_COMPILE
+ PEDecoder pe;
+
+ // We need to use different PEDecoder initialization based on the type of data we give it.
+ // We use the one with a 'bool' as the second argument when dealing with a mapped file,
+ // and we use the one that takes a COUNT_T as the second argument when dealing with a
+ // flat file.
+ if (bMappedImage)
+ {
+ if (FAILED(pe.Init(pImage, false)) ||
+ !pe.CheckNTHeaders())
+ {
+ return COR_E_BADIMAGEFORMAT;
+ }
+ }
+ else
+ {
+ pe.Init(pImage, (COUNT_T)dwFileLength);
+ }
+
+ // Minimally validate image
+ if (!pe.CheckCorHeader())
+ return COR_E_BADIMAGEFORMAT;
+
+
+ COUNT_T size = 0;
+
+ *ppMetaData = (void *)pe.GetMetadata(&size);
+
+ // Couldn't find any IL metadata in this image
+ if (*ppMetaData == NULL)
+ return CLDB_E_NO_DATA;
+
+ if (pcbMetaData != NULL)
+ *pcbMetaData = size;
+
+ return S_OK;
+#else
+ DacNotImpl();
+ return E_NOTIMPL;
+#endif
+} // CLiteWeightStgdbRW::FindImageMetaData
+
+//
+// Note: Remove once defined in winnt.h
+//
+typedef struct ANON_OBJECT_HEADER2 {
+ WORD Sig1; // Must be IMAGE_FILE_MACHINE_UNKNOWN
+ WORD Sig2; // Must be 0xffff
+ WORD Version; // >= 2 (implies the CLSID field, Flags and metadata info are present)
+ WORD Machine;
+ DWORD TimeDateStamp;
+ CLSID ClassID; // Used to invoke CoCreateInstance
+ DWORD SizeOfData; // Size of data that follows the header
+ DWORD Flags;
+ DWORD MetaDataSize; // Size of CLR metadata
+ DWORD MetaDataOffset; // Offset of CLR metadata
+} ANON_OBJECT_HEADER2;
+
+#define ANON_OBJECT_HAS_CORMETA 0x00000001
+#define ANON_OBJECT_IS_PUREMSIL 0x00000002
+
+HRESULT CLiteWeightStgdbRW::FindObjMetaData(PVOID pImage, DWORD dwFileLength, PVOID *ppMetaData, ULONG *pcbMetaData)
+{
+ DWORD dwSize = 0;
+ DWORD dwOffset = 0;
+
+ ANON_OBJECT_HEADER2 *pAnonImageHdr = (ANON_OBJECT_HEADER2 *) pImage; // Anonymous object header
+
+ // Check to see if this is a LTCG object
+ if (dwFileLength >= sizeof(ANON_OBJECT_HEADER2) &&
+ pAnonImageHdr->Sig1 == VAL16(IMAGE_FILE_MACHINE_UNKNOWN) &&
+ pAnonImageHdr->Sig2 == VAL16(IMPORT_OBJECT_HDR_SIG2))
+ {
+ // Version 1 anonymous objects don't have metadata info
+ if (VAL16(pAnonImageHdr->Version) < 2)
+ goto BadFormat;
+
+ // Anonymous objects contain the metadata info in the header
+ dwOffset = VAL32(pAnonImageHdr->MetaDataOffset);
+ dwSize = VAL32(pAnonImageHdr->MetaDataSize);
+ }
+ else
+ {
+ // Check to see if we have enough data
+ if (dwFileLength < sizeof(IMAGE_FILE_HEADER))
+ goto BadFormat;
+
+ IMAGE_FILE_HEADER *pImageHdr = (IMAGE_FILE_HEADER *) pImage; // Header for the .obj file.
+
+ // Walk each section looking for .cormeta.
+ DWORD nSections = VAL16(pImageHdr->NumberOfSections);
+
+ // Check to see if we have enough data
+ S_UINT32 nSectionsSize = S_UINT32(sizeof(IMAGE_FILE_HEADER)) + S_UINT32(nSections) * S_UINT32(sizeof(IMAGE_SECTION_HEADER));
+ if (nSectionsSize.IsOverflow() || (dwFileLength < nSectionsSize.Value()))
+ goto BadFormat;
+
+ IMAGE_SECTION_HEADER *pSectionHdr = (IMAGE_SECTION_HEADER *)(pImageHdr + 1); // Section header.
+
+ for (DWORD i=0; i<nSections; i++, pSectionHdr++)
+ {
+ // Simple comparison to section name.
+ if (memcmp((const char *) pSectionHdr->Name, g_szCORMETA, sizeof(pSectionHdr->Name)) == 0)
+ {
+ dwOffset = VAL32(pSectionHdr->PointerToRawData);
+ dwSize = VAL32(pSectionHdr->SizeOfRawData);
+ break;
+ }
+ }
+ }
+
+ if (dwOffset == 0 || dwSize == 0)
+ goto BadFormat;
+
+ // Check that raw data in the section is actually within the file.
+ {
+ S_UINT32 dwEndOffset = S_UINT32(dwOffset) + S_UINT32(dwSize);
+ if ((dwOffset >= dwFileLength) || dwEndOffset.IsOverflow() || (dwEndOffset.Value() > dwFileLength))
+ goto BadFormat;
+ }
+
+ *ppMetaData = (PVOID) ((ULONG_PTR) pImage + dwOffset);
+ *pcbMetaData = dwSize;
+ return (S_OK);
+
+BadFormat:
+ *ppMetaData = NULL;
+ *pcbMetaData = 0;
+ return (COR_E_BADIMAGEFORMAT);
+}
diff --git a/src/md/enc/rwutil.cpp b/src/md/enc/rwutil.cpp
new file mode 100644
index 0000000000..7267301c47
--- /dev/null
+++ b/src/md/enc/rwutil.cpp
@@ -0,0 +1,1413 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// RWUtil.cpp
+//
+
+//
+// contains utility code to MD directory
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "metadata.h"
+#include "rwutil.h"
+#include "utsem.h"
+#include "../inc/mdlog.h"
+
+//*****************************************************************************
+// Helper methods
+//*****************************************************************************
+void
+Unicode2UTF(
+ LPCWSTR wszSrc, // The string to convert.
+ __out_ecount(cbDst)
+ LPUTF8 szDst, // Buffer for the output UTF8 string.
+ int cbDst) // Size of the buffer for UTF8 string.
+{
+ int cchSrc = (int)wcslen(wszSrc);
+ int cchRet;
+
+ cchRet = WszWideCharToMultiByte(
+ CP_UTF8,
+ 0,
+ wszSrc,
+ cchSrc + 1,
+ szDst,
+ cbDst,
+ NULL,
+ NULL);
+
+ if (cchRet == 0)
+ {
+ _ASSERTE_MSG(FALSE, "Converting unicode string to UTF8 string failed!");
+ szDst[0] = '\0';
+ }
+} // Unicode2UTF
+
+
+HRESULT HENUMInternal::CreateSimpleEnum(
+ DWORD tkKind, // kind of token that we are iterating
+ ULONG ridStart, // starting rid
+ ULONG ridEnd, // end rid
+ HENUMInternal **ppEnum) // return the created HENUMInternal
+{
+ HENUMInternal *pEnum;
+ HRESULT hr = NOERROR;
+
+ // Don't create an empty enum.
+ if (ridStart >= ridEnd)
+ {
+ *ppEnum = 0;
+ goto ErrExit;
+ }
+
+ pEnum = new (nothrow) HENUMInternal;
+
+ // check for out of memory error
+ if (pEnum == NULL)
+ IfFailGo( E_OUTOFMEMORY );
+
+ memset(pEnum, 0, sizeof(HENUMInternal));
+ pEnum->m_tkKind = tkKind;
+ pEnum->m_EnumType = MDSimpleEnum;
+ pEnum->u.m_ulStart = pEnum->u.m_ulCur = ridStart;
+ pEnum->u.m_ulEnd = ridEnd;
+ pEnum->m_ulCount = ridEnd - ridStart;
+
+ *ppEnum = pEnum;
+ErrExit:
+ return hr;
+
+} // CreateSimpleEnum
+
+
+//*****************************************************************************
+// Helper function to destroy Enumerator
+//*****************************************************************************
+void HENUMInternal::DestroyEnum(
+ HENUMInternal *pmdEnum)
+{
+ if (pmdEnum == NULL)
+ return;
+
+ if (pmdEnum->m_EnumType == MDDynamicArrayEnum)
+ {
+ TOKENLIST *pdalist;
+ pdalist = (TOKENLIST *) &(pmdEnum->m_cursor);
+
+ // clear the embedded dynamic array before we delete the enum
+ pdalist->Clear();
+ }
+ delete pmdEnum;
+} // DestroyEnum
+
+
+//*****************************************************************************
+// Helper function to destroy Enumerator if the enumerator is empty
+//*****************************************************************************
+void HENUMInternal::DestroyEnumIfEmpty(
+ HENUMInternal **ppEnum) // reset the enumerator pointer to NULL if empty
+{
+
+ if (*ppEnum == NULL)
+ return;
+
+ _ASSERTE((*ppEnum)->m_EnumType != MDCustomEnum);
+
+ if ((*ppEnum)->m_ulCount == 0)
+ {
+ HENUMInternal::DestroyEnum(*ppEnum);
+ *ppEnum = NULL;
+ }
+} // DestroyEnumIfEmpty
+
+
+void HENUMInternal::ClearEnum(
+ HENUMInternal *pmdEnum)
+{
+ if (pmdEnum == NULL)
+ return;
+
+ if (pmdEnum->m_EnumType == MDDynamicArrayEnum)
+ {
+ TOKENLIST *pdalist;
+ pdalist = (TOKENLIST *) &(pmdEnum->m_cursor);
+
+ // clear the embedded dynamic array before we delete the enum
+ pdalist->Clear();
+ }
+} // ClearEnum
+
+
+//*****************************************************************************
+// Helper function to iterate the enum
+//*****************************************************************************
+bool HENUMInternal::EnumNext(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk) // [OUT] token to scope the search
+{
+ _ASSERTE(phEnum && ptk);
+ _ASSERTE(phEnum->m_EnumType != MDCustomEnum);
+
+ if (phEnum->u.m_ulCur >= phEnum->u.m_ulEnd)
+ return false;
+
+ if ( phEnum->m_EnumType == MDSimpleEnum )
+ {
+ *ptk = phEnum->u.m_ulCur | phEnum->m_tkKind;
+ phEnum->u.m_ulCur++;
+ }
+ else
+ {
+ TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor);
+
+ _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum );
+ *ptk = *( pdalist->Get(phEnum->u.m_ulCur++) );
+ }
+ return true;
+} // EnumNext
+
+//*****************************************************************************
+// Number of items in the enumerator.
+//*****************************************************************************
+HRESULT HENUMInternal::GetCount(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ ULONG *pCount) // ]OUT] the index of the desired item
+{
+ // Check for empty enum.
+ if (phEnum == 0)
+ return S_FALSE;
+
+ _ASSERTE(phEnum->m_EnumType != MDCustomEnum);
+
+
+ *pCount = phEnum->u.m_ulEnd - phEnum->u.m_ulStart;
+ return S_OK;
+}
+
+//*****************************************************************************
+// Get a specific element.
+//*****************************************************************************
+HRESULT HENUMInternal::GetElement(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ ULONG ix, // ]IN] the index of the desired item
+ mdToken *ptk) // [OUT] token to fill
+{
+ // Check for empty enum.
+ if (phEnum == 0)
+ return S_FALSE;
+
+ if (ix > (phEnum->u.m_ulEnd - phEnum->u.m_ulStart))
+ return S_FALSE;
+
+ if ( phEnum->m_EnumType == MDSimpleEnum )
+ {
+ *ptk = (phEnum->u.m_ulStart + ix) | phEnum->m_tkKind;
+ }
+ else
+ {
+ TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor);
+
+ _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum );
+ *ptk = *( pdalist->Get(ix) );
+ }
+
+ return S_OK;
+}
+
+//*****************************************************************************
+// Helper function to fill output token buffers given an enumerator
+//*****************************************************************************
+HRESULT HENUMInternal::EnumWithCount(
+ HENUMInternal *pEnum, // enumerator
+ ULONG cMax, // max tokens that caller wants
+ mdToken rTokens[], // output buffer to fill the tokens
+ ULONG *pcTokens) // number of tokens fill to the buffer upon return
+{
+ ULONG cTokens;
+ HRESULT hr = NOERROR;
+
+ // Check for empty enum.
+ if (pEnum == 0)
+ {
+ if (pcTokens)
+ *pcTokens = 0;
+ return S_FALSE;
+ }
+
+ // we can only fill the minimun of what caller asked for or what we have left
+ cTokens = min ( (pEnum->u.m_ulEnd - pEnum->u.m_ulCur), cMax);
+
+ if (pEnum->m_EnumType == MDSimpleEnum)
+ {
+
+ // now fill the output
+ for (ULONG i = 0; i < cTokens; i ++, pEnum->u.m_ulCur++)
+ {
+ rTokens[i] = TokenFromRid(pEnum->u.m_ulCur, pEnum->m_tkKind);
+ }
+
+ }
+ else
+ {
+ // cannot be any other kind!
+ _ASSERTE( pEnum->m_EnumType == MDDynamicArrayEnum );
+
+ // get the embedded dynamic array
+ TOKENLIST *pdalist = (TOKENLIST *)&(pEnum->m_cursor);
+
+ for (ULONG i = 0; i < cTokens; i ++, pEnum->u.m_ulCur++)
+ {
+ rTokens[i] = *( pdalist->Get(pEnum->u.m_ulCur) );
+ }
+ }
+
+ if (pcTokens)
+ *pcTokens = cTokens;
+
+ if (cTokens == 0)
+ hr = S_FALSE;
+ return hr;
+} // EnumWithCount
+
+
+//*****************************************************************************
+// Helper function to fill output token buffers given an enumerator
+// This is a variation that takes two output arrays. The tokens in the
+// enumerator are interleaved, one for each array. This is currently used by
+// EnumMethodImpl which needs to return two arrays.
+//*****************************************************************************
+HRESULT HENUMInternal::EnumWithCount(
+ HENUMInternal *pEnum, // enumerator
+ ULONG cMax, // max tokens that caller wants
+ mdToken rTokens1[], // first output buffer to fill the tokens
+ mdToken rTokens2[], // second output buffer to fill the tokens
+ ULONG *pcTokens) // number of tokens fill to each buffer upon return
+{
+ ULONG cTokens;
+ HRESULT hr = NOERROR;
+
+ // cannot be any other kind!
+ _ASSERTE( pEnum->m_EnumType == MDDynamicArrayEnum );
+
+ // Check for empty enum.
+ if (pEnum == 0)
+ {
+ if (pcTokens)
+ *pcTokens = 0;
+ return S_FALSE;
+ }
+
+ // Number of tokens must always be a multiple of 2.
+ _ASSERTE(! ((pEnum->u.m_ulEnd - pEnum->u.m_ulCur) % 2) );
+
+ // we can only fill the minimun of what caller asked for or what we have left
+ cTokens = min ( (pEnum->u.m_ulEnd - pEnum->u.m_ulCur), cMax * 2);
+
+ // get the embedded dynamic array
+ TOKENLIST *pdalist = (TOKENLIST *)&(pEnum->m_cursor);
+
+ for (ULONG i = 0; i < (cTokens / 2); i++)
+ {
+ rTokens1[i] = *( pdalist->Get(pEnum->u.m_ulCur++) );
+ rTokens2[i] = *( pdalist->Get(pEnum->u.m_ulCur++) );
+ }
+
+ if (pcTokens)
+ *pcTokens = cTokens / 2;
+
+ if (cTokens == 0)
+ hr = S_FALSE;
+ return hr;
+} // EnumWithCount
+
+
+//*****************************************************************************
+// Helper function to create HENUMInternal
+//*****************************************************************************
+HRESULT HENUMInternal::CreateDynamicArrayEnum(
+ DWORD tkKind, // kind of token that we are iterating
+ HENUMInternal **ppEnum) // return the created HENUMInternal
+{
+ HENUMInternal *pEnum;
+ HRESULT hr = NOERROR;
+ TOKENLIST *pdalist;
+
+ pEnum = new (nothrow) HENUMInternal;
+
+ // check for out of memory error
+ if (pEnum == NULL)
+ IfFailGo( E_OUTOFMEMORY );
+
+ memset(pEnum, 0, sizeof(HENUMInternal));
+ pEnum->m_tkKind = tkKind;
+ pEnum->m_EnumType = MDDynamicArrayEnum;
+
+ // run the constructor in place
+ pdalist = (TOKENLIST *) &(pEnum->m_cursor);
+ ::new (pdalist) TOKENLIST;
+
+ *ppEnum = pEnum;
+ErrExit:
+ return hr;
+
+} // _CreateDynamicArrayEnum
+
+
+
+//*****************************************************************************
+// Helper function to init HENUMInternal
+//*****************************************************************************
+void HENUMInternal::InitDynamicArrayEnum(
+ HENUMInternal *pEnum) // HENUMInternal to be initialized
+{
+ TOKENLIST *pdalist;
+
+ memset(pEnum, 0, sizeof(HENUMInternal));
+ pEnum->m_EnumType = MDDynamicArrayEnum;
+ pEnum->m_tkKind = (DWORD) -1;
+
+ // run the constructor in place
+ pdalist = (TOKENLIST *) &(pEnum->m_cursor);
+ ::new (pdalist) TOKENLIST;
+} // CreateDynamicArrayEnum
+
+
+//*****************************************************************************
+// Helper function to init HENUMInternal
+//*****************************************************************************
+void HENUMInternal::InitSimpleEnum(
+ DWORD tkKind, // kind of token that we are iterating
+ ULONG ridStart, // starting rid
+ ULONG ridEnd, // end rid
+ HENUMInternal *pEnum) // HENUMInternal to be initialized
+{
+ pEnum->m_EnumType = MDSimpleEnum;
+ pEnum->m_tkKind = tkKind;
+ pEnum->u.m_ulStart = pEnum->u.m_ulCur = ridStart;
+ pEnum->u.m_ulEnd = ridEnd;
+ pEnum->m_ulCount = ridEnd - ridStart;
+
+} // InitSimpleEnum
+
+
+
+
+//*****************************************************************************
+// Helper function to init HENUMInternal
+//*****************************************************************************
+HRESULT HENUMInternal::AddElementToEnum(
+ HENUMInternal *pEnum, // return the created HENUMInternal
+ mdToken tk) // token value to be stored
+{
+ HRESULT hr = NOERROR;
+ TOKENLIST *pdalist;
+ mdToken *ptk;
+
+ pdalist = (TOKENLIST *) &(pEnum->m_cursor);
+
+ {
+ // TODO: Revisit this violation.
+ CONTRACT_VIOLATION(ThrowsViolation);
+ ptk = ((mdToken *)pdalist->Append());
+ }
+ if (ptk == NULL)
+ IfFailGo( E_OUTOFMEMORY );
+ *ptk = tk;
+
+ // increase the count
+ pEnum->m_ulCount++;
+ pEnum->u.m_ulEnd++;
+ErrExit:
+ return hr;
+
+} // _AddElementToEnum
+
+
+
+
+
+//*****************************************************************************
+// find a token in the tokenmap.
+//*****************************************************************************
+MDTOKENMAP::~MDTOKENMAP()
+{
+ if (m_pMap)
+ m_pMap->Release();
+} // MDTOKENMAP::~MDTOKENMAP()
+
+HRESULT MDTOKENMAP::Init(
+ IUnknown *pImport) // The import that this map is for.
+{
+ HRESULT hr; // A result.
+ IMetaDataTables *pITables=0; // Table information.
+ ULONG cRows; // Count of rows in a table.
+ ULONG cTotal; // Running total of rows in db.
+ TOKENREC *pRec; // A TOKENREC record.
+ mdToken tkTable; // Token kind for a table.
+
+ hr = pImport->QueryInterface(IID_IMetaDataTables, (void**)&pITables);
+ if (hr == S_OK)
+ {
+ // Determine the size of each table.
+ cTotal = 0;
+ for (ULONG ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl)
+ {
+ // Where does this table's data start.
+ m_TableOffset[ixTbl] = cTotal;
+ // See if this table has tokens.
+ tkTable = CMiniMdRW::GetTokenForTable(ixTbl);
+ if (tkTable == (ULONG) -1)
+ {
+ // It doesn't have tokens, so we won't see any tokens for the table.
+ }
+ else
+ { // It has tokens, so we may see a token for every row.
+ pITables->GetTableInfo(ixTbl, 0, &cRows, 0,0,0);
+ // Safe: cTotal += cRows
+ if (!ClrSafeInt<ULONG>::addition(cTotal, cRows, cTotal))
+ {
+ IfFailGo(COR_E_OVERFLOW);
+ }
+ }
+ }
+ m_TableOffset[TBL_COUNT] = cTotal;
+ m_iCountIndexed = cTotal;
+ // Attempt to allocate space for all of the possible remaps.
+ if (!AllocateBlock(cTotal))
+ IfFailGo(E_OUTOFMEMORY);
+ // Note that no sorts are needed.
+ m_sortKind = Indexed;
+ // Initialize entries to "not found".
+ for (ULONG i=0; i<cTotal; ++i)
+ {
+ pRec = Get(i);
+ pRec->SetEmpty();
+ }
+ }
+#if defined(_DEBUG)
+ if (SUCCEEDED(pImport->QueryInterface(IID_IMetaDataImport, (void**)&m_pImport)))
+ {
+ // Ok, here's a pretty nasty workaround. We're going to make a big assumption here
+ // that we're owned by the pImport, and so we don't need to keep a refcount
+ // on the pImport object.
+ //
+ // If we did, we'd create a circular reference and neither this object nor
+ // the RegMeta would be freed.
+ m_pImport->Release();
+
+ }
+
+
+
+#endif
+
+ErrExit:
+ if (pITables)
+ pITables->Release();
+ return hr;
+} // HRESULT MDTOKENMAP::Init()
+
+HRESULT MDTOKENMAP::EmptyMap()
+{
+ int nCount = Count();
+ for (int i=0; i<nCount; ++i)
+ {
+ Get(i)->SetEmpty();
+ }
+
+ return S_OK;
+}// HRESULT MDTOKENMAP::Clear()
+
+
+//*****************************************************************************
+// find a token in the tokenmap.
+//*****************************************************************************
+bool MDTOKENMAP::Find(
+ mdToken tkFind, // [IN] the token value to find
+ TOKENREC **ppRec) // [OUT] point to the record found in the dynamic array
+{
+ int lo,mid,hi; // binary search indices.
+ TOKENREC *pRec = NULL;
+
+ if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString)
+ {
+ // Get the entry.
+ ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind);
+ if(ixTbl == (ULONG) -1)
+ return false;
+ ULONG iRid = RidFromToken(tkFind);
+ if((m_TableOffset[ixTbl] + iRid) > m_TableOffset[ixTbl+1])
+ return false;
+ pRec = Get(m_TableOffset[ixTbl] + iRid - 1);
+ // See if it has been set.
+ if (pRec->IsEmpty())
+ return false;
+ // Verify that it is what we think it is.
+ _ASSERTE(pRec->m_tkFrom == tkFind);
+ *ppRec = pRec;
+ return true;
+ }
+ else
+ { // Shouldn't be any unsorted records, and table must be sorted in proper ordering.
+ _ASSERTE( m_iCountTotal == m_iCountSorted &&
+ (m_sortKind == SortByFromToken || m_sortKind == Indexed) );
+ _ASSERTE( (m_iCountIndexed + m_iCountTotal) == (ULONG)Count() );
+
+ // Start with entire table.
+ lo = m_iCountIndexed;
+ hi = Count() - 1;
+
+ // While there are rows in the range...
+ while (lo <= hi)
+ { // Look at the one in the middle.
+ mid = (lo + hi) / 2;
+
+ pRec = Get(mid);
+
+ // If equal to the target, done.
+ if (tkFind == pRec->m_tkFrom)
+ {
+ *ppRec = Get(mid);
+ return true;
+ }
+
+ // If middle item is too small, search the top half.
+ if (pRec->m_tkFrom < tkFind)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ }
+
+ // Didn't find anything that matched.
+ return false;
+} // bool MDTOKENMAP::Find()
+
+
+
+//*****************************************************************************
+// remap the token
+//*****************************************************************************
+HRESULT MDTOKENMAP::Remap(
+ mdToken tkFrom,
+ mdToken *ptkTo)
+{
+ HRESULT hr = NOERROR;
+ TOKENREC *pRec;
+
+ // Remap nil to same thing (helps because System.Object has no base class.)
+ if (IsNilToken(tkFrom))
+ {
+ *ptkTo = tkFrom;
+ return hr;
+ }
+
+ if ( Find(tkFrom, &pRec) )
+ {
+ *ptkTo = pRec->m_tkTo;
+ }
+ else
+ {
+ _ASSERTE( !" Bad lookup map!");
+ hr = META_E_BADMETADATA;
+ }
+ return hr;
+} // HRESULT MDTOKENMAP::Remap()
+
+
+
+//*****************************************************************************
+// find a token in the tokenmap.
+//*****************************************************************************
+HRESULT MDTOKENMAP::InsertNotFound(
+ mdToken tkFind,
+ bool fDuplicate,
+ mdToken tkTo,
+ TOKENREC **ppRec)
+{
+ HRESULT hr = NOERROR;
+ int lo, mid, hi; // binary search indices.
+ TOKENREC *pRec;
+
+ // If possible, validate the input.
+ _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFind));
+
+ if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString)
+ {
+ // Get the entry.
+ ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind);
+ _ASSERTE(ixTbl != (ULONG) -1);
+ ULONG iRid = RidFromToken(tkFind);
+ _ASSERTE((m_TableOffset[ixTbl] + iRid) <= m_TableOffset[ixTbl+1]);
+ pRec = Get(m_TableOffset[ixTbl] + iRid - 1);
+ // See if it has been set.
+ if (!pRec->IsEmpty())
+ { // Verify that it is what we think it is.
+ _ASSERTE(pRec->m_tkFrom == tkFind);
+ }
+ // Store the data.
+ pRec->m_tkFrom = tkFind;
+ pRec->m_isDuplicate = fDuplicate;
+ pRec->m_tkTo = tkTo;
+ pRec->m_isFoundInImport = false;
+ // Return the result.
+ *ppRec = pRec;
+ }
+ else
+ { // Shouldn't be any unsorted records, and table must be sorted in proper ordering.
+ _ASSERTE( m_iCountTotal == m_iCountSorted &&
+ (m_sortKind == SortByFromToken || m_sortKind == Indexed) );
+
+ if ((Count() - m_iCountIndexed) > 0)
+ {
+ // Start with entire table.
+ lo = m_iCountIndexed;
+ hi = Count() - 1;
+
+ // While there are rows in the range...
+ while (lo < hi)
+ { // Look at the one in the middle.
+ mid = (lo + hi) / 2;
+
+ pRec = Get(mid);
+
+ // If equal to the target, done.
+ if (tkFind == pRec->m_tkFrom)
+ {
+ *ppRec = Get(mid);
+ goto ErrExit;
+ }
+
+ // If middle item is too small, search the top half.
+ if (pRec->m_tkFrom < tkFind)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ _ASSERTE(hi <= lo);
+ pRec = Get(lo);
+
+ if (tkFind == pRec->m_tkFrom)
+ {
+ if (tkTo == pRec->m_tkTo && fDuplicate == pRec->m_isDuplicate)
+ {
+ *ppRec = pRec;
+ }
+ else
+ {
+ _ASSERTE(!"inconsistent token has been added to the table!");
+ IfFailGo( E_FAIL );
+ }
+ }
+
+ if (tkFind < pRec->m_tkFrom)
+ {
+ // insert before lo;
+ pRec = Insert(lo);
+ }
+ else
+ {
+ // insert after lo
+ pRec = Insert(lo + 1);
+ }
+ }
+ else
+ {
+ // table is empty
+ pRec = Insert(m_iCountIndexed);
+ }
+
+
+ // If pRec == NULL, return E_OUTOFMEMORY
+ IfNullGo(pRec);
+
+ m_iCountTotal++;
+ m_iCountSorted++;
+
+ *ppRec = pRec;
+
+ // initialize the record
+ pRec->m_tkFrom = tkFind;
+ pRec->m_isDuplicate = fDuplicate;
+ pRec->m_tkTo = tkTo;
+ pRec->m_isFoundInImport = false;
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT MDTOKENMAP::InsertNotFound()
+
+
+//*****************************************************************************
+// find a "to" token in the tokenmap. Now that we are doing the ref to def optimization,
+// we might have several from tokens map to the same to token. We need to return a range of index
+// instead....
+//*****************************************************************************
+bool MDTOKENMAP::FindWithToToken(
+ mdToken tkFind, // [IN] the token value to find
+ int *piPosition) // [OUT] return the first from-token that has the matching to-token
+{
+ int lo, mid, hi; // binary search indices.
+ TOKENREC *pRec;
+ TOKENREC *pRec2;
+
+ // This makes sure that no insertions take place between calls to FindWithToToken.
+ // We want to avoid repeated sorting of the table.
+ _ASSERTE(m_sortKind != SortByToToken || m_iCountTotal == m_iCountSorted);
+
+ // If the map is sorted with From tokens, change it to be sorted with To tokens.
+ if (m_sortKind != SortByToToken)
+ SortTokensByToToken();
+
+ // Start with entire table.
+ lo = 0;
+ hi = Count() - 1;
+
+ // While there are rows in the range...
+ while (lo <= hi)
+ { // Look at the one in the middle.
+ mid = (lo + hi) / 2;
+
+ pRec = Get(mid);
+
+ // If equal to the target, done.
+ if (tkFind == pRec->m_tkTo)
+ {
+ for (int i = mid-1; i >= 0; i--)
+ {
+ pRec2 = Get(i);
+ if (tkFind != pRec2->m_tkTo)
+ {
+ *piPosition = i + 1;
+ return true;
+ }
+ }
+ *piPosition = 0;
+ return true;
+ }
+
+ // If middle item is too small, search the top half.
+ if (pRec->m_tkTo < tkFind)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ // Didn't find anything that matched.
+ return false;
+} // bool MDTOKENMAP::FindWithToToken()
+
+
+
+//*****************************************************************************
+// output a remapped token
+//*****************************************************************************
+mdToken MDTOKENMAP::SafeRemap(
+ mdToken tkFrom) // [IN] the token value to find
+{
+ TOKENREC *pRec;
+
+ // If possible, validate the input.
+ _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFrom));
+
+ SortTokensByFromToken();
+
+ if ( Find(tkFrom, &pRec) )
+ {
+ return pRec->m_tkTo;
+ }
+
+ return tkFrom;
+} // mdToken MDTOKENMAP::SafeRemap()
+
+
+//*****************************************************************************
+// Sorting
+//*****************************************************************************
+void MDTOKENMAP::SortTokensByToToken()
+{
+ // Only sort if there are unsorted records or the sort kind changed.
+ if (m_iCountSorted < m_iCountTotal || m_sortKind != SortByToToken)
+ {
+ // Sort the entire array.
+ m_iCountTotal = Count();
+ m_iCountIndexed = 0;
+ SortRangeToToken(0, m_iCountTotal - 1);
+ m_iCountSorted = m_iCountTotal;
+ m_sortKind = SortByToToken;
+ }
+} // void MDTOKENMAP::SortTokensByToToken()
+
+void MDTOKENMAP::SortRangeFromToken(
+ int iLeft,
+ int iRight)
+{
+ int iLast;
+ int i; // loop variable.
+
+ // if less than two elements you're done.
+ if (iLeft >= iRight)
+ return;
+
+ // The mid-element is the pivot, move it to the left.
+ Swap(iLeft, (iLeft+iRight)/2);
+ iLast = iLeft;
+
+ // move everything that is smaller than the pivot to the left.
+ for(i = iLeft+1; i <= iRight; i++)
+ if (CompareFromToken(i, iLeft) < 0)
+ Swap(i, ++iLast);
+
+ // Put the pivot to the point where it is in between smaller and larger elements.
+ Swap(iLeft, iLast);
+
+ // Sort the each partition.
+ SortRangeFromToken(iLeft, iLast-1);
+ SortRangeFromToken(iLast+1, iRight);
+} // void MDTOKENMAP::SortRangeFromToken()
+
+
+//*****************************************************************************
+// Sorting
+//*****************************************************************************
+void MDTOKENMAP::SortRangeToToken(
+ int iLeft,
+ int iRight)
+{
+ int iLast;
+ int i; // loop variable.
+
+ // if less than two elements you're done.
+ if (iLeft >= iRight)
+ return;
+
+ // The mid-element is the pivot, move it to the left.
+ Swap(iLeft, (iLeft+iRight)/2);
+ iLast = iLeft;
+
+ // move everything that is smaller than the pivot to the left.
+ for(i = iLeft+1; i <= iRight; i++)
+ if (CompareToToken(i, iLeft) < 0)
+ Swap(i, ++iLast);
+
+ // Put the pivot to the point where it is in between smaller and larger elements.
+ Swap(iLeft, iLast);
+
+ // Sort the each partition.
+ SortRangeToToken(iLeft, iLast-1);
+ SortRangeToToken(iLast+1, iRight);
+} // void MDTOKENMAP::SortRangeToToken()
+
+
+//*****************************************************************************
+// find a token in the tokenmap.
+//*****************************************************************************
+HRESULT MDTOKENMAP::AppendRecord(
+ mdToken tkFind,
+ bool fDuplicate,
+ mdToken tkTo,
+ TOKENREC **ppRec)
+{
+ HRESULT hr = NOERROR;
+ TOKENREC *pRec;
+
+ // If possible, validate the input.
+ _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFind));
+
+ // If the map is indexed, and this is a table token, update-in-place.
+ if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString)
+ {
+ // Get the entry.
+ ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind);
+ _ASSERTE(ixTbl != (ULONG) -1);
+ ULONG iRid = RidFromToken(tkFind);
+ _ASSERTE((m_TableOffset[ixTbl] + iRid) <= m_TableOffset[ixTbl+1]);
+ pRec = Get(m_TableOffset[ixTbl] + iRid - 1);
+ // See if it has been set.
+ if (!pRec->IsEmpty())
+ { // Verify that it is what we think it is.
+ _ASSERTE(pRec->m_tkFrom == tkFind);
+ }
+ }
+ else
+ {
+ pRec = Append();
+ IfNullGo(pRec);
+
+ // number of entries increased but not the sorted entry
+ m_iCountTotal++;
+ }
+
+ // Store the data.
+ pRec->m_tkFrom = tkFind;
+ pRec->m_isDuplicate = fDuplicate;
+ pRec->m_tkTo = tkTo;
+ pRec->m_isFoundInImport = false;
+ *ppRec = pRec;
+
+ErrExit:
+ return hr;
+} // HRESULT MDTOKENMAP::AppendRecord()
+
+
+
+
+//*********************************************************************************************************
+//
+// Merge Token manager's constructor
+//
+//*********************************************************************************************************
+MergeTokenManager::MergeTokenManager(MDTOKENMAP *pTkMapList, IUnknown *pHandler)
+{
+ m_cRef = 1;
+ m_pTkMapList = pTkMapList;
+ m_pDefaultHostRemap = NULL;
+ if (pHandler)
+ pHandler->QueryInterface(IID_IMapToken, (void **) &m_pDefaultHostRemap);
+} // TokenManager::TokenManager()
+
+
+
+//*********************************************************************************************************
+//
+// Merge Token manager's destructor
+//
+//*********************************************************************************************************
+MergeTokenManager::~MergeTokenManager()
+{
+ if (m_pDefaultHostRemap)
+ m_pDefaultHostRemap->Release();
+} // TokenManager::~TokenManager()
+
+
+
+
+ULONG MergeTokenManager::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+} // TokenManager::AddRef()
+
+
+
+ULONG MergeTokenManager::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (!cRef)
+ delete this;
+ return (cRef);
+} // TokenManager::Release()
+
+
+HRESULT MergeTokenManager::QueryInterface(REFIID riid, void **ppUnk)
+{
+ *ppUnk = 0;
+
+ if (riid == IID_IMapToken)
+ *ppUnk = (IUnknown *) (IMapToken *) this;
+ else
+ return (E_NOINTERFACE);
+ AddRef();
+ return (S_OK);
+} // TokenManager::QueryInterface
+
+
+
+//*********************************************************************************************************
+//
+// Token manager keep tracks a list of tokenmaps. Each tokenmap corresponding
+// to an imported scope. Note that with this, we do have problem in how to
+// tell linker regarding the token movement when the token is added by Define
+// rather than merge. This should be fixed with new merge implementation.
+// The tkImp is the old tokens in the emit scope, tkEmit is the new token in the
+// emit scope. We need to find the token from an import scope that is resolved
+// to the tkImp. We then need to tell linker about this token movement.
+// If we don't find any import scope which generates the tkImp token, that is
+// this tkImp is generated by calling DefinXXX directly on the final merged scope.
+// Then we use the default host remap to send the notification.
+//
+//*********************************************************************************************************
+HRESULT MergeTokenManager::Map(mdToken tkImp, mdToken tkEmit)
+{
+ HRESULT hr = NOERROR;
+ MDTOKENMAP *pTkMapList = m_pTkMapList;
+ bool fFoundInImport = false;
+ int iPosition;
+ TOKENREC *pRec;
+
+ _ASSERTE(m_pTkMapList);
+ while ( pTkMapList )
+ {
+ // FindWithToToken will return the first match with the To token.
+ // pTkMapList is sorted with To token. It might contain several From tokens
+ // that map to the To token due to ref to def optimiation. Make sure that
+ // all notification is sent to all of these From tokens.
+ //
+ if ( pTkMapList->FindWithToToken(tkImp, &iPosition) )
+ {
+ // make sure that we don't walk over the last entry
+ while (iPosition < pTkMapList->Count())
+ {
+ pRec = pTkMapList->Get(iPosition);
+ if (pRec->m_tkTo != tkImp)
+ {
+ // we are done!
+ break;
+ }
+
+ // more matching record...
+ fFoundInImport = true;
+ if (pTkMapList->m_pMap)
+ hr = pTkMapList->m_pMap->Map(pRec->m_tkFrom, tkEmit);
+ _ASSERTE(SUCCEEDED(hr));
+ IfFailGo( hr );
+ iPosition++;
+ }
+ }
+ pTkMapList = pTkMapList->m_pNextMap;
+ }
+
+ if (fFoundInImport == false && m_pDefaultHostRemap)
+ {
+ // use the default remap to send the notification
+ IfFailGo( m_pDefaultHostRemap->Map(tkImp, tkEmit) );
+ }
+ErrExit:
+ return hr;
+}
+
+
+
+//*********************************************************************************************************
+//
+// CMapToken's constructor
+//
+//*********************************************************************************************************
+CMapToken::CMapToken()
+{
+ m_cRef = 1;
+ m_pTKMap = NULL;
+ m_isSorted = true;
+} // TokenManager::TokenManager()
+
+
+
+//*********************************************************************************************************
+//
+// CMapToken's destructor
+//
+//*********************************************************************************************************
+CMapToken::~CMapToken()
+{
+ delete m_pTKMap;
+} // CMapToken::~CMapToken()
+
+
+ULONG CMapToken::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+} // CMapToken::AddRef()
+
+
+
+ULONG CMapToken::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (!cRef)
+ delete this;
+ return (cRef);
+} // CMapToken::Release()
+
+
+HRESULT CMapToken::QueryInterface(REFIID riid, void **ppUnk)
+{
+ *ppUnk = 0;
+
+ if (riid == IID_IMapToken)
+ *ppUnk = (IUnknown *) (IMapToken *) this;
+ else
+ return (E_NOINTERFACE);
+ AddRef();
+ return (S_OK);
+} // CMapToken::QueryInterface
+
+
+
+//*********************************************************************************************************
+//
+// Track the token mapping
+//
+//*********************************************************************************************************
+HRESULT CMapToken::Map(
+ mdToken tkFrom,
+ mdToken tkTo)
+{
+ HRESULT hr = NOERROR;
+ TOKENREC *pTkRec;
+
+ if (m_pTKMap == NULL)
+ m_pTKMap = new (nothrow) MDTOKENMAP;
+
+ IfNullGo( m_pTKMap );
+
+ IfFailGo( m_pTKMap->AppendRecord(tkFrom, false, tkTo, &pTkRec) );
+ _ASSERTE( pTkRec );
+
+ m_isSorted = false;
+ErrExit:
+ return hr;
+}
+
+
+//*********************************************************************************************************
+//
+// return what tkFrom is mapped to ptkTo. If there is no remap
+// (ie the token from is filtered out by the filter mechanism, it will return false.
+//
+//*********************************************************************************************************
+bool CMapToken::Find(
+ mdToken tkFrom,
+ TOKENREC **pRecTo)
+{
+ TOKENREC *pRec;
+ bool bRet;
+ if ( m_isSorted == false )
+ {
+ // sort the map
+ m_pTKMap->SortTokensByFromToken();
+ m_isSorted = true;
+ }
+
+ bRet = m_pTKMap->Find(tkFrom, &pRec) ;
+ if (bRet)
+ {
+ _ASSERTE(pRecTo);
+ *pRecTo = pRec;
+ }
+ else
+ {
+ pRec = NULL;
+ }
+ return bRet;
+}
+
+
+//*********************************************************************************************************
+//
+// This function returns true if tkFrom is resolved to a def token. Otherwise, it returns
+// false.
+//
+//*********************************************************************************************************
+bool TokenRemapManager::ResolveRefToDef(
+ mdToken tkRef, // [IN] ref token
+ mdToken *ptkDef) // [OUT] def token that it resolves to. If it does not resolve to a def
+ // token, it will return the tkRef token here.
+{
+ mdToken tkTo;
+
+ _ASSERTE(ptkDef);
+
+ if (TypeFromToken(tkRef) == mdtTypeRef)
+ {
+ tkTo = m_TypeRefToTypeDefMap[RidFromToken(tkRef)];
+ }
+ else
+ {
+ _ASSERTE( TypeFromToken(tkRef) == mdtMemberRef );
+ tkTo = m_MemberRefToMemberDefMap[RidFromToken(tkRef)];
+ }
+ if (RidFromToken(tkTo) == mdTokenNil)
+ {
+ *ptkDef = tkRef;
+ return false;
+ }
+ *ptkDef = tkTo;
+ return true;
+} // ResolveRefToDef
+
+
+
+//*********************************************************************************************************
+//
+// Destructor
+//
+//*********************************************************************************************************
+TokenRemapManager::~TokenRemapManager()
+{
+ m_TypeRefToTypeDefMap.Clear();
+ m_MemberRefToMemberDefMap.Clear();
+} // ~TokenRemapManager
+
+
+//*********************************************************************************************************
+//
+// Initialize the size of Ref to Def optimization table. We will grow the tables in this function.
+// We also initialize the table entries to zero.
+//
+//*********************************************************************************************************
+HRESULT TokenRemapManager::ClearAndEnsureCapacity(
+ ULONG cTypeRef,
+ ULONG cMemberRef)
+{
+ HRESULT hr = NOERROR;
+ if ( ((ULONG) (m_TypeRefToTypeDefMap.Count())) < (cTypeRef + 1) )
+ {
+ if ( m_TypeRefToTypeDefMap.AllocateBlock(cTypeRef + 1 - m_TypeRefToTypeDefMap.Count() ) == 0 )
+ IfFailGo( E_OUTOFMEMORY );
+ }
+ memset( m_TypeRefToTypeDefMap.Get(0), 0, (cTypeRef + 1) * sizeof(mdToken) );
+
+ if ( ((ULONG) (m_MemberRefToMemberDefMap.Count())) < (cMemberRef + 1) )
+ {
+ if ( m_MemberRefToMemberDefMap.AllocateBlock(cMemberRef + 1 - m_MemberRefToMemberDefMap.Count() ) == 0 )
+ IfFailGo( E_OUTOFMEMORY );
+ }
+ memset( m_MemberRefToMemberDefMap.Get(0), 0, (cMemberRef + 1) * sizeof(mdToken) );
+
+ErrExit:
+ return hr;
+} // HRESULT TokenRemapManager::ClearAndEnsureCapacity()
+
+
+
+//*********************************************************************************************************
+//
+// Constructor
+//
+//*********************************************************************************************************
+CMDSemReadWrite::CMDSemReadWrite(
+ UTSemReadWrite * pSem)
+{
+ m_fLockedForRead = false;
+ m_fLockedForWrite = false;
+ m_pSem = pSem;
+} // CMDSemReadWrite::CMDSemReadWrite
+
+
+
+//*********************************************************************************************************
+//
+// Destructor
+//
+//*********************************************************************************************************
+CMDSemReadWrite::~CMDSemReadWrite()
+{
+ _ASSERTE(!m_fLockedForRead || !m_fLockedForWrite);
+ if (m_pSem == NULL)
+ {
+ return;
+ }
+ if (m_fLockedForRead)
+ {
+ LOG((LF_METADATA, LL_EVERYTHING, "UnlockRead called from CSemReadWrite::~CSemReadWrite \n"));
+ m_pSem->UnlockRead();
+ }
+ if (m_fLockedForWrite)
+ {
+ LOG((LF_METADATA, LL_EVERYTHING, "UnlockWrite called from CSemReadWrite::~CSemReadWrite \n"));
+ m_pSem->UnlockWrite();
+ }
+} // CMDSemReadWrite::~CMDSemReadWrite
+
+//*********************************************************************************************************
+//
+// Used to obtain the read lock
+//
+//*********************************************************************************************************
+HRESULT CMDSemReadWrite::LockRead()
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(!m_fLockedForRead && !m_fLockedForWrite);
+
+ if (m_pSem == NULL)
+ {
+ INDEBUG(m_fLockedForRead = true);
+ return hr;
+ }
+
+ LOG((LF_METADATA, LL_EVERYTHING, "LockRead called from CSemReadWrite::LockRead \n"));
+ IfFailRet(m_pSem->LockRead());
+ m_fLockedForRead = true;
+
+ return hr;
+} // CMDSemReadWrite::LockRead
+
+//*********************************************************************************************************
+//
+// Used to obtain the read lock
+//
+//*********************************************************************************************************
+HRESULT CMDSemReadWrite::LockWrite()
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(!m_fLockedForRead && !m_fLockedForWrite);
+
+ if (m_pSem == NULL)
+ {
+ INDEBUG(m_fLockedForWrite = true);
+ return hr;
+ }
+
+ LOG((LF_METADATA, LL_EVERYTHING, "LockWrite called from CSemReadWrite::LockWrite \n"));
+ IfFailRet(m_pSem->LockWrite());
+ m_fLockedForWrite = true;
+
+ return hr;
+}
+
+//*********************************************************************************************************
+//
+// Convert a read lock to a write lock
+//
+//*********************************************************************************************************
+HRESULT CMDSemReadWrite::ConvertReadLockToWriteLock()
+{
+ _ASSERTE(!m_fLockedForWrite);
+
+ HRESULT hr = S_OK;
+
+ if (m_pSem == NULL)
+ {
+ INDEBUG(m_fLockedForRead = false);
+ INDEBUG(m_fLockedForWrite = true);
+ return hr;
+ }
+
+ if (m_fLockedForRead)
+ {
+ LOG((LF_METADATA, LL_EVERYTHING, "UnlockRead called from CSemReadWrite::ConvertReadLockToWriteLock \n"));
+ m_pSem->UnlockRead();
+ m_fLockedForRead = false;
+ }
+ LOG((LF_METADATA, LL_EVERYTHING, "LockWrite called from CSemReadWrite::ConvertReadLockToWriteLock\n"));
+ IfFailRet(m_pSem->LockWrite());
+ m_fLockedForWrite = true;
+
+ return hr;
+} // CMDSemReadWrite::ConvertReadLockToWriteLock
+
+
+//*********************************************************************************************************
+//
+// Unlocking for write
+//
+//*********************************************************************************************************
+void CMDSemReadWrite::UnlockWrite()
+{
+ _ASSERTE(!m_fLockedForRead);
+
+ if (m_pSem == NULL)
+ {
+ INDEBUG(m_fLockedForWrite = false);
+ return;
+ }
+ if (m_fLockedForWrite)
+ {
+ LOG((LF_METADATA, LL_EVERYTHING, "UnlockWrite called from CSemReadWrite::UnlockWrite \n"));
+ m_pSem->UnlockWrite();
+ m_fLockedForWrite = false;
+ }
+} // CMDSemReadWrite::UnlockWrite
diff --git a/src/md/enc/stdafx.cpp b/src/md/enc/stdafx.cpp
new file mode 100644
index 0000000000..8bffbd1bf6
--- /dev/null
+++ b/src/md/enc/stdafx.cpp
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// stdafx.cpp
+//
+
+//
+// Precompiled headers.
+//
+//*****************************************************************************
+#include "stdafx.h"
diff --git a/src/md/enc/stdafx.h b/src/md/enc/stdafx.h
new file mode 100644
index 0000000000..62f41ae3c7
--- /dev/null
+++ b/src/md/enc/stdafx.h
@@ -0,0 +1,27 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// stdafx.h
+//
+
+//
+// Precompiled headers.
+//
+//*****************************************************************************
+#ifndef __STDAFX_H_
+#define __STDAFX_H_
+
+#include <crtwrap.h>
+#include <winwrap.h>
+#include <utilcode.h>
+
+#include <cor.h>
+#include <corpriv.h>
+
+#include "mdcommon.h"
+
+#include "utsem.h"
+
+#endif // __STDAFX_H__
diff --git a/src/md/enc/stgio.cpp b/src/md/enc/stgio.cpp
new file mode 100644
index 0000000000..020cf66b05
--- /dev/null
+++ b/src/md/enc/stgio.cpp
@@ -0,0 +1,1443 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// StgIO.h
+//
+
+//
+// This module handles disk/memory i/o for a generic set of storage solutions,
+// including:
+// * File system handle (HFILE)
+// * IStream
+// * User supplied memory buffer (non-movable)
+//
+// The Read, Write, Seek, ... functions are all directed to the corresponding
+// method for each type of file, allowing the consumer to use one set of api's.
+//
+// File system data can be paged fully into memory in two scenarios:
+// read: Normal memory mapped file is created to manage paging.
+// write: A custom paging system provides storage for pages as required. This
+// data is invalidated when you call Rewrite on the file.
+//
+// Transactions and backups are handled in the existing file case only. The
+// Rewrite function can make a backup of the current contents, and the Restore
+// function can be used to recover the data into the current scope. The backup
+// file is flushed to disk (which is slower but safer) after the copy. The
+// Restore also flushed the recovered changes to disk. Worst case scenario you
+// get a crash after calling Rewrite but before Restore, in which case you will
+// have a foo.clb.txn file in the same directory as the source file, foo.clb in
+// this example.
+//<REVISIT_TODO>
+// @FUTURE: issues,
+// 1. For reading a .clb in an image, it would be great to memory map
+// only the portion of the file with the .clb in it.
+//</REVISIT_TODO>
+//*****************************************************************************
+#include "stdafx.h" // Standard headers.
+#include "stgio.h" // Our definitions.
+#include "corerror.h"
+#include "posterror.h"
+#include "pedecoder.h"
+#include "pedecoder.inl"
+
+//********** Types. ***********************************************************
+#if !defined(FEATURE_METADATA_STANDALONE_WINRT_RO)
+#define SMALL_ALLOC_MAP_SIZE (64 * 1024) // 64 kb is the minimum size of virtual
+ // memory you can allocate, so anything
+ // less is a waste of VM resources.
+
+#else //FEATURE_METADATA_STANDALONE_WINRT_RO
+// RoMetadata.dll is required to call CreateFileMapping on all WinMD files (even small ones) to use
+// Code Intergrity checks on Win8 - see code:#EnableCodeIntegrity
+#define SMALL_ALLOC_MAP_SIZE 0
+
+#endif //FEATURE_METADATA_STANDALONE_WINRT_RO
+
+#define MIN_WRITE_CACHE_BYTES (16 * 1024) // 16 kb for a write back cache
+
+
+//********** Locals. **********************************************************
+HRESULT MapFileError(DWORD error);
+static void *AllocateMemory(int iSize);
+static void FreeMemory(void *pbData);
+inline HRESULT MapFileError(DWORD error)
+{
+ return (PostError(HRESULT_FROM_WIN32(error)));
+}
+
+// Static to class.
+int StgIO::m_iPageSize=0; // Size of an OS page.
+int StgIO::m_iCacheSize=0; // Size for the write cache.
+
+
+
+//********** Code. ************************************************************
+StgIO::StgIO(
+ bool bAutoMap) : // Memory map for read on open?
+ m_bAutoMap(bAutoMap)
+{
+ CtorInit();
+
+ // If the system page size has not been queried, do so now.
+ if (m_iPageSize == 0)
+ {
+ SYSTEM_INFO sInfo; // Some O/S information.
+
+ // Query the system page size.
+ GetSystemInfo(&sInfo);
+ m_iPageSize = sInfo.dwPageSize;
+ m_iCacheSize = ((MIN_WRITE_CACHE_BYTES - 1) & ~(m_iPageSize - 1)) + m_iPageSize;
+ }
+}
+
+
+void StgIO::CtorInit()
+{
+ m_bWriteThrough = false;
+ m_bRewrite = false;
+ m_bFreeMem = false;
+ m_pIStream = 0;
+ m_hFile = INVALID_HANDLE_VALUE;
+ m_hModule = NULL;
+ m_hMapping = 0;
+ m_pBaseData = 0;
+ m_pData = 0;
+ m_cbData = 0;
+ m_fFlags = 0;
+ m_iType = STGIO_NODATA;
+ m_cbOffset = 0;
+ m_rgBuff = 0;
+ m_cbBuff = 0;
+ m_rgPageMap = 0;
+ m_FileType = FILETYPE_UNKNOWN;
+ *m_rcShared = '\0';
+ m_cRef = 1;
+ m_mtMappedType = MTYPE_NOMAPPING;
+}
+
+
+
+StgIO::~StgIO()
+{
+ if (m_rgBuff)
+ {
+ FreeMemory(m_rgBuff);
+ m_rgBuff = 0;
+ }
+
+ Close();
+}
+
+
+//*****************************************************************************
+// Open the base file on top of: (a) file, (b) memory buffer, or (c) stream.
+// If create flag is specified, then this will create a new file with the
+// name supplied. No data is read from an opened file. You must call
+// MapFileToMem before doing direct pointer access to the contents.
+//*****************************************************************************
+HRESULT StgIO::Open( // Return code.
+ LPCWSTR szName, // Name of the storage.
+ int fFlags, // How to open the file.
+ const void *pbBuff, // Optional buffer for memory.
+ ULONG cbBuff, // Size of buffer.
+ IStream *pIStream, // Stream for input.
+ LPSECURITY_ATTRIBUTES pAttributes) // Security token.
+{
+ HRESULT hr;
+
+ // If we were given the storage memory to begin with, then use it.
+ if (pbBuff && cbBuff)
+ {
+ _ASSERTE((fFlags & DBPROP_TMODEF_WRITE) == 0);
+
+ // Save the memory address and size only. No handles.
+ m_pData = (void *) pbBuff;
+ m_cbData = cbBuff;
+
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ // All access to data will be by memory provided.
+ if ((fFlags & DBPROP_TMODEF_SHAREDMEM) == DBPROP_TMODEF_SHAREDMEM)
+ {
+ // We're taking ownership of this memory
+ m_pBaseData = m_pData;
+ m_iType = STGIO_SHAREDMEM;
+ }
+ else
+#endif //!FEATURE_METADATA_STANDALONE_WINRT_RO
+ {
+ m_iType = STGIO_MEM;
+ }
+ goto ErrExit;
+ }
+ // Check for data backed by a stream pointer.
+ else if (pIStream)
+ {
+ // If this is for the non-create case, get the size of existing data.
+ if ((fFlags & DBPROP_TMODEF_CREATE) == 0)
+ {
+ LARGE_INTEGER iMove = { { 0, 0 } };
+ ULARGE_INTEGER iSize;
+
+ // Need the size of the data so we can map it into memory.
+ if (FAILED(hr = pIStream->Seek(iMove, STREAM_SEEK_END, &iSize)))
+ return (hr);
+ m_cbData = iSize.u.LowPart;
+ }
+ // Else there is nothing.
+ else
+ m_cbData = 0;
+
+ // Save an addref'd copy of the stream.
+ m_pIStream = pIStream;
+ m_pIStream->AddRef();
+
+ // All access to data will be by memory provided.
+ m_iType = STGIO_STREAM;
+ goto ErrExit;
+ }
+
+ // If not on memory, we need a file to do a create/open.
+ if (!szName || !*szName)
+ {
+ return (PostError(E_INVALIDARG));
+ }
+ // Check for create of a new file.
+ else if (fFlags & DBPROP_TMODEF_CREATE)
+ {
+ //<REVISIT_TODO>@future: This could chose to open the file in write through
+ // mode, which would provide better Duribility (from ACID props),
+ // but would be much slower.</REVISIT_TODO>
+
+ // Create the new file, overwriting only if caller allows it.
+ if ((m_hFile = WszCreateFile(szName, GENERIC_READ | GENERIC_WRITE, 0, 0,
+ (fFlags & DBPROP_TMODEF_FAILIFTHERE) ? CREATE_NEW : CREATE_ALWAYS,
+ 0, 0)) == INVALID_HANDLE_VALUE)
+ {
+ return (MapFileError(GetLastError()));
+ }
+
+ // Data will come from the file.
+ m_iType = STGIO_HFILE;
+ }
+ // For open in read mode, need to open the file on disk. If opening a shared
+ // memory view, it has to be opened already, so no file open.
+ else if ((fFlags & DBPROP_TMODEF_WRITE) == 0)
+ {
+ // We have not opened the file nor loaded it as module
+ _ASSERTE(m_hFile == INVALID_HANDLE_VALUE);
+ _ASSERTE(m_hModule == NULL);
+
+ // Open the file for read. Sharing is determined by caller, it can
+ // allow other readers or be exclusive.
+ DWORD dwFileSharingFlags = FILE_SHARE_DELETE;
+ if (!(fFlags & DBPROP_TMODEF_EXCLUSIVE))
+ {
+ dwFileSharingFlags |= FILE_SHARE_READ;
+
+#if !defined(DACCESS_COMPILE) && !defined(FEATURE_PAL)
+ // PEDecoder is not defined in DAC
+
+ // We prefer to use LoadLibrary if we can because it will share already loaded images (used for execution)
+ // which saves virtual memory. We only do this if our caller has indicated that this PE file is trusted
+ // and thus it is OK to do LoadLibrary (note that we still only load it as a resource, which mitigates
+ // most of the security risk anyway).
+ if ((fFlags & DBPROP_TMODEF_TRYLOADLIBRARY) != 0)
+ {
+ m_hModule = WszLoadLibraryEx(szName, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
+ if (m_hModule != NULL)
+ {
+ m_iType = STGIO_HMODULE;
+
+ m_mtMappedType = MTYPE_IMAGE;
+
+ // LoadLibraryEx returns 2 lowest bits indicating how the module was loaded
+ m_pBaseData = m_pData = (void *)(((INT_PTR)m_hModule) & ~(INT_PTR)0x3);
+
+ PEDecoder peDecoder;
+ if (SUCCEEDED(peDecoder.Init(
+ m_pBaseData,
+ false)) && // relocated
+ peDecoder.CheckNTHeaders())
+ {
+ m_cbData = peDecoder.GetNTHeaders32()->OptionalHeader.SizeOfImage;
+ }
+ else
+ {
+ // PEDecoder failed on loaded library, let's backout all our changes to this object
+ // and fall back to file mapping
+ m_iType = STGIO_NODATA;
+ m_mtMappedType = MTYPE_NOMAPPING;
+ m_pBaseData = m_pData = NULL;
+
+ FreeLibrary(m_hModule);
+ m_hModule = NULL;
+ }
+ }
+ }
+#endif //!DACCESS_COMPILE && !FEATURE_PAL
+ }
+
+ if (m_hModule == NULL)
+ { // We didn't get the loaded module (we either didn't want to or it failed)
+ HandleHolder hFile(WszCreateFile(szName,
+ GENERIC_READ,
+ dwFileSharingFlags,
+ 0,
+ OPEN_EXISTING,
+ 0,
+ 0));
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return (MapFileError(GetLastError()));
+
+ // Get size of file.
+ m_cbData = ::SetFilePointer(hFile, 0, 0, FILE_END);
+
+ // Can't read anything from an empty file.
+ if (m_cbData == 0)
+ return (PostError(CLDB_E_NO_DATA));
+
+ // Data will come from the file.
+ m_hFile = hFile.Extract();
+
+ m_iType = STGIO_HFILE;
+ }
+ }
+
+ErrExit:
+
+ // If we will ever write, then we need the buffer cache.
+ if (fFlags & DBPROP_TMODEF_WRITE)
+ {
+ // Allocate a cache buffer for writing.
+ if ((m_rgBuff = (BYTE *) AllocateMemory(m_iCacheSize)) == NULL)
+ {
+ Close();
+ return PostError(OutOfMemory());
+ }
+ m_cbBuff = 0;
+ }
+
+ // Save flags for later.
+ m_fFlags = fFlags;
+ if ((szName != NULL) && (*szName != 0))
+ {
+ WCHAR rcExt[_MAX_PATH];
+ SplitPath(szName, NULL, 0, NULL, 0, NULL, 0, rcExt, _MAX_PATH);
+ if (SString::_wcsicmp(rcExt, W(".obj")) == 0)
+ {
+ m_FileType = FILETYPE_NTOBJ;
+ }
+ else if (SString::_wcsicmp(rcExt, W(".tlb")) == 0)
+ {
+ m_FileType = FILETYPE_TLB;
+ }
+ }
+
+ // For auto map case, map the view of the file as part of open.
+ if (m_bAutoMap &&
+ (m_iType == STGIO_HFILE || m_iType == STGIO_STREAM) &&
+ !(fFlags & DBPROP_TMODEF_CREATE))
+ {
+ void * ptr;
+ ULONG cb;
+
+ if (FAILED(hr = MapFileToMem(ptr, &cb, pAttributes)))
+ {
+ Close();
+ return hr;
+ }
+ }
+ return S_OK;
+} // StgIO::Open
+
+
+//*****************************************************************************
+// Shut down the file handles and allocated objects.
+//*****************************************************************************
+void StgIO::Close()
+{
+ switch (m_iType)
+ {
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ // Free any allocated memory.
+ case STGIO_SHAREDMEM:
+ if (m_pBaseData != NULL)
+ {
+ CoTaskMemFree(m_pBaseData);
+ m_pBaseData = NULL;
+ break;
+ }
+#endif //!FEATURE_METADATA_STANDALONE_WINRT_RO
+
+ case STGIO_MEM:
+ case STGIO_HFILEMEM:
+ if (m_bFreeMem && m_pBaseData)
+ {
+ FreeMemory(m_pBaseData);
+ m_pBaseData = m_pData = 0;
+ }
+ // Intentional fall through to file case, if we kept handle open.
+
+ case STGIO_HFILE:
+ {
+ // Free the file handle.
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(m_hFile);
+
+ // If we allocated space for in memory paging, then free it.
+ }
+ break;
+
+ case STGIO_HMODULE:
+ {
+ if (m_hModule != NULL)
+ FreeLibrary(m_hModule);
+ m_hModule = NULL;
+ break;
+ }
+
+ // Free the stream pointer.
+ case STGIO_STREAM:
+ {
+ if (m_pIStream != NULL)
+ m_pIStream->Release();
+ }
+ break;
+
+ // Weird to shut down what you didn't open, isn't it? Allow for
+ // error case where dtor shuts down as an afterthought.
+ case STGIO_NODATA:
+ default:
+ return;
+ }
+
+ // Free any page map and base data.
+ FreePageMap();
+
+ // Reset state values so we don't get confused.
+ CtorInit();
+}
+
+//*****************************************************************************
+// Called to read the data into allocated memory and release the backing store.
+// Only available on read-only data.
+//*****************************************************************************
+HRESULT
+StgIO::LoadFileToMemory()
+{
+ HRESULT hr;
+ void *pData; // Allocated buffer for file.
+ ULONG cbData; // Size of the data.
+ ULONG cbRead = 0; // Data actually read.
+
+ // Make sure it is a read-only file.
+ if (m_fFlags & DBPROP_TMODEF_WRITE)
+ return E_INVALIDARG;
+
+ // Try to allocate the buffer.
+ cbData = m_cbData;
+ pData = AllocateMemory(cbData);
+ IfNullGo(pData);
+
+ // Try to read the file into the buffer.
+ IfFailGo(Read(pData, cbData, &cbRead));
+ if (cbData != cbRead)
+ {
+ _ASSERTE_MSG(FALSE, "Read didn't succeed.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Done with the old data.
+ Close();
+
+ // Open with new data.
+ hr = Open(NULL /* szName */, STGIO_READ, pData, cbData, NULL /* IStream* */, NULL /* lpSecurityAttributes */);
+ _ASSERTE(SUCCEEDED(hr)); // should not be a failure code path with open on buffer.
+
+ // Mark the new memory so that it will be freed later.
+ m_pBaseData = m_pData;
+ m_bFreeMem = true;
+
+ErrExit:
+ if (FAILED(hr) && pData)
+ FreeMemory(pData);
+
+ return hr;
+} // StgIO::LoadFileToMemory
+
+
+//*****************************************************************************
+// Read data from the storage source. This will handle all types of backing
+// storage from mmf, streams, and file handles. No read ahead or MRU
+// caching is done.
+//*****************************************************************************
+HRESULT StgIO::Read( // Return code.
+ void *pbBuff, // Write buffer here.
+ ULONG cbBuff, // How much to read.
+ ULONG *pcbRead) // How much read.
+{
+ ULONG cbCopy; // For boundary checks.
+ void *pbData; // Data buffer for mem read.
+ HRESULT hr = S_OK;
+
+ // Validate arguments, don't call if you don't need to.
+ _ASSERTE(pbBuff != 0);
+ _ASSERTE(cbBuff > 0);
+
+ // Get the data based on type.
+ switch (m_iType)
+ {
+ // For data on file, there are two possiblities:
+ // (1) We have an in memory backing store we should use, or
+ // (2) We just need to read from the file.
+ case STGIO_HFILE:
+ case STGIO_HMODULE:
+ {
+ _ASSERTE((m_hFile != INVALID_HANDLE_VALUE) || (m_hModule != NULL));
+
+ // Backing store does its own paging.
+ if (IsBackingStore() || IsMemoryMapped())
+ {
+ // Force the data into memory.
+ if (FAILED(hr = GetPtrForMem(GetCurrentOffset(), cbBuff, pbData)))
+ goto ErrExit;
+
+ // Copy it back for the user and save the size.
+ memcpy(pbBuff, pbData, cbBuff);
+ if (pcbRead)
+ *pcbRead = cbBuff;
+ }
+ // If there is no backing store, this is just a read operation.
+ else
+ {
+ _ASSERTE((m_iType == STGIO_HFILE) && (m_hFile != INVALID_HANDLE_VALUE));
+ _ASSERTE(m_hModule == NULL);
+
+ ULONG cbTemp = 0;
+ if (!pcbRead)
+ pcbRead = &cbTemp;
+ hr = ReadFromDisk(pbBuff, cbBuff, pcbRead);
+ m_cbOffset += *pcbRead;
+ }
+ }
+ break;
+
+ // Data in a stream is always just read.
+ case STGIO_STREAM:
+ {
+ _ASSERTE((IStream *) m_pIStream);
+ if (!pcbRead)
+ pcbRead = &cbCopy;
+ *pcbRead = 0;
+ hr = m_pIStream->Read(pbBuff, cbBuff, pcbRead);
+ if (SUCCEEDED(hr))
+ m_cbOffset += *pcbRead;
+ }
+ break;
+
+ // Simply copy the data from our data.
+ case STGIO_MEM:
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ case STGIO_SHAREDMEM:
+#endif
+ case STGIO_HFILEMEM:
+ {
+ _ASSERTE(m_pData && m_cbData);
+
+ // Check for read past end of buffer and adjust.
+ if (GetCurrentOffset() + cbBuff > m_cbData)
+ cbCopy = m_cbData - GetCurrentOffset();
+ else
+ cbCopy = cbBuff;
+
+ // Copy the data into the callers buffer.
+ memcpy(pbBuff, (void *) ((DWORD_PTR)m_pData + GetCurrentOffset()), cbCopy);
+ if (pcbRead)
+ *pcbRead = cbCopy;
+
+ // Save a logical offset.
+ m_cbOffset += cbCopy;
+ }
+ break;
+
+ case STGIO_NODATA:
+ default:
+ _ASSERTE(0);
+ break;
+ }
+
+ErrExit:
+ return (hr);
+}
+
+
+//*****************************************************************************
+// Write to disk. This function will cache up to a page of data in a buffer
+// and peridocially flush it on overflow and explicit request. This makes it
+// safe to do lots of small writes without too much performance overhead.
+//*****************************************************************************
+HRESULT StgIO::Write( // true/false.
+ const void *pbBuff, // Data to write.
+ ULONG cbWrite, // How much data to write.
+ ULONG *pcbWritten) // How much did get written.
+{
+ ULONG cbWriteIn=cbWrite; // Track amount written.
+ ULONG cbCopy;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(m_rgBuff != 0);
+ _ASSERTE(cbWrite);
+
+ while (cbWrite)
+ {
+ // In the case where the buffer is already huge, write the whole thing
+ // and avoid the cache.
+ if (m_cbBuff == 0 && cbWrite >= (ULONG) m_iPageSize)
+ {
+ if (SUCCEEDED(hr = WriteToDisk(pbBuff, cbWrite, pcbWritten)))
+ m_cbOffset += cbWrite;
+ break;
+ }
+ // Otherwise cache as much as we can and flush.
+ else
+ {
+ // Determine how much data goes into the cache buffer.
+ cbCopy = m_iPageSize - m_cbBuff;
+ cbCopy = min(cbCopy, cbWrite);
+
+ // Copy the data into the cache and adjust counts.
+ memcpy(&m_rgBuff[m_cbBuff], pbBuff, cbCopy);
+ pbBuff = (void *) ((DWORD_PTR)pbBuff + cbCopy);
+ m_cbBuff += cbCopy;
+ m_cbOffset += cbCopy;
+ cbWrite -= cbCopy;
+
+ // If there is enough data, then flush it to disk and reset count.
+ if (m_cbBuff >= (ULONG) m_iPageSize)
+ {
+ if (FAILED(hr = FlushCache()))
+ break;
+ }
+ }
+ }
+
+ // Return value for caller.
+ if (SUCCEEDED(hr) && pcbWritten)
+ *pcbWritten = cbWriteIn;
+ return (hr);
+}
+
+
+//*****************************************************************************
+// Moves the file pointer to the new location. This handles the different
+// types of storage systems.
+//*****************************************************************************
+HRESULT StgIO::Seek( // New offset.
+ int lVal, // How much to move.
+ ULONG fMoveType) // Direction, use Win32 FILE_xxxx.
+{
+ ULONG cbRtn = 0;
+ HRESULT hr = NOERROR;
+
+ _ASSERTE(fMoveType >= FILE_BEGIN && fMoveType <= FILE_END);
+
+ // Action taken depends on type of storage.
+ switch (m_iType)
+ {
+ case STGIO_HFILE:
+ {
+ // Use the file system's move.
+ _ASSERTE(m_hFile != INVALID_HANDLE_VALUE);
+ cbRtn = ::SetFilePointer(m_hFile, lVal, 0, fMoveType);
+
+ // Save the location redundantly.
+ if (cbRtn != 0xffffffff)
+ {
+ // make sure that m_cbOffset will stay within range
+ if (cbRtn > m_cbData || cbRtn < 0)
+ {
+ IfFailGo(STG_E_INVALIDFUNCTION);
+ }
+ m_cbOffset = cbRtn;
+ }
+ }
+ break;
+
+ case STGIO_STREAM:
+ {
+ LARGE_INTEGER iMove;
+ ULARGE_INTEGER iNewLoc;
+
+ // Need a 64-bit int.
+ iMove.QuadPart = lVal;
+
+ // The move types are named differently, but have same value.
+ if (FAILED(hr = m_pIStream->Seek(iMove, fMoveType, &iNewLoc)))
+ return (hr);
+
+ // make sure that m_cbOffset will stay within range
+ if (iNewLoc.u.LowPart > m_cbData || iNewLoc.u.LowPart < 0)
+ IfFailGo(STG_E_INVALIDFUNCTION);
+
+ // Save off only out location.
+ m_cbOffset = iNewLoc.u.LowPart;
+ }
+ break;
+
+ case STGIO_MEM:
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ case STGIO_SHAREDMEM:
+#endif
+ case STGIO_HFILEMEM:
+ case STGIO_HMODULE:
+ {
+ // We own the offset, so change our value.
+ switch (fMoveType)
+ {
+ case FILE_BEGIN:
+
+ // make sure that m_cbOffset will stay within range
+ if ((ULONG) lVal > m_cbData || lVal < 0)
+ {
+ IfFailGo(STG_E_INVALIDFUNCTION);
+ }
+ m_cbOffset = lVal;
+ break;
+
+ case FILE_CURRENT:
+
+ // make sure that m_cbOffset will stay within range
+ if (m_cbOffset + lVal > m_cbData)
+ {
+ IfFailGo(STG_E_INVALIDFUNCTION);
+ }
+ m_cbOffset = m_cbOffset + lVal;
+ break;
+
+ case FILE_END:
+ _ASSERTE(lVal < (LONG) m_cbData);
+ // make sure that m_cbOffset will stay within range
+ if (m_cbData + lVal > m_cbData)
+ {
+ IfFailGo(STG_E_INVALIDFUNCTION);
+ }
+ m_cbOffset = m_cbData + lVal;
+ break;
+ }
+
+ cbRtn = m_cbOffset;
+ }
+ break;
+
+ // Weird to seek with no data.
+ case STGIO_NODATA:
+ default:
+ _ASSERTE(0);
+ break;
+ }
+
+ErrExit:
+ return hr;
+}
+
+
+//*****************************************************************************
+// Retrieves the current offset for the storage being used. This value is
+// tracked based on Read, Write, and Seek operations.
+//*****************************************************************************
+ULONG StgIO::GetCurrentOffset() // Current offset.
+{
+ return (m_cbOffset);
+}
+
+
+//*****************************************************************************
+// Map the file contents to a memory mapped file and return a pointer to the
+// data. For read/write with a backing store, map the file using an internal
+// paging system.
+//*****************************************************************************
+HRESULT StgIO::MapFileToMem( // Return code.
+ void *&ptr, // Return pointer to file data.
+ ULONG *pcbSize, // Return size of data.
+ LPSECURITY_ATTRIBUTES pAttributes) // Security token.
+{
+ char rcShared[MAXSHMEM]; // ANSI version of shared name.
+ HRESULT hr = S_OK;
+
+ // Don't penalize for multiple calls. Also, allow calls for mem type so
+ // callers don't need to do so much checking.
+ if (IsBackingStore() ||
+ IsMemoryMapped() ||
+ (m_iType == STGIO_MEM) ||
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ (m_iType == STGIO_SHAREDMEM) ||
+#endif
+ (m_iType == STGIO_HFILEMEM))
+ {
+ ptr = m_pData;
+ if (pcbSize)
+ *pcbSize = m_cbData;
+ return (S_OK);
+ }
+
+ //#CopySmallFiles
+ // Check the size of the data we want to map. If it is small enough, then
+ // simply allocate a chunk of memory from a finer grained heap. This saves
+ // virtual memory space, page table entries, and should reduce overall working set.
+ // Note that any shared memory objects will require the handles to be in place
+ // and are not elligible. Also, open for read/write needs a full backing
+ // store.
+ if (!*m_rcShared && (m_cbData <= SMALL_ALLOC_MAP_SIZE) && (SMALL_ALLOC_MAP_SIZE > 0))
+ {
+ DWORD cbRead = m_cbData;
+ _ASSERTE(m_pData == 0);
+
+ // Just malloc a chunk of data to use.
+ m_pBaseData = m_pData = AllocateMemory(m_cbData);
+ if (!m_pData)
+ {
+ hr = OutOfMemory();
+ goto ErrExit;
+ }
+
+ // Read all of the file contents into this piece of memory.
+ IfFailGo( Seek(0, FILE_BEGIN) );
+ if (FAILED(hr = Read(m_pData, cbRead, &cbRead)))
+ {
+ FreeMemory(m_pData);
+ m_pData = 0;
+ goto ErrExit;
+ }
+ _ASSERTE(cbRead == m_cbData);
+
+ // If the file isn't being opened for exclusive mode, then free it.
+ // If it is for exclusive, then we need to keep the handle open so the
+ // file is locked, preventing other readers. Also leave it open if
+ // in read/write mode so we can truncate and rewrite.
+ if (m_hFile == INVALID_HANDLE_VALUE ||
+ ((m_fFlags & DBPROP_TMODEF_EXCLUSIVE) == 0 && (m_fFlags & DBPROP_TMODEF_WRITE) == 0))
+ {
+ // If there was a handle open, then free it.
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ {
+ VERIFY(CloseHandle(m_hFile));
+ m_hFile = INVALID_HANDLE_VALUE;
+ }
+ // Free the stream pointer.
+ else
+ if (m_pIStream != 0)
+ {
+ m_pIStream->Release();
+ m_pIStream = 0;
+ }
+
+ // Switch the type to memory only access.
+ m_iType = STGIO_MEM;
+ }
+ else
+ m_iType = STGIO_HFILEMEM;
+
+ // Free the memory when we shut down.
+ m_bFreeMem = true;
+ }
+ // Finally, a real mapping file must be created.
+ else
+ {
+ // Now we will map, so better have it right.
+ _ASSERTE(m_hFile != INVALID_HANDLE_VALUE || m_iType == STGIO_STREAM);
+ _ASSERTE(m_rgPageMap == 0);
+
+ // For read mode, use a memory mapped file since the size will never
+ // change for the life of the handle.
+ if ((m_fFlags & DBPROP_TMODEF_WRITE) == 0 && m_iType != STGIO_STREAM)
+ {
+ _ASSERTE(!*m_rcShared);
+
+ // Create a mapping object for the file.
+ _ASSERTE(m_hMapping == 0);
+
+ DWORD dwProtectionFlags = PAGE_READONLY;
+#ifdef FEATURE_METADATA_STANDALONE_WINRT_RO
+ //#EnableCodeIntegrity
+ // RoMetadata.dll is required to always map (WinMD) files with SEC_IMAGE to enable Code Integrity checkes on Win8
+ // Note: MidlRtMd.dll cannot do the same, because it runs on pre-Win8 OS versions where SEC_IMAGE-mapping will likely
+ // refuse WinMD files (they are Win8+ only in PE headers)
+ dwProtectionFlags |= SEC_IMAGE;
+#endif
+
+ if ((m_hMapping = WszCreateFileMapping(m_hFile, pAttributes, dwProtectionFlags,
+ 0, 0, *m_rcShared ? m_rcShared : 0)) == 0)
+ {
+ return (MapFileError(GetLastError()));
+ }
+#ifdef FEATURE_METADATA_STANDALONE_WINRT_RO
+ m_mtMappedType = MTYPE_IMAGE;
+#else // FEATURE_METADATA_STANDALONE_WINRT_RO
+ m_mtMappedType = MTYPE_FLAT;
+#endif // FEATURE_METADATA_STANDALONE_WINRT_RO
+ // Check to see if the memory already exists, in which case we have
+ // no guarantees it is the right piece of data.
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
+ {
+ hr = PostError(CLDB_E_SMDUPLICATE, rcShared);
+ goto ErrExit;
+ }
+
+ // Now map the file into memory so we can read from pointer access.
+ // <REVISIT_TODO>Note: Added a check for IsBadReadPtr per the Services team which
+ // indicates that under some conditions this API can give you back
+ // a totally bogus pointer.</REVISIT_TODO>
+ if ((m_pBaseData = m_pData = MapViewOfFile(m_hMapping, FILE_MAP_READ,
+ 0, 0, 0)) == 0)
+ {
+ hr = MapFileError(GetLastError());
+ if (SUCCEEDED(hr))
+ {
+ _ASSERTE_MSG(FALSE, "Error code doesn't indicate error.");
+ hr = PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ // In case we got back a bogus pointer.
+ m_pBaseData = m_pData = NULL;
+ goto ErrExit;
+ }
+ }
+ // In write mode, we need the hybrid combination of being able to back up
+ // the data in memory via cache, but then later rewrite the contents and
+ // throw away our cached copy. Memory mapped files are not good for this
+ // case due to poor write characteristics.
+ else
+ {
+ ULONG iMaxSize; // How much memory required for file.
+
+ // Figure out how many pages we'll require, round up actual data
+ // size to page size.
+ iMaxSize = (((m_cbData - 1) & ~(m_iPageSize - 1)) + m_iPageSize);
+ // Check integer overflow in previous statement
+ if (iMaxSize < m_cbData)
+ {
+ IfFailGo(PostError(COR_E_OVERFLOW));
+ }
+
+ // Allocate a bit vector to track loaded pages.
+ if ((m_rgPageMap = new (nothrow) BYTE[iMaxSize / m_iPageSize]) == 0)
+ return (PostError(OutOfMemory()));
+ memset(m_rgPageMap, 0, sizeof(BYTE) * (iMaxSize / m_iPageSize));
+
+ // Allocate space for the file contents.
+ if ((m_pBaseData = m_pData = ::ClrVirtualAlloc(0, iMaxSize, MEM_RESERVE, PAGE_NOACCESS)) == 0)
+ {
+ hr = PostError(OutOfMemory());
+ goto ErrExit;
+ }
+ }
+ }
+
+ // Reset any changes made by mapping.
+ IfFailGo( Seek(0, FILE_BEGIN) );
+
+ErrExit:
+
+ // Check for errors and clean up.
+ if (FAILED(hr))
+ {
+ if (m_hMapping)
+ CloseHandle(m_hMapping);
+ m_hMapping = 0;
+ m_pBaseData = m_pData = 0;
+ m_cbData = 0;
+ }
+ ptr = m_pData;
+ if (pcbSize)
+ *pcbSize = m_cbData;
+ return (hr);
+}
+
+
+//*****************************************************************************
+// Free the mapping object for shared memory but keep the rest of the internal
+// state intact.
+//*****************************************************************************
+HRESULT StgIO::ReleaseMappingObject() // Return code.
+{
+ // Check type first.
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ if (m_iType != STGIO_SHAREDMEM)
+ {
+ _ASSERTE(FALSE);
+ return S_OK;
+ }
+
+ // Must have an allocated handle.
+ _ASSERTE(m_hMapping != 0);
+
+ // Freeing the mapping object doesn't do any good if you still have the file.
+ _ASSERTE(m_hFile == INVALID_HANDLE_VALUE);
+
+ // Unmap the memory we allocated before freeing the handle. But keep the
+ // memory address intact.
+ if (m_pData)
+ VERIFY(UnmapViewOfFile(m_pData));
+
+ // Free the handle.
+ if (m_hMapping != 0)
+ {
+ VERIFY(CloseHandle(m_hMapping));
+ m_hMapping = 0;
+ }
+#endif //!FEATURE_METADATA_STANDALONE_WINRT_RO
+ return S_OK;
+}
+
+
+
+//*****************************************************************************
+// Resets the logical base address and size to the value given. This is for
+// cases like finding a section embedded in another format, like the .clb inside
+// of an image. GetPtrForMem, Read, and Seek will then behave as though only
+// data from pbStart to cbSize is valid.
+//*****************************************************************************
+HRESULT StgIO::SetBaseRange( // Return code.
+ void *pbStart, // Start of file data.
+ ULONG cbSize) // How big is the range.
+{
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ if (m_iType == STGIO_SHAREDMEM)
+ {
+ // The base range must be inside of the current range.
+ _ASSERTE((m_pBaseData != NULL) && (m_cbData != 0));
+ _ASSERTE(((LONG_PTR) pbStart >= (LONG_PTR) m_pBaseData));
+ _ASSERTE(((LONG_PTR) pbStart + cbSize <= (LONG_PTR) m_pBaseData + m_cbData));
+ }
+#endif //!FEATURE_METADATA_STANDALONE_WINRT_RO
+
+ // Save the base range per user request.
+ m_pData = pbStart;
+ m_cbData = cbSize;
+ return S_OK;
+}
+
+
+//*****************************************************************************
+// Caller wants a pointer to a chunk of the file. This function will make sure
+// that the memory for that chunk has been committed and will load from the
+// file if required. This algorithm attempts to load no more data from disk
+// than is necessary. It walks the required pages from lowest to highest,
+// and for each block of unloaded pages, the memory is committed and the data
+// is read from disk. If all pages are unloaded, all of them are loaded at
+// once to speed throughput from disk.
+//*****************************************************************************
+HRESULT StgIO::GetPtrForMem( // Return code.
+ ULONG cbStart, // Where to start getting memory.
+ ULONG cbSize, // How much data.
+ void *&ptr) // Return pointer to memory here.
+{
+ int iFirst, iLast; // First and last page required.
+ ULONG iOffset, iSize; // For committing ranges of memory.
+ int i, j; // Loop control.
+ HRESULT hr;
+
+ // We need either memory (mmf or user supplied) or a backing store to
+ // return a pointer. Call Read if you don't have these.
+ if (!IsBackingStore() && m_pData == 0)
+ return (PostError(BadError(E_UNEXPECTED)));
+
+ // Validate the caller isn't asking for a data value out of range.
+ if (!(ClrSafeInt<ULONG>::addition(cbStart, cbSize, iOffset)
+ && (iOffset <= m_cbData)))
+ return (PostError(E_INVALIDARG));
+
+ // This code will check for pages that need to be paged from disk in
+ // order for us to return a pointer to that memory.
+ if (IsBackingStore())
+ {
+ // Backing store is bogus when in rewrite mode.
+ if (m_bRewrite)
+ return (PostError(BadError(E_UNEXPECTED)));
+
+ // Must have the page map to continue.
+ _ASSERTE(m_rgPageMap && m_iPageSize && m_pData);
+
+ // Figure out the first and last page that are required for commit.
+ iFirst = cbStart / m_iPageSize;
+ iLast = (cbStart + cbSize - 1) / m_iPageSize;
+
+ // Avoid confusion.
+ ptr = 0;
+
+ // Do a smart load of every page required. Do not reload pages that have
+ // already been brought in from disk.
+ //<REVISIT_TODO>@FUTURE: add an optimization so that when all pages have been faulted, we no
+ // longer to a page by page search.</REVISIT_TODO>
+ for (i=iFirst; i<=iLast; )
+ {
+ // Find the first page that hasn't already been loaded.
+ while (GetBit(m_rgPageMap, i) && i<=iLast)
+ ++i;
+ if (i > iLast)
+ break;
+
+ // Offset for first thing to load.
+ iOffset = i * m_iPageSize;
+ iSize = 0;
+
+ // See how many in a row have not been loaded.
+ for (j=i; i<=iLast && !GetBit(m_rgPageMap, i); i++)
+ {
+ // Safe: iSize += m_iPageSize;
+ if (!(ClrSafeInt<ULONG>::addition(iSize, m_iPageSize, iSize)))
+ {
+ return PostError(E_INVALIDARG);
+ }
+ }
+
+ // First commit the memory for this part of the file.
+ if (::ClrVirtualAlloc((void *) ((DWORD_PTR) m_pData + iOffset),
+ iSize, MEM_COMMIT, PAGE_READWRITE) == 0)
+ return (PostError(OutOfMemory()));
+
+ // Now load that portion of the file from disk.
+ if (FAILED(hr = Seek(iOffset, FILE_BEGIN)) ||
+ FAILED(hr = ReadFromDisk((void *) ((DWORD_PTR) m_pData + iOffset), iSize, 0)))
+ {
+ return (hr);
+ }
+
+ // Change the memory to read only to avoid any modifications. Any faults
+ // that occur indicate a bug whereby the engine is trying to write to
+ // protected memory.
+ _ASSERTE(::ClrVirtualAlloc((void *) ((DWORD_PTR) m_pData + iOffset),
+ iSize, MEM_COMMIT, PAGE_READONLY) != 0);
+
+ // Record each new loaded page.
+ for (; j<i; j++)
+ SetBit(m_rgPageMap, j, true);
+ }
+
+ // Everything was brought into memory, so now return pointer to caller.
+ ptr = (void *) ((DWORD_PTR) m_pData + cbStart);
+ }
+ // Memory version or memory mapped file work the same way.
+ else if (IsMemoryMapped() ||
+ (m_iType == STGIO_MEM) ||
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ (m_iType == STGIO_SHAREDMEM) ||
+#endif
+ (m_iType == STGIO_HFILEMEM))
+ {
+ if (!(cbStart <= m_cbData))
+ return (PostError(E_INVALIDARG));
+
+ ptr = (void *) ((DWORD_PTR) m_pData + cbStart);
+ }
+ // What's left?! Add some defense.
+ else
+ {
+ _ASSERTE(0);
+ ptr = 0;
+ return (PostError(BadError(E_UNEXPECTED)));
+ }
+ return (S_OK);
+}
+
+
+//*****************************************************************************
+// For cached writes, flush the cache to the data store.
+//*****************************************************************************
+HRESULT StgIO::FlushCache()
+{
+ ULONG cbWritten;
+ HRESULT hr;
+
+ if (m_cbBuff)
+ {
+ if (FAILED(hr = WriteToDisk(m_rgBuff, m_cbBuff, &cbWritten)))
+ return (hr);
+ m_cbBuff = 0;
+ }
+ return (S_OK);
+}
+
+//*****************************************************************************
+// Tells the file system to flush any cached data it may have. This is
+// expensive, but if successful guarantees you won't lose writes short of
+// a disk failure.
+//*****************************************************************************
+HRESULT StgIO::FlushFileBuffers()
+{
+ _ASSERTE(!IsReadOnly());
+
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ {
+ if (::FlushFileBuffers(m_hFile))
+ return (S_OK);
+ else
+ return (MapFileError(GetLastError()));
+ }
+ return (S_OK);
+}
+
+
+//*****************************************************************************
+// Called after a successful rewrite of an existing file. The in memory
+// backing store is no longer valid because all new data is in memory and
+// on disk. This is essentially the same state as created, so free up some
+// working set and remember this state.
+//*****************************************************************************
+HRESULT StgIO::ResetBackingStore() // Return code.
+{
+ // Don't be calling this function for read only data.
+ _ASSERTE(!IsReadOnly());
+
+ // Free up any backing store data we no longer need now that everything
+ // is in memory.
+ FreePageMap();
+ return (S_OK);
+}
+
+
+//
+// Private.
+//
+
+
+
+//*****************************************************************************
+// This version will force the data in cache out to disk for real. The code
+// can handle the different types of storage we might be sitting on based on
+// the open type.
+//*****************************************************************************
+HRESULT StgIO::WriteToDisk( // Return code.
+ const void *pbBuff, // Buffer to write.
+ ULONG cbWrite, // How much.
+ ULONG *pcbWritten) // Return how much written.
+{
+ ULONG cbWritten; // Buffer for write funcs.
+ HRESULT hr = S_OK;
+
+ // Pretty obvious.
+ _ASSERTE(!IsReadOnly());
+
+ // Always need a buffer to write this data to.
+ if (!pcbWritten)
+ pcbWritten = &cbWritten;
+
+ // Action taken depends on type of storage.
+ switch (m_iType)
+ {
+ case STGIO_HFILE:
+ case STGIO_HFILEMEM:
+ {
+ // Use the file system's move.
+ _ASSERTE(m_hFile != INVALID_HANDLE_VALUE);
+
+ // Do the write to disk.
+ if (!::WriteFile(m_hFile, pbBuff, cbWrite, pcbWritten, 0))
+ hr = MapFileError(GetLastError());
+ }
+ break;
+
+ // Free the stream pointer.
+ case STGIO_STREAM:
+ {
+ // Delegate write to stream code.
+ hr = m_pIStream->Write(pbBuff, cbWrite, pcbWritten);
+ }
+ break;
+
+ // We cannot write to fixed read/only memory or LoadLibrary module.
+ case STGIO_HMODULE:
+ case STGIO_MEM:
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ case STGIO_SHAREDMEM:
+#endif
+ _ASSERTE(0);
+ hr = BadError(E_UNEXPECTED);
+ break;
+
+ // Weird to seek with no data.
+ case STGIO_NODATA:
+ default:
+ _ASSERTE(0);
+ break;
+ }
+ return (hr);
+}
+
+
+//*****************************************************************************
+// This version only reads from disk.
+//*****************************************************************************
+HRESULT StgIO::ReadFromDisk( // Return code.
+ void *pbBuff, // Write buffer here.
+ ULONG cbBuff, // How much to read.
+ ULONG *pcbRead) // How much read.
+{
+ ULONG cbRead;
+
+ _ASSERTE(m_iType == STGIO_HFILE || m_iType == STGIO_STREAM);
+
+ // Need to have a buffer.
+ if (!pcbRead)
+ pcbRead = &cbRead;
+
+ // Read only from file to avoid recursive logic.
+ if (m_iType == STGIO_HFILE || m_iType == STGIO_HFILEMEM)
+ {
+ if (::ReadFile(m_hFile, pbBuff, cbBuff, pcbRead, 0))
+ return (S_OK);
+ return (MapFileError(GetLastError()));
+ }
+ // Read directly from stream.
+ else
+ {
+ return (m_pIStream->Read(pbBuff, cbBuff, pcbRead));
+ }
+}
+
+
+//*****************************************************************************
+// Copy the contents of the file for this storage to the target path.
+//*****************************************************************************
+HRESULT StgIO::CopyFileInternal( // Return code.
+ LPCWSTR szTo, // Target save path for file.
+ int bFailIfThere, // true to fail if target exists.
+ int bWriteThrough) // Should copy be written through OS cache.
+{
+ DWORD iCurrent; // Save original location.
+ DWORD cbRead; // Byte count for buffer.
+ DWORD cbWrite; // Check write of bytes.
+ const DWORD cbBuff = 4096; // Size of buffer for copy (in bytes).
+ BYTE *pBuff = (BYTE*)alloca(cbBuff); // Buffer for copy.
+ HANDLE hFile; // Target file.
+ HRESULT hr = S_OK;
+
+ // Create target file.
+ if ((hFile = ::WszCreateFile(szTo, GENERIC_WRITE, 0, 0,
+ (bFailIfThere) ? CREATE_NEW : CREATE_ALWAYS,
+ (bWriteThrough) ? FILE_FLAG_WRITE_THROUGH : 0,
+ 0)) == INVALID_HANDLE_VALUE)
+ {
+ return (MapFileError(GetLastError()));
+ }
+
+ // Save current location and reset it later.
+ iCurrent = ::SetFilePointer(m_hFile, 0, 0, FILE_CURRENT);
+ ::SetFilePointer(m_hFile, 0, 0, FILE_BEGIN);
+
+ // Copy while there are bytes.
+ while (::ReadFile(m_hFile, pBuff, cbBuff, &cbRead, 0) && cbRead)
+ {
+ if (!::WriteFile(hFile, pBuff, cbRead, &cbWrite, 0) || cbWrite != cbRead)
+ {
+ hr = STG_E_WRITEFAULT;
+ break;
+ }
+ }
+
+ // Reset file offset.
+ ::SetFilePointer(m_hFile, iCurrent, 0, FILE_BEGIN);
+
+ // Close target.
+ if (!bWriteThrough)
+ VERIFY(::FlushFileBuffers(hFile));
+ ::CloseHandle(hFile);
+ return (hr);
+}
+
+
+//*****************************************************************************
+// Free the data used for backing store from disk in read/write scenario.
+//*****************************************************************************
+void StgIO::FreePageMap()
+{
+ // If a small file was allocated, then free that memory.
+ if (m_bFreeMem && m_pBaseData)
+ FreeMemory(m_pBaseData);
+ // For mmf, close handles and free resources.
+ else if (m_hMapping && m_pBaseData)
+ {
+ VERIFY(UnmapViewOfFile(m_pBaseData));
+ VERIFY(CloseHandle(m_hMapping));
+ }
+ // For our own system, free memory.
+ else if (m_rgPageMap && m_pBaseData)
+ {
+ delete [] m_rgPageMap;
+ m_rgPageMap = 0;
+ VERIFY(::ClrVirtualFree(m_pBaseData, (((m_cbData - 1) & ~(m_iPageSize - 1)) + m_iPageSize), MEM_DECOMMIT));
+ VERIFY(::ClrVirtualFree(m_pBaseData, 0, MEM_RELEASE));
+ m_pBaseData = 0;
+ m_cbData = 0;
+ }
+
+ m_pBaseData = 0;
+ m_hMapping = 0;
+ m_cbData = 0;
+}
+
+
+//*****************************************************************************
+// Check the given pointer and ensure it is aligned correct. Return true
+// if it is aligned, false if it is not.
+//*****************************************************************************
+int StgIO::IsAlignedPtr(ULONG_PTR Value, int iAlignment)
+{
+ HRESULT hr;
+ void *ptrStart = NULL;
+
+ if ((m_iType == STGIO_STREAM) ||
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ (m_iType == STGIO_SHAREDMEM) ||
+#endif
+ (m_iType == STGIO_MEM))
+ {
+ return ((Value - (ULONG_PTR) m_pData) % iAlignment == 0);
+ }
+ else
+ {
+ hr = GetPtrForMem(0, 1, ptrStart);
+ _ASSERTE(hr == S_OK && "GetPtrForMem failed");
+ _ASSERTE(Value > (ULONG_PTR) ptrStart);
+ return (((Value - (ULONG_PTR) ptrStart) % iAlignment) == 0);
+ }
+} // int StgIO::IsAlignedPtr()
+
+
+
+
+
+//*****************************************************************************
+// These helper functions are used to allocate fairly large pieces of memory,
+// more than should be taken from the runtime heap, but less that would require
+// virtual memory overhead.
+//*****************************************************************************
+// #define _TRACE_MEM_ 1
+
+void *AllocateMemory(int iSize)
+{
+ void * ptr;
+ ptr = new (nothrow) BYTE[iSize];
+
+#if defined(_DEBUG) && defined(_TRACE_MEM_)
+ static int i=0;
+ DbgWriteEx(W("AllocateMemory: (%d) 0x%08x, size %d\n"), ++i, ptr, iSize);
+#endif
+ return (ptr);
+}
+
+
+void FreeMemory(void *pbData)
+{
+#if defined(_DEBUG) && defined(_TRACE_MEM_)
+ static int i=0;
+ DbgWriteEx(W("FreeMemory: (%d) 0x%08x\n"), ++i, pbData);
+#endif
+
+ _ASSERTE(pbData);
+ delete [] (BYTE *) pbData;
+}
+
diff --git a/src/md/enc/stgtiggerstorage.cpp b/src/md/enc/stgtiggerstorage.cpp
new file mode 100644
index 0000000000..fde3e933f0
--- /dev/null
+++ b/src/md/enc/stgtiggerstorage.cpp
@@ -0,0 +1,1026 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// StgTiggerStorage.cpp
+//
+
+//
+// TiggerStorage is a stripped down version of compound doc files. Doc files
+// have some very useful and complex features to them, unfortunately nothing
+// comes for free. Given the incredibly tuned format of existing .tlb files,
+// every single byte counts and 10% added by doc files is just too expensive.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard header.
+#include "stgio.h" // I/O subsystem.
+#include "stgtiggerstorage.h" // Our interface.
+#include "stgtiggerstream.h" // Stream interface.
+#include "corerror.h"
+#include "posterror.h"
+#include "mdfileformat.h"
+#include "sstring.h"
+
+//#CLRRuntimeHostInternal_GetImageVersionString
+// External implementation of call to code:ICLRRuntimeHostInternal::GetImageVersionString.
+// Implemented in clr.dll and mscordbi.dll.
+HRESULT
+CLRRuntimeHostInternal_GetImageVersionString(
+ __out_ecount(*pcchBuffer)
+ LPWSTR wszBuffer,
+ DWORD * pcchBuffer);
+
+TiggerStorage::TiggerStorage() :
+ m_pStgIO(0),
+ m_cRef(1),
+ m_pStreamList(0),
+ m_pbExtra(0)
+{
+ memset(&m_StgHdr, 0, sizeof(STORAGEHEADER));
+}
+
+
+TiggerStorage::~TiggerStorage()
+{
+ if (m_pStgIO)
+ {
+ m_pStgIO->Release();
+ m_pStgIO = 0;
+ }
+}
+
+
+//*****************************************************************************
+// Init this storage object on top of the given storage unit.
+//*****************************************************************************
+HRESULT
+TiggerStorage::Init(
+ StgIO *pStgIO, // The I/O subsystem.
+ __in __in_z LPSTR pVersion) // 'Compiled for' CLR version
+{
+ PSTORAGESIGNATURE pSig; // Signature data for file.
+ ULONG cbData; // Offset of header data.
+ void *ptr; // Signature.
+ HRESULT hr = S_OK;
+
+ // Make sure we always start at the beginning.
+ //
+ pStgIO->Seek(0, FILE_BEGIN);
+
+ // Save the storage unit.
+ m_pStgIO = pStgIO;
+ m_pStgIO->AddRef();
+
+ // For cases where the data already exists, verify the signature.
+ if ((pStgIO->GetFlags() & DBPROP_TMODEF_CREATE) == 0)
+ {
+ // Map the contents into memory for easy access.
+ IfFailGo(pStgIO->MapFileToMem(ptr, &cbData));
+
+ // Get a pointer to the signature of the file, which is the first part.
+ IfFailGo(pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr));
+
+ // Finally, we can check the signature.
+ pSig = (PSTORAGESIGNATURE)ptr;
+ IfFailGo(MDFormat::VerifySignature(pSig, cbData));
+
+ // Read and verify the header.
+ IfFailGo(ReadHeader());
+ }
+ // For write case, dump the signature into the file up front.
+ else
+ {
+ IfFailGo(WriteSignature(pVersion));
+ }
+
+ErrExit:
+ if (FAILED(hr) && (m_pStgIO != NULL))
+ {
+ m_pStgIO->Release();
+ m_pStgIO = NULL;
+ }
+ return hr;
+} // TiggerStorage::Init
+
+//*****************************************************************************
+// This function is a workaround to allow access to the "version requested" string.
+//*****************************************************************************
+HRESULT
+TiggerStorage::GetHeaderPointer(
+ const void **ppv, // Put pointer to header here.
+ ULONG *pcb) // Put size of pointer here.
+{
+ void *ptr; // Working pointer.
+ HRESULT hr;
+
+ // Read the signature
+ if (FAILED(hr = m_pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr)))
+ return hr;
+
+ PSTORAGESIGNATURE pStorage = (PSTORAGESIGNATURE) ptr;
+ // Header data starts after signature.
+ *pcb = sizeof(STORAGESIGNATURE) + pStorage->GetVersionStringLength();
+
+ *ppv = ptr;
+
+ return S_OK;
+
+} // TiggerStorage::GetHeaderPointer
+
+//*****************************************************************************
+// Get the default "Compiled for" version used to emit the meta-data
+//*****************************************************************************
+HRESULT
+TiggerStorage::GetDefaultVersion(
+ LPCSTR *ppVersion)
+{
+ static LPSTR g_pDefaultVersion;
+
+ if (g_pDefaultVersion == NULL)
+ {
+#ifdef FEATURE_METADATA_STANDALONE_WINRT
+ g_pDefaultVersion = "";
+#else //!FEATURE_METADATA_STANDALONE_WINRT
+#ifndef DACCESS_COMPILE
+ HRESULT hr;
+
+ WCHAR wszVersion[_MAX_PATH];
+ DWORD cchVersion = _MAX_PATH;
+ //#CallTo_CLRRuntimeHostInternal_GetImageVersionString
+ IfFailRet(CLRRuntimeHostInternal_GetImageVersionString(wszVersion, &cchVersion));
+
+ CHAR szVersion[_MAX_PATH];
+ DWORD dwSize = WszWideCharToMultiByte(CP_UTF8, 0, wszVersion, -1, szVersion, _MAX_PATH, NULL, NULL);
+ if (dwSize == 0)
+ {
+ _ASSERTE_MSG(FALSE, "WideCharToMultiByte conversion failed");
+ szVersion[0] = 0;
+ dwSize = 1;
+ }
+
+ NewArrayHolder<CHAR> pVersion = new (nothrow) CHAR[dwSize];
+ IfNullRet(pVersion);
+
+ memcpy(pVersion, szVersion, dwSize);
+
+ if (InterlockedCompareExchangeT<CHAR *>(&g_pDefaultVersion, pVersion, NULL) == NULL)
+ { // We won the initialization race
+ pVersion.SuppressRelease();
+ }
+#else
+ DacNotImpl();
+#endif //DACCESS_COMPILE
+#endif //!FEATURE_METADATA_STANDALONE_WINRT
+ }
+
+ *ppVersion = g_pDefaultVersion;
+ return S_OK;
+} // TiggerStorage::GetDefaultVersion
+
+HRESULT
+TiggerStorage::SizeOfStorageSignature(LPCSTR pVersion, ULONG *pcbSignatureSize)
+{
+ HRESULT hr;
+
+ if (pVersion == NULL)
+ {
+ IfFailRet(GetDefaultVersion(&pVersion));
+ }
+ _ASSERTE(pVersion != NULL);
+
+ ULONG versionSize = (ULONG)strlen(pVersion)+1;
+ ULONG alignedVersionSize = (ULONG)ALIGN_UP(versionSize, 4);
+
+ *pcbSignatureSize = sizeof(STORAGESIGNATURE) + alignedVersionSize;
+ return S_OK;
+}
+
+
+//*****************************************************************************
+// Retrieves a the size and a pointer to the extra data that can optionally be
+// written in the header of the storage system. This data is not required to
+// be in the file, in which case *pcbExtra will come back as 0 and pbData will
+// be set to NULL. You must have initialized the storage using Init() before
+// calling this function.
+//
+// Return value: S_OK if found, S_FALSE, or error.
+//*****************************************************************************
+HRESULT
+TiggerStorage::GetExtraData(
+ ULONG *pcbExtra, // Return size of extra data.
+ BYTE *&pbData) // Return a pointer to extra data.
+{
+ // Assuming there is extra data, then return the size and a pointer to it.
+ if (m_pbExtra != NULL)
+ {
+ if ((m_StgHdr.GetFlags() & STGHDR_EXTRADATA) == 0)
+ {
+ Debug_ReportError("Inconsistent information about extra data in MetaData.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+ *pcbExtra = *(ULONG *)m_pbExtra;
+ pbData = (BYTE *)((ULONG *) m_pbExtra + 1);
+ }
+ else
+ {
+ *pcbExtra = 0;
+ pbData = NULL;
+ return S_FALSE;
+ }
+ return S_OK;
+} // TiggerStorage::GetExtraData
+
+
+//*****************************************************************************
+// Called when this stream is going away.
+//*****************************************************************************
+HRESULT
+TiggerStorage::WriteHeader(
+ STORAGESTREAMLST *pList, // List of streams.
+ ULONG cbExtraData, // Size of extra data, may be 0.
+ BYTE *pbExtraData) // Pointer to extra data for header.
+{
+ ULONG iLen; // For variable sized data.
+ ULONG cbWritten; // Track write quantity.
+ HRESULT hr;
+ SAVETRACE(ULONG cbDebugSize); // Track debug size of header.
+
+ SAVETRACE(DbgWriteEx(W("PSS: Header:\n")));
+
+ // Save the count and set flags.
+ m_StgHdr.SetiStreams(pList->Count());
+ if (cbExtraData != 0)
+ m_StgHdr.AddFlags(STGHDR_EXTRADATA);
+
+ // Write out the header of the file.
+ IfFailRet(m_pStgIO->Write(&m_StgHdr, sizeof(STORAGEHEADER), &cbWritten));
+
+ // Write out extra data if there is any.
+ if (cbExtraData != 0)
+ {
+ _ASSERTE(pbExtraData);
+ _ASSERTE((cbExtraData % 4) == 0);
+
+ // First write the length value.
+ IfFailRet(m_pStgIO->Write(&cbExtraData, sizeof(ULONG), &cbWritten));
+
+ // And then the data.
+ IfFailRet(m_pStgIO->Write(pbExtraData, cbExtraData, &cbWritten));
+ SAVETRACE(DbgWriteEx(W("PSS: extra data size %d\n"), m_pStgIO->GetCurrentOffset() - cbDebugSize);cbDebugSize=m_pStgIO->GetCurrentOffset());
+ }
+
+ // Save off each data stream.
+ for (int i = 0; i < pList->Count(); i++)
+ {
+ PSTORAGESTREAM pStream = pList->Get(i);
+
+ // How big is the structure (aligned) for this struct.
+ iLen = (ULONG)(sizeof(STORAGESTREAM) - MAXSTREAMNAME + strlen(pStream->GetName()) + 1);
+
+ // Write the header including the name to disk. Does not include
+ // full name buffer in struct, just string and null terminator.
+ IfFailRet(m_pStgIO->Write(pStream, iLen, &cbWritten));
+
+ // Align the data out to 4 bytes.
+ if (iLen != ALIGN4BYTE(iLen))
+ {
+ IfFailRet(m_pStgIO->Write(&hr, ALIGN4BYTE(iLen) - iLen, 0));
+ }
+ SAVETRACE(DbgWriteEx(W("PSS: Table %hs header size %d\n"), pStream->rcName, m_pStgIO->GetCurrentOffset() - cbDebugSize);cbDebugSize=m_pStgIO->GetCurrentOffset());
+ }
+ SAVETRACE(DbgWriteEx(W("PSS: Total size of header data %d\n"), m_pStgIO->GetCurrentOffset()));
+ // Make sure the whole thing is 4 byte aligned.
+ _ASSERTE((m_pStgIO->GetCurrentOffset() % 4) == 0);
+ return S_OK;
+} // TiggerStorage::WriteHeader
+
+
+//*****************************************************************************
+// Called when all data has been written. Forces cached data to be flushed
+// and stream lists to be validated.
+//*****************************************************************************
+HRESULT
+TiggerStorage::WriteFinished(
+ STORAGESTREAMLST *pList, // List of streams.
+ ULONG *pcbSaveSize, // Return size of total data.
+ BOOL fDeltaSave) // Was this a delta
+{
+ PSTORAGESTREAM pEntry; // Loop control.
+ HRESULT hr;
+
+ // If caller wants the total size of the file, we are there right now.
+ if (pcbSaveSize != NULL)
+ *pcbSaveSize = m_pStgIO->GetCurrentOffset();
+
+ // Flush our internal write cache to disk.
+ IfFailRet(m_pStgIO->FlushCache());
+
+ // Force user's data onto disk right now so that Commit() can be
+ // more accurate (although not totally up to the D in ACID).
+ hr = m_pStgIO->FlushFileBuffers();
+ _ASSERTE(SUCCEEDED(hr));
+
+ // Run through all of the streams and validate them against the expected
+ // list we wrote out originally.
+
+ // Robustness check: stream counts must match what was written.
+ _ASSERTE(pList->Count() == m_Streams.Count());
+ if (pList->Count() != m_Streams.Count())
+ {
+ _ASSERTE_MSG(FALSE, "Mismatch in streams, save would cause corruption.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ // If we're saving a true delta, then this sanity check won't help.
+ // @TODO - Implement a sanity check for the deltas
+ if (!fDeltaSave)
+ {
+ // Sanity check each saved stream data size and offset.
+ for (int i = 0; i < pList->Count(); i++)
+ {
+ pEntry = pList->Get(i);
+
+ _ASSERTE(pEntry->GetOffset() == m_Streams[i].GetOffset());
+ _ASSERTE(pEntry->GetSize() == m_Streams[i].GetSize());
+ _ASSERTE(strcmp(pEntry->GetName(), m_Streams[i].GetName()) == 0);
+
+ // For robustness, check that everything matches expected value,
+ // and if it does not, refuse to save the data and force a rollback.
+ // The alternative is corruption of the data file.
+ if ((pEntry->GetOffset() != m_Streams[i].GetOffset()) ||
+ (pEntry->GetSize() != m_Streams[i].GetSize()) ||
+ (strcmp(pEntry->GetName(), m_Streams[i].GetName()) != 0))
+ {
+ _ASSERTE_MSG(FALSE, "Mismatch in streams, save would cause corruption.");
+ hr = PostError(CLDB_E_FILE_CORRUPT);
+ break;
+ }
+
+ //<REVISIT_TODO>@future:
+ // if iOffset or iSize mismatches, it means a bug in GetSaveSize
+ // which we can successfully detect right here. In that case, we
+ // could use the pStgIO and seek back to the header and correct the
+ // mistmake. This will break any client who lives on the GetSaveSize
+ // value which came back originally, but would be more robust than
+ // simply throwing back an error which will corrupt the file.</REVISIT_TODO>
+ }
+ }
+ return hr;
+} // TiggerStorage::WriteFinished
+
+
+//*****************************************************************************
+// Called after a successful rewrite of an existing file. The in memory
+// backing store is no longer valid because all new data is in memory and
+// on disk. This is essentially the same state as created, so free up some
+// working set and remember this state.
+//*****************************************************************************
+HRESULT TiggerStorage::ResetBackingStore() // Return code.
+{
+ return (m_pStgIO->ResetBackingStore());
+}
+
+
+//*****************************************************************************
+// Given the name of a stream that will be persisted into a stream in this
+// storage type, figure out how big that stream would be including the user's
+// stream data and the header overhead the file format incurs. The name is
+// stored in ANSI and the header struct is aligned to 4 bytes.
+//*****************************************************************************
+HRESULT
+TiggerStorage::GetStreamSaveSize(
+ LPCWSTR szStreamName, // Name of stream.
+ UINT32 cbDataSize, // Size of data to go into stream.
+ UINT32 *pcbSaveSize) // Return data size plus stream overhead.
+{
+ UINT32 cbTotalSize; // Add up each element.
+
+ // Find out how large the name will be.
+ cbTotalSize = ::WszWideCharToMultiByte(CP_ACP, 0, szStreamName, -1, 0, 0, 0, 0);
+ _ASSERTE(cbTotalSize != 0);
+
+ // Add the size of the stream header minus the static name array.
+ cbTotalSize += sizeof(STORAGESTREAM) - MAXSTREAMNAME;
+
+ // Finally align the header value.
+ cbTotalSize = ALIGN4BYTE(cbTotalSize);
+
+ // Return the size of the user data and the header data.
+ *pcbSaveSize = cbTotalSize + cbDataSize;
+ return S_OK;
+} // TiggerStorage::GetStreamSaveSize
+
+
+//*****************************************************************************
+// Return the fixed size overhead for the storage implementation. This includes
+// the signature and fixed header overhead. The overhead in the header for each
+// stream is calculated as part of GetStreamSaveSize because these structs are
+// variable sized on the name.
+//*****************************************************************************
+HRESULT TiggerStorage::GetStorageSaveSize( // Return code.
+ ULONG *pcbSaveSize, // [in] current size, [out] plus overhead.
+ ULONG cbExtra, // How much extra data to store in header.
+ LPCSTR pRuntimeVersion)
+{
+ HRESULT hr;
+
+ ULONG cbSignatureSize;
+ IfFailRet(SizeOfStorageSignature(pRuntimeVersion, &cbSignatureSize));
+
+ *pcbSaveSize += cbSignatureSize + sizeof(STORAGEHEADER);
+ if (cbExtra)
+ *pcbSaveSize += sizeof(ULONG) + cbExtra;
+ return (S_OK);
+}
+
+
+//*****************************************************************************
+// Adjust the offset in each known stream to match where it will wind up after
+// a save operation.
+//*****************************************************************************
+HRESULT TiggerStorage::CalcOffsets( // Return code.
+ STORAGESTREAMLST *pStreamList, // List of streams for header.
+ ULONG cbExtra, // Size of variable extra data in header.
+ LPCSTR pRuntimeVersion) // The version string as it's length is part of the total size.
+{
+ PSTORAGESTREAM pEntry; // Each entry in the list.
+ ULONG cbOffset=0; // Running offset for streams.
+ int i; // Loop control.
+
+ // Prime offset up front.
+ GetStorageSaveSize(&cbOffset, cbExtra, pRuntimeVersion);
+
+ // Add on the size of each header entry.
+ for (i=0; i<pStreamList->Count(); i++)
+ {
+ VERIFY(pEntry = pStreamList->Get(i));
+ cbOffset += sizeof(STORAGESTREAM) - MAXSTREAMNAME;
+ cbOffset += (ULONG)(strlen(pEntry->GetName()) + 1);
+ cbOffset = ALIGN4BYTE(cbOffset);
+ }
+
+ // Go through each stream and reset its expected offset.
+ for (i=0; i<pStreamList->Count(); i++)
+ {
+ VERIFY(pEntry = pStreamList->Get(i));
+ pEntry->SetOffset(cbOffset);
+ cbOffset += pEntry->GetSize();
+ }
+ return (S_OK);
+}
+
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStream(
+ const OLECHAR *pwcsName,
+ DWORD grfMode,
+ DWORD reserved1,
+ DWORD reserved2,
+ IStream **ppstm)
+{
+ char rcStream[MAXSTREAMNAME];// For converted name.
+ VERIFY(Wsz_wcstombs(rcStream, pwcsName, sizeof(rcStream)));
+ return (CreateStream(rcStream, grfMode, reserved1, reserved2, ppstm));
+}
+
+
+#ifndef DACCESS_COMPILE
+HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStream(
+ LPCSTR szName,
+ DWORD grfMode,
+ DWORD reserved1,
+ DWORD reserved2,
+ IStream **ppstm)
+{
+ PSTORAGESTREAM pStream; // For lookup.
+ HRESULT hr;
+
+ _ASSERTE(szName && *szName);
+
+ // Check for existing stream, which might be an error or more likely
+ // a rewrite of a file.
+ if (SUCCEEDED(FindStream(szName, &pStream)))
+ {
+ // <REVISIT_TODO>REVIEW: STGM_FAILIFTHERE is 0, the following condition will be always false</REVISIT_TODO>
+ if (pStream->GetOffset() != 0xffffffff && ((grfMode & STGM_CREATE) == STGM_FAILIFTHERE))
+ return (PostError(STG_E_FILEALREADYEXISTS));
+ }
+ // Add a control to track this stream.
+ else if (!pStream && (pStream = m_Streams.Append()) == 0)
+ return (PostError(OutOfMemory()));
+ pStream->SetOffset(0xffffffff);
+ pStream->SetSize(0);
+ strcpy_s(pStream->GetName(), 32, szName);
+
+ // Now create a stream object to allow reading and writing.
+ TiggerStream *pNew = new (nothrow) TiggerStream;
+ if (!pNew)
+ return (PostError(OutOfMemory()));
+ *ppstm = (IStream *) pNew;
+
+ // Init the new object.
+ if (FAILED(hr = pNew->Init(this, pStream->GetName())))
+ {
+ delete pNew;
+ return (hr);
+ }
+ return (S_OK);
+}
+#endif //!DACCESS_COMPILE
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::OpenStream(
+ const OLECHAR *pwcsName,
+ void *reserved1,
+ DWORD grfMode,
+ DWORD reserved2,
+ IStream **ppstm)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStorage(
+ const OLECHAR *pwcsName,
+ DWORD grfMode,
+ DWORD dwStgFmt,
+ DWORD reserved2,
+ IStorage **ppstg)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE
+TiggerStorage::OpenStorage(
+ const OLECHAR * wcsName,
+ IStorage * pStgPriority,
+ DWORD dwMode,
+ __in
+ SNB snbExclude,
+ DWORD reserved,
+ IStorage ** ppStg)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+TiggerStorage::CopyTo(
+ DWORD cIidExclude,
+ const IID * rgIidExclude,
+ __in
+ SNB snbExclude,
+ IStorage * pStgDest)
+{
+ return E_NOTIMPL;
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::MoveElementTo(
+ const OLECHAR *pwcsName,
+ IStorage *pstgDest,
+ const OLECHAR *pwcsNewName,
+ DWORD grfFlags)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::Commit(
+ DWORD grfCommitFlags)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::Revert()
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::EnumElements(
+ DWORD reserved1,
+ void *reserved2,
+ DWORD reserved3,
+ IEnumSTATSTG **ppenum)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::DestroyElement(
+ const OLECHAR *pwcsName)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::RenameElement(
+ const OLECHAR *pwcsOldName,
+ const OLECHAR *pwcsNewName)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::SetElementTimes(
+ const OLECHAR *pwcsName,
+ const FILETIME *pctime,
+ const FILETIME *patime,
+ const FILETIME *pmtime)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::SetClass(
+ REFCLSID clsid)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::SetStateBits(
+ DWORD grfStateBits,
+ DWORD grfMask)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag)
+{
+ return (E_NOTIMPL);
+}
+
+
+
+HRESULT STDMETHODCALLTYPE TiggerStorage::OpenStream(
+ LPCWSTR szStream,
+ ULONG *pcbData,
+ void **ppAddress)
+{
+ PSTORAGESTREAM pStream; // For lookup.
+ char rcName[MAXSTREAMNAME]; // For conversion.
+ HRESULT hr;
+
+ // Convert the name for internal use.
+ VERIFY(::WszWideCharToMultiByte(CP_ACP, 0, szStream, -1, rcName, sizeof(rcName), 0, 0));
+
+ // Look for the stream which must be found for this to work. Note that
+ // this error is explicitly not posted as an error object since unfound streams
+ // are a common occurence and do not warrant a resource file load.
+ IfFailRet(FindStream(rcName, &pStream));
+
+ // Get the memory for the stream.
+ IfFailRet( m_pStgIO->GetPtrForMem(pStream->GetOffset(), pStream->GetSize(), *ppAddress) );
+ *pcbData = pStream->GetSize();
+ return (S_OK);
+}
+
+
+
+//
+// Protected.
+//
+
+
+//*****************************************************************************
+// Called by the stream implementation to write data out to disk.
+//*****************************************************************************
+HRESULT
+TiggerStorage::Write(
+ LPCSTR szName, // Name of stream we're writing.
+ const void *pData, // Data to write.
+ ULONG cbData, // Size of data.
+ ULONG *pcbWritten) // How much did we write.
+{
+ PSTORAGESTREAM pStream; // Update size data.
+ ULONG iOffset = 0; // Offset for write.
+ ULONG cbWritten; // Handle null case.
+ HRESULT hr;
+
+ // Get the stream descriptor.
+ if (FAILED(FindStream(szName, &pStream)))
+ return CLDB_E_FILE_BADWRITE;
+
+ // If we need to know the offset, keep it now.
+ if (pStream->GetOffset() == 0xffffffff)
+ {
+ iOffset = m_pStgIO->GetCurrentOffset();
+
+ // Align the storage on a 4 byte boundary.
+ if ((iOffset % 4) != 0)
+ {
+ ULONG cb;
+ ULONG pad = 0;
+
+ if (FAILED(hr = m_pStgIO->Write(&pad, ALIGN4BYTE(iOffset) - iOffset, &cb)))
+ return hr;
+ iOffset = m_pStgIO->GetCurrentOffset();
+
+ _ASSERTE((iOffset % 4) == 0);
+ }
+ }
+
+ // Avoid confusion.
+ if (pcbWritten == NULL)
+ pcbWritten = &cbWritten;
+ *pcbWritten = 0;
+
+ // Let OS do the write.
+ if (SUCCEEDED(hr = m_pStgIO->Write(pData, cbData, pcbWritten)))
+ {
+ // On success, record the new data.
+ if (pStream->GetOffset() == 0xffffffff)
+ pStream->SetOffset(iOffset);
+ pStream->SetSize(pStream->GetSize() + *pcbWritten);
+ return S_OK;
+ }
+ else
+ {
+ return hr;
+ }
+} // TiggerStorage::Write
+
+
+//
+// Private
+//
+
+HRESULT
+TiggerStorage::FindStream(
+ LPCSTR szName,
+ __out PSTORAGESTREAM *stream)
+{
+ *stream = NULL;
+ // In read mode, just walk the list and return one.
+ if (m_pStreamList != NULL)
+ {
+ PSTORAGESTREAM p = m_pStreamList;
+
+ SIZE_T pStartMD = (SIZE_T)(m_pStgIO->m_pData);
+ SIZE_T pEndMD = NULL;
+
+ if (!ClrSafeInt<SIZE_T>::addition(pStartMD, m_pStgIO->m_cbData, pEndMD))
+ {
+ Debug_ReportError("Invalid MetaData storage headers - size overflow.");
+ return CLDB_E_FILE_CORRUPT;
+ }
+
+ for (int i = 0; i < m_StgHdr.GetiStreams(); i++)
+ {
+ // Make sure this stream pointer is still inside the metadata
+ if (((SIZE_T)p < pStartMD) || ((SIZE_T)p > pEndMD))
+ {
+ Debug_ReportError("Invalid MetaData storage header - reached outside headers block.");
+ return CLDB_E_FILE_CORRUPT;
+ }
+
+ if (SString::_stricmp(p->GetName(), szName) == 0)
+ {
+ *stream = p;
+ return S_OK;
+ }
+ p = p->NextStream();
+ }
+ }
+ // In write mode, walk the array which is not on disk yet.
+ else
+ {
+ for (int j = 0; j < m_Streams.Count(); j++)
+ {
+ if (SString::_stricmp(m_Streams[j].GetName(), szName) == 0)
+ {
+ *stream = &m_Streams[j];
+ return S_OK;
+ }
+ }
+ }
+ return STG_E_FILENOTFOUND;
+} // TiggerStorage::FindStream
+
+
+//*****************************************************************************
+// Write the signature area of the file format to disk. This includes the
+// "magic" identifier and the version information.
+//*****************************************************************************
+HRESULT
+TiggerStorage::WriteSignature(
+ LPCSTR pVersion)
+{
+ STORAGESIGNATURE sSig;
+ ULONG cbWritten;
+ HRESULT hr = S_OK;
+
+ if (pVersion == NULL)
+ {
+ IfFailRet(GetDefaultVersion(&pVersion));
+ }
+ _ASSERTE(pVersion != NULL);
+
+ ULONG versionSize = (ULONG)strlen(pVersion) + 1;
+ ULONG alignedVersionSize = (ULONG)ALIGN_UP(versionSize, 4);
+
+ // Signature belongs at the start of the file.
+ _ASSERTE(m_pStgIO->GetCurrentOffset() == 0);
+
+ sSig.SetSignature(STORAGE_MAGIC_SIG);
+ sSig.SetMajorVer(FILE_VER_MAJOR);
+ sSig.SetMinorVer(FILE_VER_MINOR);
+ sSig.SetExtraDataOffset(0); // We have no extra inforation
+ sSig.SetVersionStringLength(alignedVersionSize);
+ IfFailRet(m_pStgIO->Write(&sSig, sizeof(STORAGESIGNATURE), &cbWritten));
+ IfFailRet(m_pStgIO->Write(pVersion, versionSize, &cbWritten));
+
+ // Write padding
+ if (alignedVersionSize - versionSize != 0)
+ {
+ BYTE padding[4];
+ ZeroMemory(padding, sizeof(padding));
+ IfFailRet(m_pStgIO->Write(padding, alignedVersionSize - versionSize, &cbWritten));
+ }
+
+ return hr;
+} // TiggerStorage::WriteSignature
+
+
+//*****************************************************************************
+// Read the header from disk. This reads the header for the most recent version
+// of the file format which has the header at the front of the data file.
+//*****************************************************************************
+HRESULT
+TiggerStorage::ReadHeader()
+{
+ PSTORAGESTREAM pAppend, pStream; // For copy of array.
+ void *ptr; // Working pointer.
+ ULONG iOffset; // Offset of header data.
+ ULONG cbExtra; // Size of extra data.
+ ULONG cbRead; // For calc of read sizes.
+ HRESULT hr;
+
+ // Read the signature
+ if (FAILED(hr = m_pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr)))
+ {
+ Debug_ReportError("Cannot read MetaData storage signature header.");
+ return hr;
+ }
+
+ PSTORAGESIGNATURE pStorage = (PSTORAGESIGNATURE)ptr;
+
+ // Header data starts after signature.
+ iOffset = sizeof(STORAGESIGNATURE) + pStorage->GetVersionStringLength();
+
+ // Read the storage header which has the stream counts. Throw in the extra
+ // count which might not exist, but saves us down stream.
+ if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, sizeof(STORAGEHEADER) + sizeof(ULONG), ptr)))
+ {
+ Debug_ReportError("Cannot read first MetaData storage header.");
+ return hr;
+ }
+ _ASSERTE(m_pStgIO->IsAlignedPtr((ULONG_PTR) ptr, 4));
+
+ // Read the storage header which has the stream counts. Throw in the extra
+ // count which might not exist, but saves us down stream.
+ if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, sizeof(STORAGEHEADER) + sizeof(ULONG), ptr)))
+ {
+ Debug_ReportError("Cannot read second MetaData storage header.");
+ return hr;
+ }
+ if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4))
+ {
+ Debug_ReportError("Invalid MetaData storage headers - unaligned size.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Copy the header into memory and check it.
+ memcpy(&m_StgHdr, ptr, sizeof(STORAGEHEADER));
+ IfFailRet( VerifyHeader() );
+ ptr = (void *)((PSTORAGEHEADER)ptr + 1);
+ iOffset += sizeof(STORAGEHEADER);
+
+ // Save off a pointer to the extra data.
+ if ((m_StgHdr.GetFlags() & STGHDR_EXTRADATA) != 0)
+ {
+ m_pbExtra = ptr;
+ cbExtra = sizeof(ULONG) + *(ULONG *)ptr;
+
+ // Force the extra data to get faulted in.
+ IfFailRet(m_pStgIO->GetPtrForMem(iOffset, cbExtra, ptr));
+ if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4))
+ {
+ Debug_ReportError("Invalid MetaData storage signature - unaligned extra data.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+ }
+ else
+ {
+ m_pbExtra = 0;
+ cbExtra = 0;
+ }
+ iOffset += cbExtra;
+
+ // Force the worst case scenario of bytes to get faulted in for the
+ // streams. This makes the rest of this code very simple.
+ cbRead = sizeof(STORAGESTREAM) * m_StgHdr.GetiStreams();
+ if (cbRead != 0)
+ {
+ cbRead = min(cbRead, m_pStgIO->GetDataSize() - iOffset);
+ if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, cbRead, ptr)))
+ {
+ Debug_ReportError("Invalid MetaData stogare headers.");
+ return hr;
+ }
+ if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4))
+ {
+ Debug_ReportError("Invalid MetaData stogare headers - unaligned start.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ // For read only, just access the header data.
+ if (m_pStgIO->IsReadOnly())
+ {
+ // Save a pointer to the current list of streams.
+ m_pStreamList = (PSTORAGESTREAM)ptr;
+ }
+ // For writeable, need a copy we can modify.
+ else
+ {
+ pStream = (PSTORAGESTREAM)ptr;
+
+ // Copy each of the stream headers.
+ for (int i = 0; i < m_StgHdr.GetiStreams(); i++)
+ {
+ if ((pAppend = m_Streams.Append()) == NULL)
+ return PostError(OutOfMemory());
+ // Validate that the stream header is not too big.
+ ULONG sz = pStream->GetStreamSize();
+ if (sz > sizeof(STORAGESTREAM))
+ {
+ Debug_ReportError("Invalid MetaData storage stream - data too big.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+ memcpy (pAppend, pStream, sz);
+ pStream = pStream->NextStream();
+ if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)pStream, 4))
+ {
+ Debug_ReportError("Invalid MetaData storage stream - unaligned data.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+ }
+
+ // All must be loaded and accounted for.
+ _ASSERTE(m_StgHdr.GetiStreams() == m_Streams.Count());
+ }
+ }
+ return S_OK;
+} // TiggerStorage::ReadHeader
+
+
+//*****************************************************************************
+// Verify the header is something this version of the code can support.
+//*****************************************************************************
+HRESULT TiggerStorage::VerifyHeader()
+{
+ //<REVISIT_TODO>@FUTURE: add version check for format.</REVISIT_TODO>
+ return S_OK;
+}
+
+//*****************************************************************************
+// Print the sizes of the various streams.
+//*****************************************************************************
+#if defined(_DEBUG)
+ULONG TiggerStorage::PrintSizeInfo(bool verbose)
+{
+ ULONG total = 0;
+
+ printf("Storage Header: %d\n", sizeof(STORAGEHEADER));
+ if (m_pStreamList != NULL)
+ {
+ PSTORAGESTREAM storStream = m_pStreamList;
+ PSTORAGESTREAM pNext;
+ for (int i = 0; i < m_StgHdr.GetiStreams(); i++)
+ {
+ pNext = storStream->NextStream();
+ printf("Stream #%d (%s) Header: %d, Data: %d\n",i,storStream->GetName(), (BYTE*)pNext - (BYTE*)storStream, storStream->GetSize());
+ total += storStream->GetSize();
+ storStream = pNext;
+ }
+ }
+ else
+ {
+ //<REVISIT_TODO>todo: Add support for the case where m_Streams exists and m_pStreamList does not</REVISIT_TODO>
+ }
+
+ if (m_pbExtra != NULL)
+ {
+ printf("Extra bytes: %d\n",*(ULONG*)m_pbExtra);
+ total += *(ULONG*)m_pbExtra;
+ }
+ return total;
+}
+#endif // _DEBUG
diff --git a/src/md/enc/stgtiggerstream.cpp b/src/md/enc/stgtiggerstream.cpp
new file mode 100644
index 0000000000..dfa5c27c37
--- /dev/null
+++ b/src/md/enc/stgtiggerstream.cpp
@@ -0,0 +1,138 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// StgTiggerStream.h
+//
+
+//
+// TiggerStream is the companion to the TiggerStorage CoClass. It handles the
+// streams managed inside of the storage and does the direct file i/o.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "stgtiggerstream.h"
+#include "stgtiggerstorage.h"
+#include "posterror.h"
+
+//
+//
+// IStream
+//
+//
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::Read(
+ void *pv,
+ ULONG cb,
+ ULONG *pcbRead)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::Write(
+ const void *pv,
+ ULONG cb,
+ ULONG *pcbWritten)
+{
+ return (m_pStorage->Write(m_rcStream, pv, cb, pcbWritten));
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::Seek(
+ LARGE_INTEGER dlibMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER *plibNewPosition)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::SetSize(
+ ULARGE_INTEGER libNewSize)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::CopyTo(
+ IStream *pstm,
+ ULARGE_INTEGER cb,
+ ULARGE_INTEGER *pcbRead,
+ ULARGE_INTEGER *pcbWritten)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::Commit(
+ DWORD grfCommitFlags)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::Revert()
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::LockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::UnlockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag)
+{
+ return (E_NOTIMPL);
+}
+
+
+HRESULT STDMETHODCALLTYPE TiggerStream::Clone(
+ IStream **ppstm)
+{
+ return (E_NOTIMPL);
+}
+
+
+
+
+
+
+HRESULT TiggerStream::Init( // Return code.
+ TiggerStorage *pStorage, // Parent storage.
+ LPCSTR szStream) // Stream name.
+{
+ // Save off the parent data source object and stream name.
+ m_pStorage = pStorage;
+ strncpy_s(m_rcStream, sizeof(m_rcStream), szStream, sizeof(m_rcStream)-1);
+ m_rcStream[sizeof(m_rcStream)-1] = '\0'; // force nul termination
+ return (S_OK);
+}
+
+
+ULONG TiggerStream::GetStreamSize()
+{
+ PSTORAGESTREAM pStreamInfo;
+ if (FAILED(m_pStorage->FindStream(m_rcStream, &pStreamInfo)))
+ return 0;
+ return (pStreamInfo->GetSize());
+}
diff --git a/src/md/enc/wks/.gitmirror b/src/md/enc/wks/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/enc/wks/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/md/enc/wks/CMakeLists.txt b/src/md/enc/wks/CMakeLists.txt
new file mode 100644
index 0000000000..e31d8ec056
--- /dev/null
+++ b/src/md/enc/wks/CMakeLists.txt
@@ -0,0 +1,2 @@
+include(../../md_wks.cmake)
+add_library(mdruntimerw_wks ${MDRUNTIMERW_SOURCES})
diff --git a/src/md/enc/wks/MDRuntimeRW.nativeproj b/src/md/enc/wks/MDRuntimeRW.nativeproj
new file mode 100644
index 0000000000..b3116c5abb
--- /dev/null
+++ b/src/md/enc/wks/MDRuntimeRW.nativeproj
@@ -0,0 +1,19 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <PropertyGroup>
+ <!-- All features are set in file:..\..\MD.props -->
+ <MetadataFlavor>wks</MetadataFlavor>
+ </PropertyGroup>
+
+ <!--Leaf project Properties-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\enc\enc.settings.targets" />
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>MDRuntimeRW</OutputName>
+ </PropertyGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>