summaryrefslogtreecommitdiff
path: root/src/md
diff options
context:
space:
mode:
Diffstat (limited to 'src/md')
-rw-r--r--src/md/.gitmirror1
-rw-r--r--src/md/CMakeLists.txt9
-rw-r--r--src/md/MD.props158
-rw-r--r--src/md/ceefilegen/.gitmirror1
-rw-r--r--src/md/ceefilegen/CMakeLists.txt23
-rw-r--r--src/md/ceefilegen/blobfetcher.cpp399
-rw-r--r--src/md/ceefilegen/cceegen.cpp706
-rw-r--r--src/md/ceefilegen/ceefgen.nativeproj42
-rw-r--r--src/md/ceefilegen/ceegentokenmapper.cpp160
-rw-r--r--src/md/ceefilegen/ceesectionstring.cpp133
-rw-r--r--src/md/ceefilegen/pesectionman.cpp428
-rw-r--r--src/md/ceefilegen/stdafx.cpp12
-rw-r--r--src/md/ceefilegen/stdafx.h30
-rw-r--r--src/md/compiler/.gitmirror1
-rw-r--r--src/md/compiler/CMakeLists.txt32
-rw-r--r--src/md/compiler/Compiler.settings.targets68
-rw-r--r--src/md/compiler/assemblymd.cpp758
-rw-r--r--src/md/compiler/assemblymd_emit.cpp811
-rw-r--r--src/md/compiler/cacheload.h27
-rw-r--r--src/md/compiler/classfactory.cpp173
-rw-r--r--src/md/compiler/classfactory.h95
-rw-r--r--src/md/compiler/crossgen/.gitmirror1
-rw-r--r--src/md/compiler/crossgen/CMakeLists.txt5
-rw-r--r--src/md/compiler/crossgen/MDCompiler_crossgen.nativeproj15
-rw-r--r--src/md/compiler/custattr.h117
-rw-r--r--src/md/compiler/custattr_emit.cpp2000
-rw-r--r--src/md/compiler/custattr_import.cpp282
-rw-r--r--src/md/compiler/dac/.gitmirror1
-rw-r--r--src/md/compiler/dac/CMakeLists.txt6
-rw-r--r--src/md/compiler/dac/dirs.proj19
-rw-r--r--src/md/compiler/dbi/.gitmirror1
-rw-r--r--src/md/compiler/dbi/CMakeLists.txt4
-rw-r--r--src/md/compiler/dbi/MDCompiler-dbi.props9
-rw-r--r--src/md/compiler/dbi/dirs.proj19
-rw-r--r--src/md/compiler/dirs.proj27
-rw-r--r--src/md/compiler/disp.cpp939
-rw-r--r--src/md/compiler/disp.h132
-rw-r--r--src/md/compiler/emit.cpp3001
-rw-r--r--src/md/compiler/filtermanager.cpp1458
-rw-r--r--src/md/compiler/filtermanager.h87
-rw-r--r--src/md/compiler/helper.cpp445
-rw-r--r--src/md/compiler/import.cpp3809
-rw-r--r--src/md/compiler/importhelper.cpp3415
-rw-r--r--src/md/compiler/importhelper.h368
-rw-r--r--src/md/compiler/mdperf.cpp96
-rw-r--r--src/md/compiler/mdperf.h243
-rw-r--r--src/md/compiler/mdsighelper.h128
-rw-r--r--src/md/compiler/mdutil.cpp774
-rw-r--r--src/md/compiler/mdutil.h119
-rw-r--r--src/md/compiler/mdvalidator.cpp7739
-rw-r--r--src/md/compiler/newmerger.cpp6303
-rw-r--r--src/md/compiler/newmerger.h256
-rw-r--r--src/md/compiler/regmeta.cpp1588
-rw-r--r--src/md/compiler/regmeta.h2123
-rw-r--r--src/md/compiler/regmeta_compilersupport.cpp506
-rw-r--r--src/md/compiler/regmeta_emit.cpp2116
-rw-r--r--src/md/compiler/regmeta_imetadatatables.cpp719
-rw-r--r--src/md/compiler/regmeta_import.cpp1204
-rw-r--r--src/md/compiler/regmeta_vm.cpp586
-rw-r--r--src/md/compiler/stdafx.cpp12
-rw-r--r--src/md/compiler/stdafx.h26
-rw-r--r--src/md/compiler/verifylayouts.cpp14
-rw-r--r--src/md/compiler/wks/.gitmirror1
-rw-r--r--src/md/compiler/wks/CMakeLists.txt4
-rw-r--r--src/md/compiler/wks/MDCompiler_wks.nativeproj19
-rw-r--r--src/md/compressedinteger.h87
-rw-r--r--src/md/compressedinteger.inl99
-rw-r--r--src/md/datablob.h231
-rw-r--r--src/md/datablob.inl581
-rw-r--r--src/md/databuffer.h156
-rw-r--r--src/md/databuffer.inl274
-rw-r--r--src/md/datasource/.gitmirror1
-rw-r--r--src/md/datasource/CMakeLists.txt15
-rw-r--r--src/md/datasource/DataSource.settings.targets42
-rw-r--r--src/md/datasource/api.cpp31
-rw-r--r--src/md/datasource/datatargetreader.cpp199
-rw-r--r--src/md/datasource/datatargetreader.h58
-rw-r--r--src/md/datasource/dbi/.gitmirror1
-rw-r--r--src/md/datasource/dbi/CMakeLists.txt4
-rw-r--r--src/md/datasource/dbi/DataSource-dbi.props9
-rw-r--r--src/md/datasource/dbi/dirs.proj19
-rw-r--r--src/md/datasource/dirs.proj19
-rw-r--r--src/md/datasource/remotemdinternalrwsource.cpp241
-rw-r--r--src/md/datasource/remotemdinternalrwsource.h70
-rw-r--r--src/md/datasource/stdafx.cpp12
-rw-r--r--src/md/datasource/stdafx.h23
-rw-r--r--src/md/datasource/targettypes.cpp535
-rw-r--r--src/md/datasource/targettypes.h360
-rw-r--r--src/md/debug_metadata.h101
-rw-r--r--src/md/dirs.proj25
-rw-r--r--src/md/enc/.gitmirror1
-rw-r--r--src/md/enc/CMakeLists.txt22
-rw-r--r--src/md/enc/crossgen/.gitmirror1
-rw-r--r--src/md/enc/crossgen/CMakeLists.txt5
-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.txt6
-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.txt4
-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.cpp8057
-rw-r--r--src/md/enc/liteweightstgdbrw.cpp1278
-rw-r--r--src/md/enc/mdinternalrw.cpp4355
-rw-r--r--src/md/enc/metamodelenc.cpp471
-rw-r--r--src/md/enc/metamodelrw.cpp8893
-rw-r--r--src/md/enc/peparse.cpp148
-rw-r--r--src/md/enc/rwutil.cpp1440
-rw-r--r--src/md/enc/stdafx.cpp12
-rw-r--r--src/md/enc/stdafx.h26
-rw-r--r--src/md/enc/stgio.cpp1437
-rw-r--r--src/md/enc/stgtiggerstorage.cpp1025
-rw-r--r--src/md/enc/stgtiggerstream.cpp137
-rw-r--r--src/md/enc/wks/.gitmirror1
-rw-r--r--src/md/enc/wks/CMakeLists.txt4
-rw-r--r--src/md/enc/wks/MDRuntimeRW.nativeproj19
-rw-r--r--src/md/errors_metadata.h61
-rw-r--r--src/md/export.h18
-rw-r--r--src/md/external.h17
-rw-r--r--src/md/heaps/.gitmirror1
-rw-r--r--src/md/heaps/blobheap.h324
-rw-r--r--src/md/heaps/export.h18
-rw-r--r--src/md/heaps/external.h22
-rw-r--r--src/md/heaps/guidheap.h259
-rw-r--r--src/md/heaps/stringheap.h307
-rw-r--r--src/md/hotdata/.gitmirror1
-rw-r--r--src/md/hotdata/CMakeLists.txt21
-rw-r--r--src/md/hotdata/HotData.settings.targets28
-rw-r--r--src/md/hotdata/crossgen/.gitmirror1
-rw-r--r--src/md/hotdata/crossgen/CMakeLists.txt5
-rw-r--r--src/md/hotdata/crossgen/MDHotData_crossgen.nativeproj18
-rw-r--r--src/md/hotdata/dac/.gitmirror1
-rw-r--r--src/md/hotdata/dac/CMakeLists.txt6
-rw-r--r--src/md/hotdata/dac/dirs.proj19
-rw-r--r--src/md/hotdata/dirs.proj21
-rw-r--r--src/md/hotdata/export.h25
-rw-r--r--src/md/hotdata/external.cpp13
-rw-r--r--src/md/hotdata/external.h20
-rw-r--r--src/md/hotdata/full-staticcrt/.gitmirror1
-rw-r--r--src/md/hotdata/full-staticcrt/CMakeLists.txt4
-rw-r--r--src/md/hotdata/full-staticcrt/MDHotData-staticcrt.props11
-rw-r--r--src/md/hotdata/full-staticcrt/dirs.proj19
-rw-r--r--src/md/hotdata/full/.gitmirror1
-rw-r--r--src/md/hotdata/full/CMakeLists.txt3
-rw-r--r--src/md/hotdata/full/MDHotData.nativeproj19
-rw-r--r--src/md/hotdata/heapindex.h68
-rw-r--r--src/md/hotdata/hotdataformat.h155
-rw-r--r--src/md/hotdata/hotheap.cpp185
-rw-r--r--src/md/hotdata/hotheap.h67
-rw-r--r--src/md/hotdata/hotheapsdirectoryiterator.cpp110
-rw-r--r--src/md/hotdata/hotheapsdirectoryiterator.h71
-rw-r--r--src/md/hotdata/hotheapwriter.cpp305
-rw-r--r--src/md/hotdata/hotheapwriter.h84
-rw-r--r--src/md/hotdata/hotmetadata.cpp85
-rw-r--r--src/md/hotdata/hotmetadata.h43
-rw-r--r--src/md/hotdata/hottable.cpp138
-rw-r--r--src/md/hotdata/hottable.h57
-rw-r--r--src/md/inc/.gitmirror1
-rw-r--r--src/md/inc/VerifyLayouts.inc351
-rw-r--r--src/md/inc/assemblymdinternaldisp.h723
-rw-r--r--src/md/inc/cahlprinternal.h94
-rw-r--r--src/md/inc/imptlb.h777
-rw-r--r--src/md/inc/liteweightstgdb.h257
-rw-r--r--src/md/inc/mdcolumndescriptors.h51
-rw-r--r--src/md/inc/mdfileformat.h269
-rw-r--r--src/md/inc/mdinternalrw.h862
-rw-r--r--src/md/inc/mdlog.h25
-rw-r--r--src/md/inc/metadatahash.h207
-rw-r--r--src/md/inc/metamodel.h2072
-rw-r--r--src/md/inc/metamodelro.h240
-rw-r--r--src/md/inc/metamodelrw.h1479
-rw-r--r--src/md/inc/recordpool.h161
-rw-r--r--src/md/inc/rwutil.h369
-rw-r--r--src/md/inc/stgio.h293
-rw-r--r--src/md/inc/stgtiggerstorage.h328
-rw-r--r--src/md/inc/stgtiggerstream.h112
-rw-r--r--src/md/inc/streamutil.h221
-rw-r--r--src/md/inc/verifylayouts.h186
-rw-r--r--src/md/inc/winmdinterfaces.h120
-rw-r--r--src/md/md_dac.cmake3
-rw-r--r--src/md/md_dbi.cmake17
-rw-r--r--src/md/md_wks.cmake6
-rw-r--r--src/md/runtime/.gitmirror1
-rw-r--r--src/md/runtime/CMakeLists.txt23
-rw-r--r--src/md/runtime/Runtime.settings.targets43
-rw-r--r--src/md/runtime/crossgen/.gitmirror1
-rw-r--r--src/md/runtime/crossgen/CMakeLists.txt5
-rw-r--r--src/md/runtime/crossgen/MDRuntime_crossgen.nativeproj15
-rw-r--r--src/md/runtime/dac/.gitmirror1
-rw-r--r--src/md/runtime/dac/CMakeLists.txt7
-rw-r--r--src/md/runtime/dac/dirs.proj19
-rw-r--r--src/md/runtime/dbi/.gitmirror1
-rw-r--r--src/md/runtime/dbi/CMakeLists.txt3
-rw-r--r--src/md/runtime/dbi/MDRuntime-dbi.props10
-rw-r--r--src/md/runtime/dbi/dirs.proj19
-rw-r--r--src/md/runtime/dirs.proj22
-rw-r--r--src/md/runtime/liteweightstgdb.cpp262
-rw-r--r--src/md/runtime/mdcolumndescriptors.cpp214
-rw-r--r--src/md/runtime/mdfileformat.cpp195
-rw-r--r--src/md/runtime/mdinternaldisp.cpp1834
-rw-r--r--src/md/runtime/mdinternaldisp.h44
-rw-r--r--src/md/runtime/mdinternalro.cpp3742
-rw-r--r--src/md/runtime/mdinternalro.h849
-rw-r--r--src/md/runtime/metamodel.cpp1240
-rw-r--r--src/md/runtime/metamodelcolumndefs.h401
-rw-r--r--src/md/runtime/metamodelro.cpp444
-rw-r--r--src/md/runtime/recordpool.cpp388
-rw-r--r--src/md/runtime/stdafx.cpp12
-rw-r--r--src/md/runtime/stdafx.h24
-rw-r--r--src/md/runtime/wks/.gitmirror1
-rw-r--r--src/md/runtime/wks/CMakeLists.txt5
-rw-r--r--src/md/runtime/wks/MDRuntime.nativeproj19
-rw-r--r--src/md/tables/.gitmirror1
-rw-r--r--src/md/tables/export.h16
-rw-r--r--src/md/tables/external.h22
-rw-r--r--src/md/tables/table.h243
-rw-r--r--src/md/winmd/.gitmirror1
-rw-r--r--src/md/winmd/CMakeLists.txt18
-rw-r--r--src/md/winmd/WinMD.settings.targets49
-rw-r--r--src/md/winmd/adapter.cpp2745
-rw-r--r--src/md/winmd/crossgen/.gitmirror1
-rw-r--r--src/md/winmd/crossgen/CMakeLists.txt5
-rw-r--r--src/md/winmd/crossgen/MDWinMD_crossgen.nativeproj14
-rw-r--r--src/md/winmd/dac/.gitmirror1
-rw-r--r--src/md/winmd/dac/CMakeLists.txt6
-rw-r--r--src/md/winmd/dac/dirs.proj19
-rw-r--r--src/md/winmd/dbi/.gitmirror1
-rw-r--r--src/md/winmd/dbi/CMakeLists.txt4
-rw-r--r--src/md/winmd/dbi/MDWinMD-dbi.props9
-rw-r--r--src/md/winmd/dbi/MDWinMD_dbi.nativeproj17
-rw-r--r--src/md/winmd/dbi/dirs.proj19
-rw-r--r--src/md/winmd/dirs.proj21
-rw-r--r--src/md/winmd/inc/.gitmirror1
-rw-r--r--src/md/winmd/inc/adapter.h951
-rw-r--r--src/md/winmd/inc/memotable.h205
-rw-r--r--src/md/winmd/stdafx.cpp12
-rw-r--r--src/md/winmd/stdafx.h27
-rw-r--r--src/md/winmd/winmdimport.cpp2132
-rw-r--r--src/md/winmd/winmdinternalimportro.cpp1804
-rw-r--r--src/md/winmd/wks/.gitmirror1
-rw-r--r--src/md/winmd/wks/CMakeLists.txt4
-rw-r--r--src/md/winmd/wks/MDWinMD_wks.nativeproj19
245 files changed, 105539 insertions, 0 deletions
diff --git a/src/md/.gitmirror b/src/md/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/.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/CMakeLists.txt b/src/md/CMakeLists.txt
new file mode 100644
index 0000000000..09a305d4af
--- /dev/null
+++ b/src/md/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_subdirectory(compiler)
+add_subdirectory(runtime)
+add_subdirectory(enc)
+add_subdirectory(hotdata)
+add_subdirectory(ceefilegen)
+add_subdirectory(datasource)
+if(WIN32)
+add_subdirectory(winmd)
+endif(WIN32)
diff --git a/src/md/MD.props b/src/md/MD.props
new file mode 100644
index 0000000000..69f817f846
--- /dev/null
+++ b/src/md/MD.props
@@ -0,0 +1,158 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--
+ MetadataFlavor has to be set prior to including this file:
+ wks (Desktop or CoreCLR - base on FeatureCoreclr setting)
+ dac
+ mscordbi
+ WinRT-RO
+ WinRT-RW
+ -->
+ <!--
+ MetaData features and defines:
+ FeatureMetadataEmit ... Basic set of emit APIs (IMetaDataEmit*, etc.).
+ FEATURE_METADATA_EMIT
+ Enabled for: Desktop, CoreCLR, dac, mscordbi, WinRT-RW.
+ Disabled for: WinRT-RO.
+ FeatureMetadataEmitAll ... Additional emit APIs (Merging, saving EnC, etc.) mostly for compiler support.
+ Note: Requires FeatureMetadataEmit=true (definition of IMetaDataEmit* APIs).
+ FEATURE_METADATA_EMIT_ALL
+ Enabled for: Desktop.
+ Disabled for: CoreCLR, dac, mscordbi, WinRT-RO, WinRT-RW.
+ FeatureMetadataEmitInDebugger ... Most emit APIs are E_NOTIMPL (as we shipped them in 3.5 and 4.0)
+ Note: Requires FeatureMetadataEmit=true (definition of IMetaDataEmit* APIs) and
+ FeatureMetadataEmitAll=false (some E_NOTIMPL for debuggers are rather under #ifdef *_EMIT_ALL).
+ FEATURE_METADATA_EMIT_IN_DEBUGGER
+ Enabled for: dac, mscordbi
+ Disabled for: Desktop, CoreCLR, WinRT-RO, WinRT-RW
+ List of APIs that return E_NOTIMPL is defined by what we shipped in 3.5/4.0:
+ IMetaDataAssemblyEmit - All methods supported.
+ IMetaDataEmit - Most methods return E_NOTIMPL, except:
+ Save
+ SaveToStream
+ SaveToMemory
+ GetSaveSize
+ TranslateSigWithScope
+ DefineTypeDef
+ SetModuleProps
+ SetHandler
+ IMetaDataEmit2 - All methods return E_NOTIMPL.
+ FeatureMetadataInternalAPIs ... IMDInternalRO and IMDInternalRW implementation.
+ FEATURE_METADATA_INTERNAL_APIS
+ Enabled for: Desktop, CoreCLR, dac, mscordbi.
+ Disabled for: WinRT-RO, WinRT-RW.
+ FeatureMetadataInVM ... Implementation depending on VM (e.g. code:IMetaDataAssemblyImport::FindAssembliesByName - Fusion dependency, IMetaDataValidator).
+ FEATURE_METADATA_IN_VM
+ Enabled for: Desktop, CoreCLR.
+ Disabled for: dac, mscordbi, WinRT-RO, WinRT-RW.
+ FeatureMetadataStandaloneWinRT ... Specifics for both WinRT DLLs (e.g. disabling old formats - v1.x, 2.0 Beta1, NT 5 Beta).
+ FEATURE_METADATA_STANDALONE_WINRT
+ Enabled for: WinRT-RO, WinRT-RW.
+ Disabled for: Desktop, CoreCLR, dac, mscordbi.
+ FeatureMetadataStandaloneWinRTReadOnly ... Specifics for WinRT-RO DLL (no dependencies on ole32.dll).
+ FEATURE_METADATA_STANDALONE_WINRT_RO
+ Enabled for: WinRT-RO.
+ Disabled for: Desktop, CoreCLR, dac, mscordbi, WinRT-RW.
+
+ FEATURE_METADATA_LOAD_TRUSTED_IMAGES ... Enabled only by mscordbi.
+ FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN ... Enabled only by mscordbi
+ Normally Reopening the MD on a new block of memory does not delete any previous memory. In scenarios where the MD is updated
+ many times (reflection.emit), the debugger continually updates the MD and memory growth becomes N^2 relative to the size of metadata.
+ This feature deletes old memory blocks during re-open if we can determine that we haven't given out pointers to their data.
+ See bug .net 4.5 bug 458597 as an example of 1.5GB memory growth.
+ FEATURE_METADATA_CUSTOM_DATA_SOURCE ... Enabled only by mscordbi.
+ This allows a metadata reader to be initialized with an implementation of IMDCustomDataSource which provides more flexibility for
+ how the data flows into the metadata reader.
+ FEATURE_METADATA_DEBUGGEE_DATA_SOURCE ... Enabled only by mscordbi.
+ This is a data source implementation that marshals data out of the debuggee's implementation of MDInternalRW and provides it
+ as a custom data source to a debugger hosted implementation of the metadata reader.
+ FEATURE_METADATA_VERIFY_LAYOUTS ... Enabled only by wks.
+ A set of static asserts that verify the data structures used in MD match the layouts expected in the debuggee data source.
+
+ FEATURE_METADATA_PERF_STATS ... Optional flavor of MetaData for MD perf investigations (not used for a very long time).
+
+ Note: FeatureMetadata* settings and MetadataFlavor value will be checked in file:MD.targets which is included by all subdirectories.
+ -->
+
+ <PropertyGroup Condition="'$(MetadataFlavor)' == 'WinRT-RO' or '$(MetadataFlavor)' == 'WinRT-RW'">
+ <!-- Standalone CLR product (no features enabled by default in clr.props) -->
+ <ClrProduct>Standalone</ClrProduct>
+ </PropertyGroup>
+
+ <!--Import the settings. It has to be done early before FeatureCoreclr is used. -->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup Condition="'$(MetadataFlavor)' == 'wks'">
+ <!-- Enable all features -->
+ <FeatureMetadataEmit>true</FeatureMetadataEmit>
+
+ <!-- Desktop -->
+ <FeatureMetadataEmitAll Condition="'$(FeatureCoreclr)' != 'true'">true</FeatureMetadataEmitAll>
+ <FeatureMetadataValidator Condition="'$(FeatureCoreclr)' != 'true'">true</FeatureMetadataValidator>
+
+ <FeatureMetadataInternalAPIs>true</FeatureMetadataInternalAPIs>
+ <FeatureMetadataInVM>true</FeatureMetadataInVM>
+ <FeatureMetadataVerifyLayouts>true</FeatureMetadataVerifyLayouts>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(MetadataFlavor)' == 'dac'">
+ <!-- Disable all APIs except Public Import and Internal Import&Emit -->
+ <FeatureMetadataEmit>true</FeatureMetadataEmit>
+ <FeatureMetadataEmitInDebugger>true</FeatureMetadataEmitInDebugger>
+ <FeatureMetadataInternalAPIs>true</FeatureMetadataInternalAPIs>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(MetadataFlavor)' == 'mscordbi'">
+ <!-- Enable all internal APIs and all public APIs (with some some implemented as E_NOTIMPL) -->
+ <FeatureMetadataEmit>true</FeatureMetadataEmit>
+ <FeatureMetadataEmitInDebugger>true</FeatureMetadataEmitInDebugger>
+ <FeatureMetadataInternalAPIs>true</FeatureMetadataInternalAPIs>
+ <FeatureMetadataCustomDataSource>true</FeatureMetadataCustomDataSource>
+ <FeatureMetadataDebuggeeDataSource>true</FeatureMetadataDebuggeeDataSource>
+ <!-- Enable mscordbi-only (perf) feature -->
+ <CDefines>$(CDefines);FEATURE_METADATA_LOAD_TRUSTED_IMAGES;FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN</CDefines>
+
+ <!-- Changing defaults set by clr.props -->
+ <LinkNoLibraries>true</LinkNoLibraries>
+ <LinkUseCMT>true</LinkUseCMT>
+ <UseMsvcrt>false</UseMsvcrt>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(MetadataFlavor)' == 'WinRT-RO' or '$(MetadataFlavor)' == 'WinRT-RW'">
+ <!-- Standalone CLR product (no features enabled by default in clr.props) -->
+ <ClrProduct>Standalone</ClrProduct>
+
+ <FeatureUtilcodeNoDependencies>true</FeatureUtilcodeNoDependencies>
+ <FeatureUseLcid>true</FeatureUseLcid>
+
+ <FeatureMetadataStandaloneWinRT>true</FeatureMetadataStandaloneWinRT>
+ <FeatureMetadataStandaloneWinRTReadOnly Condition="'$(MetadataFlavor)' == 'WinRT-RO'">true</FeatureMetadataStandaloneWinRTReadOnly>
+
+ <FeatureMetadataEmit Condition="'$(MetadataFlavor)' == 'WinRT-RW'">true</FeatureMetadataEmit>
+
+ <!-- Link static CRT -->
+ <LinkNoLibraries>true</LinkNoLibraries>
+ <LinkUseCMT>true</LinkUseCMT>
+ <UseMsvcrt>false</UseMsvcrt>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(CrossGenCompile)' == 'true'">
+ <!-- Override settings of certain features to disable them for crossgen unconditionally -->
+ <FeatureMetadataEmitAll>false</FeatureMetadataEmitAll>
+ <FeatureMetadataValidator>false</FeatureMetadataValidator>
+ </PropertyGroup>
+
+ <!-- Verification of FeatureMetadata* and MetadataFlavor set - hook it up early in the build process (e.g. ResolveProjectReferencesForCompile) -->
+ <Target Name="MetadataFlavorAndFeaturesCheck"
+ BeforeTargets="ResolveProjectReferencesForCompile">
+ <Error Condition="'$(MetadataFlavor)' != 'wks' and '$(MetadataFlavor)' != 'dac' and '$(MetadataFlavor)' != 'mscordbi' and '$(MetadataFlavor)' != 'WinRT-RO' and '$(MetadataFlavor)' != 'WinRT-RW'"
+ Text="Unrecognized MetadataFlavor value '$(MetadataFlavor)'." />
+
+ <Error Condition="'$(FeatureMetadataEmitAll)' == 'true' and '$(FeatureMetadataEmit)' != 'true'"
+ Text="FeatureMetadataEmitAll (compiler support) depends on FeatureMetadataEmit (IMetaDataEmit interfaces) - see file:MD.props" />
+ <Error Condition="'$(FeatureMetadataEmitInDebugger)' == 'true' and '$(FeatureMetadataEmit)' != 'true'"
+ Text="FeatureMetadataEmitInDebugger (bunch of E_NOTIMPLs) depends on FeatureMetadataEmit (IMetaDataEmit interfaces) - see file:MD.props" />
+ <Error Condition="'$(FeatureMetadataEmitInDebugger)' == 'true' and '$(FeatureMetadataEmitAll)' == 'true'"
+ Text="FeatureMetadataEmitInDebugger (bunch of E_NOTIMPLs) depends on FeatureMetadataEmitAll (compiler support) disabled - see file:MD.props" />
+ </Target>
+
+</Project> \ No newline at end of file
diff --git a/src/md/ceefilegen/.gitmirror b/src/md/ceefilegen/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/ceefilegen/.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/ceefilegen/CMakeLists.txt b/src/md/ceefilegen/CMakeLists.txt
new file mode 100644
index 0000000000..a1b9107caf
--- /dev/null
+++ b/src/md/ceefilegen/CMakeLists.txt
@@ -0,0 +1,23 @@
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_definitions(-D__TODO_PORT_TO_WRAPPERS__)
+include_directories("../inc")
+
+set(CEEFILEGEN_SOURCES
+ blobfetcher.cpp
+ cceegen.cpp
+ ceegentokenmapper.cpp
+ ceesectionstring.cpp
+ pesectionman.cpp
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+add_precompiled_header(stdafx.h stdafx.cpp CEEFILEGEN_SOURCES)
+
+add_library_clr(ceefgen
+ STATIC
+ ${CEEFILEGEN_SOURCES}
+)
diff --git a/src/md/ceefilegen/blobfetcher.cpp b/src/md/ceefilegen/blobfetcher.cpp
new file mode 100644
index 0000000000..4e934d0b24
--- /dev/null
+++ b/src/md/ceefilegen/blobfetcher.cpp
@@ -0,0 +1,399 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// Implementation for CBlobFetcher
+//
+
+//
+//
+//*****************************************************************************
+#include "stdafx.h" // for ASSERTE and friends
+#include "blobfetcher.h"
+#include "log.h"
+
+//-----------------------------------------------------------------------------
+// round up to a certain alignment
+static inline unsigned roundUp(unsigned val, unsigned align) {
+ _ASSERTE((align & (align - 1)) == 0); // align must be a power of 2
+
+ return((val + (align-1)) & ~(align-1));
+}
+
+//-----------------------------------------------------------------------------
+// round up to a certain alignment
+static inline unsigned padForAlign(unsigned val, unsigned align) {
+ _ASSERTE((align & (align - 1)) == 0); // align must be a power of 2
+ return ((-int(val)) & (align-1));
+}
+
+//*****************************************************************************
+// Pillar implementation
+//*****************************************************************************
+//-----------------------------------------------------------------------------
+CBlobFetcher::CPillar::CPillar()
+{
+ m_dataAlloc = NULL;
+ m_dataStart = NULL;
+ m_dataCur = NULL;
+ m_dataEnd = NULL;
+
+ // Default initial size is 4K bytes.
+ m_nTargetSize = 0x1000;
+}
+
+//-----------------------------------------------------------------------------
+CBlobFetcher::CPillar::~CPillar() {
+// Sanity check to make sure nobody messed up the pts
+ _ASSERTE((m_dataCur >= m_dataStart) && (m_dataCur <= m_dataEnd));
+
+ delete [] m_dataAlloc;
+}
+
+
+//-----------------------------------------------------------------------------
+// Transfer ownership of data, so src will lose data and this will get it.
+// Data itself will remain untouched, just ptrs & ownership change
+//-----------------------------------------------------------------------------
+void CBlobFetcher::CPillar::StealDataFrom(CBlobFetcher::CPillar & src)
+{
+// We should only be moving into an empty Pillar
+ _ASSERTE(m_dataStart == NULL);
+
+
+ m_dataAlloc = src.m_dataAlloc;
+ m_dataStart = src.m_dataStart;
+ m_dataCur = src.m_dataCur;
+ m_dataEnd = src.m_dataEnd;
+
+ m_nTargetSize = src.m_nTargetSize;
+
+// Take away src's claim to data. This prevents multiple ownership and double deleting
+ src.m_dataAlloc = src.m_dataStart = src.m_dataCur = src.m_dataEnd = NULL;
+
+}
+
+//-----------------------------------------------------------------------------
+// Allocate a block in this particular pillar
+//-----------------------------------------------------------------------------
+/* make a new block 'len' bytes long' However, move the pointer 'pad' bytes
+ over so that the memory has the correct alignment characteristics.
+
+ If the return value is NULL, there are two possibilities:
+ - This CPillar reserved less memory than needed for the current allocation.
+ - We are out-of-memory. In this case, CPillar:GetDataLen() will be 0.
+ */
+
+char * CBlobFetcher::CPillar::MakeNewBlock(unsigned len, unsigned pad) {
+
+ _ASSERTE(pad < maxAlign);
+
+ // Make sure we have memory in this block to allocatate
+ if (m_dataStart == NULL) {
+
+ // make sure allocate at least as big as length
+ unsigned nNewTargetSize = max(m_nTargetSize, len);
+
+ //
+ // We need to allocate memory with an offset of "pad" from
+ // being "maxAlign" aligned. (data % maxAlign == pad).
+ // Since "new" doesn't do this, allocate some extra
+ // to handle the worst possible alignment case.
+ //
+ unsigned allocationSize = nNewTargetSize + (maxAlign-1);
+ // Check for integer overflow
+ if (allocationSize < nNewTargetSize)
+ { // Integer overflow happened, fail the allocation
+ return NULL;
+ }
+
+ m_dataAlloc = new (nothrow) char[allocationSize];
+
+ if (m_dataAlloc == NULL)
+ return NULL;
+
+ // Ensure that no uninitialized values are placed into the pe file.
+ // While most of the logic carefully memset's appropriate pad bytes to 0, at least
+ // one place has been found where that wasn't true.
+ memset(m_dataAlloc, 0, allocationSize);
+
+ m_nTargetSize = nNewTargetSize;
+
+ m_dataStart = m_dataAlloc +
+ ((pad - (UINT_PTR)(m_dataAlloc)) & (((UINT_PTR)maxAlign)-1));
+
+ _ASSERTE((UINT_PTR)(m_dataStart) % maxAlign == pad);
+
+ m_dataCur = m_dataStart;
+
+ m_dataEnd = &m_dataStart[m_nTargetSize];
+ }
+
+ _ASSERTE(m_dataCur >= m_dataStart);
+ _ASSERTE((int) len > 0);
+
+ // If this block is full, then get out, we'll have to try another block
+ if (m_dataCur + len > m_dataEnd) {
+ return NULL;
+ }
+
+ char* ret = m_dataCur;
+ m_dataCur += len;
+ _ASSERTE(m_dataCur <= m_dataEnd);
+ return(ret);
+}
+
+
+//*****************************************************************************
+// Blob Fetcher Implementation
+//*****************************************************************************
+
+//-----------------------------------------------------------------------------
+CBlobFetcher::CBlobFetcher()
+{
+ // Setup storage
+ m_pIndex = NULL;
+ m_nIndexMax = 1; // start off with arbitrary small size @@@ (minimum is 1)
+ m_nIndexUsed = 0;
+ _ASSERTE(m_nIndexUsed < m_nIndexMax); // use <, not <=
+
+ m_nDataLen = 0;
+
+ m_pIndex = new CPillar[m_nIndexMax];
+ _ASSERTE(m_pIndex);
+ //<TODO>@FUTURE: what do we do here if we run out of memory??!!</TODO>
+}
+
+//-----------------------------------------------------------------------------
+CBlobFetcher::~CBlobFetcher()
+{
+ delete [] m_pIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Dynamic mem allocation, but we can't move old blocks (since others
+// have pointers to them), so we need a fancy way to grow
+// Returns NULL if the memory could not be allocated.
+//-----------------------------------------------------------------------------
+char* CBlobFetcher::MakeNewBlock(unsigned len, unsigned align) {
+
+ _ASSERTE(m_pIndex);
+ _ASSERTE(0 < align && align <= maxAlign);
+
+ // deal with alignment
+ unsigned pad = padForAlign(m_nDataLen, align);
+ char* pChRet = NULL;
+ if (pad != 0) {
+ pChRet = m_pIndex[m_nIndexUsed].MakeNewBlock(pad, 0);
+
+ // Did we run out of memory?
+ if (pChRet == NULL && m_pIndex[m_nIndexUsed].GetDataLen() == 0)
+ return NULL;
+
+ // if don't have space for the pad, then need to allocate a new pillar
+ // the allocation will handle the padding for the alignment of m_nDataLen
+ if (pChRet) {
+ memset(pChRet, 0, pad);
+ m_nDataLen += pad;
+ pad = 0;
+ }
+ }
+#ifdef _DEBUG
+ if (pChRet)
+ _ASSERTE((m_nDataLen % align) == 0);
+#endif
+
+ // Quickly computing total data length is tough since we have alignment problems
+ // We'll do it by getting the length of all the completely full pillars so far
+ // and then adding on the size of the current pillar
+ unsigned nPreDataLen = m_nDataLen - m_pIndex[m_nIndexUsed].GetDataLen();
+
+ pChRet = m_pIndex[m_nIndexUsed].MakeNewBlock(len + pad, 0);
+
+ // Did we run out of memory?
+ if (pChRet == NULL && m_pIndex[m_nIndexUsed].GetDataLen() == NULL)
+ return NULL;
+
+ if (pChRet == NULL) {
+
+ nPreDataLen = m_nDataLen;
+
+ if (m_nIndexUsed + 1 == m_nIndexMax) {
+ // entire array of pillars are full, re-org
+
+ const unsigned nNewMax = m_nIndexMax * 2; // arbitrary new size
+
+ CPillar* pNewIndex = new (nothrow) CPillar[nNewMax];
+ if (pNewIndex == NULL)
+ return NULL;
+
+ // Copy old stuff
+ for(unsigned i = 0; i < m_nIndexMax; i++)
+ pNewIndex[i].StealDataFrom(m_pIndex[i]);
+
+ delete [] m_pIndex;
+
+ m_nIndexMax = nNewMax;
+ m_pIndex = pNewIndex;
+
+ STRESS_LOG2(LF_LOADER, LL_INFO10, "CBlobFetcher %08X reallocates m_pIndex %08X\n", this, m_pIndex);
+ }
+
+ m_nIndexUsed ++; // current pillar is full, move to next
+
+ // Make sure the new pillar is large enough to hold the data
+ // How we do this is *totally arbitrary* and has been optimized for how
+ // we intend to use this.
+
+ unsigned minSizeOfNewPillar = (3 * m_nDataLen) / 2;
+ if (minSizeOfNewPillar < len)
+ minSizeOfNewPillar = len;
+
+ if (m_pIndex[m_nIndexUsed].GetAllocateSize() < minSizeOfNewPillar) {
+ m_pIndex[m_nIndexUsed].SetAllocateSize(roundUp(minSizeOfNewPillar, maxAlign));
+ }
+
+ // Under stress, we have seen that m_pIndex[0] is empty, but
+ // m_pIndex[1] is not. This assert tries to catch that scenario.
+ _ASSERTE(m_pIndex[0].GetDataLen() != 0);
+
+ // Now that we're on new pillar, try again
+ pChRet = m_pIndex[m_nIndexUsed].MakeNewBlock(len + pad, m_nDataLen % maxAlign);
+ if (pChRet == NULL)
+ return NULL;
+ _ASSERTE(pChRet);
+
+ // The current pointer picks up at the same alignment that the last block left off
+ _ASSERTE(nPreDataLen % maxAlign == ((UINT_PTR) pChRet) % maxAlign);
+ }
+
+ if (pad != 0) {
+ memset(pChRet, 0, pad);
+ pChRet += pad;
+ }
+
+ m_nDataLen = nPreDataLen + m_pIndex[m_nIndexUsed].GetDataLen();
+
+ _ASSERTE(((unsigned) m_nDataLen - len) % align == 0);
+ _ASSERTE((UINT_PTR(pChRet) % align) == 0);
+ return pChRet;
+}
+
+//-----------------------------------------------------------------------------
+// Index segment as if this were linear (middle weight function)
+//-----------------------------------------------------------------------------
+char * CBlobFetcher::ComputePointer(unsigned offset) const
+{
+ _ASSERTE(m_pIndex);
+ unsigned idx = 0;
+
+ if (offset == 0) {
+ // if ask for a 0 offset and no data, return NULL
+ if (m_pIndex[0].GetDataLen() == 0)
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ while (offset >= m_pIndex[idx].GetDataLen()) {
+ offset -= m_pIndex[idx].GetDataLen();
+ idx ++;
+ // Overflow - have asked for an offset greater than what exists
+ if (idx > m_nIndexUsed) {
+ _ASSERTE(!"CBlobFetcher::ComputePointer() Overflow");
+ return NULL;
+ }
+ }
+ }
+
+ char * ptr = (char*) (m_pIndex[idx].GetRawDataStart() + offset);
+ return ptr;
+}
+
+//-----------------------------------------------------------------------------
+// See if a pointer came from this blob fetcher
+//-----------------------------------------------------------------------------
+BOOL CBlobFetcher::ContainsPointer( __in char *ptr) const
+{
+ _ASSERTE(m_pIndex);
+
+ CPillar *p = m_pIndex;
+ CPillar *pEnd = p + m_nIndexUsed;
+
+ unsigned offset = 0;
+
+ while (p <= pEnd) {
+ if (p->Contains(ptr))
+ return TRUE;
+
+ offset += p->GetDataLen();
+ p++;
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Find a pointer as if this were linear (middle weight function)
+//-----------------------------------------------------------------------------
+unsigned CBlobFetcher::ComputeOffset(__in char *ptr) const
+{
+ _ASSERTE(m_pIndex);
+
+ CPillar *p = m_pIndex;
+ CPillar *pEnd = p + m_nIndexUsed;
+
+ unsigned offset = 0;
+
+ while (p <= pEnd) {
+ if (p->Contains(ptr))
+ return offset + p->GetOffset(ptr);
+
+ offset += p->GetDataLen();
+ p++;
+ }
+
+ _ASSERTE(!"Pointer not found");
+ return 0;
+}
+
+
+//Take the data from our previous blob and copy it into our new blob
+//after whatever was already in that blob.
+HRESULT CBlobFetcher::Merge(CBlobFetcher *destination) {
+ unsigned dataLen;
+ char *dataBlock;
+ char *dataCurr;
+ unsigned idx;
+ _ASSERTE(destination);
+
+ dataLen = GetDataLen();
+ _ASSERTE( dataLen >= 0 );
+
+ // Make sure there actually is data in the previous blob before trying to append it.
+ if ( 0 == dataLen )
+ {
+ return S_OK;
+ }
+
+ //Get the length of our data and get a new block large enough to hold all of it.
+ dataBlock = destination->MakeNewBlock(dataLen, 1);
+ if (dataBlock == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ //Copy all of the bytes using the write algorithm from PEWriter.cpp
+ dataCurr=dataBlock;
+ for (idx=0; idx<=m_nIndexUsed; idx++) {
+ if (m_pIndex[idx].GetDataLen()>0) {
+ _ASSERTE(dataCurr<dataBlock+dataLen);
+ memcpy(dataCurr, m_pIndex[idx].GetRawDataStart(), m_pIndex[idx].GetDataLen());
+ dataCurr+=m_pIndex[idx].GetDataLen();
+ }
+ }
+
+ return S_OK;
+
+}
diff --git a/src/md/ceefilegen/cceegen.cpp b/src/md/ceefilegen/cceegen.cpp
new file mode 100644
index 0000000000..268093cd6b
--- /dev/null
+++ b/src/md/ceefilegen/cceegen.cpp
@@ -0,0 +1,706 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+
+#include "stdafx.h"
+
+#include "corerror.h"
+
+#ifdef EnC_SUPPORTED
+#define ENC_DELTA_HACK
+#endif
+
+
+//*****************************************************************************
+// Creation for new CCeeGen instances
+//
+// Both allocate and call virtual Init() (Can't call v-func in a ctor,
+// but we want to create in 1 call);
+//*****************************************************************************
+
+HRESULT STDMETHODCALLTYPE CreateICeeGen(REFIID riid, void **pCeeGen)
+{
+ if (riid != IID_ICeeGen)
+ return E_NOTIMPL;
+ if (!pCeeGen)
+ return E_POINTER;
+ CCeeGen *pCeeFileGen;
+ HRESULT hr = CCeeGen::CreateNewInstance(pCeeFileGen);
+ if (FAILED(hr))
+ return hr;
+ pCeeFileGen->AddRef();
+ *(CCeeGen**)pCeeGen = pCeeFileGen;
+ return S_OK;
+}
+
+HRESULT CCeeGen::CreateNewInstance(CCeeGen* & pGen) // static, public
+{
+ pGen = new CCeeGen();
+ _ASSERTE(pGen != NULL);
+ TESTANDRETURNMEMORY(pGen);
+
+ pGen->m_peSectionMan = new PESectionMan;
+ _ASSERTE(pGen->m_peSectionMan != NULL);
+ TESTANDRETURNMEMORY(pGen->m_peSectionMan);
+
+ HRESULT hr = pGen->m_peSectionMan->Init();
+ TESTANDRETURNHR(hr);
+
+ hr = pGen->Init();
+ TESTANDRETURNHR(hr);
+
+ return hr;
+
+}
+
+STDMETHODIMP CCeeGen::QueryInterface(REFIID riid, void** ppv)
+{
+ if (!ppv)
+ return E_POINTER;
+
+ *ppv = NULL;
+
+ if (riid == IID_IUnknown)
+ *ppv = (IUnknown*)(ICeeGen*)this;
+ else if (riid == IID_ICeeGen)
+ *ppv = (ICeeGen*)this;
+ else if (riid == IID_ICeeGenInternal)
+ *ppv = (ICeeGenInternal*)this;
+ if (*ppv == NULL)
+ return E_NOINTERFACE;
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) CCeeGen::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRefs);
+}
+
+STDMETHODIMP_(ULONG) CCeeGen::Release(void)
+{
+ if (InterlockedDecrement(&m_cRefs) == 0) {
+ Cleanup();
+ delete this;
+ return 0;
+ }
+ return 1;
+}
+
+STDMETHODIMP CCeeGen::SetInitialGrowth(DWORD growth)
+{
+ getIlSection().SetInitialGrowth(growth);
+
+ return S_OK;
+}
+
+STDMETHODIMP CCeeGen::EmitString (__in LPWSTR lpString, ULONG *RVA)
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ if (! RVA)
+ IfFailGo(E_POINTER);
+ hr = getStringSection().getEmittedStringRef(lpString, RVA);
+ErrExit:
+
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+STDMETHODIMP CCeeGen::GetString(ULONG RVA, __inout LPWSTR *lpString)
+{
+ HRESULT hr = E_FAIL;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ if (! lpString)
+ IfFailGo(E_POINTER);
+ *lpString = (LPWSTR)getStringSection().computePointer(RVA);
+
+
+ErrExit:
+
+ END_ENTRYPOINT_NOTHROW;
+ if (*lpString)
+ return S_OK;
+ return hr;
+}
+
+STDMETHODIMP CCeeGen::AllocateMethodBuffer(ULONG cchBuffer, UCHAR **lpBuffer, ULONG *RVA)
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ULONG methodOffset = 0;
+
+ if (! cchBuffer)
+ IfFailGo(E_INVALIDARG);
+ if (! lpBuffer || ! RVA)
+ IfFailGo(E_POINTER);
+ *lpBuffer = (UCHAR*) getIlSection().getBlock(cchBuffer, 4); // Dword align
+ IfNullGo(*lpBuffer);
+
+ // have to compute the method offset after getting the block, not
+ // before (since alignment might shift it up
+ // for in-memory, just return address and will calc later
+ methodOffset = getIlSection().dataLen() - cchBuffer;
+
+ *RVA = methodOffset;
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+}
+
+STDMETHODIMP CCeeGen::GetMethodBuffer(ULONG RVA, UCHAR **lpBuffer)
+{
+ HRESULT hr = E_FAIL;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ if (! lpBuffer)
+ IfFailGo(E_POINTER);
+ *lpBuffer = (UCHAR*)getIlSection().computePointer(RVA);
+
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ if (lpBuffer != NULL && *lpBuffer != 0)
+ return S_OK;
+
+ return hr;
+}
+
+STDMETHODIMP CCeeGen::ComputePointer(HCEESECTION section, ULONG RVA, UCHAR **lpBuffer)
+{
+ HRESULT hr = E_FAIL;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ if (! lpBuffer)
+ IfFailGo(E_POINTER);
+ *lpBuffer = (UCHAR*) ((CeeSection *)section)->computePointer(RVA);
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ if (lpBuffer != NULL && *lpBuffer != 0)
+ return S_OK;
+ return hr;
+}
+
+STDMETHODIMP CCeeGen::GetIMapTokenIface (
+ IUnknown **pIMapToken)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(!"E_NOTIMPL");
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP CCeeGen::AddNotificationHandler (
+ IUnknown *pHandler)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+ _ASSERTE(!"E_NOTIMPL");
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP CCeeGen::GenerateCeeFile ()
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(!"E_NOTIMPL");
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP CCeeGen::GenerateCeeMemoryImage (void **)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(!"E_NOTIMPL");
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP CCeeGen::GetIlSection (
+ HCEESECTION *section)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *section = (HCEESECTION)(m_sections[m_ilIdx]);
+ END_ENTRYPOINT_NOTHROW;
+
+ return S_OK;
+}
+
+STDMETHODIMP CCeeGen::GetStringSection(HCEESECTION *section)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(!"E_NOTIMPL");
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP CCeeGen::AddSectionReloc (
+ HCEESECTION section,
+ ULONG offset,
+ HCEESECTION relativeTo,
+ CeeSectionRelocType relocType)
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ hr = m_sections[m_ilIdx]->addSectReloc(offset, *(m_sections[m_ilIdx]), relocType);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+STDMETHODIMP CCeeGen::GetSectionCreate (
+ const char *name,
+ DWORD flags,
+ HCEESECTION *section)
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ short sectionIdx;
+ hr = getSectionCreate (name, flags, (CeeSection **)section, &sectionIdx);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+STDMETHODIMP CCeeGen::GetSectionDataLen (
+ HCEESECTION section,
+ ULONG *dataLen)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CeeSection *pSection = (CeeSection*) section;
+ *dataLen = pSection->dataLen();
+ END_ENTRYPOINT_NOTHROW;
+
+ return NOERROR;
+}
+
+STDMETHODIMP CCeeGen::GetSectionBlock (
+ HCEESECTION section,
+ ULONG len,
+ ULONG align,
+ void **ppBytes)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CeeSection *pSection = (CeeSection*) section;
+ *ppBytes = (BYTE *)pSection->getBlock(len, align);
+ END_ENTRYPOINT_NOTHROW;
+
+ if (*ppBytes == 0)
+ return E_OUTOFMEMORY;
+ return NOERROR;
+}
+
+STDMETHODIMP CCeeGen::TruncateSection (
+ HCEESECTION section,
+ ULONG len)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(!"E_NOTIMPL");
+ END_ENTRYPOINT_NOTHROW;
+ return E_NOTIMPL;
+}
+
+
+
+CCeeGen::CCeeGen() // protected ctor
+{
+// All other init done in InitCommon()
+ m_cRefs = 0;
+ m_peSectionMan = NULL;
+ m_pTokenMap = NULL;
+ m_pRemapHandler = NULL;
+
+}
+
+// Shared init code between derived classes, called by virtual Init()
+HRESULT CCeeGen::Init() // not-virtual, protected
+{
+// Public, Virtual init must create our SectionManager, and
+// Common init does the rest
+ _ASSERTE(m_peSectionMan != NULL);
+
+ HRESULT hr = S_OK;
+
+ PESection *section = NULL;
+ CeeSection *ceeSection = NULL;
+
+ m_corHeader = NULL;
+
+ m_numSections = 0;
+ m_allocSections = 10;
+ m_sections = new CeeSection * [ m_allocSections ];
+ if (m_sections == NULL) {
+ hr = E_OUTOFMEMORY;
+ goto LExit;
+ }
+
+ m_pTokenMap = NULL;
+ m_fTokenMapSupported = FALSE;
+ m_pRemapHandler = NULL;
+
+ // These text section needs special support for handling string management now that we have
+ // merged the sections together, so create it with an underlying CeeSectionString rather than the
+ // more generic CeeSection
+
+ hr = m_peSectionMan->getSectionCreate(".text", sdExecute, &section);
+ if (FAILED(hr)) {
+ goto LExit;
+ }
+
+ ceeSection = new CeeSectionString(*this, *section);
+ if (ceeSection == NULL) {
+ hr = E_OUTOFMEMORY;
+ goto LExit;
+ }
+
+ hr = addSection(ceeSection, &m_stringIdx);
+
+ m_textIdx = m_stringIdx;
+
+ m_metaIdx = m_textIdx; // meta section is actually in .text
+ m_ilIdx = m_textIdx; // il section is actually in .text
+ m_corHdrIdx = -1;
+ m_encMode = FALSE;
+
+LExit:
+ if (FAILED(hr)) {
+ Cleanup();
+ }
+
+ return hr;
+}
+
+// For EnC mode, generate strings into .rdata section rather than .text section
+HRESULT CCeeGen::setEnCMode()
+{
+ PESection *section = NULL;
+ HRESULT hr = m_peSectionMan->getSectionCreate(".rdata", sdExecute, &section);
+ TESTANDRETURNHR(hr);
+ CeeSection *ceeSection = new CeeSectionString(*this, *section);
+ if (ceeSection == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ hr = addSection(ceeSection, &m_stringIdx);
+ if (SUCCEEDED(hr))
+ m_encMode = TRUE;
+ return hr;
+}
+
+
+HRESULT CCeeGen::cloneInstance(CCeeGen *destination) { //public, virtual
+ _ASSERTE(destination);
+
+ destination->m_pTokenMap = m_pTokenMap;
+ destination->m_fTokenMapSupported = m_fTokenMapSupported;
+ destination->m_pRemapHandler = m_pRemapHandler;
+
+ //Create a deep copy of the section manager (and each of it's sections);
+ return m_peSectionMan->cloneInstance(destination->m_peSectionMan);
+}
+
+HRESULT CCeeGen::Cleanup() // virtual
+{
+ HRESULT hr;
+ for (int i = 0; i < m_numSections; i++) {
+ delete m_sections[i];
+ }
+
+ delete [] m_sections;
+
+ CeeGenTokenMapper *pMapper = m_pTokenMap;
+ if (pMapper) {
+ if (pMapper->m_pIImport) {
+ IMetaDataEmit *pIIEmit;
+ if (SUCCEEDED( hr = pMapper->m_pIImport->QueryInterface(IID_IMetaDataEmit, (void **) &pIIEmit)))
+ {
+ pIIEmit->SetHandler(NULL);
+ pIIEmit->Release();
+ }
+ _ASSERTE(SUCCEEDED(hr));
+ pMapper->m_pIImport->Release();
+ }
+ pMapper->Release();
+ m_pTokenMap = NULL;
+ }
+
+ if (m_pRemapHandler)
+ {
+ m_pRemapHandler->Release();
+ m_pRemapHandler = NULL;
+ }
+
+ if (m_peSectionMan) {
+ m_peSectionMan->Cleanup();
+ delete m_peSectionMan;
+ }
+
+ return S_OK;
+}
+
+HRESULT CCeeGen::addSection(CeeSection *section, short *sectionIdx)
+{
+ if (m_numSections >= m_allocSections)
+ {
+ _ASSERTE(m_allocSections > 0);
+ while (m_numSections >= m_allocSections)
+ m_allocSections <<= 1;
+ CeeSection **newSections = new CeeSection * [m_allocSections];
+ if (newSections == NULL)
+ return E_OUTOFMEMORY;
+ CopyMemory(newSections, m_sections, m_numSections * sizeof(*m_sections));
+ if (m_sections != NULL)
+ delete [] m_sections;
+ m_sections = newSections;
+ }
+
+ if (sectionIdx)
+ *sectionIdx = m_numSections;
+
+ m_sections[m_numSections++] = section;
+ return S_OK;
+}
+
+HRESULT CCeeGen::getSectionCreate (const char *name, DWORD flags, CeeSection **section, short *sectionIdx)
+{
+ if (strcmp(name, ".il") == 0)
+ name = ".text";
+ else if (strcmp(name, ".meta") == 0)
+ name = ".text";
+ else if (strcmp(name, ".rdata") == 0 && !m_encMode)
+ name = ".text";
+ for (int i=0; i<m_numSections; i++) {
+ if (strcmp((const char *)m_sections[i]->name(), name) == 0) {
+ if (section)
+ *section = m_sections[i];
+ if (sectionIdx)
+ *sectionIdx = i;
+ return S_OK;
+ }
+ }
+ PESection *pewSect = NULL;
+ HRESULT hr = m_peSectionMan->getSectionCreate(name, flags, &pewSect);
+ TESTANDRETURNHR(hr);
+ CeeSection *newSect = new CeeSection(*this, *pewSect);
+ // if this fails, the PESection will get nuked in the destructor for CCeeGen
+ if (newSect == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ hr = addSection(newSect, sectionIdx);
+ TESTANDRETURNHR(hr);
+ if (section)
+ *section = newSect;
+ return S_OK;
+}
+
+
+HRESULT CCeeGen::emitMetaData(IMetaDataEmit *emitter, CeeSection* section, DWORD offset, BYTE* buffer, unsigned buffLen)
+{
+ HRESULT hr = S_OK;
+
+ ReleaseHolder<IStream> metaStream(NULL);
+
+ IfFailRet((HRESULT)CreateStreamOnHGlobal(NULL, TRUE, &metaStream));
+
+ if (! m_fTokenMapSupported) {
+ IUnknown *pMapTokenIface;
+ IfFailGoto(getMapTokenIface(&pMapTokenIface, emitter), Exit);
+
+ // Set a callback for token remap and save the tokens which change.
+ IfFailGoto(emitter->SetHandler(pMapTokenIface), Exit);
+ }
+
+ // generate the metadata
+ IfFailGoto(emitter->SaveToStream(metaStream, 0), Exit);
+
+ // get size of stream and get sufficient storage for it
+
+ if (section == 0) {
+ section = &getMetaSection();
+ STATSTG statStg;
+ IfFailGoto((HRESULT)(metaStream->Stat(&statStg, STATFLAG_NONAME)), Exit);
+
+ buffLen = statStg.cbSize.u.LowPart;
+ if(m_objSwitch)
+ {
+ CeeSection* pSect;
+ DWORD flags = IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE | IMAGE_SCN_ALIGN_1BYTES; // 0x00100A00
+ IfFailGoto(getSectionCreate(".cormeta",flags,&pSect,&m_metaIdx), Exit);
+ }
+ buffer = (BYTE *)section->getBlock(buffLen, sizeof(DWORD));
+ IfNullGoto(buffer, Exit);
+ offset = getMetaSection().dataLen() - buffLen;
+ }
+ else {
+ _ASSERTE(buffer[buffLen-1] || true); // Dereference 'buffer'
+ _ASSERTE(section->computeOffset(PCHAR(buffer)) == offset);
+ }
+
+ // reset seek pointer and read from stream
+ {
+ LARGE_INTEGER disp = { {0, 0} };
+ IfFailGoto((HRESULT)metaStream->Seek(disp, STREAM_SEEK_SET, NULL), Exit);
+ }
+ ULONG metaDataLen;
+ IfFailGoto((HRESULT)metaStream->Read(buffer, buffLen+1, &metaDataLen), Exit);
+
+ _ASSERTE(metaDataLen <= buffLen);
+
+#ifdef ENC_DELTA_HACK
+ extern int __cdecl fclose(FILE *);
+ WCHAR szFileName[256];
+ DWORD len = GetEnvironmentVariable(W("COMP_ENC_EMIT"), szFileName, ARRAYSIZE(szFileName));
+ _ASSERTE(len < (ARRAYSIZE(szFileName) + 6)); // +6 for the .dmeta
+ if (len > 0 && len < (ARRAYSIZE(szFileName) + 6))
+ {
+ wcscat_s(szFileName, ARRAYSIZE(szFileName), W(".dmeta"));
+ FILE *pDelta;
+ int ec = _wfopen_s(&pDelta, szFileName, W("wb"));
+ if (FAILED(ec)) { return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); }
+ fwrite(buffer, 1, metaDataLen, pDelta);
+ fclose(pDelta);
+ }
+#endif
+
+
+ // Set meta virtual address to offset of metadata within .meta, and
+ // and add a reloc for this offset, which will get turned
+ // into an rva when the pewriter writes out the file.
+
+ m_corHeader->MetaData.VirtualAddress = VAL32(offset);
+ getCorHeaderSection().addSectReloc(m_corHeaderOffset + offsetof(IMAGE_COR20_HEADER, MetaData), *section, srRelocAbsolute);
+ m_corHeader->MetaData.Size = VAL32(metaDataLen);
+
+Exit:
+
+ if (! m_fTokenMapSupported) {
+ // Remove the handler that we set
+ hr = emitter->SetHandler(NULL);
+ }
+
+#ifdef _DEBUG
+ if (FAILED(hr) && hr != E_OUTOFMEMORY)
+ _ASSERTE(!"Unexpected Failure");
+#endif
+
+ return hr;
+}
+
+// Create the COM header - it goes at front of .meta section
+// Need to do this before the meta data is copied in, but don't do at
+// the same time because may not have metadata
+HRESULT CCeeGen::allocateCorHeader()
+{
+ HRESULT hr = S_OK;
+ CeeSection *corHeaderSection = NULL;
+ if (m_corHdrIdx < 0) {
+ hr = getSectionCreate(".text0", sdExecute, &corHeaderSection, &m_corHdrIdx);
+ TESTANDRETURNHR(hr);
+
+ m_corHeaderOffset = corHeaderSection->dataLen();
+ m_corHeader = (IMAGE_COR20_HEADER*)corHeaderSection->getBlock(sizeof(IMAGE_COR20_HEADER));
+ if (! m_corHeader)
+ return E_OUTOFMEMORY;
+ memset(m_corHeader, 0, sizeof(IMAGE_COR20_HEADER));
+ }
+ return S_OK;
+}
+
+HRESULT CCeeGen::getMethodRVA(ULONG codeOffset, ULONG *codeRVA)
+{
+ _ASSERTE(codeRVA);
+ // for runtime conversion, just return the offset and will calculate real address when need the code
+ *codeRVA = codeOffset;
+ return S_OK;
+}
+
+HRESULT CCeeGen::getMapTokenIface(IUnknown **pIMapToken, IMetaDataEmit *emitter)
+{
+ if (! pIMapToken)
+ return E_POINTER;
+ if (! m_pTokenMap) {
+ // Allocate the token mapper. As code is generated, each moved token will be added to
+ // the mapper and the client will also add a TokenMap reloc for it so we can update later
+ CeeGenTokenMapper *pMapper = new CeeGenTokenMapper;
+ if (pMapper == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (emitter) {
+ HRESULT hr;
+ hr = emitter->QueryInterface(IID_IMetaDataImport, (PVOID *) &pMapper->m_pIImport);
+ _ASSERTE(SUCCEEDED(hr));
+ }
+ m_pTokenMap = pMapper;
+ m_fTokenMapSupported = (emitter == 0);
+
+ // If we've been holding onto a token remap handler waiting
+ // for the token mapper to get created, add it to the token
+ // mapper now and release our hold on it.
+ if (m_pRemapHandler && m_pTokenMap)
+ {
+ m_pTokenMap->AddTokenMapper(m_pRemapHandler);
+ m_pRemapHandler->Release();
+ m_pRemapHandler = NULL;
+ }
+ }
+ *pIMapToken = getTokenMapper()->GetMapTokenIface();
+ return S_OK;
+}
+
+HRESULT CCeeGen::addNotificationHandler(IUnknown *pHandler)
+{
+ // Null is no good...
+ if (!pHandler)
+ return E_POINTER;
+
+ HRESULT hr = S_OK;
+ IMapToken *pIMapToken = NULL;
+
+ // Is this an IMapToken? If so, we can put it to good use...
+ if (SUCCEEDED(pHandler->QueryInterface(IID_IMapToken,
+ (void**)&pIMapToken)))
+ {
+ // You gotta have a token mapper to use an IMapToken, though.
+ if (m_pTokenMap)
+ {
+ hr = m_pTokenMap->AddTokenMapper(pIMapToken);
+ pIMapToken->Release();
+ }
+ else
+ {
+ // Hold onto it for later, just in case a token mapper
+ // gets created. We're holding a reference to it here,
+ // too.
+ m_pRemapHandler = pIMapToken;
+ }
+ }
+
+ return hr;
+}
diff --git a/src/md/ceefilegen/ceefgen.nativeproj b/src/md/ceefilegen/ceefgen.nativeproj
new file mode 100644
index 0000000000..ddaf673d2d
--- /dev/null
+++ b/src/md/ceefilegen/ceefgen.nativeproj
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <PropertyGroup Label="Globals">
+ <SccProjectName>SAK</SccProjectName>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <UserIncludes>$(UserIncludes);
+ .;
+ ..\inc</UserIncludes>
+ <!--OK to delete NO_NTDLL for devdiv builds.-->
+ <CDefines>$(CDefines);UNICODE;_UNICODE</CDefines>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -D__TODO_PORT_TO_WRAPPERS__</ClAdditionalOptions>
+ <OutputName>ceefgen</OutputName>
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <PCHHeader>stdafx.h</PCHHeader>
+ <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders>
+ <PCHCompile>stdafx.cpp</PCHCompile>
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <CppCompile Include="BlobFetcher.cpp" />
+ <CppCompile Include="CCeeGen.cpp" />
+ <CppCompile Include="CeeGenTokenMapper.cpp" />
+ <CppCompile Include="CeeSectionString.cpp" />
+ <CppCompile Include="PESectionMan.cpp" />
+ </ItemGroup>
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/ceefilegen/ceegentokenmapper.cpp b/src/md/ceefilegen/ceegentokenmapper.cpp
new file mode 100644
index 0000000000..95ccbcd3f2
--- /dev/null
+++ b/src/md/ceefilegen/ceegentokenmapper.cpp
@@ -0,0 +1,160 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// CeeGenTokenMapper.cpp
+//
+
+//
+// This helper class tracks mapped tokens from their old value to the new value
+// which can happen when the data is optimized on save.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "ceegentokenmapper.h"
+
+
+//*****************************************************************************
+// At this point, only a select set of token values are stored for remap.
+// If others should become required, this needs to get updated.
+//*****************************************************************************
+int CeeGenTokenMapper::IndexForType(mdToken tk)
+{
+#ifdef CEEGEN_TRACKED_TOKEN
+#undef CEEGEN_TRACKED_TOKEN
+#endif
+#define CEEGEN_TRACKED_TOKEN(type) case INDEX_OF_TYPE(mdt ## type): return (tkix ## type);
+
+ int iType = INDEX_OF_TYPE(TypeFromToken(tk));
+ switch(iType)
+ {
+ CEEGEN_TRACKED_TOKENS()
+ }
+
+ return (-1);
+}
+
+
+//*****************************************************************************
+// Called by the meta data engine when a token is remapped to a new location.
+// This value is recorded in the m_rgMap array based on type and rid of the
+// from token value.
+//*****************************************************************************
+HRESULT __stdcall CeeGenTokenMapper::Map(
+ mdToken tkFrom,
+ mdToken tkTo)
+{
+ HRESULT hr = S_OK;
+ mdToken *pToken = NULL;
+ ULONG ridFrom = 0;
+ TOKENMAP *pMap = NULL;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ if ( IndexForType(tkFrom) == -1 )
+ {
+ // It is a type that we are not tracking, such as mdtProperty or mdtEvent,
+ // just return S_OK.
+ goto ErrExit;
+ }
+
+ _ASSERTE(IndexForType(tkFrom) < GetMaxMapSize());
+ _ASSERTE(IndexForType(tkTo) != -1 && IndexForType(tkTo) < GetMaxMapSize());
+
+ // If there is another token mapper that the user wants called, go
+ // ahead and call it now.
+ if (m_pIMapToken)
+ m_pIMapToken->Map(tkFrom, tkTo);
+
+ ridFrom = RidFromToken(tkFrom);
+ pMap = &m_rgMap[IndexForType(tkFrom)];
+
+ // If there isn't enough entries, fill out array up to the count
+ // and mark the token to nil so we know there is no valid data yet.
+ if ((ULONG) pMap->Count() <= ridFrom)
+ {
+ for (int i=ridFrom - pMap->Count() + 1; i; i--)
+ {
+ pToken = pMap->Append();
+ if (!pToken)
+ break;
+ *pToken = mdTokenNil;
+ }
+ _ASSERTE(!pToken || pMap->Get(ridFrom) == pToken);
+ }
+ else
+ pToken = pMap->Get(ridFrom);
+
+ IfNullGo(pToken);
+
+ *pToken = tkTo;
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+}
+
+
+//*****************************************************************************
+// Check the given token to see if it has moved to a new location. If so,
+// return true and give back the new token.
+//*****************************************************************************
+int CeeGenTokenMapper::HasTokenMoved(
+ mdToken tkFrom,
+ mdToken &tkTo)
+{
+ mdToken tk;
+
+ int i = IndexForType(tkFrom);
+ if(i == -1) return false;
+
+ _ASSERTE(i < GetMaxMapSize());
+ TOKENMAP *pMap = &m_rgMap[i];
+
+ // Assume nothing moves.
+ tkTo = tkFrom;
+
+ // If the array is smaller than the index, can't have moved.
+ if ((ULONG) pMap->Count() <= RidFromToken(tkFrom))
+ return (false);
+
+ // If the entry is set to 0, then nothing there.
+ tk = *pMap->Get(RidFromToken(tkFrom));
+ if (tk == mdTokenNil)
+ return (false);
+
+ // Had to move to a new location, return that new location.
+ tkTo = tk;
+ return (true);
+}
+
+
+//*****************************************************************************
+// Hand out a copy of the meta data information.
+//*****************************************************************************
+
+HRESULT CeeGenTokenMapper::GetMetaData(
+ IMetaDataImport **ppIImport)
+{
+ if (m_pIImport)
+ return (m_pIImport->QueryInterface(IID_IMetaDataImport, (PVOID *) ppIImport));
+ *ppIImport = 0;
+ return E_FAIL;
+}
+
+
+HRESULT __stdcall CeeGenTokenMapper::QueryInterface(REFIID iid, PVOID *ppIUnk)
+{
+ if (iid == IID_IUnknown || iid == IID_IMapToken)
+ *ppIUnk = static_cast<IMapToken*>(this);
+ else
+ {
+ *ppIUnk = 0;
+ return (E_NOINTERFACE);
+ }
+ AddRef();
+ return (S_OK);
+}
+
+
diff --git a/src/md/ceefilegen/ceesectionstring.cpp b/src/md/ceefilegen/ceesectionstring.cpp
new file mode 100644
index 0000000000..a1a9e7f17f
--- /dev/null
+++ b/src/md/ceefilegen/ceesectionstring.cpp
@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ===========================================================================
+// File: CeeSectionString.cpp
+//
+
+//
+// ===========================================================================
+#include "stdafx.h"
+
+struct StringTableEntry {
+ ULONG m_hashId;
+ int m_offset;
+ StringTableEntry *m_next;
+};
+
+CeeSectionString::CeeSectionString(CCeeGen &ceeFile, CeeSectionImpl &impl)
+ : CeeSection(ceeFile, impl)
+{
+ memset(stringTable, 0, sizeof(stringTable));
+}
+
+void CeeSectionString::deleteEntries(StringTableEntry *e)
+{
+ if (!e)
+ return;
+ deleteEntries(e->m_next);
+ delete e;
+}
+
+#ifdef RDATA_STATS
+int CeeSectionString::dumpEntries(StringTableEntry *e)
+{
+ if (!e)
+ return 0;
+ else {
+ printf(" HashId: %d, value: %S\n", e->m_hashId, computOffset(e->m_offset));
+ return dumpEntries(e->m_next) + 1;
+ }
+}
+
+void CeeSectionString::dumpTable()
+{
+ int sum = 0, count = 0;
+ for (int i=0; i < MaxRealEntries; i++) {
+ if (stringTable[i]) {
+ printf("Bucket %d\n", i);
+ printf("Total size: %d\n\n",
+ count = dumpEntries(stringTable[i]));
+ sum += count;
+ }
+ }
+ printf("Total number strings: %d\n\n", sum);
+}
+#endif
+
+CeeSectionString::~CeeSectionString()
+{
+#ifdef RDATA_STATS
+ dumpTable();
+#endif
+ for (int i=0; i < MaxRealEntries; i++)
+ deleteEntries(stringTable[i]);
+}
+
+StringTableEntry* CeeSectionString::createEntry(__in_z LPWSTR target, ULONG hashId)
+{
+ StringTableEntry *entry = new StringTableEntry;
+ if (!entry)
+ return NULL;
+ entry->m_next = NULL;
+ entry->m_hashId = hashId;
+ entry->m_offset = dataLen();
+ size_t len = (wcslen(target)+1) * sizeof(wchar_t);
+ if (len > ULONG_MAX) {
+ delete entry;
+ return NULL;
+ }
+ void *buf = getBlock((ULONG)len);
+ if (!buf) {
+ delete entry;
+ return NULL;
+ }
+ memcpy(buf, target, len);
+ return entry;
+}
+
+// Searches through the linked list looking for a match on hashID. If
+// multiple elements hash to the same value, a strcmp must be done to
+// check for match. The goal is to have very large hashId space so that
+// string compares are minimized
+StringTableEntry *CeeSectionString::findStringInsert(
+ StringTableEntry *&head, __in_z LPWSTR target, ULONG hashId)
+{
+ StringTableEntry *cur, *prev;
+ cur = prev = head;
+ while (cur && cur->m_hashId < hashId) {
+ prev = cur;
+ cur = cur->m_next;
+ }
+ while (cur && cur->m_hashId == hashId) {
+ if (wcscmp(target, (LPWSTR)(computePointer(cur->m_offset))) == 0)
+ return cur;
+ prev = cur;
+ cur = cur->m_next;
+ }
+ // didn't find in chain so insert at prev
+ StringTableEntry *entry = createEntry(target, hashId);
+ if (cur == head) {
+ head = entry;
+ entry->m_next = prev;
+ } else {
+ prev->m_next = entry;
+ entry->m_next = cur;
+ }
+ return entry;
+}
+
+HRESULT CeeSectionString::getEmittedStringRef(__in_z LPWSTR target, StringRef *ref)
+{
+ TESTANDRETURN(ref!=NULL, E_POINTER);
+ ULONG hashId = HashString(target) % MaxVirtualEntries;
+ ULONG bucketIndex = hashId / MaxRealEntries;
+
+ StringTableEntry *entry;
+ entry = findStringInsert(stringTable[bucketIndex], target, hashId);
+
+ if (! entry)
+ return E_OUTOFMEMORY;
+ *ref = entry->m_offset;
+ return S_OK;
+}
diff --git a/src/md/ceefilegen/pesectionman.cpp b/src/md/ceefilegen/pesectionman.cpp
new file mode 100644
index 0000000000..8a2c963491
--- /dev/null
+++ b/src/md/ceefilegen/pesectionman.cpp
@@ -0,0 +1,428 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// PESectionMan implementation
+//
+
+
+#include "stdafx.h"
+
+/*****************************************************************/
+HRESULT PESectionMan::Init()
+{
+ const int initNumSections = 16;
+ sectStart = new (nothrow) PESection*[initNumSections];
+ if (!sectStart)
+ return E_OUTOFMEMORY;
+ sectCur = sectStart;
+ sectEnd = &sectStart[initNumSections];
+
+ return S_OK;
+}
+
+/*****************************************************************/
+HRESULT PESectionMan::Cleanup()
+{
+ for (PESection** ptr = sectStart; ptr < sectCur; ptr++)
+ delete *ptr;
+ delete [] sectStart;
+
+ return S_OK;
+}
+
+/*****************************************************************/
+// <REVISIT_TODO>this class is located in it's own DLL (MsCorXvt.dll)
+// Since DLL allocates, The DLL must delete; we can't simply delete from
+// the client (This is a bug in VC, see knowledge base Q122675)</REVISIT_TODO>
+void PESectionMan::sectionDestroy(PESection **section)
+{
+ // check if this section is referenced in other sections' relocs
+ for(PESection** ptr = sectStart; ptr < sectCur; ptr++)
+ {
+ if(ptr != section)
+ {
+ for(PESectionReloc* cur = (*ptr)->m_relocStart; cur < (*ptr)->m_relocCur; cur++)
+ {
+ if(cur->section == *section) // here it is! Delete the reference
+ {
+ for(PESectionReloc* tmp = cur; tmp < (*ptr)->m_relocCur; tmp++)
+ {
+ memcpy(tmp,(tmp+1),sizeof(PESectionReloc));
+ }
+ (*ptr)->m_relocCur--;
+ cur--; // no position shift this time
+ }
+ }
+ }
+ }
+ delete *section;
+ *section = NULL;
+}
+/*****************************************************************/
+
+/******************************************************************/
+// Apply the relocs for all the sections
+// Called by: ClassConverter after loading up during an in-memory conversion,
+
+HRESULT PESectionMan::applyRelocs(CeeGenTokenMapper *pTokenMapper)
+{
+ HRESULT hr;
+
+ // Cycle through each of the sections
+ for(PESection ** ppCurSection = sectStart; ppCurSection < sectCur; ppCurSection++) {
+ IfFailRet((*ppCurSection)->applyRelocs(pTokenMapper));
+ } // End sections
+ return S_OK;
+}
+
+
+/*****************************************************************/
+PESection* PESectionMan::getSection(const char* name)
+{
+ int len = (int)strlen(name);
+
+ // the section name can be at most 8 characters including the null.
+ if (len < 8)
+ len++;
+ else
+ len = 8;
+
+ // dbPrintf(("looking for section %s\n", name));
+ for(PESection** cur = sectStart; cur < sectCur; cur++) {
+ // dbPrintf(("searching section %s\n", (*cur)->m_ame));
+ if (strncmp((*cur)->m_name, name, len) == 0) {
+ // dbPrintf(("found section %s\n", (*cur)->m_name));
+ return(*cur);
+ }
+ }
+ return(0);
+}
+
+/******************************************************************/
+HRESULT PESectionMan::getSectionCreate(const char* name, unsigned flags,
+ PESection **section)
+{
+ PESection* ret = getSection(name);
+
+ // If there is an existing section with the given name, return that
+ if (ret != NULL) {
+ *section = ret;
+ return(S_OK);
+ }
+
+ // Check if there is space for a new section
+ if (sectCur >= sectEnd) {
+ unsigned curLen = (unsigned)(sectCur-sectStart);
+ unsigned newLen = (curLen * 2) + 1;
+ PESection** sectNew = new (nothrow) PESection*[newLen];
+ if (sectNew == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ memcpy(sectNew, sectStart, sizeof(PESection*)*curLen);
+ delete [] sectStart;
+ sectStart = sectNew;
+ sectCur = &sectStart[curLen];
+ sectEnd = &sectStart[newLen];
+ }
+
+ HRESULT hr;
+ IfFailRet(newSection(name, &ret, flags));
+
+ // dbPrintf(("MAKING NEW %s SECTION data starts at 0x%x\n", name, ret->dataStart));
+ *sectCur++ = ret;
+ _ASSERTE(sectCur <= sectEnd);
+ *section = ret;
+ return(S_OK);
+}
+
+/******************************************************************/
+HRESULT PESectionMan::newSection(const char* name, PESection **section,
+ unsigned flags, unsigned estSize, unsigned estRelocs)
+{
+ PESection * ret = new (nothrow) PESection(name, flags, estSize, estRelocs);
+ if (ret == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ *section = ret;
+ return S_OK;
+}
+
+//Clone each of our sections. This will cause a deep copy of the sections
+HRESULT PESectionMan::cloneInstance(PESectionMan *destination) {
+ _ASSERTE(destination);
+ PESection *pSection;
+ PESection **destPtr;
+ HRESULT hr = NOERROR;
+
+ //Copy each of the sections
+ for (PESection** ptr = sectStart; ptr < sectCur; ptr++) {
+ destPtr = destination->sectStart;
+ pSection = NULL;
+
+ // try to find the matching section by name
+ for (; destPtr < destination->sectCur; destPtr++)
+ {
+ if (strcmp((*destPtr)->m_name, (*ptr)->m_name) == 0)
+ {
+ pSection = *destPtr;
+ break;
+ }
+ }
+ if (destPtr >= destination->sectCur)
+ {
+ // cannot find a section in the destination with matching name
+ // so create one!
+ IfFailRet( destination->getSectionCreate((*ptr)->m_name,
+ (*ptr)->flags(),
+ &pSection) );
+ }
+ if (pSection)
+ IfFailRet( (*ptr)->cloneInstance(pSection) );
+ }
+
+ //destination->sectEnd=destination->sectStart + (sectEnd-sectStart);
+ return S_OK;
+}
+
+
+//*****************************************************************************
+// Implementation for PESection
+//*****************************************************************************
+PESection::PESection(const char *name, unsigned flags,
+ unsigned estSize, unsigned estRelocs)
+{
+ dirEntry = -1;
+
+ // No init needed for CBlobFectcher m_pIndex
+
+ m_relocStart = new (nothrow) PESectionReloc[estRelocs];
+ if (m_relocStart == NULL)
+ {
+ // Can't report an error out of here - just initialize
+ // as if estRelocs was 0 (all three m_reloc pointers will be NULL).
+ // We'll lazily grow as needed.
+ estRelocs = 0;
+ }
+ m_relocCur = m_relocStart;
+ m_relocEnd = &m_relocStart[estRelocs];
+ m_header = NULL;
+ m_baseRVA = 0;
+ m_filePos = 0;
+ m_filePad = 0;
+ m_flags = flags;
+
+ _ASSERTE(strlen(name)<sizeof(m_name));
+ strncpy_s(m_name, sizeof(m_name), name, strlen(name));
+}
+
+
+/******************************************************************/
+PESection::~PESection() {
+ delete [] m_relocStart;
+}
+
+
+/******************************************************************/
+void PESection::writeSectReloc(unsigned val, CeeSection& relativeTo, CeeSectionRelocType reloc, CeeSectionRelocExtra *extra)
+{
+ addSectReloc(dataLen(), relativeTo, reloc, extra);
+ unsigned* ptr = (unsigned*) getBlock(4);
+ *ptr = val;
+}
+
+/******************************************************************/
+HRESULT PESection::addSectReloc(unsigned offset, CeeSection& relativeToIn,
+ CeeSectionRelocType reloc, CeeSectionRelocExtra *extra)
+{
+ return addSectReloc(offset,
+ (PESection *)&relativeToIn.getImpl(), reloc, extra);
+}
+
+/******************************************************************/
+HRESULT PESection::addSectReloc(unsigned offset, PESection *relativeTo,
+ CeeSectionRelocType reloc, CeeSectionRelocExtra *extra)
+{
+ /* dbPrintf(("******** GOT a section reloc for section %s offset 0x%x to section %x offset 0x%x\n",
+ header->m_name, offset, relativeTo->m_name, *((unsigned*) dataStart + offset))); */
+ _ASSERTE(offset < dataLen());
+
+ if (m_relocCur >= m_relocEnd) {
+ unsigned curLen = (unsigned)(m_relocCur-m_relocStart);
+ unsigned newLen = curLen * 2 + 1;
+ PESectionReloc* relocNew = new (nothrow) PESectionReloc[newLen];
+ if (relocNew == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ memcpy(relocNew, m_relocStart, sizeof(PESectionReloc)*curLen);
+ delete m_relocStart;
+ m_relocStart = relocNew;
+ m_relocCur = &m_relocStart[curLen];
+ m_relocEnd = &m_relocStart[newLen];
+ }
+
+ m_relocCur->type = reloc;
+ m_relocCur->offset = offset;
+ m_relocCur->section = relativeTo;
+ if (extra)
+ m_relocCur->extra = *extra;
+ m_relocCur++;
+ assert(m_relocCur <= m_relocEnd);
+ return S_OK;
+}
+
+/******************************************************************/
+// Compute a pointer (wrap blobfetcher)
+char * PESection::computePointer(unsigned offset) const // virtual
+{
+ return m_blobFetcher.ComputePointer(offset);
+}
+
+/******************************************************************/
+BOOL PESection::containsPointer(__in char *ptr) const // virtual
+{
+ return m_blobFetcher.ContainsPointer(ptr);
+}
+
+/******************************************************************/
+// Compute an offset (wrap blobfetcher)
+unsigned PESection::computeOffset(__in char *ptr) const // virtual
+{
+ return m_blobFetcher.ComputeOffset(ptr);
+}
+
+
+/******************************************************************/
+HRESULT PESection::addBaseReloc(unsigned offset, CeeSectionRelocType reloc,
+ CeeSectionRelocExtra *extra)
+{
+ HRESULT hr = E_FAIL;
+
+ // Use for fixing up pointers pointing outside of the module.
+ //
+ // We only record base relocs for cross module pc-rel pointers
+ //
+
+ switch (reloc)
+ {
+#ifdef _WIN64
+ case srRelocDir64Ptr:
+#endif
+ case srRelocAbsolutePtr:
+ case srRelocHighLowPtr:
+ // For non pc-rel pointers we don't need to record a section reloc
+ hr = S_OK;
+ break;
+
+#if defined (_TARGET_X86_) || defined (_TARGET_AMD64_)
+ case srRelocRelativePtr:
+ case srRelocRelative:
+ hr = addSectReloc(offset, NULL, reloc, extra);
+ break;
+#endif
+
+ default:
+ _ASSERTE(!"unhandled reloc in PESection::addBaseReloc");
+ break;
+ }
+ return hr;
+}
+
+/******************************************************************/
+// Dynamic mem allocation, but we can't move old blocks (since others
+// have pointers to them), so we need a fancy way to grow
+char* PESection::getBlock(unsigned len, unsigned align)
+{
+ return m_blobFetcher.MakeNewBlock(len, align);
+}
+
+unsigned PESection::dataLen()
+{
+ return m_blobFetcher.GetDataLen();
+}
+
+// Apply all the relocs for in memory conversion
+
+// <REVISIT_TODO>@FUTURE: Currently, our VM is rather inefficient in dealing with in-memory RVA.
+// @FUTURE: VM is given an index to memory pool and a helper will return the memory pointer given the index.
+// @FUTURE: We will consider having the coverter resolve RVAs into addresses.</REVISIT_TODO>
+
+HRESULT PESection::applyRelocs(CeeGenTokenMapper *pTokenMapper)
+{
+ // For each section, go through each of its relocs
+ for(PESectionReloc* pCurReloc = m_relocStart; pCurReloc < m_relocCur; pCurReloc++) {
+ if (pCurReloc->type == srRelocMapToken) {
+ unsigned * pos = (unsigned*)
+ m_blobFetcher.ComputePointer(pCurReloc->offset);
+ mdToken newToken;
+ PREFIX_ASSUME(pos != NULL);
+ if (pTokenMapper->HasTokenMoved(*pos, newToken)) {
+ // we have a mapped token
+ *pos = newToken;
+ }
+ }
+
+#if 0
+ _ASSERTE(pCurReloc->offset + 4 <= CurSection.m_blobFetcher.GetDataLen());
+ unsigned * pAddr = (unsigned *)
+ CurSection.m_blobFetcher.ComputePointer(pCurReloc->offset);
+ _ASSERTE(pCurReloc->type == srRelocAbsolute);
+
+ // Current contents contain an offset into pCurReloc->section
+ // computePointer() is like pCurReloc-section + *pAddr, but for non-linear section
+ // This will resolve *pAddr to be a complete address
+ *pAddr = (unsigned) pCurReloc->section->computePointer(*pAddr);
+#endif
+
+ } // End relocs
+ return S_OK;
+}
+
+HRESULT PESection::cloneInstance(PESection *destination) {
+ PESectionReloc *cur;
+ INT32 newSize;
+ HRESULT hr = NOERROR;
+
+ _ASSERTE(destination);
+
+ destination->dirEntry = dirEntry;
+
+ //Merge the information currently in the BlobFetcher into
+ //out current blob fetcher
+ m_blobFetcher.Merge(&(destination->m_blobFetcher));
+
+ //Copy the name.
+ strncpy_s(destination->m_name, sizeof(destination->m_name), m_name, sizeof(m_name) - 1);
+
+ //Clone the relocs
+ //If the arrays aren't the same size, reallocate as necessary.
+ //<REVISIT_TODO>@FUTURE: Make this a ref-counted structure and don't copy it.</REVISIT_TODO>
+
+ newSize = (INT32)(m_relocCur-m_relocStart);
+
+ if (newSize>(destination->m_relocEnd - destination->m_relocStart)) {
+ delete destination->m_relocStart;
+
+ destination->m_relocStart = new (nothrow) PESectionReloc[newSize];
+ if (destination->m_relocStart == NULL)
+ IfFailGo( E_OUTOFMEMORY );
+ destination->m_relocEnd = destination->m_relocStart+(newSize);
+ }
+
+ //copy the correct data over into our new array.
+ memcpy(destination->m_relocStart, m_relocStart, sizeof(PESectionReloc)*(newSize));
+ destination->m_relocCur = destination->m_relocStart + (newSize);
+ for (cur=destination->m_relocStart; cur<destination->m_relocCur; cur++) {
+ cur->section=destination;
+ }
+ErrExit:
+ return hr;
+}
+
+void PESection::SetInitialGrowth(unsigned growth)
+{
+ m_blobFetcher.SetInitialGrowth(growth);
+}
diff --git a/src/md/ceefilegen/stdafx.cpp b/src/md/ceefilegen/stdafx.cpp
new file mode 100644
index 0000000000..91eba13666
--- /dev/null
+++ b/src/md/ceefilegen/stdafx.cpp
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// stdafx.cpp
+//
+
+//
+// Host for precompiled header.
+//
+//*****************************************************************************
+#include "stdafx.h" // Precompiled header key.
diff --git a/src/md/ceefilegen/stdafx.h b/src/md/ceefilegen/stdafx.h
new file mode 100644
index 0000000000..90d8c1a2de
--- /dev/null
+++ b/src/md/ceefilegen/stdafx.h
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// stdafx.h
+//
+
+//
+// Common include file for utility code.
+//*****************************************************************************
+
+#define _CRT_DEPENDENCY_ //this code depends on the crt file functions
+#include <crtwrap.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h> // for qsort
+#include <windows.h>
+#include <time.h>
+
+#include <corerror.h>
+#include <utilcode.h>
+
+#include <corpriv.h>
+
+#include "pesectionman.h"
+
+#include "ceegen.h"
+#include "ceesectionstring.h"
diff --git a/src/md/compiler/.gitmirror b/src/md/compiler/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/compiler/.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/compiler/CMakeLists.txt b/src/md/compiler/CMakeLists.txt
new file mode 100644
index 0000000000..4d99d11edc
--- /dev/null
+++ b/src/md/compiler/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(MDCOMPILER_SOURCES
+ assemblymd.cpp
+ assemblymd_emit.cpp
+ classfactory.cpp
+ custattr_import.cpp
+ custattr_emit.cpp
+ disp.cpp
+ emit.cpp
+ filtermanager.cpp
+ helper.cpp
+ import.cpp
+ importhelper.cpp
+ mdutil.cpp
+ regmeta.cpp
+ regmeta_compilersupport.cpp
+ regmeta_emit.cpp
+ regmeta_import.cpp
+ regmeta_imetadatatables.cpp
+ regmeta_vm.cpp
+ verifylayouts.cpp
+)
+
+convert_to_absolute_path(MDCOMPILER_SOURCES ${MDCOMPILER_SOURCES})
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+add_subdirectory(dac)
+add_subdirectory(wks)
+add_subdirectory(dbi)
+add_subdirectory(crossgen)
diff --git a/src/md/compiler/Compiler.settings.targets b/src/md/compiler/Compiler.settings.targets
new file mode 100644
index 0000000000..97f2504796
--- /dev/null
+++ b/src/md/compiler/Compiler.settings.targets
@@ -0,0 +1,68 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <!--
+ We build MetaData in several flavors:
+ - Full version (wks) - part of clr.dll/coreclr.dll.
+ - dac version - does not need Emit APIs.
+ - Standalone versions for:
+ * mscordbi.dll (hands the interfaces over to debugger client (e.g. VS) - does not need Emit APIs.
+ * CorDbg - does not need Emit APIs.
+ * WinRT
+ - Read-Only version (ships in Windows) - does not need Emit and Internal APIs.
+ - Read-Writer version (ships as private component of MidlRt.exe SDK tool) - does not need Internal APIs.
+ -->
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\MD.props" />
+
+ <PropertyGroup>
+ <MDCompilerSrcDirectory>$(ClrSrcDirectory)\MD\Compiler\</MDCompilerSrcDirectory>
+ <UserIncludes>
+ $(UserIncludes);
+ $(ClrSrcDirectory)\md\inc;
+ $(ClrSrcDirectory)\vm;
+ $(ClrSrcDirectory)\strongname\inc
+ </UserIncludes>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE</ClAdditionalOptions>
+ <!--OK to delete NO_NTDLL for devdiv builds.-->
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <PCHHeader>stdafx.h</PCHHeader>
+ <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders>
+ <!--PCH: Both precompiled header and cpp are on the same $(MDCompilerSrcDirectory)\ path this is likely to be wrong.-->
+ <PCHCompile>$(MDCompilerSrcDirectory)\stdafx.cpp</PCHCompile>
+ <PCHObject>stdafx_compiler.obj</PCHObject>
+ <LinkUseCMT>false</LinkUseCMT>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="$(ClrSrcDirectory)inc\corguids.nativeproj">
+ <Comment>clrinternal.h</Comment>
+ </ProjectReference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <CppCompile Include="$(MDCompilerSrcDirectory)\AssemblyMD.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\AssemblyMD_Emit.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\ClassFactory.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\CustAttr_Import.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\CustAttr_Emit.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\Disp.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\Emit.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\FilterManager.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\Helper.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\Import.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\ImportHelper.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\MDPerf.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\MDUtil.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\MDValidator.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\NewMerger.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_CompilerSupport.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_Emit.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_Import.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_IMetaDataTables.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_VM.cpp" />
+ <CppCompile Include="$(MDCompilerSrcDirectory)\VerifyLayouts.cpp" />
+ </ItemGroup>
+</Project>
diff --git a/src/md/compiler/assemblymd.cpp b/src/md/compiler/assemblymd.cpp
new file mode 100644
index 0000000000..8c0a22af4d
--- /dev/null
+++ b/src/md/compiler/assemblymd.cpp
@@ -0,0 +1,758 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// AssemblyMD.cpp
+//
+
+//
+// Implementation for the assembly meta data import code (code:IMetaDataAssemblyImport).
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+
+#include <strongname.h>
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4102)
+#endif
+
+//*******************************************************************************
+// Get the properties for the given Assembly token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::GetAssemblyProps( // S_OK or error.
+ 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.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ DWORD *pdwAssemblyFlags) // [OUT] Flags.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ AssemblyRec *pRecord;
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "RegMeta::GetAssemblyProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ mda, ppbPublicKey, pcbPublicKey, pulHashAlgId, szName, cchName, pchName, pMetaData,
+ pdwAssemblyFlags));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mda) == mdtAssembly && RidFromToken(mda));
+ IfFailGo(pMiniMd->GetAssemblyRecord(RidFromToken(mda), &pRecord));
+
+ if (ppbPublicKey != NULL)
+ {
+ IfFailGo(pMiniMd->getPublicKeyOfAssembly(pRecord, (const BYTE **)ppbPublicKey, pcbPublicKey));
+ }
+ if (pulHashAlgId)
+ *pulHashAlgId = pMiniMd->getHashAlgIdOfAssembly(pRecord);
+ if (pMetaData)
+ {
+ pMetaData->usMajorVersion = pMiniMd->getMajorVersionOfAssembly(pRecord);
+ pMetaData->usMinorVersion = pMiniMd->getMinorVersionOfAssembly(pRecord);
+ pMetaData->usBuildNumber = pMiniMd->getBuildNumberOfAssembly(pRecord);
+ pMetaData->usRevisionNumber = pMiniMd->getRevisionNumberOfAssembly(pRecord);
+ IfFailGo(pMiniMd->getLocaleOfAssembly(pRecord, pMetaData->szLocale,
+ pMetaData->cbLocale, &pMetaData->cbLocale));
+ pMetaData->ulProcessor = 0;
+ pMetaData->ulOS = 0;
+ }
+ if (pdwAssemblyFlags)
+ {
+ *pdwAssemblyFlags = pMiniMd->getFlagsOfAssembly(pRecord);
+
+#ifdef FEATURE_WINDOWSPHONE
+ // Turn on the afPublicKey if PublicKey blob is not empty
+ DWORD cbPublicKey;
+ const BYTE *pbPublicKey;
+ IfFailGo(pMiniMd->getPublicKeyOfAssembly(pRecord, &pbPublicKey, &cbPublicKey));
+ if (cbPublicKey != 0)
+ *pdwAssemblyFlags |= afPublicKey;
+#else
+ if (ppbPublicKey)
+ {
+ if (pcbPublicKey && *pcbPublicKey)
+ *pdwAssemblyFlags |= afPublicKey;
+ }
+ else
+ {
+#ifdef _DEBUG
+ // Assert that afPublicKey is set if PublicKey blob is not empty
+ DWORD cbPublicKey;
+ const BYTE *pbPublicKey;
+ IfFailGo(pMiniMd->getPublicKeyOfAssembly(pRecord, &pbPublicKey, &cbPublicKey));
+ bool hasPublicKey = cbPublicKey != 0;
+ bool hasPublicKeyFlag = ( *pdwAssemblyFlags & afPublicKey ) != 0;
+ _ASSERTE( hasPublicKey == hasPublicKeyFlag );
+#endif
+ }
+#endif // FEATURE_WINDOWSPHONE
+ }
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szName || pchName)
+ IfFailGo(pMiniMd->getNameOfAssembly(pRecord, szName, cchName, pchName));
+ErrExit:
+
+ STOP_MD_PERF(GetAssemblyProps);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetAssemblyProps
+
+//*******************************************************************************
+// Get the properties for the given AssemblyRef token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::GetAssemblyRefProps( // S_OK or error.
+ 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.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ const void **ppbHashValue, // [OUT] Hash blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ DWORD *pdwAssemblyRefFlags) // [OUT] Flags.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ AssemblyRefRec *pRecord;
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "RegMeta::GetAssemblyRefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ mdar, ppbPublicKeyOrToken, pcbPublicKeyOrToken, szName, cchName,
+ pchName, pMetaData, ppbHashValue, pdwAssemblyRefFlags));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mdar) == mdtAssemblyRef && RidFromToken(mdar));
+ IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(mdar), &pRecord));
+
+ if (ppbPublicKeyOrToken != NULL)
+ {
+ IfFailGo(pMiniMd->getPublicKeyOrTokenOfAssemblyRef(pRecord, (const BYTE **)ppbPublicKeyOrToken, pcbPublicKeyOrToken));
+ }
+ if (pMetaData)
+ {
+ pMetaData->usMajorVersion = pMiniMd->getMajorVersionOfAssemblyRef(pRecord);
+ pMetaData->usMinorVersion = pMiniMd->getMinorVersionOfAssemblyRef(pRecord);
+ pMetaData->usBuildNumber = pMiniMd->getBuildNumberOfAssemblyRef(pRecord);
+ pMetaData->usRevisionNumber = pMiniMd->getRevisionNumberOfAssemblyRef(pRecord);
+ IfFailGo(pMiniMd->getLocaleOfAssemblyRef(pRecord, pMetaData->szLocale,
+ pMetaData->cbLocale, &pMetaData->cbLocale));
+ pMetaData->ulProcessor = 0;
+ pMetaData->ulOS = 0;
+ }
+ if (ppbHashValue != NULL)
+ {
+ IfFailGo(pMiniMd->getHashValueOfAssemblyRef(pRecord, (const BYTE **)ppbHashValue, pcbHashValue));
+ }
+ if (pdwAssemblyRefFlags)
+ *pdwAssemblyRefFlags = pMiniMd->getFlagsOfAssemblyRef(pRecord);
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szName || pchName)
+ IfFailGo(pMiniMd->getNameOfAssemblyRef(pRecord, szName, cchName, pchName));
+ErrExit:
+
+ STOP_MD_PERF(GetAssemblyRefProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetAssemblyRefProps
+
+//*******************************************************************************
+// Get the properties for the given File token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::GetFileProps( // S_OK or error.
+ mdFile mdf, // [IN] The File for which to get the properties.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in 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.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ FileRec *pRecord;
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "RegMeta::GetFileProps(%#08x, %#08x, %#08x, %#08x, %#08x, %#08x, %#08x)\n",
+ mdf, szName, cchName, pchName, ppbHashValue, pcbHashValue, pdwFileFlags));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mdf) == mdtFile && RidFromToken(mdf));
+ IfFailGo(pMiniMd->GetFileRecord(RidFromToken(mdf), &pRecord));
+
+ if (ppbHashValue != NULL)
+ {
+ IfFailGo(pMiniMd->getHashValueOfFile(pRecord, (const BYTE **)ppbHashValue, pcbHashValue));
+ }
+ if (pdwFileFlags != NULL)
+ *pdwFileFlags = pMiniMd->getFlagsOfFile(pRecord);
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if ((szName != NULL) || (pchName != NULL))
+ {
+ IfFailGo(pMiniMd->getNameOfFile(pRecord, szName, cchName, pchName));
+ }
+
+ErrExit:
+ STOP_MD_PERF(GetFileProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetFileProps
+
+//*******************************************************************************
+// Get the properties for the given ExportedType token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::GetExportedTypeProps( // S_OK or error.
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags) // [OUT] Flags.
+{
+ HRESULT hr = S_OK; // A result.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ExportedTypeRec *pRecord; // The exported type.
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ int bTruncation=0; // Was there name truncation?
+
+ LOG((LOGMD, "RegMeta::GetExportedTypeProps(%#08x, %#08x, %#08x, %#08x, %#08x, %#08x, %#08x)\n",
+ mdct, szName, cchName, pchName,
+ ptkImplementation, ptkTypeDef, pdwExportedTypeFlags));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mdct) == mdtExportedType && RidFromToken(mdct));
+ IfFailGo(pMiniMd->GetExportedTypeRecord(RidFromToken(mdct), &pRecord));
+
+ if (szName || pchName)
+ {
+ LPCSTR szTypeNamespace;
+ LPCSTR szTypeName;
+
+ IfFailGo(pMiniMd->getTypeNamespaceOfExportedType(pRecord, &szTypeNamespace));
+ PREFIX_ASSUME(szTypeNamespace != NULL);
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzTypeNamespace, szTypeNamespace);
+ IfNullGo(wzTypeNamespace);
+
+ IfFailGo(pMiniMd->getTypeNameOfExportedType(pRecord, &szTypeName));
+ _ASSERTE(*szTypeName);
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzTypeName, szTypeName);
+ IfNullGo(wzTypeName);
+
+ if (szName)
+ bTruncation = ! (ns::MakePath(szName, cchName, wzTypeNamespace, wzTypeName));
+ if (pchName)
+ {
+ if (bTruncation || !szName)
+ *pchName = ns::GetFullLength(wzTypeNamespace, wzTypeName);
+ else
+ *pchName = (ULONG)(wcslen(szName) + 1);
+ }
+ }
+ if (ptkImplementation)
+ *ptkImplementation = pMiniMd->getImplementationOfExportedType(pRecord);
+ if (ptkTypeDef)
+ *ptkTypeDef = pMiniMd->getTypeDefIdOfExportedType(pRecord);
+ if (pdwExportedTypeFlags)
+ *pdwExportedTypeFlags = pMiniMd->getFlagsOfExportedType(pRecord);
+
+ if (bTruncation && hr == S_OK)
+ {
+ if ((szName != NULL) && (cchName > 0))
+ { // null-terminate the truncated output string
+ szName[cchName - 1] = W('\0');
+ }
+ hr = CLDB_S_TRUNCATION;
+ }
+
+ErrExit:
+ STOP_MD_PERF(GetExportedTypeProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetExportedTypeProps
+
+//*******************************************************************************
+// Get the properties for the given Resource token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::GetManifestResourceProps( // S_OK or error.
+ mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties.
+ __out_ecount_part_opt(cchName, *pchName)LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in 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.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ManifestResourceRec *pRecord;
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "RegMeta::GetManifestResourceProps("
+ "%#08x, %#08x, %#08x, %#08x, %#08x, %#08x, %#08x)\n",
+ mdmr, szName, cchName, pchName,
+ ptkImplementation, pdwOffset,
+ pdwResourceFlags));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mdmr) == mdtManifestResource && RidFromToken(mdmr));
+ IfFailGo(pMiniMd->GetManifestResourceRecord(RidFromToken(mdmr), &pRecord));
+
+ if (ptkImplementation)
+ *ptkImplementation = pMiniMd->getImplementationOfManifestResource(pRecord);
+ if (pdwOffset)
+ *pdwOffset = pMiniMd->getOffsetOfManifestResource(pRecord);
+ if (pdwResourceFlags)
+ *pdwResourceFlags = pMiniMd->getFlagsOfManifestResource(pRecord);
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szName || pchName)
+ IfFailGo(pMiniMd->getNameOfManifestResource(pRecord, szName, cchName, pchName));
+ErrExit:
+
+ STOP_MD_PERF(GetManifestResourceProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetManifestResourceProps
+
+
+//*******************************************************************************
+// Enumerating through all of the AssemblyRefs.
+//*******************************************************************************
+STDMETHODIMP RegMeta::EnumAssemblyRefs( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdAssemblyRef rAssemblyRefs[], // [OUT] Put AssemblyRefs here.
+ ULONG cMax, // [IN] Max AssemblyRefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumAssemblyRefs(%#08x, %#08x, %#08x, %#08x)\n",
+ phEnum, rAssemblyRefs, cMax, pcTokens));
+ START_MD_PERF();
+
+ LOCKREAD();
+
+ if (*ppmdEnum == 0)
+ {
+ // instantiate a new ENUM.
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // create the enumerator.
+ IfFailGo(HENUMInternal::CreateSimpleEnum(
+ mdtAssemblyRef,
+ 1,
+ pMiniMd->getCountAssemblyRefs() + 1,
+ &pEnum) );
+
+ // set the output parameter.
+ *ppmdEnum = pEnum;
+ }
+ else
+ pEnum = *ppmdEnum;
+
+ // we can only fill the minimum of what the caller asked for or what we have left.
+ IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rAssemblyRefs, pcTokens));
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumAssemblyRefs);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumAssemblyRefs
+
+//*******************************************************************************
+// Enumerating through all of the Files.
+//*******************************************************************************
+STDMETHODIMP RegMeta::EnumFiles( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdFile rFiles[], // [OUT] Put Files here.
+ ULONG cMax, // [IN] Max Files to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumFiles(%#08x, %#08x, %#08x, %#08x)\n",
+ phEnum, rFiles, cMax, pcTokens));
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (*ppmdEnum == 0)
+ {
+ // instantiate a new ENUM.
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // create the enumerator.
+ IfFailGo(HENUMInternal::CreateSimpleEnum(
+ mdtFile,
+ 1,
+ pMiniMd->getCountFiles() + 1,
+ &pEnum) );
+
+ // set the output parameter.
+ *ppmdEnum = pEnum;
+ }
+ else
+ pEnum = *ppmdEnum;
+
+ // we can only fill the minimum of what the caller asked for or what we have left.
+ IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rFiles, pcTokens));
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumFiles);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumFiles
+
+//*******************************************************************************
+// Enumerating through all of the ExportedTypes.
+//*******************************************************************************
+STDMETHODIMP RegMeta::EnumExportedTypes( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdExportedType rExportedTypes[], // [OUT] Put ExportedTypes here.
+ ULONG cMax, // [IN] Max ExportedTypes to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumExportedTypes(%#08x, %#08x, %#08x, %#08x)\n",
+ phEnum, rExportedTypes, cMax, pcTokens));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (*ppmdEnum == 0)
+ {
+ // instantiate a new ENUM.
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ if (pMiniMd->HasDelete() &&
+ ((m_OptionValue.m_ImportOption & MDImportOptionAllExportedTypes) == 0))
+ {
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtExportedType, &pEnum) );
+
+ // add all Types to the dynamic array if name is not _Delete
+ for (ULONG index = 1; index <= pMiniMd->getCountExportedTypes(); index ++ )
+ {
+ ExportedTypeRec *pRec;
+ IfFailGo(pMiniMd->GetExportedTypeRecord(index, &pRec));
+ LPCSTR szTypeName;
+ IfFailGo(pMiniMd->getTypeNameOfExportedType(pRec, &szTypeName));
+ if (IsDeletedName(szTypeName))
+ {
+ continue;
+ }
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtExportedType) ) );
+ }
+ }
+ else
+ {
+ // create the enumerator.
+ IfFailGo(HENUMInternal::CreateSimpleEnum(
+ mdtExportedType,
+ 1,
+ pMiniMd->getCountExportedTypes() + 1,
+ &pEnum) );
+ }
+
+ // set the output parameter.
+ *ppmdEnum = pEnum;
+ }
+ else
+ pEnum = *ppmdEnum;
+
+ // we can only fill the minimum of what the caller asked for or what we have left.
+ IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rExportedTypes, pcTokens));
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumExportedTypes);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumExportedTypes
+
+//*******************************************************************************
+// Enumerating through all of the Resources.
+//*******************************************************************************
+STDMETHODIMP RegMeta::EnumManifestResources( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdManifestResource rManifestResources[], // [OUT] Put ManifestResources here.
+ ULONG cMax, // [IN] Max Resources to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumManifestResources(%#08x, %#08x, %#08x, %#08x)\n",
+ phEnum, rManifestResources, cMax, pcTokens));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (*ppmdEnum == 0)
+ {
+ // instantiate a new ENUM.
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // create the enumerator.
+ IfFailGo(HENUMInternal::CreateSimpleEnum(
+ mdtManifestResource,
+ 1,
+ pMiniMd->getCountManifestResources() + 1,
+ &pEnum) );
+
+ // set the output parameter.
+ *ppmdEnum = pEnum;
+ }
+ else
+ pEnum = *ppmdEnum;
+
+ // we can only fill the minimum of what the caller asked for or what we have left.
+ IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rManifestResources, pcTokens));
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumManifestResources);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumManifestResources
+
+//*******************************************************************************
+// Get the Assembly token for the given scope..
+//*******************************************************************************
+STDMETHODIMP RegMeta::GetAssemblyFromScope( // S_OK or error
+ mdAssembly *ptkAssembly) // [OUT] Put token here.
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMd = NULL;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::GetAssemblyFromScope(%#08x)\n", ptkAssembly));
+ START_MD_PERF();
+ LOCKREAD();
+ _ASSERTE(ptkAssembly);
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+ if (pMiniMd->getCountAssemblys())
+ {
+ *ptkAssembly = TokenFromRid(1, mdtAssembly);
+ }
+ else
+ {
+ IfFailGo( CLDB_E_RECORD_NOTFOUND );
+ }
+ErrExit:
+ STOP_MD_PERF(GetAssemblyFromScope);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetAssemblyFromScope
+
+//*******************************************************************************
+// Find the ExportedType given the name.
+//*******************************************************************************
+STDMETHODIMP RegMeta::FindExportedTypeByName( // S_OK or error
+ LPCWSTR szName, // [IN] Name of the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] Enclosing ExportedType.
+ mdExportedType *ptkExportedType) // [OUT] Put the ExportedType token here.
+{
+ HRESULT hr = S_OK; // A result.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = NULL;
+ LPSTR szNameUTF8 = NULL;
+
+ LOG((LOGMD, "MD RegMeta::FindExportedTypeByName(%S, %#08x, %#08x)\n",
+ MDSTR(szName), tkEnclosingType, ptkExportedType));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+
+ // Validate name for prefix.
+ if (!szName)
+ IfFailGo(E_INVALIDARG);
+
+ _ASSERTE(szName && ptkExportedType);
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+ UTF8STR(szName, szNameUTF8);
+ LPCSTR szTypeName;
+ LPCSTR szTypeNamespace;
+
+ ns::SplitInline(szNameUTF8, szTypeNamespace, szTypeName);
+
+ IfFailGo(ImportHelper::FindExportedType(pMiniMd,
+ szTypeNamespace,
+ szTypeName,
+ tkEnclosingType,
+ ptkExportedType));
+ErrExit:
+ STOP_MD_PERF(FindExportedTypeByName);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::FindExportedTypeByName
+
+//*******************************************************************************
+// Find the ManifestResource given the name.
+//*******************************************************************************
+STDMETHODIMP RegMeta::FindManifestResourceByName( // S_OK or error
+ LPCWSTR szName, // [IN] Name of the ManifestResource.
+ mdManifestResource *ptkManifestResource) // [OUT] Put the ManifestResource token here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LPCUTF8 szNameTmp = NULL;
+ CMiniMdRW *pMiniMd = NULL;
+
+ LOG((LOGMD, "MD RegMeta::FindManifestResourceByName(%S, %#08x)\n",
+ MDSTR(szName), ptkManifestResource));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+
+ // Validate name for prefix.
+ if (!szName)
+ IfFailGo(E_INVALIDARG);
+
+ _ASSERTE(szName && ptkManifestResource);
+
+ ManifestResourceRec *pRecord;
+ ULONG cRecords; // Count of records.
+ LPUTF8 szUTF8Name; // UTF8 version of the name passed in.
+ ULONG i;
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+ *ptkManifestResource = mdManifestResourceNil;
+ cRecords = pMiniMd->getCountManifestResources();
+ UTF8STR(szName, szUTF8Name);
+
+ // Search for the TypeRef.
+ for (i = 1; i <= cRecords; i++)
+ {
+ IfFailGo(pMiniMd->GetManifestResourceRecord(i, &pRecord));
+ IfFailGo(pMiniMd->getNameOfManifestResource(pRecord, &szNameTmp));
+ if (! strcmp(szUTF8Name, szNameTmp))
+ {
+ *ptkManifestResource = TokenFromRid(i, mdtManifestResource);
+ goto ErrExit;
+ }
+ }
+ IfFailGo( CLDB_E_RECORD_NOTFOUND );
+ErrExit:
+
+ STOP_MD_PERF(FindManifestResourceByName);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::FindManifestResourceByName
+
+extern HRESULT STDMETHODCALLTYPE
+ GetAssembliesByName(LPCWSTR szAppBase,
+ LPCWSTR szPrivateBin,
+ LPCWSTR szAssemblyName,
+ IUnknown *ppIUnk[],
+ ULONG cMax,
+ ULONG *pcAssemblies);
+
+//*******************************************************************************
+// Used to find assemblies either in Fusion cache or on disk at build time.
+//*******************************************************************************
+STDMETHODIMP RegMeta::FindAssembliesByName( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
+ IUnknown *ppIUnk[], // [OUT] put IMetaDataAssemblyImport pointers here
+ ULONG cMax, // [IN] The max number to put
+ ULONG *pcAssemblies) // [OUT] The number of assemblies returned.
+{
+#ifdef FEATURE_METADATA_IN_VM
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::FindAssembliesByName(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ szAppBase, szPrivateBin, szAssemblyName, ppIUnk, cMax, pcAssemblies));
+ START_MD_PERF();
+
+ // No need to lock this function. It is going through fusion to find the matching Assemblies by name
+
+ IfFailGo(GetAssembliesByName(szAppBase, szPrivateBin,
+ szAssemblyName, ppIUnk, cMax, pcAssemblies));
+
+ErrExit:
+ STOP_MD_PERF(FindAssembliesByName);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_IN_VM
+ // Calls to fusion are not suported outside VM
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_IN_VM
+} // RegMeta::FindAssembliesByName
diff --git a/src/md/compiler/assemblymd_emit.cpp b/src/md/compiler/assemblymd_emit.cpp
new file mode 100644
index 0000000000..72fb034221
--- /dev/null
+++ b/src/md/compiler/assemblymd_emit.cpp
@@ -0,0 +1,811 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// AssemblyMD.cpp
+//
+
+//
+// Implementation for the assembly meta data emit code (code:IMetaDataAssemblyEmit).
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+
+#include <strongname.h>
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4102)
+#endif
+
+#ifdef FEATURE_METADATA_EMIT
+
+//*******************************************************************************
+// Define an Assembly and set the attributes.
+//*******************************************************************************
+STDMETHODIMP RegMeta::DefineAssembly( // S_OK or error.
+ const void *pbPublicKey, // [IN] Public key of the assembly.
+ ULONG cbPublicKey, // [IN] Count of bytes in the public key.
+ ULONG ulHashAlgId, // [IN] Hash Algorithm.
+ LPCWSTR szName, // [IN] Name of the assembly.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ DWORD dwAssemblyFlags, // [IN] Flags.
+ mdAssembly *pma) // [OUT] Returned Assembly token.
+{
+ HRESULT hr = S_OK;
+
+ AssemblyRec *pRecord = NULL; // The assembly record.
+ ULONG iRecord; // RID of the assembly record.
+
+ if (szName == NULL || pMetaData == NULL || pma == NULL)
+ return E_INVALIDARG;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::DefineAssembly(0x%08x, 0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ pbPublicKey, cbPublicKey, ulHashAlgId, MDSTR(szName), pMetaData,
+ dwAssemblyFlags, pma));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ _ASSERTE(szName && pMetaData && pma);
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ // Assembly defs always contain a full public key (assuming they're strong
+ // named) rather than the tokenized version. Force the flag on to indicate
+ // this, and this way blindly copying public key & flags from a def to a ref
+ // will work (though the ref will be bulkier than strictly necessary).
+ if (cbPublicKey != 0)
+ dwAssemblyFlags |= afPublicKey;
+
+ if (CheckDups(MDDupAssembly))
+ { // Should be no more than one -- just check count of records.
+ if (m_pStgdb->m_MiniMd.getCountAssemblys() > 0)
+ { // S/b only one, so we know the rid.
+ iRecord = 1;
+ // If ENC, let them update the existing record.
+ if (IsENCOn())
+ IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRecord(iRecord, &pRecord));
+ else
+ { // Not ENC, so it is a duplicate.
+ *pma = TokenFromRid(iRecord, mdtAssembly);
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ }
+ else
+ { // Not ENC, not duplicate checking, so shouldn't already have one.
+ _ASSERTE(m_pStgdb->m_MiniMd.getCountAssemblys() == 0);
+ }
+
+ // Create a new record, if needed.
+ if (pRecord == NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddAssemblyRecord(&pRecord, &iRecord));
+ }
+
+ // Set the output parameter.
+ *pma = TokenFromRid(iRecord, mdtAssembly);
+
+ IfFailGo(_SetAssemblyProps(*pma, pbPublicKey, cbPublicKey, ulHashAlgId, szName, pMetaData, dwAssemblyFlags));
+
+ErrExit:
+
+ STOP_MD_PERF(DefineAssembly);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::DefineAssembly
+
+//*******************************************************************************
+// Define an AssemblyRef and set the attributes.
+//*******************************************************************************
+STDMETHODIMP RegMeta::DefineAssemblyRef( // S_OK or error.
+ const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly.
+ ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token.
+ LPCWSTR szName, // [IN] Name of the assembly being referenced.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwAssemblyRefFlags, // [IN] Flags.
+ mdAssemblyRef *pmar) // [OUT] Returned AssemblyRef token.
+{
+ HRESULT hr = S_OK;
+
+ AssemblyRefRec *pRecord = NULL;
+ ULONG iRecord;
+
+ if (szName == NULL || pmar == NULL || pMetaData == NULL)
+ return E_INVALIDARG;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::DefineAssemblyRef(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ pbPublicKeyOrToken, cbPublicKeyOrToken, MDSTR(szName), pMetaData, pbHashValue,
+ cbHashValue, dwAssemblyRefFlags, pmar));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(szName && pmar);
+
+ if (CheckDups(MDDupAssemblyRef))
+ {
+ LPUTF8 szUTF8Name, szUTF8Locale;
+ UTF8STR(szName, szUTF8Name);
+ UTF8STR(pMetaData->szLocale, szUTF8Locale);
+ hr = ImportHelper::FindAssemblyRef(&m_pStgdb->m_MiniMd,
+ szUTF8Name,
+ szUTF8Locale,
+ pbPublicKeyOrToken,
+ cbPublicKeyOrToken,
+ pMetaData->usMajorVersion,
+ pMetaData->usMinorVersion,
+ pMetaData->usBuildNumber,
+ pMetaData->usRevisionNumber,
+ dwAssemblyRefFlags,
+ pmar);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRefRecord(RidFromToken(*pmar), &pRecord));
+ }
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ }
+
+ // Create a new record if needed.
+ if (pRecord == NULL)
+ {
+ // Create a new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddAssemblyRefRecord(&pRecord, &iRecord));
+
+ // Set the output parameter.
+ *pmar = TokenFromRid(iRecord, mdtAssemblyRef);
+ }
+
+ // Set rest of the attributes.
+ SetCallerDefine();
+ IfFailGo(_SetAssemblyRefProps(*pmar, pbPublicKeyOrToken, cbPublicKeyOrToken, szName, pMetaData,
+ pbHashValue, cbHashValue,
+ dwAssemblyRefFlags));
+ErrExit:
+ SetCallerExternal();
+
+ STOP_MD_PERF(DefineAssemblyRef);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::DefineAssemblyRef
+
+//*******************************************************************************
+// Define a File and set the attributes.
+//*******************************************************************************
+STDMETHODIMP RegMeta::DefineFile( // S_OK or error.
+ LPCWSTR szName, // [IN] Name of the file.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwFileFlags, // [IN] Flags.
+ mdFile *pmf) // [OUT] Returned File token.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ FileRec *pRecord = NULL;
+ ULONG iRecord;
+
+ LOG((LOGMD, "RegMeta::DefineFile(%S, %#08x, %#08x, %#08x, %#08x)\n",
+ MDSTR(szName), pbHashValue, cbHashValue, dwFileFlags, pmf));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(szName && pmf);
+
+ if (CheckDups(MDDupFile))
+ {
+ LPUTF8 szUTF8Name;
+ UTF8STR(szName, szUTF8Name);
+ hr = ImportHelper::FindFile(&m_pStgdb->m_MiniMd, szUTF8Name, pmf);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetFileRecord(RidFromToken(*pmf), &pRecord));
+ }
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ }
+
+ // Create a new record if needed.
+ if (pRecord == NULL)
+ {
+ // Create a new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddFileRecord(&pRecord, &iRecord));
+
+ // Set the output parameter.
+ *pmf = TokenFromRid(iRecord, mdtFile);
+
+ // Set the name.
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_File, FileRec::COL_Name, pRecord, szName));
+ }
+
+ // Set rest of the attributes.
+ IfFailGo(_SetFileProps(*pmf, pbHashValue, cbHashValue, dwFileFlags));
+ErrExit:
+
+ STOP_MD_PERF(DefineFile);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::DefineFile
+
+//*******************************************************************************
+// Define a ExportedType and set the attributes.
+//*******************************************************************************
+STDMETHODIMP RegMeta::DefineExportedType( // S_OK or error.
+ LPCWSTR szName, // [IN] Name of the Com Type.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef tkTypeDef, // [IN] TypeDef token within the file.
+ DWORD dwExportedTypeFlags, // [IN] Flags.
+ mdExportedType *pmct) // [OUT] Returned ExportedType token.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ExportedTypeRec *pRecord = NULL;
+ ULONG iRecord;
+ LPSTR szNameUTF8;
+ LPCSTR szTypeNameUTF8;
+ LPCSTR szTypeNamespaceUTF8;
+
+ LOG((LOGMD, "RegMeta::DefineExportedType(%S, %#08x, %08x, %#08x, %#08x)\n",
+ MDSTR(szName), tkImplementation, tkTypeDef,
+ dwExportedTypeFlags, pmct));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ // Validate name for prefix.
+ if (szName == NULL)
+ IfFailGo(E_INVALIDARG);
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ //SLASHES2DOTS_NAMESPACE_BUFFER_UNICODE(szName, szName);
+
+ UTF8STR(szName, szNameUTF8);
+ // Split the name into name/namespace pair.
+ ns::SplitInline(szNameUTF8, szTypeNamespaceUTF8, szTypeNameUTF8);
+
+ _ASSERTE(szName && dwExportedTypeFlags != ULONG_MAX && pmct);
+ _ASSERTE(TypeFromToken(tkImplementation) == mdtFile ||
+ TypeFromToken(tkImplementation) == mdtAssemblyRef ||
+ TypeFromToken(tkImplementation) == mdtExportedType ||
+ tkImplementation == mdTokenNil);
+
+ if (CheckDups(MDDupExportedType))
+ {
+ hr = ImportHelper::FindExportedType(&m_pStgdb->m_MiniMd,
+ szTypeNamespaceUTF8,
+ szTypeNameUTF8,
+ tkImplementation,
+ pmct);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(RidFromToken(*pmct), &pRecord));
+ }
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ }
+
+ // Create a new record if needed.
+ if (pRecord == NULL)
+ {
+ // Create a new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddExportedTypeRecord(&pRecord, &iRecord));
+
+ // Set the output parameter.
+ *pmct = TokenFromRid(iRecord, mdtExportedType);
+
+ // Set the TypeName and TypeNamespace.
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_ExportedType,
+ ExportedTypeRec::COL_TypeName, pRecord, szTypeNameUTF8));
+ if (szTypeNamespaceUTF8)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_ExportedType,
+ ExportedTypeRec::COL_TypeNamespace, pRecord, szTypeNamespaceUTF8));
+ }
+ }
+
+ // Set rest of the attributes.
+ IfFailGo(_SetExportedTypeProps(*pmct, tkImplementation, tkTypeDef,
+ dwExportedTypeFlags));
+ErrExit:
+
+ STOP_MD_PERF(DefineExportedType);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::DefineExportedType
+
+//*******************************************************************************
+// Define a Resource and set the attributes.
+//*******************************************************************************
+STDMETHODIMP RegMeta::DefineManifestResource( // S_OK or error.
+ LPCWSTR szName, // [IN] Name of the ManifestResource.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource.
+ DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file.
+ DWORD dwResourceFlags, // [IN] Flags.
+ mdManifestResource *pmmr) // [OUT] Returned ManifestResource token.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ManifestResourceRec *pRecord = NULL;
+ ULONG iRecord;
+
+ LOG((LOGMD, "RegMeta::DefineManifestResource(%S, %#08x, %#08x, %#08x, %#08x)\n",
+ MDSTR(szName), tkImplementation, dwOffset, dwResourceFlags, pmmr));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(szName && dwResourceFlags != ULONG_MAX && pmmr);
+ _ASSERTE(TypeFromToken(tkImplementation) == mdtFile ||
+ TypeFromToken(tkImplementation) == mdtAssemblyRef ||
+ tkImplementation == mdTokenNil);
+
+ if (CheckDups(MDDupManifestResource))
+ {
+ LPUTF8 szUTF8Name;
+ UTF8STR(szName, szUTF8Name);
+ hr = ImportHelper::FindManifestResource(&m_pStgdb->m_MiniMd, szUTF8Name, pmmr);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(RidFromToken(*pmmr), &pRecord));
+ }
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ }
+
+ // Create a new record if needed.
+ if (pRecord == NULL)
+ {
+ // Create a new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddManifestResourceRecord(&pRecord, &iRecord));
+
+ // Set the output parameter.
+ *pmmr = TokenFromRid(iRecord, mdtManifestResource);
+
+ // Set the name.
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_ManifestResource,
+ ManifestResourceRec::COL_Name, pRecord, szName));
+ }
+
+ // Set the rest of the attributes.
+ IfFailGo(_SetManifestResourceProps(*pmmr, tkImplementation,
+ dwOffset, dwResourceFlags));
+
+ErrExit:
+
+ STOP_MD_PERF(DefineManifestResource);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::DefineManifestResource
+
+//*******************************************************************************
+// Set the specified attributes on the given Assembly token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::SetAssemblyProps( // S_OK or error.
+ mdAssembly ma, // [IN] Assembly token.
+ const void *pbPublicKey, // [IN] Public key of the assembly.
+ ULONG cbPublicKey, // [IN] Count of bytes in the public key.
+ ULONG ulHashAlgId, // [IN] Hash Algorithm.
+ LPCWSTR szName, // [IN] Name of the assembly.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ DWORD dwAssemblyFlags) // [IN] Flags.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(TypeFromToken(ma) == mdtAssembly && RidFromToken(ma));
+
+ LOG((LOGMD, "RegMeta::SetAssemblyProps(%#08x, %#08x, %#08x, %#08x %S, %#08x, %#08x)\n",
+ ma, pbPublicKey, cbPublicKey, ulHashAlgId, MDSTR(szName), pMetaData, dwAssemblyFlags));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ IfFailGo(_SetAssemblyProps(ma, pbPublicKey, cbPublicKey, ulHashAlgId, szName, pMetaData, dwAssemblyFlags));
+
+ErrExit:
+ STOP_MD_PERF(SetAssemblyProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP SetAssemblyProps()
+
+//*******************************************************************************
+// Set the specified attributes on the given AssemblyRef token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::SetAssemblyRefProps( // S_OK or error.
+ mdAssemblyRef ar, // [IN] AssemblyRefToken.
+ const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly.
+ ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token.
+ LPCWSTR szName, // [IN] Name of the assembly being referenced.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwAssemblyRefFlags) // [IN] Flags.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(TypeFromToken(ar) == mdtAssemblyRef && RidFromToken(ar));
+
+ LOG((LOGMD, "RegMeta::SetAssemblyRefProps(0x%08x, 0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ ar, pbPublicKeyOrToken, cbPublicKeyOrToken, MDSTR(szName), pMetaData, pbHashValue, cbHashValue,
+ dwAssemblyRefFlags));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ IfFailGo(_SetAssemblyRefProps(
+ ar,
+ pbPublicKeyOrToken,
+ cbPublicKeyOrToken,
+ szName,
+ pMetaData,
+ pbHashValue,
+ cbHashValue,
+ dwAssemblyRefFlags));
+
+ErrExit:
+ STOP_MD_PERF(SetAssemblyRefProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::SetAssemblyRefProps
+
+//*******************************************************************************
+// Set the specified attributes on the given File token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::SetFileProps( // S_OK or error.
+ mdFile file, // [IN] File token.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwFileFlags) // [IN] Flags.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ _ASSERTE(TypeFromToken(file) == mdtFile && RidFromToken(file));
+
+ LOG((LOGMD, "RegMeta::SetFileProps(%#08x, %#08x, %#08x, %#08x)\n",
+ file, pbHashValue, cbHashValue, dwFileFlags));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ IfFailGo( _SetFileProps(file, pbHashValue, cbHashValue, dwFileFlags) );
+
+ErrExit:
+
+ STOP_MD_PERF(SetFileProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::SetFileProps
+
+//*******************************************************************************
+// Set the specified attributes on the given ExportedType token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::SetExportedTypeProps( // S_OK or error.
+ mdExportedType ct, // [IN] ExportedType token.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef tkTypeDef, // [IN] TypeDef token within the file.
+ DWORD dwExportedTypeFlags) // [IN] Flags.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ LOG((LOGMD, "RegMeta::SetExportedTypeProps(%#08x, %#08x, %#08x, %#08x)\n",
+ ct, tkImplementation, tkTypeDef, dwExportedTypeFlags));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo( _SetExportedTypeProps( ct, tkImplementation, tkTypeDef, dwExportedTypeFlags) );
+
+ErrExit:
+
+ STOP_MD_PERF(SetExportedTypeProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::SetExportedTypeProps
+
+//*******************************************************************************
+// Set the specified attributes on the given ManifestResource token.
+//*******************************************************************************
+STDMETHODIMP RegMeta::SetManifestResourceProps(// S_OK or error.
+ mdManifestResource mr, // [IN] ManifestResource token.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource.
+ DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file.
+ DWORD dwResourceFlags) // [IN] Flags.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::SetManifestResourceProps(%#08x, %#08x, %#08x, %#08x)\n",
+ mr, tkImplementation, dwOffset,
+ dwResourceFlags));
+
+ _ASSERTE(TypeFromToken(tkImplementation) == mdtFile ||
+ TypeFromToken(tkImplementation) == mdtAssemblyRef ||
+ tkImplementation == mdTokenNil);
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo( _SetManifestResourceProps( mr, tkImplementation, dwOffset, dwResourceFlags) );
+
+ErrExit:
+
+ STOP_MD_PERF(SetManifestResourceProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::SetManifestResourceProps()
+
+//*******************************************************************************
+// Helper: Set the specified attributes on the given Assembly token.
+//*******************************************************************************
+HRESULT RegMeta::_SetAssemblyProps( // S_OK or error.
+ mdAssembly ma, // [IN] Assembly token.
+ const void *pbPublicKey, // [IN] Originator of the assembly.
+ ULONG cbPublicKey, // [IN] Count of bytes in the Originator blob.
+ ULONG ulHashAlgId, // [IN] Hash Algorithm.
+ LPCWSTR szName, // [IN] Name of the assembly.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ DWORD dwAssemblyFlags) // [IN] Flags.
+{
+ AssemblyRec *pRecord = NULL; // The assembly record.
+ HRESULT hr = S_OK;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRecord(RidFromToken(ma), &pRecord));
+
+ // Set the data.
+ if (pbPublicKey)
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Assembly, AssemblyRec::COL_PublicKey,
+ pRecord, pbPublicKey, cbPublicKey));
+ if (ulHashAlgId != ULONG_MAX)
+ pRecord->SetHashAlgId(ulHashAlgId);
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_Assembly, AssemblyRec::COL_Name, pRecord, szName));
+ if (pMetaData->usMajorVersion != USHRT_MAX)
+ pRecord->SetMajorVersion(pMetaData->usMajorVersion);
+ if (pMetaData->usMinorVersion != USHRT_MAX)
+ pRecord->SetMinorVersion(pMetaData->usMinorVersion);
+ if (pMetaData->usBuildNumber != USHRT_MAX)
+ pRecord->SetBuildNumber(pMetaData->usBuildNumber);
+ if (pMetaData->usRevisionNumber != USHRT_MAX)
+ pRecord->SetRevisionNumber(pMetaData->usRevisionNumber);
+ if (pMetaData->szLocale)
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_Assembly, AssemblyRec::COL_Locale,
+ pRecord, pMetaData->szLocale));
+
+ dwAssemblyFlags = (dwAssemblyFlags & ~afPublicKey) | (cbPublicKey ? afPublicKey : 0);
+ pRecord->SetFlags(dwAssemblyFlags);
+ IfFailGo(UpdateENCLog(ma));
+
+ErrExit:
+
+
+ return hr;
+} // HRESULT RegMeta::_SetAssemblyProps()
+
+//*******************************************************************************
+// Helper: Set the specified attributes on the given AssemblyRef token.
+//*******************************************************************************
+HRESULT RegMeta::_SetAssemblyRefProps( // S_OK or error.
+ mdAssemblyRef ar, // [IN] AssemblyRefToken.
+ const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly.
+ ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token.
+ LPCWSTR szName, // [IN] Name of the assembly being referenced.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwAssemblyRefFlags) // [IN] Flags.
+{
+ AssemblyRefRec *pRecord;
+ HRESULT hr = S_OK;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRefRecord(RidFromToken(ar), &pRecord));
+
+ if (pbPublicKeyOrToken)
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken,
+ pRecord, pbPublicKeyOrToken, cbPublicKeyOrToken));
+ if (szName)
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_AssemblyRef, AssemblyRefRec::COL_Name,
+ pRecord, szName));
+ if (pMetaData)
+ {
+ if (pMetaData->usMajorVersion != USHRT_MAX)
+ pRecord->SetMajorVersion(pMetaData->usMajorVersion);
+ if (pMetaData->usMinorVersion != USHRT_MAX)
+ pRecord->SetMinorVersion(pMetaData->usMinorVersion);
+ if (pMetaData->usBuildNumber != USHRT_MAX)
+ pRecord->SetBuildNumber(pMetaData->usBuildNumber);
+ if (pMetaData->usRevisionNumber != USHRT_MAX)
+ pRecord->SetRevisionNumber(pMetaData->usRevisionNumber);
+ if (pMetaData->szLocale)
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_AssemblyRef,
+ AssemblyRefRec::COL_Locale, pRecord, pMetaData->szLocale));
+
+ }
+ if (pbHashValue)
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue,
+ pRecord, pbHashValue, cbHashValue));
+ if (dwAssemblyRefFlags != ULONG_MAX)
+ pRecord->SetFlags(PrepareForSaving(dwAssemblyRefFlags));
+
+ IfFailGo(UpdateENCLog(ar));
+
+ErrExit:
+
+
+ return hr;
+} // RegMeta::_SetAssemblyRefProps
+
+//*******************************************************************************
+// Helper: Set the specified attributes on the given File token.
+//*******************************************************************************
+HRESULT RegMeta::_SetFileProps( // S_OK or error.
+ mdFile file, // [IN] File token.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwFileFlags) // [IN] Flags.
+{
+ FileRec *pRecord;
+ HRESULT hr = S_OK;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetFileRecord(RidFromToken(file), &pRecord));
+
+ if (pbHashValue)
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_File, FileRec::COL_HashValue, pRecord,
+ pbHashValue, cbHashValue));
+ if (dwFileFlags != ULONG_MAX)
+ pRecord->SetFlags(dwFileFlags);
+
+ IfFailGo(UpdateENCLog(file));
+ErrExit:
+ return hr;
+} // RegMeta::_SetFileProps
+
+//*******************************************************************************
+// Helper: Set the specified attributes on the given ExportedType token.
+//*******************************************************************************
+HRESULT RegMeta::_SetExportedTypeProps( // S_OK or error.
+ mdExportedType ct, // [IN] ExportedType token.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef tkTypeDef, // [IN] TypeDef token within the file.
+ DWORD dwExportedTypeFlags) // [IN] Flags.
+{
+ ExportedTypeRec *pRecord;
+ HRESULT hr = S_OK;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(RidFromToken(ct), &pRecord));
+
+ if(! IsNilToken(tkImplementation))
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ExportedType, ExportedTypeRec::COL_Implementation,
+ pRecord, tkImplementation));
+ if (! IsNilToken(tkTypeDef))
+ {
+ _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef);
+ pRecord->SetTypeDefId(tkTypeDef);
+ }
+ if (dwExportedTypeFlags != ULONG_MAX)
+ pRecord->SetFlags(dwExportedTypeFlags);
+
+ IfFailGo(UpdateENCLog(ct));
+ErrExit:
+ return hr;
+} // RegMeta::_SetExportedTypeProps
+
+//*******************************************************************************
+// Helper: Set the specified attributes on the given ManifestResource token.
+//*******************************************************************************
+HRESULT RegMeta::_SetManifestResourceProps(// S_OK or error.
+ mdManifestResource mr, // [IN] ManifestResource token.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource.
+ DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file.
+ DWORD dwResourceFlags) // [IN] Flags.
+{
+ ManifestResourceRec *pRecord = NULL;
+ HRESULT hr = S_OK;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(RidFromToken(mr), &pRecord));
+
+ // Set the attributes.
+ if (tkImplementation != mdTokenNil)
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ManifestResource,
+ ManifestResourceRec::COL_Implementation, pRecord, tkImplementation));
+ if (dwOffset != ULONG_MAX)
+ pRecord->SetOffset(dwOffset);
+ if (dwResourceFlags != ULONG_MAX)
+ pRecord->SetFlags(dwResourceFlags);
+
+ IfFailGo(UpdateENCLog(mr));
+
+ErrExit:
+ return hr;
+} // RegMeta::_SetManifestResourceProps
+
+#endif //FEATURE_METADATA_EMIT
diff --git a/src/md/compiler/cacheload.h b/src/md/compiler/cacheload.h
new file mode 100644
index 0000000000..e1c3126fd9
--- /dev/null
+++ b/src/md/compiler/cacheload.h
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// CacheLoad.h
+//
+
+//
+// Class for returning the memory image where the image lives
+//
+//*****************************************************************************
+#ifndef __CACHELOAD__H__
+#define __CACHELOAD__H__
+
+
+#undef INTERFACE
+#define INTERFACE ICacheLoad
+DECLARE_INTERFACE_(ICacheLoad, IUnknown)
+{
+ STDMETHOD(GetCachedImaged)(
+ LPVOID* pImage);
+
+ STDMETHOD(SetCachedImaged)(
+ LPVOID pImage);
+};
+
+#endif
diff --git a/src/md/compiler/classfactory.cpp b/src/md/compiler/classfactory.cpp
new file mode 100644
index 0000000000..603f7975aa
--- /dev/null
+++ b/src/md/compiler/classfactory.cpp
@@ -0,0 +1,173 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// ClassFactory.cpp
+//
+
+//
+// Dll* routines for entry points, and support for COM framework. The class
+// factory and other routines live in this module.
+//
+// This file is not included in the standalone metadata version, because standalone can't use COM,
+// let alone COM-activation. So this file gets linked into mscorwks.dll, and then the mscorwks
+// class factory delegates to this co-creation routine.
+//
+//*****************************************************************************
+#include "stdafx.h"
+
+#ifdef FEATURE_METADATA_IN_VM
+
+#include "classfactory.h"
+#include "disp.h"
+#include "regmeta.h"
+#include "mscoree.h"
+#include "corhost.h"
+
+#include "clrprivhosting.h"
+
+extern HRESULT TypeNameFactoryCreateObject(REFIID riid, void **ppUnk);
+
+#include <ndpversion.h>
+
+
+//********** Locals. **********************************************************
+HINSTANCE GetModuleInst();
+
+// @telesto - why does Telesto export any Co-classes at all?
+
+// This map contains the list of coclasses which are exported from this module.
+// NOTE: CLSID_CorMetaDataDispenser must be the first entry in this table!
+const COCLASS_REGISTER g_CoClasses[] =
+{
+// pClsid szProgID pfnCreateObject
+ { &CLSID_CorMetaDataDispenser, W("CorMetaDataDispenser"), Disp::CreateObject },
+#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) // coreclr doesn't export these
+ { &CLSID_CorMetaDataDispenserRuntime, W("CorMetaDataDispenserRuntime"), Disp::CreateObject },
+
+ { &CLSID_CorRuntimeHost, W("CorRuntimeHost"), CorHost::CreateObject },
+ { &CLSID_CLRRuntimeHost, W("CLRRuntimeHost"), CorHost2::CreateObject },
+ { &__uuidof(CLRPrivRuntime), W("CLRPrivRuntime"), CorHost2::CreateObject },
+ { &CLSID_TypeNameFactory, NULL, (PFN_CREATE_OBJ)TypeNameFactoryCreateObject },
+#endif // FEATURE_CORECLR && !CROSSGEN_COMPILE
+ { NULL, NULL, NULL }
+};
+
+
+//*****************************************************************************
+// Called by COM to get a class factory for a given CLSID. If it is one we
+// support, instantiate a class factory object and prepare for create instance.
+//
+// Notes:
+// This gets invoked from mscorwks's DllGetClassObject.
+//*****************************************************************************
+STDAPI MetaDataDllGetClassObject( // Return code.
+ REFCLSID rclsid, // The class to desired.
+ REFIID riid, // Interface wanted on class factory.
+ LPVOID FAR *ppv) // Return interface pointer here.
+{
+ MDClassFactory *pClassFactory; // To create class factory object.
+ const COCLASS_REGISTER *pCoClass; // Loop control.
+ HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
+
+ // Scan for the right one.
+ for (pCoClass=g_CoClasses; pCoClass->pClsid; pCoClass++)
+ {
+ if (*pCoClass->pClsid == rclsid)
+ {
+ // Allocate the new factory object.
+ pClassFactory = new (nothrow) MDClassFactory(pCoClass);
+ if (!pClassFactory)
+ return (E_OUTOFMEMORY);
+
+ // Pick the v-table based on the caller's request.
+ hr = pClassFactory->QueryInterface(riid, ppv);
+
+ // Always release the local reference, if QI failed it will be
+ // the only one and the object gets freed.
+ pClassFactory->Release();
+ break;
+ }
+ }
+ return hr;
+}
+
+
+//*****************************************************************************
+//
+//********** Class factory code.
+//
+//*****************************************************************************
+
+
+//*****************************************************************************
+// QueryInterface is called to pick a v-table on the co-class.
+//*****************************************************************************
+HRESULT STDMETHODCALLTYPE MDClassFactory::QueryInterface(
+ REFIID riid,
+ void **ppvObject)
+{
+ HRESULT hr;
+
+ // Avoid confusion.
+ *ppvObject = NULL;
+
+ // Pick the right v-table based on the IID passed in.
+ if (riid == IID_IUnknown)
+ *ppvObject = (IUnknown *) this;
+ else if (riid == IID_IClassFactory)
+ *ppvObject = (IClassFactory *) this;
+
+ // If successful, add a reference for out pointer and return.
+ if (*ppvObject)
+ {
+ hr = S_OK;
+ AddRef();
+ }
+ else
+ hr = E_NOINTERFACE;
+ return hr;
+}
+
+
+//*****************************************************************************
+// CreateInstance is called to create a new instance of the coclass for which
+// this class was created in the first place. The returned pointer is the
+// v-table matching the IID if there.
+//*****************************************************************************
+HRESULT STDMETHODCALLTYPE MDClassFactory::CreateInstance(
+ IUnknown *pUnkOuter,
+ REFIID riid,
+ void **ppvObject)
+{
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ // Avoid confusion.
+ *ppvObject = NULL;
+ _ASSERTE(m_pCoClass);
+
+ // Aggregation is not supported by these objects.
+ if (pUnkOuter)
+ IfFailGo(CLASS_E_NOAGGREGATION);
+
+ // Ask the object to create an instance of itself, and check the iid.
+ hr = (*m_pCoClass->pfnCreateObject)(riid, ppvObject);
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+}
+
+HRESULT STDMETHODCALLTYPE
+MDClassFactory::LockServer(
+ BOOL fLock)
+{
+ // @FUTURE: Should we return E_NOTIMPL instead of S_OK?
+ return S_OK;
+}
+
+#endif //FEATURE_METADATA_IN_VM
diff --git a/src/md/compiler/classfactory.h b/src/md/compiler/classfactory.h
new file mode 100644
index 0000000000..3430fed0f2
--- /dev/null
+++ b/src/md/compiler/classfactory.h
@@ -0,0 +1,95 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// ClassFactory.h
+//
+
+//
+// Class factories are used by the pluming in COM to activate new objects.
+// This module contains the class factory code to instantiate the debugger
+// objects described in <cordb.h>.
+//
+//*****************************************************************************
+#ifndef __ClassFactory__h__
+#define __ClassFactory__h__
+
+#include "disp.h"
+
+
+// This typedef is for a function which will create a new instance of an object.
+typedef HRESULT (* PFN_CREATE_OBJ)(REFIID riid, void **ppvObject);
+
+//*****************************************************************************
+// This structure is used to declare a global list of coclasses. The class
+// factory object is created with a pointer to the correct one of these, so
+// that when create instance is called, it can be created.
+//*****************************************************************************
+struct COCLASS_REGISTER
+{
+ const GUID *pClsid; // Class ID of the coclass.
+ LPCWSTR szProgID; // Prog ID of the class.
+ PFN_CREATE_OBJ pfnCreateObject; // Creation function for an instance.
+};
+
+
+
+//*****************************************************************************
+// One class factory object satifies all of our clsid's, to reduce overall
+// code bloat.
+//*****************************************************************************
+class MDClassFactory :
+ public IClassFactory
+{
+ MDClassFactory() { } // Can't use without data.
+
+public:
+ MDClassFactory(const COCLASS_REGISTER *pCoClass)
+ : m_cRef(1), m_pCoClass(pCoClass)
+ { }
+
+ virtual ~MDClassFactory() {}
+
+ //
+ // IUnknown methods.
+ //
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+ REFIID riid,
+ void **ppvObject);
+
+ virtual ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ virtual ULONG STDMETHODCALLTYPE Release()
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef <= 0)
+ delete this;
+ return (cRef);
+ }
+
+
+ //
+ // IClassFactory methods.
+ //
+
+ virtual HRESULT STDMETHODCALLTYPE CreateInstance(
+ IUnknown *pUnkOuter,
+ REFIID riid,
+ void **ppvObject);
+
+ virtual HRESULT STDMETHODCALLTYPE LockServer(
+ BOOL fLock);
+
+
+private:
+ LONG m_cRef; // Reference count.
+ const COCLASS_REGISTER *m_pCoClass; // The class we belong to.
+};
+
+
+
+#endif // __ClassFactory__h__
diff --git a/src/md/compiler/crossgen/.gitmirror b/src/md/compiler/crossgen/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/compiler/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/compiler/crossgen/CMakeLists.txt b/src/md/compiler/crossgen/CMakeLists.txt
new file mode 100644
index 0000000000..7baf17448b
--- /dev/null
+++ b/src/md/compiler/crossgen/CMakeLists.txt
@@ -0,0 +1,5 @@
+include(${CLR_DIR}/crossgen.cmake)
+include(../../md_wks.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDCOMPILER_SOURCES)
+add_library_clr(mdcompiler_crossgen ${MDCOMPILER_SOURCES})
diff --git a/src/md/compiler/crossgen/MDCompiler_crossgen.nativeproj b/src/md/compiler/crossgen/MDCompiler_crossgen.nativeproj
new file mode 100644
index 0000000000..8ea56d2cdc
--- /dev/null
+++ b/src/md/compiler/crossgen/MDCompiler_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>mdcompiler_crossgen</OutputName>
+ </PropertyGroup>
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\Compiler\Compiler.settings.targets" />
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/compiler/custattr.h b/src/md/compiler/custattr.h
new file mode 100644
index 0000000000..119c05922a
--- /dev/null
+++ b/src/md/compiler/custattr.h
@@ -0,0 +1,117 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+#ifndef __CustAttr__h__
+#define __CustAttr__h__
+
+//#include "stdafx.h"
+#include "corhdr.h"
+#include "cahlprinternal.h"
+#include "sarray.h"
+#include "factory.h"
+
+//*****************************************************************************
+// Argument parsing. The custom attributes may have ctor arguments, and may
+// have named arguments. The arguments are defined by the following tables.
+//
+// These tables also include a member to contain the value of the argument,
+// which is used at runtime. When parsing a given custom attribute, a copy
+// of the argument descriptors is filled in with the values for the instance
+// of the custom attribute.
+//
+// For each ctor arg, there is a CaArg struct, with the type. At runtime,
+// a value is filled in for each ctor argument.
+//
+// For each named arg, there is a CaNamedArg struct, with the name of the
+// argument, the expected type of the argument, if the type is an enum,
+// the name of the enum. Also, at runtime, a value is filled in for
+// each named argument found.
+//
+// Note that arrays and variants are not supported.
+//
+// At runtime, after the args have been parsed, the tag field of CaValue
+// can be used to determine if a particular arg was given.
+//*****************************************************************************
+struct CaArg
+{
+ void InitEnum(CorSerializationType _enumType, INT64 _val = 0)
+ {
+ CaTypeCtor caType(SERIALIZATION_TYPE_ENUM, SERIALIZATION_TYPE_UNDEFINED, _enumType, NULL, 0);
+ Init(caType, _val);
+ }
+ void Init(CorSerializationType _type, INT64 _val = 0)
+ {
+ _ASSERTE(_type != SERIALIZATION_TYPE_ENUM);
+ _ASSERTE(_type != SERIALIZATION_TYPE_SZARRAY);
+ CaTypeCtor caType(_type);
+ Init(caType, _val);
+ }
+ void Init(CaType _type, INT64 _val = 0)
+ {
+ type = _type;
+ memset(&val, 0, sizeof(CaValue));
+ val.i8 = _val;
+ }
+
+ CaType type;
+ CaValue val;
+};
+
+struct CaNamedArg
+{
+ void InitI4FieldEnum(LPCSTR _szName, LPCSTR _szEnumName, INT64 _val = 0)
+ {
+ CaTypeCtor caType(SERIALIZATION_TYPE_ENUM, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_I4, _szEnumName, (ULONG)strlen(_szEnumName));
+ Init(_szName, SERIALIZATION_TYPE_FIELD, caType, _val);
+ }
+
+ void InitBoolField(LPCSTR _szName, INT64 _val = 0)
+ {
+ CaTypeCtor caType(SERIALIZATION_TYPE_BOOLEAN);
+ Init(_szName, SERIALIZATION_TYPE_FIELD, caType, _val);
+ }
+
+ void Init(LPCSTR _szName, CorSerializationType _propertyOrField, CaType _type, INT64 _val = 0)
+ {
+ szName = _szName;
+ cName = _szName ? (ULONG)strlen(_szName) : 0;
+ propertyOrField = _propertyOrField;
+ type = _type;
+
+ memset(&val, 0, sizeof(CaValue));
+ val.i8 = _val;
+ }
+
+ LPCSTR szName;
+ ULONG cName;
+ CorSerializationType propertyOrField;
+ CaType type;
+ CaValue val;
+};
+
+struct CaNamedArgCtor : public CaNamedArg
+{
+ CaNamedArgCtor()
+ {
+ memset(this, 0, sizeof(CaNamedArg));
+ }
+};
+
+HRESULT ParseEncodedType(
+ CustomAttributeParser &ca,
+ CaType* pCaType);
+
+HRESULT ParseKnownCaArgs(
+ CustomAttributeParser &ca, // The Custom Attribute blob.
+ CaArg *pArgs, // Array of argument descriptors.
+ ULONG cArgs); // Count of argument descriptors.
+
+HRESULT ParseKnownCaNamedArgs(
+ CustomAttributeParser &ca, // The Custom Attribute blob.
+ CaNamedArg *pNamedArgs, // Array of argument descriptors.
+ ULONG cNamedArgs); // Count of argument descriptors.
+
+#endif
diff --git a/src/md/compiler/custattr_emit.cpp b/src/md/compiler/custattr_emit.cpp
new file mode 100644
index 0000000000..3e23600176
--- /dev/null
+++ b/src/md/compiler/custattr_emit.cpp
@@ -0,0 +1,2000 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// CustAttr_Emit.cpp
+//
+
+//
+// Implementation for the meta data custom attribute emit code (code:IMetaDataEmit).
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "metadata.h"
+#include "corerror.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+#include "mdperf.h"
+#include "posterror.h"
+#include "cahlprinternal.h"
+#include "custattr.h"
+#include "corhdr.h"
+#include <metamodelrw.h>
+
+#ifdef FEATURE_METADATA_EMIT
+
+//*****************************************************************************
+//*****************************************************************************
+// Support for "Pseudo Custom Attributes"
+
+
+//*****************************************************************************
+// Enumeration of known custom attributes.
+//*****************************************************************************
+#define KnownCaList() \
+ KnownCa(UNKNOWN) \
+ KnownCa(DllImportAttribute) \
+ KnownCa(GuidAttribute) \
+ KnownCa(ComImportAttribute) \
+ KnownCa(InterfaceTypeAttribute) \
+ KnownCa(ClassInterfaceAttribute) \
+ KnownCa(SerializableAttribute) \
+ KnownCa(NonSerializedAttribute) \
+ KnownCa(MethodImplAttribute1) \
+ KnownCa(MethodImplAttribute2) \
+ KnownCa(MethodImplAttribute3) \
+ KnownCa(MarshalAsAttribute1) \
+ KnownCa(MarshalAsAttribute2) \
+ KnownCa(PreserveSigAttribute) \
+ KnownCa(InAttribute) \
+ KnownCa(OutAttribute) \
+ KnownCa(OptionalAttribute) \
+ KnownCa(StructLayoutAttribute1) \
+ KnownCa(StructLayoutAttribute2) \
+ KnownCa(FieldOffsetAttribute) \
+ KnownCa(TypeLibVersionAttribute) \
+ KnownCa(ComCompatibleVersionAttribute) \
+ KnownCa(SpecialNameAttribute) \
+ KnownCa(AllowPartiallyTrustedCallersAttribute) \
+ KnownCa(WindowsRuntimeImportAttribute) \
+
+// Ids for the CA's. CA_DllImportAttribute, etc.
+#define KnownCa(x) CA_##x,
+enum {
+ KnownCaList()
+ CA_COUNT
+};
+
+
+//*****************************************************************************
+// Properties of the known custom attributes.
+//
+// These tables describe the known custom attributes. For each custom
+// attribute, we know the namespace and name of the custom attribute,
+// the types to which the CA applies, the ctor args, and possible named
+// args. There is a flag which specifies whether the custom attribute
+// should be kept, in addition to any processing done with the data.
+//*****************************************************************************
+const BOOL bKEEPCA = TRUE;
+const BOOL bDONTKEEPCA = FALSE;
+const BOOL bMATCHBYSIG = TRUE;
+const BOOL bMATCHBYNAME = FALSE;
+
+struct KnownCaProp
+{
+ LPCUTF8 szNamespace; // Namespace of the custom attribute.
+ LPCUTF8 szName; // Name of the custom attribute.
+ const mdToken * rTypes; // Types that the CA applies to.
+ BOOL bKeepCa; // Keep the CA after processing?
+ const CaArg * pArgs; // List of ctor argument descriptors.
+ ULONG cArgs; // Count of ctor argument descriptors.
+ const CaNamedArg * pNamedArgs; // List of named arg descriptors.
+ ULONG cNamedArgs; // Count of named arg descriptors.
+ BOOL bMatchBySig; // For overloads; match by sig, not just name.
+ // WARNING: All overloads need the flag!
+};
+
+// Recognized targets for known custom attributes.
+// If Target includes mdtAssembly, then make sure to include mdtTypeRef as well,
+// aLink uses mdtTypeRef target temporarily for assembly target attributes
+const mdToken DllImportTargets[] = {mdtMethodDef, (ULONG32) -1};
+const mdToken GuidTargets[] = {mdtTypeDef, mdtTypeRef, mdtModule, mdtAssembly, (ULONG32) -1};
+const mdToken ComImportTargets[] = {mdtTypeDef, (ULONG32) -1};
+const mdToken InterfaceTypeTargets[] = {mdtTypeDef, (ULONG32) -1};
+const mdToken ClassInterfaceTargets[] = {mdtTypeDef, mdtAssembly, mdtTypeRef, (ULONG32) -1};
+const mdToken SerializableTargets[] = {mdtTypeDef, (ULONG32) -1};
+const mdToken NotInGCHeapTargets[] = {mdtTypeDef, (ULONG32) -1};
+const mdToken NonSerializedTargets[] = {mdtFieldDef, (ULONG32) -1};
+const mdToken MethodImplTargets[] = {mdtMethodDef, (ULONG32) -1};
+const mdToken MarshalTargets[] = {mdtFieldDef, mdtParamDef, mdtProperty, (ULONG32) -1};
+const mdToken PreserveSigTargets[] = {mdtMethodDef, (ULONG32) -1};
+const mdToken InOutTargets[] = {mdtParamDef, (ULONG32) -1};
+const mdToken StructLayoutTargets[] = {mdtTypeDef, (ULONG32) -1};
+const mdToken FieldOffsetTargets[] = {mdtFieldDef, (ULONG32) -1};
+const mdToken TypeLibVersionTargets[] = {mdtAssembly, mdtTypeRef,(ULONG32) -1};
+const mdToken ComCompatibleVersionTargets[] = {mdtAssembly, mdtTypeRef, (ULONG32) -1};
+const mdToken SpecialNameTargets[] = {mdtTypeDef, mdtMethodDef, mdtFieldDef, mdtProperty, mdtEvent, (ULONG32) -1};
+const mdToken AllowPartiallyTrustedCallersTargets[] = {mdtAssembly, mdtTypeRef, (ULONG32) -1};
+const mdToken WindowsRuntimeImportTargets[] = {mdtTypeDef, (ULONG32) -1};
+
+
+//#ifndef CEE_CALLCONV
+// # define CEE_CALLCONV (IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS)
+//#endif
+
+#define DEFINE_CA_CTOR_ARGS(NAME) \
+ const CaArg r##NAME##Args[] = \
+ {
+#define DEFINE_CA_CTOR_ARG(TYPE) {{TYPE}},
+#define DEFINE_CA_CTOR_ARGS_END() \
+ };
+
+
+
+#define DEFINE_CA_NAMED_ARGS(NAME) \
+ const CaNamedArg r##NAME##NamedArgs[] = \
+ {
+
+#define DEFINE_CA_NAMED_ARG(NAME, PROPORFIELD, TYPE, ARRAYTYPE, ENUMTYPE, ENUMNAME) \
+ { NAME, sizeof(NAME) - 1, PROPORFIELD, { TYPE, ARRAYTYPE, ENUMTYPE, ENUMNAME, sizeof(ENUMNAME) - 1 } },
+
+#define DEFINE_CA_NAMED_PROP_I4ENUM(NAME, ENUM) \
+ DEFINE_CA_NAMED_ARG(NAME, SERIALIZATION_TYPE_PROPERTY, SERIALIZATION_TYPE_ENUM, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_I4, ENUM)
+#define DEFINE_CA_NAMED_FIELD_I4ENUM(NAME, ENUM) \
+ DEFINE_CA_NAMED_ARG(NAME, SERIALIZATION_TYPE_FIELD, SERIALIZATION_TYPE_ENUM, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_I4, ENUM)
+#define DEFINE_CA_NAMED_PROP(NAME, TYPE) \
+ DEFINE_CA_NAMED_ARG(NAME, SERIALIZATION_TYPE_PROPERTY, TYPE, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_UNDEFINED, "")
+#define DEFINE_CA_NAMED_FIELD(NAME, TYPE) \
+ DEFINE_CA_NAMED_ARG(NAME, SERIALIZATION_TYPE_FIELD, TYPE, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_UNDEFINED, "")
+#define DEFINE_CA_NAMED_PROP_BOOL(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_BOOLEAN)
+#define DEFINE_CA_NAMED_FIELD_BOOL(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_BOOLEAN)
+#define DEFINE_CA_NAMED_PROP_STRING(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_STRING)
+#define DEFINE_CA_NAMED_FIELD_STRING(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_STRING)
+#define DEFINE_CA_NAMED_PROP_TYPE(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_TYPE)
+#define DEFINE_CA_NAMED_FIELD_TYPE(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_TYPE)
+#define DEFINE_CA_NAMED_PROP_I2(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_I2)
+#define DEFINE_CA_NAMED_FIELD_I2(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_I2)
+#define DEFINE_CA_NAMED_PROP_I4(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_I4)
+#define DEFINE_CA_NAMED_FIELD_I4(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_I4)
+
+#define DEFINE_CA_NAMED_ARGS_END() \
+ };
+
+//-----------------------------------------------------------------------------
+// index 0 is used as a placeholder.
+const KnownCaProp UNKNOWNProps = {0};
+
+//-----------------------------------------------------------------------------
+// DllImport args, named args, and known attribute properties.
+DEFINE_CA_CTOR_ARGS(DllImportAttribute)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_STRING)
+DEFINE_CA_CTOR_ARGS_END()
+
+// NOTE: Keep this enum in sync with the array of named arguments.
+enum DllImportNamedArgs
+{
+ DI_CallingConvention,
+ DI_CharSet,
+ DI_EntryPoint,
+ DI_ExactSpelling,
+ DI_SetLastError,
+ DI_PreserveSig,
+ DI_BestFitMapping,
+ DI_ThrowOnUnmappableChar,
+ DI_COUNT
+};
+
+DEFINE_CA_NAMED_ARGS(DllImportAttribute)
+ DEFINE_CA_NAMED_FIELD_I4ENUM("CallingConvention", "System.Runtime.InteropServices.CallingConvention")
+ DEFINE_CA_NAMED_FIELD_I4ENUM("CharSet", "System.Runtime.InteropServices.CharSet")
+ DEFINE_CA_NAMED_FIELD_STRING("EntryPoint")
+ DEFINE_CA_NAMED_FIELD_BOOL("ExactSpelling")
+ DEFINE_CA_NAMED_FIELD_BOOL("SetLastError")
+ DEFINE_CA_NAMED_FIELD_BOOL("PreserveSig")
+ DEFINE_CA_NAMED_FIELD_BOOL("BestFitMapping")
+ DEFINE_CA_NAMED_FIELD_BOOL("ThrowOnUnmappableChar")
+DEFINE_CA_NAMED_ARGS_END()
+
+const KnownCaProp DllImportAttributeProps = {"System.Runtime.InteropServices", "DllImportAttribute", DllImportTargets, bDONTKEEPCA,
+ rDllImportAttributeArgs, lengthof(rDllImportAttributeArgs),
+ rDllImportAttributeNamedArgs, lengthof(rDllImportAttributeNamedArgs)};
+
+//-----------------------------------------------------------------------------
+// GUID args, named args (none), and known attribute properties.
+DEFINE_CA_CTOR_ARGS(GuidAttribute)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_STRING)
+DEFINE_CA_CTOR_ARGS_END()
+
+const KnownCaProp GuidAttributeProps = {"System.Runtime.InteropServices", "GuidAttribute", GuidTargets, bKEEPCA,
+ rGuidAttributeArgs, lengthof(rGuidAttributeArgs)};
+
+//-----------------------------------------------------------------------------
+// ComImport args (none), named args (none), and known attribute properties.
+const KnownCaProp ComImportAttributeProps = {"System.Runtime.InteropServices", "ComImportAttribute", ComImportTargets};
+
+//-----------------------------------------------------------------------------
+// Interface type args, named args (none), and known attribute properties.
+DEFINE_CA_CTOR_ARGS(InterfaceTypeAttribute)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U2)
+DEFINE_CA_CTOR_ARGS_END()
+
+const KnownCaProp InterfaceTypeAttributeProps = {"System.Runtime.InteropServices", "InterfaceTypeAttribute", InterfaceTypeTargets, bKEEPCA,
+ rInterfaceTypeAttributeArgs, lengthof(rInterfaceTypeAttributeArgs)};
+
+//-----------------------------------------------------------------------------
+// Class interface type args, named args (none), and known attribute properties.
+DEFINE_CA_CTOR_ARGS(ClassInterfaceAttribute)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U2)
+DEFINE_CA_CTOR_ARGS_END()
+
+const KnownCaProp ClassInterfaceAttributeProps = {"System.Runtime.InteropServices", "ClassInterfaceAttribute", ClassInterfaceTargets, bKEEPCA,
+ rClassInterfaceAttributeArgs, lengthof(rClassInterfaceAttributeArgs)};
+
+//-----------------------------------------------------------------------------
+// Serializable args (none), named args (none), and known attribute properties.
+const KnownCaProp SerializableAttributeProps = {"System", "SerializableAttribute", SerializableTargets};
+
+//-----------------------------------------------------------------------------
+// NonSerialized args (none), named args (none), and known attribute properties.
+const KnownCaProp NonSerializedAttributeProps = {"System", "NonSerializedAttribute", NonSerializedTargets};
+
+//-----------------------------------------------------------------------------
+// SpecialName args (none), named args (none), and known attribute properties.
+const KnownCaProp SpecialNameAttributeProps = {"System.Runtime.CompilerServices", "SpecialNameAttribute", SpecialNameTargets, bDONTKEEPCA};
+
+//-----------------------------------------------------------------------------
+// WindowsRuntimeImport args (none), named args (none), and known attribute properties.
+const KnownCaProp WindowsRuntimeImportAttributeProps = {"System.Runtime.InteropServices.WindowsRuntime", "WindowsRuntimeImportAttribute", WindowsRuntimeImportTargets};
+
+
+//-----------------------------------------------------------------------------
+// MethodImpl #1 args (none), named args, and known attribute properties.
+// MethodImpl #2 args (short), named args, and known attribute properties.
+// MethodImpl #3 args (enum), named args, and known attribute properties.
+// Note: first two match by signature; third by name only, because signature matching code is not
+// strong enough for enums.
+DEFINE_CA_CTOR_ARGS(MethodImplAttribute2)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I2)
+DEFINE_CA_CTOR_ARGS_END()
+
+DEFINE_CA_CTOR_ARGS(MethodImplAttribute3)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U4)
+DEFINE_CA_CTOR_ARGS_END()
+
+enum MethodImplAttributeNamedArgs
+{
+ MI_CodeType,
+ MI_COUNT
+};
+
+DEFINE_CA_NAMED_ARGS(MethodImplAttribute)
+ DEFINE_CA_NAMED_FIELD_I4ENUM("MethodCodeType", "System.Runtime.CompilerServices.MethodCodeType")
+DEFINE_CA_NAMED_ARGS_END()
+
+const KnownCaProp MethodImplAttribute1Props = {"System.Runtime.CompilerServices", "MethodImplAttribute", MethodImplTargets, bDONTKEEPCA,
+ 0, 0,
+ rMethodImplAttributeNamedArgs, lengthof(rMethodImplAttributeNamedArgs),
+ bMATCHBYSIG};
+const KnownCaProp MethodImplAttribute2Props = {"System.Runtime.CompilerServices", "MethodImplAttribute", MethodImplTargets, bDONTKEEPCA,
+ rMethodImplAttribute2Args, lengthof(rMethodImplAttribute2Args),
+ rMethodImplAttributeNamedArgs, lengthof(rMethodImplAttributeNamedArgs),
+ bMATCHBYSIG};
+const KnownCaProp MethodImplAttribute3Props = {"System.Runtime.CompilerServices", "MethodImplAttribute", MethodImplTargets, bDONTKEEPCA,
+ rMethodImplAttribute3Args, lengthof(rMethodImplAttribute3Args),
+ rMethodImplAttributeNamedArgs, lengthof(rMethodImplAttributeNamedArgs),
+ bMATCHBYNAME};
+
+//-----------------------------------------------------------------------------
+// Marshal args, named args, and known attribute properties.
+DEFINE_CA_CTOR_ARGS(MarshalAsAttribute2)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U4)
+DEFINE_CA_CTOR_ARGS_END()
+
+DEFINE_CA_CTOR_ARGS(MarshalAsAttribute1)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I2)
+DEFINE_CA_CTOR_ARGS_END()
+
+// NOTE: Keep this enum in sync with the array of named arguments.
+enum MarshalNamedArgs
+{
+ M_ArraySubType,
+ M_SafeArraySubType,
+ M_SafeArrayUserDefinedSubType,
+ M_SizeParamIndex,
+ M_SizeConst,
+ M_MarshalType,
+ M_MarshalTypeRef,
+ M_MarshalCookie,
+ M_IidParameterIndex,
+ M_COUNT
+};
+
+DEFINE_CA_NAMED_ARGS(MarshalAsAttribute)
+ DEFINE_CA_NAMED_FIELD_I4ENUM("ArraySubType", "System.Runtime.InteropServices.UnmanagedType")
+ DEFINE_CA_NAMED_FIELD_I4ENUM("SafeArraySubType", "System.Runtime.InteropServices.VarEnum")
+ DEFINE_CA_NAMED_FIELD_TYPE("SafeArrayUserDefinedSubType")
+ DEFINE_CA_NAMED_FIELD_I2("SizeParamIndex")
+ DEFINE_CA_NAMED_FIELD_I4("SizeConst")
+ DEFINE_CA_NAMED_FIELD_STRING("MarshalType")
+ DEFINE_CA_NAMED_FIELD_TYPE("MarshalTypeRef")
+ DEFINE_CA_NAMED_FIELD_STRING("MarshalCookie")
+ DEFINE_CA_NAMED_FIELD_I4("IidParameterIndex")
+DEFINE_CA_NAMED_ARGS_END()
+
+const KnownCaProp MarshalAsAttribute1Props = {"System.Runtime.InteropServices", "MarshalAsAttribute", MarshalTargets, bDONTKEEPCA,
+ rMarshalAsAttribute1Args, lengthof(rMarshalAsAttribute1Args),
+ rMarshalAsAttributeNamedArgs, lengthof(rMarshalAsAttributeNamedArgs),
+ bMATCHBYSIG};
+
+const KnownCaProp MarshalAsAttribute2Props = {"System.Runtime.InteropServices", "MarshalAsAttribute", MarshalTargets, bDONTKEEPCA,
+ rMarshalAsAttribute2Args, lengthof(rMarshalAsAttribute2Args),
+ rMarshalAsAttributeNamedArgs, lengthof(rMarshalAsAttributeNamedArgs),
+ bMATCHBYNAME};
+
+//-----------------------------------------------------------------------------
+// PreserveSignature args, named args (none), and known attribute properties.
+const KnownCaProp PreserveSigAttributeProps = {"System.Runtime.InteropServices", "PreserveSigAttribute", PreserveSigTargets, bDONTKEEPCA};
+
+//-----------------------------------------------------------------------------
+// In args (none), named args (none), and known attribute properties.
+const KnownCaProp InAttributeProps = {"System.Runtime.InteropServices", "InAttribute", InOutTargets};
+
+//-----------------------------------------------------------------------------
+// Out args (none), named args (none), and known attribute properties.
+const KnownCaProp OutAttributeProps = {"System.Runtime.InteropServices", "OutAttribute", InOutTargets};
+
+//-----------------------------------------------------------------------------
+// Optional args (none), named args (none), and known attribute properties.
+const KnownCaProp OptionalAttributeProps = {"System.Runtime.InteropServices", "OptionalAttribute", InOutTargets};
+
+//-----------------------------------------------------------------------------
+// StructLayout args, named args, and known attribute properties.
+DEFINE_CA_CTOR_ARGS(StructLayoutAttribute2)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4)
+DEFINE_CA_CTOR_ARGS_END()
+
+DEFINE_CA_CTOR_ARGS(StructLayoutAttribute1)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I2)
+DEFINE_CA_CTOR_ARGS_END()
+
+// NOTE: Keep this enum in sync with the array of named arguments.
+enum StructLayoutNamedArgs
+{
+ SL_Pack,
+ SL_Size,
+ SL_CharSet,
+ SL_COUNT
+};
+
+DEFINE_CA_NAMED_ARGS(StructLayoutAttribute)
+ DEFINE_CA_NAMED_FIELD_I4("Pack")
+ DEFINE_CA_NAMED_FIELD_I4("Size")
+ DEFINE_CA_NAMED_FIELD_I4ENUM("CharSet", "System.Runtime.InteropServices.CharSet")
+DEFINE_CA_NAMED_ARGS_END()
+
+const KnownCaProp StructLayoutAttribute1Props = {"System.Runtime.InteropServices", "StructLayoutAttribute", StructLayoutTargets, bDONTKEEPCA,
+ rStructLayoutAttribute1Args, lengthof(rStructLayoutAttribute1Args),
+ rStructLayoutAttributeNamedArgs, lengthof(rStructLayoutAttributeNamedArgs),
+ bMATCHBYSIG};
+const KnownCaProp StructLayoutAttribute2Props = {"System.Runtime.InteropServices", "StructLayoutAttribute", StructLayoutTargets, bDONTKEEPCA,
+ rStructLayoutAttribute2Args, lengthof(rStructLayoutAttribute2Args),
+ rStructLayoutAttributeNamedArgs, lengthof(rStructLayoutAttributeNamedArgs),
+ bMATCHBYNAME};
+
+//-----------------------------------------------------------------------------
+// FieldOffset args, named args (none), and known attribute properties.
+DEFINE_CA_CTOR_ARGS(FieldOffsetAttribute)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U4)
+DEFINE_CA_CTOR_ARGS_END()
+
+const KnownCaProp FieldOffsetAttributeProps = {"System.Runtime.InteropServices", "FieldOffsetAttribute", FieldOffsetTargets, bDONTKEEPCA,
+ rFieldOffsetAttributeArgs, lengthof(rFieldOffsetAttributeArgs)};
+
+DEFINE_CA_CTOR_ARGS(TypeLibVersionAttribute)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4)
+DEFINE_CA_CTOR_ARGS_END()
+
+const KnownCaProp TypeLibVersionAttributeProps = {"System.Runtime.InteropServices", "TypeLibVersionAttribute", TypeLibVersionTargets, bKEEPCA,
+ rTypeLibVersionAttributeArgs, lengthof(rTypeLibVersionAttributeArgs)};
+
+
+DEFINE_CA_CTOR_ARGS(ComCompatibleVersionAttribute)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4)
+ DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4)
+DEFINE_CA_CTOR_ARGS_END()
+
+const KnownCaProp ComCompatibleVersionAttributeProps = {"System.Runtime.InteropServices", "ComCompatibleVersionAttribute", ComCompatibleVersionTargets, bKEEPCA,
+ rComCompatibleVersionAttributeArgs, lengthof(rComCompatibleVersionAttributeArgs)};
+
+
+//-----------------------------------------------------------------------------
+// APTCA args (none), named args (none), and known attribute properties.
+const KnownCaProp AllowPartiallyTrustedCallersAttributeProps = {"System.Security", "AllowPartiallyTrustedCallersAttribute", AllowPartiallyTrustedCallersTargets, bKEEPCA};
+
+
+
+//-----------------------------------------------------------------------------
+// Array of known custom attribute properties
+#undef KnownCa
+#define KnownCa(x) &x##Props,
+const KnownCaProp * const rKnownCaProps[CA_COUNT] =
+{
+ KnownCaList()
+};
+
+//*****************************************************************************
+// Helper to turn on or off a single bit in a bitmask.
+//*****************************************************************************
+template<class T> FORCEINLINE void SetBitValue(T &bitmask, T bit, int bVal)
+{
+ if (bVal)
+ bitmask |= bit;
+ else
+ bitmask &= ~bit;
+} // template<class T> FORCEINLINE void SetBitValue()
+
+HRESULT ParseEncodedType(
+ CustomAttributeParser &ca,
+ CaType* pCaType)
+{
+ CONTRACTL
+ {
+ PRECONDITION(CheckPointer(pCaType));
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ CorSerializationType* pType = &pCaType->tag;
+
+ IfFailGo(ca.GetTag(pType));
+
+ if (*pType == SERIALIZATION_TYPE_SZARRAY)
+ {
+ IfFailGo(ca.GetTag(&pCaType->arrayType));
+ pType = &pCaType->arrayType;
+ }
+
+ if (*pType == SERIALIZATION_TYPE_ENUM)
+ {
+ // We cannot determine the underlying type without loading the Enum.
+ pCaType->enumType = SERIALIZATION_TYPE_UNDEFINED;
+ IfFailGo(ca.GetNonNullString(&pCaType->szEnumName, &pCaType->cEnumName));
+ }
+
+ErrExit:
+ return hr;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Helper to parse the values for the ctor argument list and the named argument list.
+//
+
+HRESULT ParseKnownCaValue(
+ CustomAttributeParser &ca,
+ CaValue* pCaArg,
+ CaType* pCaParam)
+{
+ CONTRACTL
+ {
+ PRECONDITION(CheckPointer(pCaArg));
+ PRECONDITION(CheckPointer(pCaParam));
+ PRECONDITION(pCaParam->tag != SERIALIZATION_TYPE_TAGGED_OBJECT && pCaParam->tag != SERIALIZATION_TYPE_SZARRAY);
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ CorSerializationType underlyingType;
+
+ pCaArg->type = *pCaParam;
+
+ underlyingType = pCaArg->type.tag == SERIALIZATION_TYPE_ENUM ? pCaArg->type.enumType : pCaArg->type.tag;
+
+ // Grab the value.
+ switch (underlyingType)
+ {
+ case SERIALIZATION_TYPE_BOOLEAN:
+ case SERIALIZATION_TYPE_I1:
+ case SERIALIZATION_TYPE_U1:
+ IfFailGo(ca.GetU1(&pCaArg->u1));
+ break;
+
+ case SERIALIZATION_TYPE_CHAR:
+ case SERIALIZATION_TYPE_I2:
+ case SERIALIZATION_TYPE_U2:
+ IfFailGo(ca.GetU2(&pCaArg->u2));
+ break;
+
+ case SERIALIZATION_TYPE_I4:
+ case SERIALIZATION_TYPE_U4:
+ IfFailGo(ca.GetU4(&pCaArg->u4));
+ break;
+
+ case SERIALIZATION_TYPE_I8:
+ case SERIALIZATION_TYPE_U8:
+ IfFailGo(ca.GetU8(&pCaArg->u8));
+ break;
+
+ case SERIALIZATION_TYPE_R4:
+ IfFailGo(ca.GetR4(&pCaArg->r4));
+ break;
+
+ case SERIALIZATION_TYPE_R8:
+ IfFailGo(ca.GetR8(&pCaArg->r8));
+ break;
+
+ case SERIALIZATION_TYPE_STRING:
+ case SERIALIZATION_TYPE_TYPE:
+ IfFailGo(ca.GetString(&pCaArg->str.pStr, &pCaArg->str.cbStr));
+ break;
+
+ default:
+ // The arguments of all known custom attributes are Type, String, Enum, or primitive types.
+ _ASSERTE(!"Unexpected internal error");
+ hr = E_FAIL;
+ break;
+ } // End switch
+
+ErrExit:
+ return hr;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Helper to parse the nanmed argument list.
+// This function is used by code:RegMeta::DefineCustomAttribute for IMetaDataEmit2 and
+// should not have any VM dependency.
+//
+
+HRESULT ParseKnownCaNamedArgs(
+ CustomAttributeParser &ca, // The Custom Attribute blob.
+ CaNamedArg *pNamedParams, // Array of argument descriptors.
+ ULONG cNamedParams)
+{
+ WRAPPER_NO_CONTRACT;
+
+ HRESULT hr = S_OK;
+ ULONG ixParam;
+ INT32 ixArg;
+ INT16 cActualArgs;
+ CaNamedArgCtor namedArg;
+ CaNamedArg* pNamedParam;
+
+ // Get actual count of named arguments.
+ if (FAILED(ca.GetI2(&cActualArgs)))
+ cActualArgs = 0; // Everett behavior
+
+ for (ixParam = 0; ixParam < cNamedParams; ixParam++)
+ pNamedParams[ixParam].val.type.tag = SERIALIZATION_TYPE_UNDEFINED;
+
+ // For each named argument...
+ for (ixArg = 0; ixArg < cActualArgs; ixArg++)
+ {
+ // Field or property?
+ IfFailGo(ca.GetTag(&namedArg.propertyOrField));
+ if (namedArg.propertyOrField != SERIALIZATION_TYPE_FIELD && namedArg.propertyOrField != SERIALIZATION_TYPE_PROPERTY)
+ IfFailGo(PostError(META_E_CA_INVALID_ARGTYPE));
+
+ // Get argument type information
+ IfFailGo(ParseEncodedType(ca, &namedArg.type));
+
+ // Get name of Arg.
+ if (FAILED(ca.GetNonEmptyString(&namedArg.szName, &namedArg.cName)))
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+
+ // Match arg by name and type
+ for (ixParam = 0; ixParam < cNamedParams; ixParam++)
+ {
+ pNamedParam = &pNamedParams[ixParam];
+
+ // Match type
+ if (pNamedParam->type.tag != SERIALIZATION_TYPE_TAGGED_OBJECT)
+ {
+ if (namedArg.type.tag != pNamedParam->type.tag)
+ continue;
+
+ // Match array type
+ if (namedArg.type.tag == SERIALIZATION_TYPE_SZARRAY &&
+ pNamedParam->type.arrayType != SERIALIZATION_TYPE_TAGGED_OBJECT &&
+ namedArg.type.arrayType != pNamedParam->type.arrayType)
+ continue;
+ }
+
+ // Match name (and its length to avoid substring matching)
+ if ((pNamedParam->cName != namedArg.cName) ||
+ (strncmp(pNamedParam->szName, namedArg.szName, namedArg.cName) != 0))
+ {
+ continue;
+ }
+
+ // If enum, match enum name.
+ if (pNamedParam->type.tag == SERIALIZATION_TYPE_ENUM ||
+ (pNamedParam->type.tag == SERIALIZATION_TYPE_SZARRAY && pNamedParam->type.arrayType == SERIALIZATION_TYPE_ENUM ))
+ {
+ if (pNamedParam->type.cEnumName > namedArg.type.cEnumName)
+ continue; // name cannot possibly match
+
+ if (strncmp(pNamedParam->type.szEnumName, namedArg.type.szEnumName, pNamedParam->type.cEnumName) != 0 ||
+ (pNamedParam->type.cEnumName < namedArg.type.cEnumName &&
+ namedArg.type.szEnumName[pNamedParam->type.cEnumName] != ','))
+ continue;
+
+ // TODO: For now assume the property\field array size is correct - later we should verify this
+ namedArg.type.enumType = pNamedParam->type.enumType;
+ }
+
+ // Found a match.
+ break;
+ }
+
+ // Better have found an argument.
+ if (ixParam == cNamedParams)
+ {
+ MAKE_WIDEPTR_FROMUTF8N(pWideStr, namedArg.szName, namedArg.cName)
+ IfFailGo(PostError(META_E_CA_UNKNOWN_ARGUMENT, wcslen(pWideStr), pWideStr));
+ }
+
+ // Argument had better not have been seen already.
+ if (pNamedParams[ixParam].val.type.tag != SERIALIZATION_TYPE_UNDEFINED)
+ {
+ MAKE_WIDEPTR_FROMUTF8N(pWideStr, namedArg.szName, namedArg.cName)
+ IfFailGo(PostError(META_E_CA_REPEATED_ARG, wcslen(pWideStr), pWideStr));
+ }
+
+ IfFailGo(ParseKnownCaValue(ca, &pNamedParams[ixParam].val, &namedArg.type));
+ }
+
+ErrExit:
+ return hr;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Helper to parse the ctor argument list.
+// This function is used by code:RegMeta::DefineCustomAttribute for IMetaDataEmit2 and
+// should not have any VM dependency.
+//
+
+HRESULT ParseKnownCaArgs(
+ CustomAttributeParser &ca, // The Custom Attribute blob.
+ CaArg* pArgs, // Array of argument descriptors.
+ ULONG cArgs) // Count of argument descriptors.
+{
+ WRAPPER_NO_CONTRACT;
+
+ HRESULT hr = S_OK; // A result.
+ ULONG ix; // Loop control.
+
+ // If there is a blob, check the prolog.
+ if (FAILED(ca.ValidateProlog()))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+
+ // For each expected arg...
+ for (ix=0; ix<cArgs; ++ix)
+ {
+ CaArg* pArg = &pArgs[ix];
+ IfFailGo(ParseKnownCaValue(ca, &pArg->val, &pArg->type));
+ }
+
+ErrExit:
+ return hr;
+} // ParseKnownCaArgs
+
+//*****************************************************************************
+// Create a CustomAttribute record from a blob with the specified parent.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineCustomAttribute(
+ mdToken tkOwner, // [IN] The object to put the value on.
+ mdToken tkCtor, // [IN] Constructor of the CustomAttribute type (MemberRef/MethodDef).
+ void const *pCustomAttribute, // [IN] Custom Attribute data.
+ ULONG cbCustomAttribute, // [IN] Size of custom Attribute data.
+ mdCustomAttribute *pcv) // [OUT, OPTIONAL] Put custom Attribute token here.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CustomAttributeRec *pRecord = NULL; // New custom Attribute record.
+ RID iRecord; // New custom Attribute RID.
+ CMiniMdRW *pMiniMd = &m_pStgdb->m_MiniMd;
+ int ixKnown; // Index of known custom attribute.
+
+ LOG((LOGMD, "RegMeta::DefineCustomAttribute(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", tkOwner, tkCtor,
+ pCustomAttribute, cbCustomAttribute, pcv));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ _ASSERTE(TypeFromToken(tkCtor) == mdtMethodDef || TypeFromToken(tkCtor) == mdtMemberRef);
+
+ if (TypeFromToken(tkOwner) == mdtCustomAttribute)
+ IfFailGo(E_INVALIDARG);
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ if (IsNilToken(tkOwner) ||
+ IsNilToken(tkCtor) ||
+ (TypeFromToken(tkCtor) != mdtMethodDef &&
+ TypeFromToken(tkCtor) != mdtMemberRef) )
+ {
+ IfFailGo(E_INVALIDARG);
+ }
+
+ // See if this is a known custom attribute.
+ IfFailGo(_IsKnownCustomAttribute(tkCtor, &ixKnown));
+ if (ixKnown != 0)
+ {
+ int bKeep = false;
+ hr = _HandleKnownCustomAttribute(tkOwner, pCustomAttribute, cbCustomAttribute, ixKnown, &bKeep);
+ if (pcv)
+ *pcv = mdCustomAttributeNil;
+ IfFailGo(hr);
+ if (!bKeep)
+ goto ErrExit;
+ }
+
+ if (((TypeFromToken(tkOwner) == mdtTypeDef) || (TypeFromToken(tkOwner) == mdtMethodDef)) &&
+ (TypeFromToken(tkCtor) == mdtMethodDef || TypeFromToken(tkCtor) == mdtMemberRef))
+ {
+ CHAR szBuffer[MAX_CLASS_NAME + 1];
+ LPSTR szName = szBuffer;
+ LPCSTR szNamespace;
+ LPCSTR szClass;
+ TypeRefRec *pTypeRefRec = NULL;
+ TypeDefRec *pTypeDefRec = NULL;
+ mdToken tkParent;
+
+ if (TypeFromToken(tkCtor) == mdtMemberRef)
+ {
+ MemberRefRec *pMemberRefRec;
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkCtor), &pMemberRefRec));
+ tkParent = pMiniMd->getClassOfMemberRef(pMemberRefRec);
+ if (TypeFromToken(tkParent) == mdtTypeRef)
+ {
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkParent), &pTypeRefRec));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szClass));
+ ns::MakePath(szName, sizeof(szBuffer) - 1, szNamespace, szClass);
+ }
+ else if (TypeFromToken(tkParent) == mdtTypeDef)
+ {
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec));
+ }
+ }
+ else
+ {
+ IfFailGo(pMiniMd->FindParentOfMethodHelper(tkCtor, &tkParent));
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec));
+ }
+
+ if (pTypeDefRec != NULL)
+ {
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szClass));
+ ns::MakePath(szName, sizeof(szBuffer) - 1, szNamespace, szClass);
+ }
+
+ if ((TypeFromToken(tkOwner) == mdtMethodDef) && strcmp(szName, COR_REQUIRES_SECOBJ_ATTRIBUTE_ANSI) == 0)
+ {
+ // Turn REQ_SO attribute into flag bit on the methoddef.
+ MethodRec *pMethod;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkOwner), &pMethod));
+ pMethod->AddFlags(mdRequireSecObject);
+ IfFailGo(UpdateENCLog(tkOwner));
+ goto ErrExit;
+ }
+ else if (strcmp(szName, COR_SUPPRESS_UNMANAGED_CODE_CHECK_ATTRIBUTE_ANSI) == 0)
+ {
+ // If we spot an unmanged code check suppression attribute, turn on
+ // the bit that says there's declarative security on the
+ // class/method, but still write the attribute itself.
+ if (TypeFromToken(tkOwner) == mdtTypeDef)
+ {
+ IfFailGo(_TurnInternalFlagsOn(tkOwner, tdHasSecurity));
+ }
+ else if (TypeFromToken(tkOwner) == mdtMethodDef)
+ {
+ IfFailGo(_TurnInternalFlagsOn(tkOwner, mdHasSecurity));
+ }
+ IfFailGo(UpdateENCLog(tkOwner));
+ }
+ }
+
+ IfFailGo(m_pStgdb->m_MiniMd.AddCustomAttributeRecord(&pRecord, &iRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Type, pRecord, tkCtor));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Parent, pRecord, tkOwner));
+
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_CustomAttribute, CustomAttributeRec::COL_Value, pRecord, pCustomAttribute, cbCustomAttribute));
+
+ // Give token back to caller.
+ if (pcv != NULL)
+ *pcv = TokenFromRid(iRecord, mdtCustomAttribute);
+
+ IfFailGo(m_pStgdb->m_MiniMd.AddCustomAttributesToHash(TokenFromRid(iRecord, mdtCustomAttribute)) );
+
+ IfFailGo(UpdateENCLog(TokenFromRid(iRecord, mdtCustomAttribute)));
+
+ErrExit:
+ STOP_MD_PERF(DefineCustomAttribute);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineCustomAttribute
+
+//*****************************************************************************
+// Replace the blob of an existing custom attribute.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetCustomAttributeValue( // Return code.
+ mdCustomAttribute tkAttr, // [IN] The object to be Attributed.
+ void const *pCustomAttribute, // [IN] Custom Attribute data.
+ ULONG cbCustomAttribute) // [IN] Size of custom Attribute data.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CustomAttributeRec *pRecord = NULL;// Existing custom Attribute record.
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(tkAttr) == mdtCustomAttribute && !InvalidRid(tkAttr));
+
+ // Retrieve and update the custom value.
+ IfFailGo(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(tkAttr), &pRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_CustomAttribute, CustomAttributeRec::COL_Value, pRecord, pCustomAttribute, cbCustomAttribute));
+
+ IfFailGo(UpdateENCLog(tkAttr));
+ErrExit:
+
+ STOP_MD_PERF(SetCustomAttributeValue);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetCustomAttributeValue
+
+//*****************************************************************************
+//*****************************************************************************
+HRESULT RegMeta::_IsKnownCustomAttribute( // S_OK, S_FALSE, or error.
+ mdToken tkCtor, // [IN] Token of custom attribute's constructor.
+ int *pca) // [OUT] Put value from KnownCustAttr enum here.
+{
+ HRESULT hr = S_OK; // A result.
+ CCustAttrHashKey sLookup; // For looking up a custom attribute.
+ CCustAttrHashKey *pFound; // Result of a lookup.
+ LPCSTR szNamespace = ""; // Namespace of custom attribute type.
+ LPCSTR szName = ""; // Name of custom attribute type.
+ TypeDefRec *pTypeDefRec = NULL; // Parent record, when a TypeDef.
+ TypeRefRec *pTypeRefRec = NULL; // Parent record, when a TypeRef.
+ CMiniMdRW *pMiniMd = &m_pStgdb->m_MiniMd;
+ int ixCa; // Index of Known CustomAttribute, or 0.
+ int i; // Loop control.
+ mdToken tkParent;
+
+ *pca = 0;
+
+ // Only for Custom Attributes.
+ _ASSERTE(TypeFromToken(tkCtor) != mdtTypeRef && TypeFromToken(tkCtor) != mdtTypeDef);
+
+ sLookup.tkType = tkCtor;
+
+ // See if this custom attribute type has been seen before.
+ if ((pFound = m_caHash.Find(&sLookup)))
+ { // Yes, already seen.
+ *pca = pFound->ca;
+ hr = (pFound->ca == CA_UNKNOWN) ? S_FALSE : S_OK;
+ goto ErrExit;
+ }
+
+ // Hasn't been seen before. See if it is well known.
+
+ // Get the CA name.
+ if (TypeFromToken(tkCtor) == mdtMemberRef)
+ {
+ MemberRefRec *pMember;
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkCtor), &pMember));
+ tkParent = pMiniMd->getClassOfMemberRef(pMember);
+ if (TypeFromToken(tkParent) == mdtTypeRef)
+ {
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkParent), &pTypeRefRec));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szName));
+ }
+ else if (TypeFromToken(tkParent) == mdtTypeDef)
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec));
+ }
+ else
+ {
+ IfFailGo(pMiniMd->FindParentOfMethodHelper(tkCtor, &tkParent));
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec));
+ }
+
+ if (pTypeDefRec)
+ {
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szName));
+ }
+
+ // Search in list of Known CAs.
+ for (ixCa=0, i=1; i<CA_COUNT; ++i)
+ {
+ if (strcmp(szName, rKnownCaProps[i]->szName) != 0)
+ continue;
+ if (strcmp(szNamespace, rKnownCaProps[i]->szNamespace) == 0)
+ {
+ // Some custom attributes have overloaded ctors. For those,
+ // see if this is the matching overload.
+ if (rKnownCaProps[i]->bMatchBySig)
+ {
+ // Name matches. Does the signature?
+ PCCOR_SIGNATURE pSig = NULL; // Signature of a method.
+ ULONG cbSig = 0; // Size of the signature.
+ ULONG cParams; // Count of signature parameters.
+ ULONG cb; // Size of an element
+ ULONG elem; // Signature element.
+ ULONG j; // Loop control.
+
+ // Get the signature.
+ if (TypeFromToken(tkCtor) == mdtMemberRef)
+ {
+ MemberRefRec *pMember;
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkCtor), &pMember));
+ IfFailGo(pMiniMd->getSignatureOfMemberRef(pMember, &pSig, &cbSig));
+ }
+ else
+ {
+ MethodRec *pMethod;
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkCtor), &pMethod));
+ IfFailGo(pMiniMd->getSignatureOfMethod(pMethod, &pSig, &cbSig));
+ }
+
+ // Skip calling convention.
+ cb = CorSigUncompressData(pSig, &elem);
+ pSig += cb;
+ cbSig -= cb;
+ // Count of params.
+ cb = CorSigUncompressData(pSig, &cParams);
+ pSig += cb;
+ cbSig -= cb;
+
+ // If param count mismatch, not the right CA.
+ if (cParams != rKnownCaProps[i]->cArgs)
+ continue;
+
+ // Count is fine, check each param. Skip return type (better be void).
+ cb = CorSigUncompressData(pSig, &elem);
+ _ASSERTE(elem == ELEMENT_TYPE_VOID);
+ pSig += cb;
+ cbSig -= cb;
+ for (j=0; j<cParams; ++j)
+ {
+ // Get next element from method signature.
+ cb = CorSigUncompressData(pSig, &elem);
+ pSig += cb;
+ cbSig -= cb;
+ if (rKnownCaProps[i]->pArgs[j].type.tag != (CorSerializationType) elem)
+ break;
+ }
+
+ // All matched?
+ if (j != cParams)
+ continue;
+ }
+ // All matched.
+ ixCa = i;
+ break;
+ }
+ }
+
+ // Add to hash.
+ sLookup.ca = ixCa;
+ pFound = m_caHash.Add(&sLookup);
+ IfNullGo(pFound);
+ *pFound = sLookup;
+ *pca = ixCa;
+
+ErrExit:
+ return hr;
+} // RegMeta::_IsKnownCustomAttribute
+
+//*****************************************************************************
+//*****************************************************************************
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+HRESULT RegMeta::_HandleKnownCustomAttribute( // S_OK or error.
+ mdToken tkObj, // [IN] Object being attributed.
+ const void *pData, // [IN] Custom Attribute data blob.
+ ULONG cbData, // [IN] Count of bytes in the data.
+ int ixCa, // [IN] Value from KnownCustAttr enum.
+ int *bKeep) // [OUT] If true, keep the CA after processing.
+{
+ HRESULT hr = S_OK; // A result.
+ ULONG ixTbl; // Index of table with object.
+ void *pRow; // Whatever sort of record it is.
+ CMiniMdRW *pMiniMd = &m_pStgdb->m_MiniMd;
+ mdToken tkObjType; // Type of the object.
+ ULONG ix; // Loop control.
+ KnownCaProp const *props=rKnownCaProps[ixCa]; // For convenience.
+ CustomAttributeParser ca(pData, cbData);
+ CQuickArray<CaArg> qArgs; // Un-named arguments.
+ CQuickArray<CaNamedArg> qNamedArgs; // Named arguments.
+ CQuickArray<BYTE> qNativeType;// Native type string.
+
+ _ASSERTE(ixCa > 0 && ixCa < CA_COUNT);
+ *bKeep = props->bKeepCa || m_bKeepKnownCa;
+
+ // Validate that target is valid for attribute.
+ tkObjType = TypeFromToken(tkObj);
+ for (ix=0; props->rTypes[ix] != (mdToken) -1; ++ix)
+ {
+ if (props->rTypes[ix] == tkObjType)
+ break;
+ }
+ // Was the type found in list of valid targets?
+ if (props->rTypes[ix] == (mdToken) -1)
+ { // No, error.
+ IfFailGo(PostError(META_E_CA_INVALID_TARGET));
+ }
+ // Get the row.
+ ixTbl = pMiniMd->GetTblForToken(tkObj);
+ _ASSERTE(ixTbl >= 0 && ixTbl <= pMiniMd->GetCountTables());
+ IfFailGo(pMiniMd->getRow(ixTbl, RidFromToken(tkObj), &pRow));
+
+ // If this custom attribute expects any args...
+ if (props->cArgs || props->cNamedArgs)
+ { // Initialize array ctor arg descriptors.
+ IfFailGo(qArgs.ReSizeNoThrow(props->cArgs));
+ for (ix=0; ix<props->cArgs; ++ix)
+ qArgs[ix] = props->pArgs[ix];
+ // Parse any ctor args (unnamed, fixed args).
+ IfFailGo(ParseKnownCaArgs(ca, qArgs.Ptr(), props->cArgs));
+
+ // If this custom attribute accepts named args, parse them, or if there
+ // are unused bytes, parse them.
+ if (props->cNamedArgs || ca.BytesLeft() > 0)
+ { // Initialize array of named arg descriptors.
+ IfFailGo(qNamedArgs.ReSizeNoThrow(props->cNamedArgs));
+ for (ix=0; ix<props->cNamedArgs; ++ix)
+ qNamedArgs[ix] = props->pNamedArgs[ix];
+ // Parse named args.
+ IfFailGo(ParseKnownCaNamedArgs(ca, qNamedArgs.Ptr(), props->cNamedArgs));
+ }
+ }
+
+ switch (ixCa)
+ {
+ case CA_DllImportAttribute:
+ {
+ // Validate parameters.
+ if (qArgs[0].val.str.cbStr == 0 || qArgs[0].val.str.pStr == NULL)
+ {
+ // no name for DllImport.
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ }
+
+ // Retrieve / create a ModuleRef on the dll name.
+ mdModuleRef mrModule;
+ CQuickArray<char> qDllName;
+ IfFailGo(qDllName.ReSizeNoThrow(qArgs[0].val.str.cbStr+1));
+ memcpy(qDllName.Ptr(), qArgs[0].val.str.pStr, qArgs[0].val.str.cbStr);
+ qDllName[qArgs[0].val.str.cbStr] = '\0';
+ hr = ImportHelper::FindModuleRef(pMiniMd, qDllName.Ptr(), &mrModule);
+ if (hr != S_OK)
+ {
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzDllName, qDllName.Ptr());
+ if (wzDllName == NULL)
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ IfFailGo(_DefineModuleRef(wzDllName, &mrModule));
+ }
+
+ // Create a p/invoke map entry.
+ ULONG dwFlags; dwFlags=0;
+ // Was a calling convention set?
+ if (qNamedArgs[DI_CallingConvention].val.type.tag)
+ { // Calling convention makes no sense on a field.
+ if (TypeFromToken(tkObj) == mdtFieldDef)
+ IfFailGo(PostError(META_E_CA_INVALID_ARG_FOR_TYPE, qNamedArgs[DI_CallingConvention].szName));
+ // Turn off all callconv bits, then turn on specified value.
+ dwFlags &= ~pmCallConvMask;
+ switch (qNamedArgs[DI_CallingConvention].val.u4)
+ { //<TODO>@future: sigh. keep in sync with System.Runtime.InteropServices.CallingConvention</TODO>
+ case 0: break; // 0 means "do nothing"
+ case 1: dwFlags |= pmCallConvWinapi; break;
+ case 2: dwFlags |= pmCallConvCdecl; break;
+ case 3: dwFlags |= pmCallConvStdcall; break;
+ case 4: dwFlags |= pmCallConvThiscall; break;
+ case 5: dwFlags |= pmCallConvFastcall; break;
+ default:
+ _ASSERTE(!"Flags are out of sync! ");
+ break;
+ }
+ }
+ else
+ if (TypeFromToken(tkObj) == mdtMethodDef)
+ { // No calling convention specified for a method. Default to pmCallConvWinApi.
+ dwFlags = (dwFlags & ~pmCallConvMask) | pmCallConvWinapi;
+ }
+
+ // Charset
+ if (qNamedArgs[DI_CharSet].val.type.tag)
+ { // Turn of all charset bits, then turn on specified bits.
+ dwFlags &= ~pmCharSetMask;
+ switch (qNamedArgs[DI_CharSet].val.u4)
+ { //<TODO>@future: keep in sync with System.Runtime.InteropServices.CharSet</TODO>
+ case 0: break; // 0 means "do nothing"
+ case 1: dwFlags |= pmCharSetNotSpec; break;
+ case 2: dwFlags |= pmCharSetAnsi; break;
+ case 3: dwFlags |= pmCharSetUnicode; break;
+ case 4: dwFlags |= pmCharSetAuto; break;
+ default:
+ _ASSERTE(!"Flags are out of sync! ");
+ break;
+ }
+ }
+ if (qNamedArgs[DI_ExactSpelling].val.u1)
+ dwFlags |= pmNoMangle;
+ if (qNamedArgs[DI_SetLastError].val.type.tag)
+ { // SetLastError makes no sense on a field.
+ if (TypeFromToken(tkObj) == mdtFieldDef)
+ IfFailGo(PostError(META_E_CA_INVALID_ARG_FOR_TYPE, qNamedArgs[DI_SetLastError].szName));
+ if (qNamedArgs[DI_SetLastError].val.u1)
+ dwFlags |= pmSupportsLastError;
+ }
+
+ // If an entrypoint name was specified, use it, otherrwise grab the name from the member.
+ LPCWSTR wzEntry;
+ if (qNamedArgs[DI_EntryPoint].val.type.tag)
+ {
+ if (qNamedArgs[DI_EntryPoint].val.str.cbStr > 0)
+ {
+ MAKE_WIDEPTR_FROMUTF8N_NOTHROW(wzEntryName, qNamedArgs[DI_EntryPoint].val.str.pStr, qNamedArgs[DI_EntryPoint].val.str.cbStr);
+ if (wzEntryName == NULL)
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ wzEntry = wzEntryName;
+ }
+ else
+ wzEntry = W("");
+ }
+ else
+ {
+ LPCUTF8 szMember = NULL;
+ if (TypeFromToken(tkObj) == mdtMethodDef)
+ {
+ IfFailGo(pMiniMd->getNameOfMethod(reinterpret_cast<MethodRec*>(pRow), &szMember));
+ }
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzMemberName, szMember);
+ if (wzMemberName == NULL)
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ wzEntry = wzMemberName;
+ }
+
+ // Set the miPreserveSig bit based on the value of the preserve sig flag.
+ if (qNamedArgs[DI_PreserveSig].val.type.tag && !qNamedArgs[DI_PreserveSig].val.u1)
+ reinterpret_cast<MethodRec*>(pRow)->RemoveImplFlags(miPreserveSig);
+ else
+ reinterpret_cast<MethodRec*>(pRow)->AddImplFlags(miPreserveSig);
+
+ if (qNamedArgs[DI_BestFitMapping].val.type.tag)
+ {
+ if (qNamedArgs[DI_BestFitMapping].val.u1)
+ dwFlags |= pmBestFitEnabled;
+ else
+ dwFlags |= pmBestFitDisabled;
+ }
+
+ if (qNamedArgs[DI_ThrowOnUnmappableChar].val.type.tag)
+ {
+ if (qNamedArgs[DI_ThrowOnUnmappableChar].val.u1)
+ dwFlags |= pmThrowOnUnmappableCharEnabled;
+ else
+ dwFlags |= pmThrowOnUnmappableCharDisabled;
+ }
+
+ // Finally, create the PInvokeMap entry.,
+ IfFailGo(_DefinePinvokeMap(tkObj, dwFlags, wzEntry, mrModule));
+ goto ErrExit;
+ }
+ break;
+
+ case CA_GuidAttribute:
+ { // Just verify the attribute. It still gets stored as a real custom attribute.
+ // format is "{01234567-0123-0123-0123-001122334455}"
+ GUID guid;
+ WCHAR wzGuid[40];
+ int cch = qArgs[0].val.str.cbStr;
+
+ // Guid should be 36 characters; need to add curlies.
+ if (cch == 36)
+ {
+ WszMultiByteToWideChar(CP_UTF8, 0, qArgs[0].val.str.pStr,cch, wzGuid+1,39);
+ wzGuid[0] = '{';
+ wzGuid[37] = '}';
+ wzGuid[38] = 0;
+ hr = IIDFromString(wzGuid, &guid);
+ }
+ else
+ hr = META_E_CA_INVALID_UUID;
+ if (hr != S_OK)
+ IfFailGo(PostError(META_E_CA_INVALID_UUID));
+ goto ErrExit;
+ }
+ break;
+
+ case CA_ComImportAttribute:
+ reinterpret_cast<TypeDefRec*>(pRow)->AddFlags(tdImport);
+ break;
+
+ case CA_InterfaceTypeAttribute:
+ {
+ // Verify the attribute.
+ if (qArgs[0].val.u2 >= ifLast)
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ }
+ break;
+
+ case CA_ClassInterfaceAttribute:
+ {
+ // Verify the attribute.
+ if (qArgs[0].val.u2 >= clsIfLast)
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ }
+ break;
+
+ case CA_SpecialNameAttribute:
+
+ switch (TypeFromToken(tkObj))
+ {
+ case mdtTypeDef:
+ reinterpret_cast<TypeDefRec*>(pRow)->AddFlags(tdSpecialName);
+ break;
+
+ case mdtMethodDef:
+ reinterpret_cast<MethodRec*>(pRow)->AddFlags(mdSpecialName);
+ break;
+
+ case mdtFieldDef:
+ reinterpret_cast<FieldRec*>(pRow)->AddFlags(fdSpecialName);
+ break;
+
+ case mdtProperty:
+ reinterpret_cast<PropertyRec*>(pRow)->AddPropFlags(prSpecialName);
+ break;
+
+ case mdtEvent:
+ reinterpret_cast<EventRec*>(pRow)->AddEventFlags(evSpecialName);
+ break;
+
+ default:
+ _ASSERTE(!"Unfamilar type for SpecialName custom attribute");
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ }
+
+ break;
+ case CA_SerializableAttribute:
+ reinterpret_cast<TypeDefRec*>(pRow)->AddFlags(tdSerializable);
+ break;
+
+ case CA_NonSerializedAttribute:
+ reinterpret_cast<FieldRec*>(pRow)->AddFlags(fdNotSerialized);
+ break;
+
+ case CA_InAttribute:
+ reinterpret_cast<ParamRec*>(pRow)->AddFlags(pdIn);
+ break;
+
+ case CA_OutAttribute:
+ reinterpret_cast<ParamRec*>(pRow)->AddFlags(pdOut);
+ break;
+
+ case CA_OptionalAttribute:
+ reinterpret_cast<ParamRec*>(pRow)->AddFlags(pdOptional);
+ break;
+
+ case CA_MethodImplAttribute2:
+ // Force to wider value.
+ qArgs[0].val.u4 = (unsigned)qArgs[0].val.i2;
+ // Fall through to validation.
+ case CA_MethodImplAttribute3:
+ // Validate bits.
+ if (qArgs[0].val.u4 & ~(miUserMask))
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ reinterpret_cast<MethodRec*>(pRow)->AddImplFlags(qArgs[0].val.u4);
+ if (!qNamedArgs[MI_CodeType].val.type.tag)
+ break;
+ // fall through to set the code type.
+ case CA_MethodImplAttribute1:
+ {
+ USHORT usFlags = reinterpret_cast<MethodRec*>(pRow)->GetImplFlags();
+ if (qNamedArgs[MI_CodeType].val.i4 & ~(miCodeTypeMask))
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ // Mask out old value, put in new one.
+ usFlags = (usFlags & ~miCodeTypeMask) | qNamedArgs[MI_CodeType].val.i4;
+ reinterpret_cast<MethodRec*>(pRow)->SetImplFlags(usFlags);
+ }
+ break;
+
+ case CA_MarshalAsAttribute1:
+ // Force the U2 to a wider U4 value explicitly.
+ qArgs[0].val.u4 = qArgs[0].val.u2;
+ // Fall through to handle the CA.
+ case CA_MarshalAsAttribute2:
+ IfFailGo(_HandleNativeTypeCustomAttribute(tkObj, qArgs.Ptr(), qNamedArgs.Ptr(), qNativeType));
+ break;
+
+ case CA_PreserveSigAttribute:
+ reinterpret_cast<MethodRec*>(pRow)->AddImplFlags(miPreserveSig);
+ break;
+
+ case CA_StructLayoutAttribute1:
+ {
+ // Convert the I2 to a U2, then wide to an I4, then fall through.
+ qArgs[0].val.i4 = static_cast<int>(static_cast<USHORT>(qArgs[0].val.i2));
+ }
+ case CA_StructLayoutAttribute2:
+ {
+ // Get a copy of the flags to work with.
+ ULONG dwFlags;
+ dwFlags = reinterpret_cast<TypeDefRec*>(pRow)->GetFlags();
+ // Class layout. Keep in sync with LayoutKind.
+ switch (qArgs[0].val.i4)
+ {
+ case 0: // tdSequentialLayout:
+ dwFlags = (dwFlags & ~tdLayoutMask) | tdSequentialLayout;
+ break;
+ case 2: // tdExplicitLayout:
+ dwFlags = (dwFlags & ~tdLayoutMask) | tdExplicitLayout;
+ break;
+ case 3: // tdAutoLayout:
+ dwFlags = (dwFlags & ~tdLayoutMask) | tdAutoLayout;
+ break;
+ default:
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ break;
+ }
+
+ // Class packing and size.
+ ULONG ulSize, ulPack;
+ ulPack = ulSize = ULONG_MAX;
+ if (qNamedArgs[SL_Pack].val.type.tag)
+ { // Only 1,2,4,8,16,32,64,128 are legal values.
+ ulPack = qNamedArgs[SL_Pack].val.u4;
+ if ((ulPack > 128) ||
+ (ulPack & (ulPack-1)))
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ }
+ if (qNamedArgs[SL_Size].val.type.tag)
+ {
+ if (qNamedArgs[SL_Size].val.u4 > INT_MAX)
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ ulSize = qNamedArgs[SL_Size].val.u4;
+ }
+ if (ulPack!=ULONG_MAX || ulSize!=ULONG_MAX)
+ IfFailGo(_SetClassLayout(tkObj, ulPack, ulSize));
+
+ // Class character set.
+ if (qNamedArgs[SL_CharSet].val.type.tag)
+ {
+ switch (qNamedArgs[SL_CharSet].val.u4)
+ {
+ //case 1: // Not specified.
+ // IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ // break;
+ case 2: // ANSI
+ dwFlags = (dwFlags & ~tdStringFormatMask) | tdAnsiClass;
+ break;
+ case 3: // Unicode
+ dwFlags = (dwFlags & ~tdStringFormatMask) | tdUnicodeClass;
+ break;
+ case 4: // Auto
+ dwFlags = (dwFlags & ~tdStringFormatMask) | tdAutoClass;
+ break;
+ default:
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ break;
+ }
+ }
+
+ // Persist possibly-changed value of flags.
+ reinterpret_cast<TypeDefRec*>(pRow)->SetFlags(dwFlags);
+ }
+ break;
+
+ case CA_FieldOffsetAttribute:
+ if (qArgs[0].val.u4 > INT_MAX)
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ IfFailGo(_SetFieldOffset(tkObj, qArgs[0].val.u4));
+ break;
+
+ case CA_TypeLibVersionAttribute:
+ if ((qArgs[0].val.i4 < 0) || (qArgs[1].val.i4 < 0))
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ break;
+
+ case CA_ComCompatibleVersionAttribute:
+ if ( (qArgs[0].val.i4 < 0) || (qArgs[1].val.i4 < 0) || (qArgs[2].val.i4 < 0) || (qArgs[3].val.i4 < 0) )
+ IfFailGo(PostError(META_E_CA_INVALID_VALUE));
+ break;
+
+ case CA_AllowPartiallyTrustedCallersAttribute:
+ break;
+
+ case CA_WindowsRuntimeImportAttribute:
+ reinterpret_cast<TypeDefRec*>(pRow)->AddFlags(tdWindowsRuntime);
+ break;
+
+ default:
+ _ASSERTE(!"Unexpected custom attribute type");
+ // Turn into ordinary custom attribute.
+ *bKeep = true;
+ hr = S_OK;
+ goto ErrExit;
+ break;
+ }
+
+ IfFailGo(UpdateENCLog(tkObj));
+
+ErrExit:
+ return hr;
+} // RegMeta::_HandleKnownCustomAttribute
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+//*****************************************************************************
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+HRESULT RegMeta::_HandleNativeTypeCustomAttribute(// S_OK or error.
+ mdToken tkObj, // The token this CA is applied on.
+ CaArg *pArgs, // Pointer to args.
+ CaNamedArg *pNamedArgs, // Pointer to named args.
+ CQuickArray<BYTE> &qNativeType) // Native type is built here.
+{
+ HRESULT hr = S_OK; // A result.
+ int cch = 0; // Size of a string argument.
+ ULONG cb; // Count of some character operation.
+ ULONG cbNative; // Size of native type string.
+ ULONG cbMax; // Max size of native type string.
+ BYTE *pbNative; // Pointer into native type buffer.
+ mdToken tkObjType; // The type of the token.
+ mdToken tkSetter; // Token for Property setter.
+ mdToken tkGetter; // Token for property getter.
+ mdParamDef tkParam; // Parameter of getter/setter.
+ ULONG cParams; // Count of params for getter/setter.
+ HCORENUM phEnum = 0; // Enumerator for params.
+ ULONG ulSeq; // Sequence of a param.
+
+ // Retrieve the type of the token.
+ tkObjType = TypeFromToken(tkObj);
+
+ // Compute maximum size of the native type.
+ if (pArgs[0].val.i4 == NATIVE_TYPE_CUSTOMMARSHALER)
+ { // N_T_* + 3 string lengths
+ cbMax = sizeof(ULONG) * 4;
+ // Marshal type - name of the type
+ cbMax += pNamedArgs[M_MarshalType].val.str.cbStr;
+ // Marshal type - type of the custom marshaler
+ cbMax += pNamedArgs[M_MarshalTypeRef].val.str.cbStr;
+ // String cookie.
+ cbMax += pNamedArgs[M_MarshalCookie].val.str.cbStr;
+ }
+ else if (pArgs[0].val.i4 == NATIVE_TYPE_SAFEARRAY)
+ { // N_T_* + safe array sub-type + string length.
+ cbMax = sizeof(ULONG) * 3;
+ // Safe array record sub type.
+ cbMax += pNamedArgs[M_SafeArrayUserDefinedSubType].val.str.cbStr;
+ }
+ else
+ { // N_T_* + sub-type + size + additive + NativeTypeArrayFlags.
+ cbMax = sizeof(ULONG) * 4 + sizeof(UINT16);
+ }
+
+ // IidParameterIndex.
+ cbMax += sizeof(DWORD);
+
+ // Extra space to prevent buffer overrun.
+ cbMax += 8;
+
+ // Size the array.
+ IfFailGo(qNativeType.ReSizeNoThrow(cbMax));
+ pbNative = qNativeType.Ptr();
+ cbNative = 0;
+
+ //<TODO>@FUTURE: check for valid combinations of args.</TODO>
+
+ // Put in the NativeType.
+ cb = CorSigCompressData(pArgs[0].val.i4, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Put in additional information, depending on native type.
+ switch (pArgs[0].val.i4)
+ {
+ case NATIVE_TYPE_INTF:
+ case NATIVE_TYPE_IUNKNOWN:
+ case NATIVE_TYPE_IDISPATCH:
+ // Validate that the IidParameterIndex field is valid if set.
+ if (pNamedArgs[M_IidParameterIndex].val.type.tag)
+ {
+ int iidparam = pNamedArgs[M_IidParameterIndex].val.i4;
+ if (iidparam < 0)
+ IfFailGo(PostError(META_E_CA_NEGATIVE_PARAMINDEX));
+
+ cb = CorSigCompressData(pNamedArgs[M_IidParameterIndex].val.i4, pbNative);
+ if (cb == ((ULONG)(-1)))
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ }
+ break;
+
+
+ case NATIVE_TYPE_FIXEDARRAY:
+ // Validate that only fields valid for NATIVE_TYPE_FIXEDARRAY are set.
+ if (pNamedArgs[M_SafeArraySubType].val.type.tag)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ if (pNamedArgs[M_SizeParamIndex].val.type.tag)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // This native type is only applicable on fields.
+ if (tkObjType != mdtFieldDef)
+ IfFailGo(PostError(META_E_CA_NT_FIELDONLY));
+
+ if (pNamedArgs[M_SizeConst].val.type.tag)
+ {
+ // Make sure the size is not negative.
+ if (pNamedArgs[M_SizeConst].val.i4 < 0)
+ IfFailGo(PostError(META_E_CA_NEGATIVE_CONSTSIZE));
+
+ cb = CorSigCompressData(pNamedArgs[M_SizeConst].val.i4, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_NEGATIVE_CONSTSIZE));
+ }
+
+ }
+ else
+ {
+ cb = CorSigCompressData(1, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Is there a sub type?
+ if (pNamedArgs[M_ArraySubType].val.type.tag)
+ {
+ // Put in the sub type.
+ cb = CorSigCompressData(pNamedArgs[M_ArraySubType].val.i4, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ }
+ break;
+
+ case NATIVE_TYPE_FIXEDSYSSTRING:
+ // Validate that the required fields are set.
+ if (!pNamedArgs[M_SizeConst].val.type.tag)
+ IfFailGo(PostError(META_E_CA_FIXEDSTR_SIZE_REQUIRED));
+
+ // Validate that other array fields are not set.
+ if (pNamedArgs[M_ArraySubType].val.type.tag)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ if (pNamedArgs[M_SizeParamIndex].val.type.tag)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ if (pNamedArgs[M_SafeArraySubType].val.type.tag)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // This native type is only applicable on fields.
+ if (tkObjType != mdtFieldDef)
+ IfFailGo(PostError(META_E_CA_NT_FIELDONLY));
+
+ // Put in the constant value.
+ cb = CorSigCompressData(pNamedArgs[M_SizeConst].val.i4, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ break;
+
+ case NATIVE_TYPE_BYVALSTR:
+ // This native type is only applicable on parameters.
+ if (tkObjType != mdtParamDef)
+ IfFailGo(PostError(META_E_CA_INVALID_TARGET));
+ break;
+
+ case NATIVE_TYPE_SAFEARRAY:
+ // Validate that other array fields are not set.
+ if (pNamedArgs[M_ArraySubType].val.type.tag)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ if (pNamedArgs[M_SizeParamIndex].val.type.tag)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ if (pNamedArgs[M_SizeConst].val.type.tag)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Is there a safe array sub type?
+ if (pNamedArgs[M_SafeArraySubType].val.type.tag)
+ {
+ // Put in the safe array sub type.
+ cb = CorSigCompressData(pNamedArgs[M_SafeArraySubType].val.i4, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // When the SAFEARRAY contains user defined types, the type of the
+ // UDT can be specified in the SafeArrayUserDefinedSubType field.
+ if (pNamedArgs[M_SafeArrayUserDefinedSubType].val.type.tag)
+ {
+ // Validate that this is only set for valid VT's.
+ if (pNamedArgs[M_SafeArraySubType].val.i4 != VT_RECORD &&
+ pNamedArgs[M_SafeArraySubType].val.i4 != VT_DISPATCH &&
+ pNamedArgs[M_SafeArraySubType].val.i4 != VT_UNKNOWN)
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ }
+
+ // Encode the size of the string.
+ cch = pNamedArgs[M_SafeArrayUserDefinedSubType].val.str.cbStr;
+ cb = CorSigCompressData(cch, pbNative);
+ if (cb == ((ULONG)(-1)))
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ cbNative += cb;
+ pbNative += cb;
+
+ // Check that memcpy will fit and then encode the type name itself.
+ if (ovadd_gt(cbNative, cch, cbMax))
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ memcpy(pbNative, pNamedArgs[M_SafeArrayUserDefinedSubType].val.str.pStr, cch);
+ cbNative += cch;
+ pbNative += cch;
+ _ASSERTE(cbNative <= cbMax);
+ }
+ }
+ break;
+
+ case NATIVE_TYPE_ARRAY:
+ // Validate that the array sub type is not set.
+ if (pNamedArgs[M_SafeArraySubType].val.type.tag)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Is there a sub type?
+ if (pNamedArgs[M_ArraySubType].val.type.tag)
+ {
+ // Do some validation on the array sub type.
+ if (pNamedArgs[M_ArraySubType].val.i4 == NATIVE_TYPE_CUSTOMMARSHALER)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Put in the sub type.
+ cb = CorSigCompressData(pNamedArgs[M_ArraySubType].val.i4, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ }
+ else
+ {
+ // Put in the sub type.
+ cb = CorSigCompressData(NATIVE_TYPE_MAX, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ }
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Is there a parameter index?
+ if (pNamedArgs[M_SizeParamIndex].val.type.tag)
+ {
+ // Make sure the parameter index is not negative.
+ if (pNamedArgs[M_SizeParamIndex].val.i4 < 0)
+ IfFailGo(PostError(META_E_CA_NEGATIVE_PARAMINDEX));
+
+ // Yes, put it in.
+ cb = CorSigCompressData(pNamedArgs[M_SizeParamIndex].val.i2, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Is there a const?
+ if (pNamedArgs[M_SizeConst].val.type.tag)
+ {
+ // Make sure the size is not negative.
+ if (pNamedArgs[M_SizeConst].val.i4 < 0)
+ IfFailGo(PostError(META_E_CA_NEGATIVE_CONSTSIZE));
+
+ // Yes, put it in.
+ cb = CorSigCompressData(pNamedArgs[M_SizeConst].val.i4, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Put in the flag indicating the size param index was specified.
+ cb = CorSigCompressData((UINT16)ntaSizeParamIndexSpecified, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ }
+ }
+ else
+ {
+ // Is there a const?
+ if (pNamedArgs[M_SizeConst].val.type.tag)
+ {
+ // Put in a param index of 0.
+ cb = CorSigCompressData(0, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Put in the constant value.
+ cb = CorSigCompressData(pNamedArgs[M_SizeConst].val.i4, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Set the flags field to 0 to indicate the size param index was not specified.
+ cb = CorSigCompressData((UINT16)0, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ }
+ }
+ break;
+
+ case NATIVE_TYPE_CUSTOMMARSHALER:
+ // Validate that the marshaler type field is set.
+ if (!pNamedArgs[M_MarshalType].val.type.tag && !pNamedArgs[M_MarshalTypeRef].val.type.tag)
+ IfFailGo(PostError(META_E_CA_CUSTMARSH_TYPE_REQUIRED));
+
+ // Put in the place holder for the unmanaged typelib guid.
+ cb = CorSigCompressData(0, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Put in the place holder for the unmanaged type name.
+ cb = CorSigCompressData(0, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ if (cbNative > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+
+ // Put in the marshaler type name.
+ if (pNamedArgs[M_MarshalType].val.type.tag)
+ {
+ cch = pNamedArgs[M_MarshalType].val.str.cbStr;
+ cb = CorSigCompressData(cch, pbNative);
+ if (cb == ((ULONG)(-1)))
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ cbNative += cb;
+ pbNative += cb;
+ // Check that memcpy will fit.
+ if ((cbNative+cch) > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ memcpy(pbNative, pNamedArgs[M_MarshalType].val.str.pStr, cch);
+ cbNative += cch;
+ pbNative += cch;
+ _ASSERTE(cbNative <= cbMax);
+ }
+ else
+ {
+ cch = pNamedArgs[M_MarshalTypeRef].val.str.cbStr;
+ cb = CorSigCompressData(cch, pbNative);
+ if (cb == ((ULONG)(-1)))
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ cbNative += cb;
+ pbNative += cb;
+ // Check that memcpy will fit.
+ if ((cbNative+cch) > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ memcpy(pbNative, pNamedArgs[M_MarshalTypeRef].val.str.pStr, cch);
+ cbNative += cch;
+ pbNative += cch;
+ _ASSERTE(cbNative <= cbMax);
+ }
+
+ // Put in the cookie.
+ cch = pNamedArgs[M_MarshalCookie].val.str.cbStr;
+ cb = CorSigCompressData(cch, pbNative);
+ if (cb == ((ULONG)(-1)))
+ {
+ IfFailGo(PostError(META_E_CA_INVALID_BLOB));
+ }
+ cbNative += cb;
+ pbNative += cb;
+ // Check that memcpy will fit.
+ if ((cbNative+cch) > cbMax)
+ IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS));
+ memcpy(pbNative, pNamedArgs[M_MarshalCookie].val.str.pStr, cch);
+ cbNative += cch;
+ pbNative += cch;
+ break;
+ }
+ _ASSERTE(cbNative <= cbMax);
+
+ // Resize to actual size.
+ IfFailGo(qNativeType.ReSizeNoThrow(cbNative));
+
+ // Now apply the native type to actual token. If it is a property token,
+ // apply to the methods.
+ switch (TypeFromToken(tkObj))
+ {
+ case mdtParamDef:
+ case mdtFieldDef:
+ IfFailGo(_SetFieldMarshal(tkObj, (PCCOR_SIGNATURE)qNativeType.Ptr(), (DWORD)qNativeType.Size()));
+ break;
+
+ case mdtProperty:
+ // Get any setter/getter methods.
+ IfFailGo(GetPropertyProps(tkObj, 0,0,0,0,0,0,0,0,0,0, &tkSetter, &tkGetter, 0,0,0));
+ // For getter, put the field marshal on the return value.
+ if (!IsNilToken(tkGetter))
+ {
+ // Search for first param.
+ mdToken tk;
+ tkParam = mdParamDefNil;
+ do {
+ IfFailGo(EnumParams(&phEnum, tkGetter, &tk, 1, &cParams));
+ if (cParams > 0)
+ {
+ IfFailGo(GetParamProps(tk, 0, &ulSeq, 0,0,0,0,0,0,0));
+ if (ulSeq == 0)
+ {
+ tkParam = tk;
+ break;
+ }
+ }
+
+ } while (hr == S_OK);
+ if (!IsNilToken(tkParam))
+ IfFailGo(_SetFieldMarshal(tkParam, (PCCOR_SIGNATURE)qNativeType.Ptr(), (DWORD)qNativeType.Size()));
+ CloseEnum(phEnum);
+ phEnum = 0;
+ }
+ if (!IsNilToken(tkSetter))
+ {
+ // Determine the last param.
+ PCCOR_SIGNATURE pSig;
+ ULONG cbSig;
+ mdToken tk;
+ ULONG iSeq;
+ IfFailGo(GetMethodProps(tkSetter, 0,0,0,0,0, &pSig,&cbSig, 0,0));
+ tkParam = mdParamDefNil;
+ CorSigUncompressData(pSig+1, &iSeq);
+ // Search for last param.
+ if (iSeq != 0)
+ {
+ do {
+ IfFailGo(EnumParams(&phEnum, tkSetter, &tk, 1, &cParams));
+ if (cParams > 0)
+ {
+ IfFailGo(GetParamProps(tk, 0, &ulSeq, 0,0,0,0,0,0,0));
+ if (ulSeq == iSeq)
+ {
+ tkParam = tk;
+ break;
+ }
+ }
+ } while (hr == S_OK);
+ }
+ // If found one that is not return value
+ if (!IsNilToken(tkParam))
+ IfFailGo(_SetFieldMarshal(tkParam, (PCCOR_SIGNATURE)qNativeType.Ptr(), (DWORD)qNativeType.Size()));
+ CloseEnum(phEnum);
+ phEnum = 0;
+ }
+ break;
+
+ default:
+ _ASSERTE(!"Should not have this token type in _HandleNativeTypeCustomAttribute()");
+ break;
+ }
+
+ErrExit:
+ if (phEnum)
+ CloseEnum(phEnum);
+ return hr;
+} // RegMeta::_HandleNativeTypeCustomAttribute
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+#endif //FEATURE_METADATA_EMIT
diff --git a/src/md/compiler/custattr_import.cpp b/src/md/compiler/custattr_import.cpp
new file mode 100644
index 0000000000..7710eb3790
--- /dev/null
+++ b/src/md/compiler/custattr_import.cpp
@@ -0,0 +1,282 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+
+//
+// CustAttr_Import.cpp
+//
+// Implementation for the meta data custom attribute import code (code:IMetaDataImport).
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "metadata.h"
+#include "corerror.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+#include "mdperf.h"
+#include "posterror.h"
+#include "cahlprinternal.h"
+#include "custattr.h"
+#include "corhdr.h"
+#include <metamodelrw.h>
+
+//*****************************************************************************
+// Implementation of hash for custom attribute types.
+//*****************************************************************************
+unsigned int CCustAttrHash::Hash(const CCustAttrHashKey *pData)
+{
+ return static_cast<unsigned int>(pData->tkType);
+} // unsigned long CCustAttrHash::Hash()
+unsigned int CCustAttrHash::Compare(const CCustAttrHashKey *p1, CCustAttrHashKey *p2)
+{
+ if (p1->tkType == p2->tkType)
+ return 0;
+ return 1;
+} // unsigned long CCustAttrHash::Compare()
+CCustAttrHash::ELEMENTSTATUS CCustAttrHash::Status(CCustAttrHashKey *p)
+{
+ if (p->tkType == FREE)
+ return (FREE);
+ if (p->tkType == DELETED)
+ return (DELETED);
+ return (USED);
+} // CCustAttrHash::ELEMENTSTATUS CCustAttrHash::Status()
+void CCustAttrHash::SetStatus(CCustAttrHashKey *p, CCustAttrHash::ELEMENTSTATUS s)
+{
+ p->tkType = s;
+} // void CCustAttrHash::SetStatus()
+void* CCustAttrHash::GetKey(CCustAttrHashKey *p)
+{
+ return &p->tkType;
+} // void* CCustAttrHash::GetKey()
+
+
+//*****************************************************************************
+// Get the value of a CustomAttribute, using only TypeName for lookup.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetCustomAttributeByName( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCWSTR wzName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) // [OUT] Put size of data here.
+{
+ HRESULT hr; // A result.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LPUTF8 szName; // Name in UFT8.
+ int iLen; // A length.
+ CMiniMdRW *pMiniMd = NULL;
+
+ START_MD_PERF();
+ LOCKREAD();
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ iLen = WszWideCharToMultiByte(CP_UTF8,0, wzName,-1, NULL,0, 0,0);
+ szName = (LPUTF8)_alloca(iLen);
+ VERIFY(WszWideCharToMultiByte(CP_UTF8,0, wzName,-1, szName,iLen, 0,0));
+
+ hr = ImportHelper::GetCustomAttributeByName(pMiniMd, tkObj, szName, ppData, pcbData);
+
+ErrExit:
+
+ STOP_MD_PERF(GetCustomAttributeByName);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetCustomAttributeByName()
+
+
+//*****************************************************************************
+// Enumerate the CustomAttributes for a given token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumCustomAttributes(
+ HCORENUM *phEnum, // Pointer to the enum.
+ mdToken tk, // Token to scope the enumeration.
+ mdToken tkType, // Type to limit the enumeration.
+ mdCustomAttribute rCustomAttributes[], // Put CustomAttributes here.
+ ULONG cMax, // Max CustomAttributes to put.
+ ULONG *pcCustomAttributes) // Put # tokens returned here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ HENUMInternal *pEnum = *ppmdEnum;
+ CustomAttributeRec *pRec;
+ ULONG index;
+
+ LOG((LOGMD, "RegMeta::EnumCustomAttributes(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, tk, tkType, rCustomAttributes, cMax, pcCustomAttributes));
+ START_MD_PERF();
+ LOCKREAD();
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ CLookUpHash *pHashTable = pMiniMd->m_pLookUpHashs[TBL_CustomAttribute];
+
+ // Does caller want all custom Values?
+ if (IsNilToken(tk))
+ {
+ IfFailGo( HENUMInternal::CreateSimpleEnum(mdtCustomAttribute, 1, pMiniMd->getCountCustomAttributes()+1, &pEnum) );
+ }
+ else
+ { // Scope by some object.
+ if ( pMiniMd->IsSorted( TBL_CustomAttribute ) )
+ {
+ // Get CustomAttributes for the object.
+ IfFailGo(pMiniMd->getCustomAttributeForToken(tk, &ridEnd, &ridStart));
+
+ if (IsNilToken(tkType))
+ {
+ // Simple enumerator for object's entire list.
+ IfFailGo( HENUMInternal::CreateSimpleEnum( mdtCustomAttribute, ridStart, ridEnd, &pEnum) );
+ }
+ else
+ {
+ // Dynamic enumerator for subsetted list.
+
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtCustomAttribute, &pEnum) );
+
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(pMiniMd->GetCustomAttributeRecord(index, &pRec));
+ if (tkType == pMiniMd->getTypeOfCustomAttribute(pRec))
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtCustomAttribute) ) );
+ }
+ }
+ }
+ }
+ else
+ {
+
+ if (pHashTable)
+ {
+ // table is not sorted but hash is built
+ // We want to create dynmaic array to hold the dynamic enumerator.
+ TOKENHASHENTRY *p;
+ ULONG iHash;
+ int pos;
+ mdToken tkParentTmp;
+ mdToken tkTypeTmp;
+
+ // Hash the data.
+ iHash = pMiniMd->HashCustomAttribute(tk);
+
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtCustomAttribute, &pEnum) );
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pHashTable->FindFirst(iHash, pos);
+ p;
+ p = pHashTable->FindNext(pos))
+ {
+
+ CustomAttributeRec *pCustomAttribute;
+ IfFailGo(pMiniMd->GetCustomAttributeRecord(RidFromToken(p->tok), &pCustomAttribute));
+ tkParentTmp = pMiniMd->getParentOfCustomAttribute(pCustomAttribute);
+ tkTypeTmp = pMiniMd->getTypeOfCustomAttribute(pCustomAttribute);
+ if (tkParentTmp == tk)
+ {
+ if (IsNilToken(tkType) || tkType == tkTypeTmp)
+ {
+ // compare the blob value
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(p->tok, mdtCustomAttribute )) );
+ }
+ }
+ }
+ }
+ else
+ {
+
+ // table is not sorted and hash is not built so we have to create dynmaic array
+ // create the dynamic enumerator and loop through CA table linearly
+ //
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountCustomAttributes() + 1;
+
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtCustomAttribute, &pEnum) );
+
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(pMiniMd->GetCustomAttributeRecord(index, &pRec));
+ if ( tk == pMiniMd->getParentOfCustomAttribute(pRec) &&
+ (tkType == pMiniMd->getTypeOfCustomAttribute(pRec) || IsNilToken(tkType)))
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtCustomAttribute) ) );
+ }
+ }
+ }
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rCustomAttributes, pcCustomAttributes);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumCustomAttributes);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumCustomAttributes()
+
+
+//*****************************************************************************
+// Get information about a CustomAttribute.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetCustomAttributeProps(
+ mdCustomAttribute cv, // The attribute token
+ mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here.
+ mdToken *ptkType, // [OUT, OPTIONAL] Put TypeDef/TypeRef token here.
+ void const **ppBlob, // [OUT, OPTIONAL] Put pointer to data here.
+ ULONG *pcbSize) // [OUT, OPTIONAL] Put size of data here.
+{
+ HRESULT hr = S_OK; // A result.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd;
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(cv) == mdtCustomAttribute);
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+ CustomAttributeRec *pCustomAttributeRec; // The custom value record.
+
+ IfFailGo(pMiniMd->GetCustomAttributeRecord(RidFromToken(cv), &pCustomAttributeRec));
+
+ if (ptkObj)
+ *ptkObj = pMiniMd->getParentOfCustomAttribute(pCustomAttributeRec);
+
+ if (ptkType)
+ *ptkType = pMiniMd->getTypeOfCustomAttribute(pCustomAttributeRec);
+
+ if (ppBlob != NULL)
+ {
+ IfFailGo(pMiniMd->getValueOfCustomAttribute(pCustomAttributeRec, (const BYTE **)ppBlob, pcbSize));
+ }
+
+ErrExit:
+
+ STOP_MD_PERF(GetCustomAttributeProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetCustomAttributeProps
diff --git a/src/md/compiler/dac/.gitmirror b/src/md/compiler/dac/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/compiler/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/compiler/dac/CMakeLists.txt b/src/md/compiler/dac/CMakeLists.txt
new file mode 100644
index 0000000000..dda76e1cd9
--- /dev/null
+++ b/src/md/compiler/dac/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+include(${CLR_DIR}/dac.cmake)
+include(../../md_dac.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDCOMPILER_SOURCES)
+add_library_clr(mdcompiler_dac ${MDCOMPILER_SOURCES})
diff --git a/src/md/compiler/dac/dirs.proj b/src/md/compiler/dac/dirs.proj
new file mode 100644
index 0000000000..cf39ab9ea2
--- /dev/null
+++ b/src/md/compiler/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\mdcompiler_dac.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/compiler/dbi/.gitmirror b/src/md/compiler/dbi/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/compiler/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/compiler/dbi/CMakeLists.txt b/src/md/compiler/dbi/CMakeLists.txt
new file mode 100644
index 0000000000..b870984309
--- /dev/null
+++ b/src/md/compiler/dbi/CMakeLists.txt
@@ -0,0 +1,4 @@
+include(../../md_dbi.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDCOMPILER_SOURCES)
+add_library_clr(mdcompiler-dbi ${MDCOMPILER_SOURCES}) \ No newline at end of file
diff --git a/src/md/compiler/dbi/MDCompiler-dbi.props b/src/md/compiler/dbi/MDCompiler-dbi.props
new file mode 100644
index 0000000000..a9c469c98e
--- /dev/null
+++ b/src/md/compiler/dbi/MDCompiler-dbi.props
@@ -0,0 +1,9 @@
+<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>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\Compiler\Compiler.settings.targets" />
+
+</Project>
diff --git a/src/md/compiler/dbi/dirs.proj b/src/md/compiler/dbi/dirs.proj
new file mode 100644
index 0000000000..89a842727c
--- /dev/null
+++ b/src/md/compiler/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 Condition="'$(FeatureDbiDebugging)'=='true'" Include="HostLocal\mdcompiler-dbi.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/compiler/dirs.proj b/src/md/compiler/dirs.proj
new file mode 100644
index 0000000000..2e97d62490
--- /dev/null
+++ b/src/md/compiler/dirs.proj
@@ -0,0 +1,27 @@
+<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\mdcompiler_wks.nativeproj" />
+ <ProjectFile Include="dac\dirs.proj" />
+ <ProjectFile Include="dbi\dirs.proj" />
+ </ItemGroup>
+
+ <!--The following projects will build during PHASE 1 of the Desktop build -->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreClr)' != 'true'">
+ <ProjectFile Include="winrt-ro\mdcompiler-winrt-ro.nativeproj" />
+ <ProjectFile Include="winrt-rw\mdcompiler-winrt-rw.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/compiler/disp.cpp b/src/md/compiler/disp.cpp
new file mode 100644
index 0000000000..b091729744
--- /dev/null
+++ b/src/md/compiler/disp.cpp
@@ -0,0 +1,939 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// Disp.cpp
+//
+
+//
+// Implementation for the meta data dispenser code.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "disp.h"
+#include "regmeta.h"
+#include "mdutil.h"
+#include <corerror.h>
+#include <mdlog.h>
+#include <mdcommon.h>
+#ifdef FEATURE_COMINTEROP_TLB_SUPPORT
+#include <imptlb.h>
+#endif
+
+#ifdef EnC_SUPPORTED
+#define ENC_DELTA_HACK
+#endif
+
+//*****************************************************************************
+// Ctor.
+//*****************************************************************************
+Disp::Disp() : m_cRef(0)
+{
+#if defined(LOGGING)
+ // InitializeLogging() calls scattered around the code.
+ // <TODO>@future: make this make some sense.</TODO>
+ InitializeLogging();
+#endif
+
+ m_OptionValue.m_DupCheck = MDDupDefault;
+ m_OptionValue.m_RefToDefCheck = MDRefToDefDefault;
+ m_OptionValue.m_NotifyRemap = MDNotifyDefault;
+ m_OptionValue.m_UpdateMode = MDUpdateFull;
+ m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderDefault;
+ m_OptionValue.m_ThreadSafetyOptions = MDThreadSafetyDefault;
+ m_OptionValue.m_GenerateTCEAdapters = FALSE;
+ m_OptionValue.m_ImportOption = MDImportOptionDefault;
+ m_OptionValue.m_LinkerOption = MDAssembly;
+ m_OptionValue.m_RuntimeVersion = NULL;
+ m_OptionValue.m_MetadataVersion = MDDefaultVersion;
+ m_OptionValue.m_MergeOptions = MergeFlagsNone;
+ m_OptionValue.m_InitialSize = MDInitialSizeDefault;
+ m_OptionValue.m_LocalRefPreservation = MDPreserveLocalRefsNone;
+
+ // Allow Avalon to use the SecurityCriticalAttribute
+ if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_FORCE_ASSEMREF_DUPCHECK))
+ {
+ m_OptionValue.m_DupCheck = (CorCheckDuplicatesFor)(m_OptionValue.m_DupCheck|MDDupAssemblyRef);
+ }
+
+} // Disp::Disp
+
+Disp::~Disp()
+{
+ if (m_OptionValue.m_RuntimeVersion != NULL)
+ delete [] m_OptionValue.m_RuntimeVersion;
+} // Disp::~Disp
+
+//*****************************************************************************
+// Create a brand new scope. This is based on the CLSID that was used to get
+// the dispenser.
+//*****************************************************************************
+__checkReturn
+HRESULT
+Disp::DefineScope(
+ REFCLSID rclsid, // [in] What version to create.
+ DWORD dwCreateFlags, // [in] Flags on the create.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+#ifdef FEATURE_METADATA_EMIT
+ HRESULT hr = S_OK;
+ PathString szFileName(PathString::Literal, W("file:"));
+ PathString szFileNameSuffix;
+ DWORD len;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ RegMeta *pMeta = 0;
+ OptionValue optionForNewScope = m_OptionValue;
+
+
+ LOG((LF_METADATA, LL_INFO10, "Disp::DefineScope(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", rclsid, dwCreateFlags, riid, ppIUnk));
+
+ if (dwCreateFlags)
+ IfFailGo(E_INVALIDARG);
+
+ // Figure out what version of the metadata to emit
+ if (rclsid == CLSID_CLR_v1_MetaData)
+ {
+#ifdef FEATURE_METADATA_STANDALONE_WINRT
+ IfFailGo(E_NOTIMPL);
+#else
+ optionForNewScope.m_MetadataVersion = MDVersion1;
+#endif //!FEATURE_METADATA_STANDALONE_WINRT
+ }
+ else if (rclsid == CLSID_CLR_v2_MetaData)
+ {
+ optionForNewScope.m_MetadataVersion = MDVersion2;
+ }
+ else
+ {
+ // If it is a version we don't understand, then we cannot continue.
+ IfFailGo(CLDB_E_FILE_OLDVER);
+ }
+
+#ifdef ENC_DELTA_HACK
+// Testers need this flag for their tests.
+
+ EX_TRY{
+ len = WszGetEnvironmentVariable(W("COMP_ENC_OPENSCOPE"), szFileNameSuffix);
+ szFileName.Append(szFileNameSuffix);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (len > 0)
+ {
+ // _ASSERTE(!"ENC override on DefineScope");
+// m_OptionValue.m_UpdateMode = MDUpdateENC;
+// m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderDefault;
+// hr = OpenScope(szFileName, ofWrite, riid, ppIUnk);
+
+ IMetaDataEmit *pMetaEmit;
+ hr = OpenScope(szFileName, ofWrite, IID_IMetaDataEmit, (IUnknown **)&pMetaEmit);
+ DWORD cb;
+ CQuickBytes pbMetadata;
+ hr = pMetaEmit->GetSaveSize(cssAccurate,&cb);
+ _ASSERTE(SUCCEEDED(hr));
+
+ IfFailGo(pbMetadata.ReSizeNoThrow(cb));
+
+ hr = pMetaEmit->SaveToMemory(pbMetadata.Ptr(),cb);
+ _ASSERTE(SUCCEEDED(hr));
+// hr = OpenScopeOnMemory( pbMetadata.Ptr(), cb, ofWrite|MDUpdateENC|MDUpdateDelta, riid, ppIUnk);
+
+
+ VARIANT encOption;
+ V_VT(&encOption) = VT_UI4;
+ V_UI4(&encOption) = MDUpdateENC;
+ SetOption(MetaDataSetENC, &encOption);
+ V_UI4(&encOption) = MDErrorOutOfOrderDefault;
+ SetOption(MetaDataErrorIfEmitOutOfOrder, &encOption);
+ hr = OpenScopeOnMemory( pbMetadata.Ptr(), cb, ofWrite, riid, ppIUnk);
+ _ASSERTE(SUCCEEDED(hr));
+ BOOL fResult = SUCCEEDED(hr);
+ // print out a message so people know what's happening
+ printf("Defining scope for EnC using %S %s\n",
+ static_cast<LPCWSTR>(szFileNameSuffix), fResult ? "succeeded" : "failed");
+
+ goto ErrExit;
+ }
+#endif // ENC_DELTA_HACK
+
+ // Create a new coclass for this guy.
+ pMeta = new (nothrow) RegMeta();
+ IfNullGo(pMeta);
+
+ IfFailGo(pMeta->SetOption(&optionForNewScope));
+
+ // Create the MiniMd-style scope.
+ IfFailGo(pMeta->CreateNewMD());
+
+ // Get the requested interface.
+ IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk));
+
+ // Add the new RegMeta to the cache.
+ IfFailGo(pMeta->AddToCache());
+
+ LOG((LOGMD, "{%08x} Created new emit scope\n", pMeta));
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pMeta != NULL)
+ delete pMeta;
+ *ppIUnk = NULL;
+ }
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT
+} // Disp::DefineScope
+
+
+//*****************************************************************************
+// Deliver scope to caller of OpenScope or OpenScopeOnMemory (this may
+// involve wrapping a WinMD adapter.)
+//*****************************************************************************
+static HRESULT DeliverScope(IMDCommon *pMDCommon, REFIID riid, DWORD dwOpenFlags, IUnknown **ppIUnk)
+{
+ HRESULT hr;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+#if !defined(FEATURE_METADATA_STANDALONE_WINRT) && defined(FEATURE_COMINTEROP)
+ IfFailGo((dwOpenFlags & ofNoTransform) ? S_FALSE : CheckIfWinMDAdapterNeeded(pMDCommon));
+ if (hr == S_OK)
+ {
+ IfFailGo(CreateWinMDImport(pMDCommon, riid, (void**)ppIUnk));
+ }
+ else
+#endif
+ {
+ IfFailGo(pMDCommon->QueryInterface(riid, (void**)ppIUnk));
+ }
+
+ ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+//*****************************************************************************
+// Open an existing scope.
+//*****************************************************************************
+HRESULT Disp::OpenScope( // Return code.
+ LPCWSTR szFileName, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+ BEGIN_ENTRYPOINT_NOTHROW;
+ LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope(%S, 0x%08x, 0x%08x, 0x%08x)\n", MDSTR(szFileName), dwOpenFlags, riid, ppIUnk));
+
+ IMDCommon *pMDCommon = NULL;
+
+ // Validate that there is some sort of file name.
+ if ((szFileName == NULL) || (szFileName[0] == 0) || (ppIUnk == NULL))
+ IfFailGo(E_INVALIDARG);
+
+ *ppIUnk = NULL;
+ IfFailGo(OpenRawScope(szFileName, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon));
+ IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk));
+ ErrExit:
+ if (pMDCommon)
+ pMDCommon->Release();
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+
+//*****************************************************************************
+// Open a raw view of existing scope.
+//*****************************************************************************
+__checkReturn
+HRESULT
+Disp::OpenRawScope(
+ LPCWSTR szFileName, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(szFileName != NULL);
+ _ASSERTE(ppIUnk != NULL);
+ RegMeta *pMeta = NULL;
+
+#ifdef FEATURE_METADATA_LOAD_TRUSTED_IMAGES
+ // Don't assert for code:ofTrustedImage (reserved) flag if the feature is supported
+ _ASSERTE(!IsOfReserved(dwOpenFlags & ~ofTrustedImage));
+#else
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+#endif //!FEATURE_METADATA_LOAD_TRUSTED_IMAGES
+
+ {
+ }
+
+ if (IsOfReadOnly(dwOpenFlags) && IsOfReadWrite(dwOpenFlags))
+ { // Invalid combination of flags - ofReadOnly & ofWrite
+ IfFailGo(E_INVALIDARG);
+ }
+ // If open-for-read, and there is already an open-for-read copy, return it.
+ if (IsOfReadOnly(dwOpenFlags))
+ {
+ RegMeta::FindCachedReadOnlyEntry(szFileName, dwOpenFlags, &pMeta);
+ if (pMeta != NULL)
+ {
+ // Return the requested interface.
+ hr = pMeta->QueryInterface(riid, (void **) ppIUnk);
+ if (FAILED(hr))
+ {
+ pMeta = NULL; // Don't delete cached RegMeta!
+ }
+ else
+ {
+ pMeta->Release(); // Give back refcount from QI
+ LOG((LOGMD, "{%08x} Found in cache '%S'\n", pMeta, MDSTR(szFileName)));
+ }
+
+ goto ErrExit;
+ }
+ }
+ // Create a new coclass for this guy.
+ pMeta = new (nothrow) RegMeta();
+ IfNullGo(pMeta);
+
+ IfFailGo(pMeta->SetOption(&m_OptionValue));
+
+ // Always initialize the RegMeta's stgdb.
+ // <TODO>@FUTURE: there are some cleanup for the open code!!</TODO>
+ if (memcmp(szFileName, W("file:"), 10) == 0)
+ {
+ szFileName = &szFileName[5];
+ }
+
+ // Try to open the MiniMd-style scope.
+ IfFailGo(pMeta->OpenExistingMD(szFileName, 0 /* pbData */,0 /* cbData */, dwOpenFlags));
+
+ // Obtain the requested interface.
+ IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk) );
+
+ // Add the new RegMeta to the cache. If this is read-only, any future opens will
+ // find this entry. If, due to another thread concurrently opening the same file,
+ // there is already another copy in the cache, well, then there will be two
+ // read-only copies in the cache. This is considered to be somewhat of a corner
+ // case, and the only harm is temporary memory usage. All requests will be
+ // satisfied by one or the other (depending on search algorithm), and eventually,
+ // the "other" copy will be released.
+ IfFailGo(pMeta->AddToCache());
+
+ LOG((LOGMD, "{%08x} Successfully opened '%S'\n", pMeta, MDSTR(szFileName)));
+
+#if defined(_DEBUG)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
+ {
+ int DumpMD_impl(RegMeta *pMD);
+ DumpMD_impl(pMeta);
+ }
+#endif // _DEBUG
+
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pMeta != NULL)
+ delete pMeta;
+ *ppIUnk = NULL;
+ }
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // Disp::OpenScope
+
+
+//*****************************************************************************
+// Open an existing scope.
+//*****************************************************************************
+HRESULT Disp::OpenScopeOnMemory( // Return code.
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData, // [in] Size of the data pointed to by pData.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+ BEGIN_ENTRYPOINT_NOTHROW;
+ LOG((LF_METADATA, LL_INFO10, "Disp::OpenScopeOnMemory(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", pData, cbData, dwOpenFlags, riid, ppIUnk));
+
+ IMDCommon *pMDCommon = NULL;
+
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+ if (ppIUnk == NULL)
+ IfFailGo(E_INVALIDARG);
+ *ppIUnk = NULL;
+ IfFailGo(OpenRawScopeOnMemory(pData, cbData, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon));
+ IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk));
+ ErrExit:
+ if (pMDCommon)
+ pMDCommon->Release();
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+//*****************************************************************************
+// Open a raw voew of existing scope.
+//*****************************************************************************
+HRESULT Disp::OpenRawScopeOnMemory( // Return code.
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData, // [in] Size of the data pointed to by pData.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ RegMeta *pMeta = 0;
+
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+
+ // Create a new coclass for this guy.
+ pMeta = new (nothrow) RegMeta();
+ IfNullGo(pMeta);
+ IfFailGo(pMeta->SetOption(&m_OptionValue));
+
+
+ PREFIX_ASSUME(pMeta != NULL);
+ // Always initialize the RegMeta's stgdb.
+ IfFailGo(pMeta->OpenExistingMD(0 /* szFileName */, const_cast<void*>(pData), cbData, dwOpenFlags));
+
+ LOG((LOGMD, "{%08x} Opened new scope on memory, pData: %08x cbData: %08x\n", pMeta, pData, cbData));
+
+ // Return the requested interface.
+ IfFailGo( pMeta->QueryInterface(riid, (void **) ppIUnk) );
+
+ // Add the new RegMeta to the cache.
+ IfFailGo(pMeta->AddToCache());
+
+#if defined(_DEBUG)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
+ {
+ int DumpMD_impl(RegMeta *pMD);
+ DumpMD_impl(pMeta);
+ }
+#endif // _DEBUG
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pMeta) delete pMeta;
+ *ppIUnk = 0;
+ }
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // Disp::OpenScopeOnMemory
+
+#if defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+
+#include <metahost.h>
+// Pointer to the activated CLR interface provided by the shim.
+extern ICLRRuntimeInfo * g_pCLRRuntime;
+
+#endif
+
+//*****************************************************************************
+// Get the directory where the CLR system resides.
+//
+// Implements public API code:IMetaDataDispenserEx::GetCORSystemDirectory.
+//*****************************************************************************
+HRESULT
+Disp::GetCORSystemDirectory(
+ __out_ecount (cchBuffer) LPWSTR szBuffer, // [out] Buffer for the directory name
+ DWORD cchBuffer, // [in] Size of the buffer
+ DWORD *pcchBuffer) // [out] Number of characters returned
+{
+#if defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // This implies a machine-wide CLR install root, which may not exist for some CLR
+ // skus using standalone metadata.
+ *pcchBuffer = cchBuffer;
+ hr = g_pCLRRuntime->GetRuntimeDirectory(szBuffer, pcchBuffer);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_IN_VM || FEATURE_CORECLR
+
+#ifdef FEATURE_CORECLR
+ UNREACHABLE_MSG("Calling IMetaDataDispenser::GetCORSystemDirectory! This code should not be "
+ "reachable or needs to be reimplemented for CoreCLR!");
+#endif //FEATURE_CORECLR
+
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_IN_VM || FEATURE_CORECLR
+} // Disp::GetCORSystemDirectory
+
+HRESULT Disp::FindAssembly( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szGlobalBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
+ LPCWSTR szName, // [OUT] buffer - to hold name
+ ULONG cchName, // [IN] the name buffer's size
+ ULONG *pcName) // [OUT] the number of characters returend in the buffer
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+} // Disp::FindAssembly
+
+HRESULT Disp::FindAssemblyModule( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szGlobalBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] The assembly name or code base of the assembly
+ LPCWSTR szModuleName, // [IN] required - the name of the module
+ __out_ecount (cchName) LPWSTR szName, // [OUT] buffer - to hold name
+ ULONG cchName, // [IN] the name buffer's size
+ ULONG *pcName) // [OUT] the number of characters returend in the buffer
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+} // Disp::FindAssemblyModule
+
+//*****************************************************************************
+// Open a scope on an ITypeInfo
+//*****************************************************************************
+HRESULT Disp::OpenScopeOnITypeInfo( // Return code.
+ ITypeInfo *pITI, // [in] ITypeInfo to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+} // Disp::OpenScopeOnITypeInfo
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+
+//*****************************************************************************
+// IMetaDataDispenserCustom
+//*****************************************************************************
+
+HRESULT Disp::OpenScopeOnCustomDataSource( // S_OK or error
+ IMDCustomDataSource *pCustomSource, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+ BEGIN_ENTRYPOINT_NOTHROW;
+ LOG((LF_METADATA, LL_INFO10, "Disp::OpenScopeOnCustomDataSource(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", pCustomSource, dwOpenFlags, riid, ppIUnk));
+
+ IMDCommon *pMDCommon = NULL;
+
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+ if (ppIUnk == NULL)
+ IfFailGo(E_INVALIDARG);
+ *ppIUnk = NULL;
+ IfFailGo(OpenRawScopeOnCustomDataSource(pCustomSource, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon));
+ IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk));
+ErrExit:
+ if (pMDCommon)
+ pMDCommon->Release();
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+
+//*****************************************************************************
+// Open a raw view of existing scope.
+//*****************************************************************************
+HRESULT Disp::OpenRawScopeOnCustomDataSource( // Return code.
+ IMDCustomDataSource* pDataSource, // [in] scope data.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ RegMeta *pMeta = 0;
+
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+
+ // Create a new coclass for this guy.
+ pMeta = new (nothrow)RegMeta();
+ IfNullGo(pMeta);
+ IfFailGo(pMeta->SetOption(&m_OptionValue));
+
+
+ PREFIX_ASSUME(pMeta != NULL);
+ // Always initialize the RegMeta's stgdb.
+ // TODO
+ IfFailGo(pMeta->OpenExistingMD(pDataSource, dwOpenFlags));
+
+ LOG((LOGMD, "{%08x} Opened new scope on custom data source, pDataSource: %08x\n", pMeta, pDataSource));
+
+ // Return the requested interface.
+ IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk));
+
+ // Add the new RegMeta to the cache.
+ IfFailGo(pMeta->AddToCache());
+
+#if defined(_DEBUG)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
+ {
+ int DumpMD_impl(RegMeta *pMD);
+ DumpMD_impl(pMeta);
+ }
+#endif // _DEBUG
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pMeta) delete pMeta;
+ *ppIUnk = 0;
+ }
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // Disp::OpenRawScopeOnCustomDataSource
+
+#endif
+
+//*****************************************************************************
+// IUnknown
+//*****************************************************************************
+
+ULONG Disp::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+} // Disp::AddRef
+
+ULONG Disp::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ return cRef;
+} // Disp::Release
+
+HRESULT Disp::QueryInterface(REFIID riid, void **ppUnk)
+{
+ *ppUnk = 0;
+
+ if (riid == IID_IUnknown)
+ *ppUnk = (IUnknown *) (IMetaDataDispenser *) this;
+ else if (riid == IID_IMetaDataDispenser)
+ *ppUnk = (IMetaDataDispenser *) this;
+ else if (riid == IID_IMetaDataDispenserEx)
+ *ppUnk = (IMetaDataDispenserEx *) this;
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ else if (riid == IID_IMetaDataDispenserCustom)
+ *ppUnk = static_cast<IMetaDataDispenserCustom*>(this);
+#endif
+ else
+ return E_NOINTERFACE;
+ AddRef();
+ return S_OK;
+} // Disp::QueryInterface
+
+
+//*****************************************************************************
+// Called by the class factory template to create a new instance of this object.
+//*****************************************************************************
+HRESULT Disp::CreateObject(REFIID riid, void **ppUnk)
+{
+ HRESULT hr;
+ Disp *pDisp = new (nothrow) Disp();
+
+ if (pDisp == 0)
+ return (E_OUTOFMEMORY);
+
+ hr = pDisp->QueryInterface(riid, ppUnk);
+ if (FAILED(hr))
+ delete pDisp;
+ return hr;
+} // Disp::CreateObject
+
+//*****************************************************************************
+// This routine provides the user a way to set certain properties on the
+// Dispenser.
+//
+// Implements public API code:IMetaDataDispenserEx::SetOption.
+//*****************************************************************************
+__checkReturn
+HRESULT
+Disp::SetOption(
+ REFGUID optionid, // [in] GUID for the option to be set.
+ const VARIANT *pvalue) // [in] Value to which the option is to be set.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LF_METADATA, LL_INFO10, "Disp::SetOption(0x%08x, 0x%08x)\n", optionid, pvalue));
+
+ if (optionid == MetaDataCheckDuplicatesFor)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_DupCheck = (CorCheckDuplicatesFor) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataRefToDefCheck)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_RefToDefCheck = (CorRefToDefCheck) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataErrorIfEmitOutOfOrder)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_ErrorIfEmitOutOfOrder = (CorErrorIfEmitOutOfOrder) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataThreadSafetyOptions)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_ThreadSafetyOptions = (CorThreadSafetyOptions) V_UI4(pvalue);
+ }
+// Note: mscordbi had all these options accessible in 3.5/4.0 RTM, let's keep it this way for AppCompat.
+#if defined(FEATURE_METADATA_EMIT_ALL) || defined(FEATURE_METADATA_EMIT_IN_DEBUGGER)
+ else if (optionid == MetaDataNotificationForTokenMovement)
+ { // Note: This is not used in CLR sources anymore, but we store the value and return it back in
+ // IMetaDataDispenserEx::GetOption (code:RegMeta::GetOption), so we keep it here for backward-compat.
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_NotifyRemap = (CorNotificationForTokenMovement)V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataSetENC)
+ { // EnC update mode (also aliased as code:MetaDataSetUpdate)
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_UpdateMode = V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataImportOption)
+ { // Allows enumeration of EnC deleted items by Import API
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_ImportOption = (CorImportOptions) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataLinkerOptions)
+ { // Used only by code:RegMeta::UnmarkAll (code:IMetaDataFilter::UnmarkAll)
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_LinkerOption = (CorLinkerOptions) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataMergerOptions)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_MergeOptions = (MergeFlags) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataGenerateTCEAdapters)
+ { // Note: This is not used in CLR sources anymore, but we store the value and return it back in
+ // IMetaDataDispenserEx::GetOption (code:RegMeta::GetOption), so we keep it for backward-compat.
+ if (V_VT(pvalue) != VT_BOOL)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_GenerateTCEAdapters = V_BOOL(pvalue);
+ }
+ else if (optionid == MetaDataTypeLibImportNamespace)
+ { // Note: This is not used in CLR sources anymore, keep it here for backward-compat
+ if (V_VT(pvalue) != VT_BSTR && V_VT(pvalue) != VT_EMPTY && V_VT(pvalue) != VT_NULL)
+ {
+ _ASSERTE(!"Invalid Variant Type value for namespace.");
+ IfFailGo(E_INVALIDARG);
+ }
+ }
+#endif //FEATURE_METADATA_EMIT_ALL || FEATURE_METADATA_EMIT_IN_DEBUGGER
+ else if (optionid == MetaDataRuntimeVersion)
+ {
+ if (V_VT(pvalue) != VT_BSTR && V_VT(pvalue) != VT_EMPTY && V_VT(pvalue) != VT_NULL)
+ {
+ _ASSERTE(!"Invalid Variant Type value for version.");
+ IfFailGo(E_INVALIDARG);
+ }
+ if (m_OptionValue.m_RuntimeVersion)
+ delete [] m_OptionValue.m_RuntimeVersion;
+
+ if ((V_VT(pvalue) == VT_EMPTY) || (V_VT(pvalue) == VT_NULL) || (*V_BSTR(pvalue) == 0))
+ {
+ m_OptionValue.m_RuntimeVersion = NULL;
+ }
+ else
+ {
+ INT32 len = WszWideCharToMultiByte(CP_UTF8, 0, V_BSTR(pvalue), -1, NULL, 0, NULL, NULL);
+ m_OptionValue.m_RuntimeVersion = new (nothrow) char[len];
+ if (m_OptionValue.m_RuntimeVersion == NULL)
+ IfFailGo(E_INVALIDARG);
+ WszWideCharToMultiByte(CP_UTF8, 0, V_BSTR(pvalue), -1, m_OptionValue.m_RuntimeVersion, len, NULL, NULL);
+ }
+ }
+ else if (optionid == MetaDataInitialSize)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_InitialSize = V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataPreserveLocalRefs)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+
+ m_OptionValue.m_LocalRefPreservation = (CorLocalRefPreservation) V_UI4(pvalue);
+ }
+ else
+ {
+ _ASSERTE(!"Invalid GUID");
+ IfFailGo(E_INVALIDARG);
+ }
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // Disp::SetOption
+
+//*****************************************************************************
+// This routine provides the user a way to set certain properties on the
+// Dispenser.
+//
+// Implements public API code:IMetaDataDispenserEx::GetOption.
+//*****************************************************************************
+HRESULT Disp::GetOption( // Return code.
+ REFGUID optionid, // [in] GUID for the option to be set.
+ VARIANT *pvalue) // [out] Value to which the option is currently set.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LF_METADATA, LL_INFO10, "Disp::GetOption(0x%08x, 0x%08x)\n", optionid, pvalue));
+
+ _ASSERTE(pvalue);
+ if (optionid == MetaDataCheckDuplicatesFor)
+ {
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_DupCheck;
+ }
+ else if (optionid == MetaDataRefToDefCheck)
+ {
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_RefToDefCheck;
+ }
+ else if (optionid == MetaDataErrorIfEmitOutOfOrder)
+ {
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_ErrorIfEmitOutOfOrder;
+ }
+// Note: mscordbi had all these options accessible in 3.5/4.0 RTM, let's keep it this way for AppCompat.
+#if defined(FEATURE_METADATA_EMIT_ALL) || defined(FEATURE_METADATA_EMIT_IN_DEBUGGER)
+ else if (optionid == MetaDataNotificationForTokenMovement)
+ { // Note: This is not used in CLR sources anymore, but we store the value and return it here,
+ // so we keep it for backward-compat.
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_NotifyRemap;
+ }
+ else if (optionid == MetaDataSetENC)
+ { // EnC update mode (also aliased as code:MetaDataSetUpdate)
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_UpdateMode;
+ }
+ else if (optionid == MetaDataLinkerOptions)
+ { // Allows enumeration of EnC deleted items by Import API
+ V_VT(pvalue) = VT_BOOL;
+ V_UI4(pvalue) = m_OptionValue.m_LinkerOption;
+ }
+ else if (optionid == MetaDataGenerateTCEAdapters)
+ { // Note: This is not used in CLR sources anymore, but we store the value and return it here,
+ // so we keep it for backward-compat.
+ V_VT(pvalue) = VT_BOOL;
+ V_BOOL(pvalue) = m_OptionValue.m_GenerateTCEAdapters;
+ }
+#endif //FEATURE_METADATA_EMIT_ALL || FEATURE_METADATA_EMIT_IN_DEBUGGER
+ else
+ {
+ _ASSERTE(!"Invalid GUID");
+ IfFailGo(E_INVALIDARG);
+ }
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // Disp::GetOption
+
+#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
+
+//---------------------------------------------------------------------------------------
+//
+// Process detach destruction.
+// Called from DllMain of clr.dll/RoMetadata.dll/MidlrtMd.dll.
+//
+void DeleteMetaData()
+{
+ LOADEDMODULES::DeleteStatics();
+}
+
+#endif //FEATURE_METADATA_IN_VM || FEATURE_METADATA_STANDALONE_WINRT
+
+//
+// This is the entrypoint for usages of MetaData that need to start with the dispenser (e.g.
+// mscordbi.dll and profiling API).
+//
+// Notes:
+// This could be merged with the class factory support.
+HRESULT InternalCreateMetaDataDispenser(REFIID riid, void ** pMetaDataDispenserOut)
+{
+ _ASSERTE(pMetaDataDispenserOut != NULL);
+ return Disp::CreateObject(riid, pMetaDataDispenserOut);
+}
diff --git a/src/md/compiler/disp.h b/src/md/compiler/disp.h
new file mode 100644
index 0000000000..76a49e0d24
--- /dev/null
+++ b/src/md/compiler/disp.h
@@ -0,0 +1,132 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// Disp.h
+//
+
+//
+// Class factories are used by the pluming in COM to activate new objects.
+// This module contains the class factory code to instantiate the debugger
+// objects described in <cordb.h>.
+//
+//*****************************************************************************
+#ifndef __Disp__h__
+#define __Disp__h__
+
+
+class Disp :
+ public IMetaDataDispenserEx
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ , IMetaDataDispenserCustom
+#endif
+{
+public:
+ Disp();
+ virtual ~Disp();
+
+ // *** IUnknown methods ***
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
+ STDMETHODIMP_(ULONG) AddRef(void);
+ STDMETHODIMP_(ULONG) Release(void);
+
+ // *** IMetaDataDispenser methods ***
+ STDMETHODIMP DefineScope( // Return code.
+ REFCLSID rclsid, // [in] What version to create.
+ DWORD dwCreateFlags, // [in] Flags on the create.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk); // [out] Return interface on success.
+
+ STDMETHODIMP OpenScope( // Return code.
+ LPCWSTR szScope, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk); // [out] Return interface on success.
+
+ STDMETHODIMP OpenScopeOnMemory( // Return code.
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData, // [in] Size of the data pointed to by pData.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk); // [out] Return interface on success.
+
+ // *** IMetaDataDispenserEx methods ***
+ STDMETHODIMP SetOption( // Return code.
+ REFGUID optionid, // [in] GUID for the option to be set.
+ const VARIANT *pvalue); // [in] Value to which the option is to be set.
+
+ STDMETHODIMP GetOption( // Return code.
+ REFGUID optionid, // [in] GUID for the option to be set.
+ VARIANT *pvalue); // [out] Value to which the option is currently set.
+
+ STDMETHODIMP OpenScopeOnITypeInfo( // Return code.
+ ITypeInfo *pITI, // [in] ITypeInfo to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk); // [out] Return interface on success.
+
+ STDMETHODIMP GetCORSystemDirectory( // Return code.
+ __out_ecount (cchBuffer) LPWSTR szBuffer, // [out] Buffer for the directory name
+ DWORD cchBuffer, // [in] Size of the buffer
+ DWORD* pchBuffer); // [OUT] Number of characters returned
+
+ STDMETHODIMP FindAssembly( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szGlobalBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
+ LPCWSTR szName, // [OUT] buffer - to hold name
+ ULONG cchName, // [IN] the name buffer's size
+ ULONG *pcName); // [OUT] the number of characters returend in the buffer
+
+ STDMETHODIMP FindAssemblyModule( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szGlobalBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
+ LPCWSTR szModuleName, // [IN] required - the name of the module
+ __out_ecount (cchName)LPWSTR szName,// [OUT] buffer - to hold name
+ ULONG cchName, // [IN] the name buffer's size
+ ULONG *pcName); // [OUT] the number of characters returend in the buffer
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ // *** IMetaDataDispenserCustom methods ***
+ STDMETHODIMP OpenScopeOnCustomDataSource( // S_OK or error
+ IMDCustomDataSource *pCustomSource, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk); // [out] Return interface on success.
+#endif
+
+ // Class factory hook-up.
+ static HRESULT CreateObject(REFIID riid, void **ppUnk);
+
+private:
+ HRESULT OpenRawScope( // Return code.
+ LPCWSTR szScope, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk); // [out] Return interface on success.
+
+ HRESULT OpenRawScopeOnMemory( // Return code.
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData, // [in] Size of the data pointed to by pData.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk); // [out] Return interface on success.
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ HRESULT OpenRawScopeOnCustomDataSource( // Return code.
+ IMDCustomDataSource* pDataSource, // [in] scope data.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk); // [out] Return interface on success.
+#endif
+
+
+private:
+ LONG m_cRef; // Ref count
+ OptionValue m_OptionValue; // values can be set by using SetOption
+};
+
+#endif // __Disp__h__
diff --git a/src/md/compiler/emit.cpp b/src/md/compiler/emit.cpp
new file mode 100644
index 0000000000..c77e28fa0f
--- /dev/null
+++ b/src/md/compiler/emit.cpp
@@ -0,0 +1,3001 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// Emit.cpp
+//
+
+//
+// Implementation for the meta data emit code.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+
+#ifdef FEATURE_METADATA_EMIT
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4102)
+#endif
+
+//*****************************************************************************
+// Create and set a new MethodDef record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineMethod( // S_OK or error.
+ mdTypeDef td, // Parent TypeDef
+ LPCWSTR szName, // Name of member
+ DWORD dwMethodFlags, // Member attributes
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ ULONG ulCodeRVA,
+ DWORD dwImplFlags,
+ mdMethodDef *pmd) // Put member token here
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MethodRec *pRecord = NULL; // The new record.
+ RID iRecord; // The new record's RID.
+ LPUTF8 szNameUtf8;
+ UTF8STR(szName, szNameUtf8);
+
+ LOG((LOGMD, "MD: RegMeta::DefineMethod(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, MDSTR(szName), dwMethodFlags, pvSigBlob, cbSigBlob, ulCodeRVA, dwImplFlags, pmd));
+ START_MD_PERF();
+
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(pmd);
+
+ // Make sure no one sets the reserved bits on the way in.
+ dwMethodFlags &= (~mdReservedMask);
+
+ IsGlobalMethodParent(&td);
+
+ // See if this method has already been defined.
+ if (CheckDups(MDDupMethodDef))
+ {
+ hr = ImportHelper::FindMethod(
+ &(m_pStgdb->m_MiniMd),
+ td,
+ szNameUtf8,
+ pvSigBlob,
+ cbSigBlob,
+ pmd);
+
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(*pmd), &pRecord));
+ }
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ }
+
+ // Create the new record.
+ if (pRecord == NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddMethodRecord(&pRecord, &iRecord));
+
+ // Give token back to caller.
+ *pmd = TokenFromRid(iRecord, mdtMethodDef);
+
+ // Add to parent's list of child records.
+ IfFailGo(m_pStgdb->m_MiniMd.AddMethodToTypeDef(RidFromToken(td), iRecord));
+
+ IfFailGo(UpdateENCLog(td, CMiniMdRW::eDeltaMethodCreate));
+
+ // record the more defs are introduced.
+ SetMemberDefDirty(true);
+ }
+
+ // Set the method properties.
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Method, MethodRec::COL_Name, pRecord, szNameUtf8));
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Method, MethodRec::COL_Signature, pRecord, pvSigBlob, cbSigBlob));
+
+ // <TODO>@FUTURE: possible performance improvement here to check _ first of all.</TODO>
+ // .ctor and .cctor below are defined in corhdr.h. However, corhdr.h does not have the
+ // the W() macro we need (since it's distributed to windows). We substitute the values of each
+ // macro in the code below to work around this issue.
+ // #define COR_CTOR_METHOD_NAME_W L".ctor"
+ // #define COR_CCTOR_METHOD_NAME_W L".cctor"
+
+ if (!wcscmp(szName, W(".ctor")) || // COR_CTOR_METHOD_NAME_W
+ !wcscmp(szName, W(".cctor")) || // COR_CCTOR_METHOD_NAME_W
+ !wcsncmp(szName, W("_VtblGap"), 8) )
+ {
+ dwMethodFlags |= mdRTSpecialName | mdSpecialName;
+ }
+ SetCallerDefine();
+ IfFailGo(_SetMethodProps(*pmd, dwMethodFlags, ulCodeRVA, dwImplFlags));
+
+ IfFailGo(m_pStgdb->m_MiniMd.AddMemberDefToHash(*pmd, td) );
+
+ErrExit:
+ SetCallerExternal();
+
+ STOP_MD_PERF(DefineMethod);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineMethod
+
+//*****************************************************************************
+// Create and set a MethodImpl Record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineMethodImpl( // S_OK or error.
+ mdTypeDef td, // [IN] The class implementing the method
+ mdToken tkBody, // [IN] Method body, MethodDef or MethodRef
+ mdToken tkDecl) // [IN] Method declaration, MethodDef or MethodRef
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MethodImplRec *pMethodImplRec = NULL;
+ RID iMethodImplRec;
+
+ LOG((LOGMD, "MD RegMeta::DefineMethodImpl(0x%08x, 0x%08x, 0x%08x)\n",
+ td, tkBody, tkDecl));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+ _ASSERTE(TypeFromToken(tkBody) == mdtMemberRef || TypeFromToken(tkBody) == mdtMethodDef);
+ _ASSERTE(TypeFromToken(tkDecl) == mdtMemberRef || TypeFromToken(tkDecl) == mdtMethodDef);
+ _ASSERTE(!IsNilToken(td) && !IsNilToken(tkBody) && !IsNilToken(tkDecl));
+
+ // Check for duplicates.
+ if (CheckDups(MDDupMethodDef))
+ {
+ hr = ImportHelper::FindMethodImpl(&m_pStgdb->m_MiniMd, td, tkBody, tkDecl, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ // Create the MethodImpl record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddMethodImplRecord(&pMethodImplRec, &iMethodImplRec));
+
+ // Set the values.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl, MethodImplRec::COL_Class,
+ pMethodImplRec, td));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl, MethodImplRec::COL_MethodBody,
+ pMethodImplRec, tkBody));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl, MethodImplRec::COL_MethodDeclaration,
+ pMethodImplRec, tkDecl));
+
+ IfFailGo( m_pStgdb->m_MiniMd.AddMethodImplToHash(iMethodImplRec) );
+
+ IfFailGo(UpdateENCLog2(TBL_MethodImpl, iMethodImplRec));
+ErrExit:
+
+ STOP_MD_PERF(DefineMethodImpl);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineMethodImpl
+
+
+//*****************************************************************************
+// Set or update RVA and ImplFlags for the given MethodDef or FieldDef record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetMethodImplFlags( // [IN] S_OK or error.
+ mdMethodDef md, // [IN] Method for which to set impl flags
+ DWORD dwImplFlags)
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MethodRec *pMethodRec;
+
+ LOG((LOGMD, "MD RegMeta::SetMethodImplFlags(0x%08x, 0x%08x)\n",
+ md, dwImplFlags));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ _ASSERTE(TypeFromToken(md) == mdtMethodDef && dwImplFlags != ULONG_MAX);
+
+ // Get the record.
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec));
+ pMethodRec->SetImplFlags(static_cast<USHORT>(dwImplFlags));
+
+ IfFailGo(UpdateENCLog(md));
+
+ErrExit:
+ STOP_MD_PERF(SetMethodImplFlags);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetMethodImplFlags
+
+
+//*****************************************************************************
+// Set or update RVA and ImplFlags for the given MethodDef or FieldDef record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetFieldRVA( // [IN] S_OK or error.
+ mdFieldDef fd, // [IN] Field for which to set offset
+ ULONG ulRVA) // [IN] The offset
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ FieldRVARec *pFieldRVARec;
+ RID iFieldRVA;
+ FieldRec *pFieldRec;
+
+ LOG((LOGMD, "MD RegMeta::SetFieldRVA(0x%08x, 0x%08x)\n",
+ fd, ulRVA));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ _ASSERTE(TypeFromToken(fd) == mdtFieldDef);
+
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldRVAHelper(fd, &iFieldRVA));
+
+ if (InvalidRid(iFieldRVA))
+ {
+ // turn on the has field RVA bit
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec));
+ pFieldRec->AddFlags(fdHasFieldRVA);
+
+ // Create a new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddFieldRVARecord(&pFieldRVARec, &iFieldRVA));
+
+ // Set the data.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldRVA, FieldRVARec::COL_Field,
+ pFieldRVARec, fd));
+ IfFailGo( m_pStgdb->m_MiniMd.AddFieldRVAToHash(iFieldRVA) );
+ }
+ else
+ {
+ // Get the record.
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRVARecord(iFieldRVA, &pFieldRVARec));
+ }
+
+ // Set the data.
+ pFieldRVARec->SetRVA(ulRVA);
+
+ IfFailGo(UpdateENCLog2(TBL_FieldRVA, iFieldRVA));
+
+ErrExit:
+ STOP_MD_PERF(SetFieldRVA);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetFieldRVA
+
+
+//*****************************************************************************
+// Helper: Set or update RVA and ImplFlags for the given MethodDef or MethodImpl record.
+//*****************************************************************************
+HRESULT RegMeta::_SetRVA( // [IN] S_OK or error.
+ mdToken tk, // [IN] Member for which to set offset
+ ULONG ulCodeRVA, // [IN] The offset
+ DWORD dwImplFlags)
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ _ASSERTE(TypeFromToken(tk) == mdtMethodDef || TypeFromToken(tk) == mdtFieldDef);
+ _ASSERTE(!IsNilToken(tk));
+
+ if (TypeFromToken(tk) == mdtMethodDef)
+ {
+ MethodRec *pMethodRec;
+
+ // Get the record.
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethodRec));
+
+ // Set the data.
+ pMethodRec->SetRVA(ulCodeRVA);
+
+ // Do not set the flag value unless its valid.
+ if (dwImplFlags != ULONG_MAX)
+ pMethodRec->SetImplFlags(static_cast<USHORT>(dwImplFlags));
+
+ IfFailGo(UpdateENCLog(tk));
+ }
+ else // TypeFromToken(tk) == mdtFieldDef
+ {
+ _ASSERTE(dwImplFlags==0 || dwImplFlags==ULONG_MAX);
+
+ FieldRVARec *pFieldRVARec;
+ RID iFieldRVA;
+ FieldRec *pFieldRec;
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldRVAHelper(tk, &iFieldRVA));
+
+ if (InvalidRid(iFieldRVA))
+ {
+ // turn on the has field RVA bit
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pFieldRec));
+ pFieldRec->AddFlags(fdHasFieldRVA);
+
+ // Create a new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddFieldRVARecord(&pFieldRVARec, &iFieldRVA));
+
+ // Set the data.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldRVA, FieldRVARec::COL_Field,
+ pFieldRVARec, tk));
+
+ IfFailGo( m_pStgdb->m_MiniMd.AddFieldRVAToHash(iFieldRVA) );
+
+ }
+ else
+ {
+ // Get the record.
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRVARecord(iFieldRVA, &pFieldRVARec));
+ }
+
+ // Set the data.
+ pFieldRVARec->SetRVA(ulCodeRVA);
+
+ IfFailGo(UpdateENCLog2(TBL_FieldRVA, iFieldRVA));
+ }
+
+ErrExit:
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::_SetRVA
+
+//*****************************************************************************
+// Given a name, create a TypeRef.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineTypeRefByName( // S_OK or error.
+ mdToken tkResolutionScope, // [IN] ModuleRef or AssemblyRef.
+ LPCWSTR szName, // [IN] Name of the TypeRef.
+ mdTypeRef *ptr) // [OUT] Put TypeRef token here.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ LOG((LOGMD, "MD RegMeta::DefineTypeRefByName(0x%08x, %S, 0x%08x)\n",
+ tkResolutionScope, MDSTR(szName), ptr));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ // Common helper function does all of the work.
+ IfFailGo(_DefineTypeRef(tkResolutionScope, szName, TRUE, ptr));
+
+ErrExit:
+ STOP_MD_PERF(DefineTypeRefByName);
+
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineTypeRefByName
+
+//*****************************************************************************
+// Create a reference, in an emit scope, to a TypeDef in another scope.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineImportType( // S_OK or error.
+ IMetaDataAssemblyImport *pAssemImport, // [IN] Assemby containing the TypeDef.
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ IMetaDataImport *pImport, // [IN] Scope containing the TypeDef.
+ mdTypeDef tdImport, // [IN] The imported TypeDef.
+ IMetaDataAssemblyEmit *pAssemEmit, // [IN] Assembly into which the TypeDef is imported.
+ mdTypeRef *ptr) // [OUT] Put TypeRef token here.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ IMetaDataImport2 *pImport2 = NULL;
+ IMDCommon *pImport2MDCommon = NULL;
+
+ IMDCommon *pAssemImportMDCommon = NULL;
+
+ RegMeta *pAssemEmitRM = NULL;
+ CMiniMdRW *pMiniMdAssemEmit = NULL;
+ CMiniMdRW *pMiniMdEmit = NULL;
+
+ IMetaModelCommon *pAssemImportMetaModelCommon;
+ IMetaModelCommon *pImport2MetaModelCommon;
+
+ LOG((LOGMD, "MD RegMeta::DefineImportType(0x%08x, 0x%08x, 0x%08x, 0x%08x, "
+ "0x%08x, 0x%08x, 0x%08x)\n",
+ pAssemImport, pbHashValue, cbHashValue,
+ pImport, tdImport, pAssemEmit, ptr));
+
+ START_MD_PERF();
+
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+ IfFailGo(pImport->QueryInterface(IID_IMetaDataImport2, (void**)&pImport2));
+
+ if (pAssemImport)
+ {
+ IfFailGo(pAssemImport->QueryInterface(IID_IMDCommon, (void**)&pAssemImportMDCommon));
+ }
+
+ pAssemImportMetaModelCommon = pAssemImportMDCommon ? pAssemImportMDCommon->GetMetaModelCommon() : 0;
+
+ IfFailGo(pImport2->QueryInterface(IID_IMDCommon, (void**)&pImport2MDCommon));
+ pImport2MetaModelCommon = pImport2MDCommon->GetMetaModelCommon();
+
+ pAssemEmitRM = static_cast<RegMeta*>(pAssemEmit);
+ pMiniMdAssemEmit = pAssemEmitRM ? static_cast<CMiniMdRW*>(&pAssemEmitRM->m_pStgdb->m_MiniMd) : 0;
+ pMiniMdEmit = &m_pStgdb->m_MiniMd;
+
+ IfFailGo(ImportHelper::ImportTypeDef(
+ pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pAssemImportMetaModelCommon,
+ pbHashValue, cbHashValue,
+ pImport2MetaModelCommon,
+ tdImport,
+ false, // Do not optimize to TypeDef if import and emit scopes are identical.
+ ptr));
+
+ErrExit:
+ if (pImport2)
+ pImport2->Release();
+ if (pImport2MDCommon)
+ pImport2MDCommon->Release();
+ if (pAssemImportMDCommon)
+ pAssemImportMDCommon->Release();
+ STOP_MD_PERF(DefineImportType);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineImportType
+
+//*****************************************************************************
+// Create and set a MemberRef record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineMemberRef( // S_OK or error
+ mdToken tkImport, // [IN] ClassRef or ClassDef importing a member.
+ LPCWSTR szName, // [IN] member's name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMemberRef *pmr) // [OUT] memberref token
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MemberRefRec *pRecord = 0; // The MemberRef record.
+ RID iRecord; // RID of new MemberRef record.
+ LPUTF8 szNameUtf8;
+ UTF8STR(szName, szNameUtf8);
+
+ LOG((LOGMD, "MD RegMeta::DefineMemberRef(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ tkImport, MDSTR(szName), pvSigBlob, cbSigBlob, pmr));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(tkImport) == mdtTypeRef ||
+ TypeFromToken(tkImport) == mdtModuleRef ||
+ TypeFromToken(tkImport) == mdtMethodDef ||
+ TypeFromToken(tkImport) == mdtTypeSpec ||
+ (TypeFromToken(tkImport) == mdtTypeDef) ||
+ IsNilToken(tkImport));
+
+ _ASSERTE(szName && pvSigBlob && cbSigBlob && pmr);
+
+ // _ASSERTE(_IsValidToken(tkImport));
+
+ // Set token to m_tdModule if referring to a global function.
+ if (IsNilToken(tkImport))
+ tkImport = m_tdModule;
+
+ // If the MemberRef already exists, just return the token, else
+ // create a new record.
+ if (CheckDups(MDDupMemberRef))
+ {
+ hr = ImportHelper::FindMemberRef(&(m_pStgdb->m_MiniMd), tkImport, szNameUtf8, pvSigBlob, cbSigBlob, pmr);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ IfFailGo(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(*pmr), &pRecord));
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND) // MemberRef exists
+ IfFailGo(hr);
+ }
+
+ if (!pRecord)
+ { // Create the record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddMemberRefRecord(&pRecord, &iRecord));
+
+ // record the more defs are introduced.
+ SetMemberDefDirty(true);
+
+ // Give token to caller.
+ *pmr = TokenFromRid(iRecord, mdtMemberRef);
+ }
+
+ // Save row data.
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_MemberRef, MemberRefRec::COL_Name, pRecord, szNameUtf8));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MemberRef, MemberRefRec::COL_Class, pRecord, tkImport));
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_MemberRef, MemberRefRec::COL_Signature, pRecord,
+ pvSigBlob, cbSigBlob));
+
+ IfFailGo(m_pStgdb->m_MiniMd.AddMemberRefToHash(*pmr) );
+
+ IfFailGo(UpdateENCLog(*pmr));
+
+ErrExit:
+
+ STOP_MD_PERF(DefineMemberRef);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineMemberRef
+
+//*****************************************************************************
+// Create a MemberRef record based on a member in an import scope.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineImportMember( // S_OK or error.
+ IMetaDataAssemblyImport *pAssemImport, // [IN] Assemby containing the Member.
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ IMetaDataImport *pImport, // [IN] Import scope, with member.
+ mdToken mbMember, // [IN] Member in import scope.
+ IMetaDataAssemblyEmit *pAssemEmit, // [IN] Assembly into which the Member is imported.
+ mdToken tkImport, // [IN] Classref or classdef in emit scope.
+ mdMemberRef *pmr) // [OUT] Put member ref here.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ LOG((LOGMD, "MD RegMeta::DefineImportMember("
+ "0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x,"
+ " 0x%08x, 0x%08x, 0x%08x)\n",
+ pAssemImport, pbHashValue, cbHashValue, pImport, mbMember,
+ pAssemEmit, tkImport, pmr));
+ START_MD_PERF();
+
+ // No need to lock this function. All the functions that it calls are public APIs.
+
+ _ASSERTE(pImport && pmr);
+ _ASSERTE(TypeFromToken(tkImport) == mdtTypeRef || TypeFromToken(tkImport) == mdtModuleRef ||
+ IsNilToken(tkImport) || TypeFromToken(tkImport) == mdtTypeSpec);
+ _ASSERTE((TypeFromToken(mbMember) == mdtMethodDef && mbMember != mdMethodDefNil) ||
+ (TypeFromToken(mbMember) == mdtFieldDef && mbMember != mdFieldDefNil));
+
+ CQuickArray<WCHAR> qbMemberName; // Name of the imported member.
+ CQuickArray<WCHAR> qbScopeName; // Name of the imported member's scope.
+ GUID mvidImport; // MVID of the import module.
+ GUID mvidEmit; // MVID of the emit module.
+ ULONG cchName; // Length of a name, in wide chars.
+ PCCOR_SIGNATURE pvSig; // Member's signature.
+ ULONG cbSig; // Length of member's signature.
+ CQuickBytes cqbTranslatedSig; // Buffer for signature translation.
+ ULONG cbTranslatedSig; // Length of translated signature.
+
+ if (TypeFromToken(mbMember) == mdtMethodDef)
+ {
+ do {
+ hr = pImport->GetMethodProps(mbMember, 0, qbMemberName.Ptr(),(DWORD)qbMemberName.MaxSize(),&cchName,
+ 0, &pvSig,&cbSig, 0,0);
+ if (hr == CLDB_S_TRUNCATION)
+ {
+ IfFailGo(qbMemberName.ReSizeNoThrow(cchName));
+ continue;
+ }
+ break;
+ } while (1);
+ }
+ else // TypeFromToken(mbMember) == mdtFieldDef
+ {
+ do {
+ hr = pImport->GetFieldProps(mbMember, 0, qbMemberName.Ptr(),(DWORD)qbMemberName.MaxSize(),&cchName,
+ 0, &pvSig,&cbSig, 0,0, 0);
+ if (hr == CLDB_S_TRUNCATION)
+ {
+ IfFailGo(qbMemberName.ReSizeNoThrow(cchName));
+ continue;
+ }
+ break;
+ } while (1);
+ }
+ IfFailGo(hr);
+
+ IfFailGo(cqbTranslatedSig.ReSizeNoThrow(cbSig * 3)); // Set size conservatively.
+
+ IfFailGo(TranslateSigWithScope(
+ pAssemImport,
+ pbHashValue,
+ cbHashValue,
+ pImport,
+ pvSig,
+ cbSig,
+ pAssemEmit,
+ static_cast<IMetaDataEmit*>(static_cast<IMetaDataEmit2*>(this)),
+ (COR_SIGNATURE *)cqbTranslatedSig.Ptr(),
+ cbSig * 3,
+ &cbTranslatedSig));
+
+ // Define ModuleRef for imported Member functions
+
+ // Check if the Member being imported is a global function.
+ IfFailGo(GetScopeProps(0, 0, 0, &mvidEmit));
+ IfFailGo(pImport->GetScopeProps(0, 0,&cchName, &mvidImport));
+ if (mvidEmit != mvidImport && IsNilToken(tkImport))
+ {
+ IfFailGo(qbScopeName.ReSizeNoThrow(cchName));
+ IfFailGo(pImport->GetScopeProps(qbScopeName.Ptr(),(DWORD)qbScopeName.MaxSize(),
+ 0, 0));
+ IfFailGo(DefineModuleRef(qbScopeName.Ptr(), &tkImport));
+ }
+
+ // Define MemberRef base on the name, sig, and parent
+ IfFailGo(DefineMemberRef(
+ tkImport,
+ qbMemberName.Ptr(),
+ reinterpret_cast<PCCOR_SIGNATURE>(cqbTranslatedSig.Ptr()),
+ cbTranslatedSig,
+ pmr));
+
+ErrExit:
+ STOP_MD_PERF(DefineImportMember);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineImportMember
+
+//*****************************************************************************
+// Define and set a Event record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineEvent(
+ mdTypeDef td, // [IN] the class/interface on which the event is being defined
+ LPCWSTR szEvent, // [IN] Name of the event
+ DWORD dwEventFlags, // [IN] CorEventAttr
+ mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef(to the Event class
+ mdMethodDef mdAddOn, // [IN] required add method
+ mdMethodDef mdRemoveOn, // [IN] required remove method
+ mdMethodDef mdFire, // [IN] optional fire method
+ mdMethodDef rmdOtherMethods[], // [IN] optional array of other methods associate with the event
+ mdEvent *pmdEvent) // [OUT] output event token
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ LOG((LOGMD, "MD RegMeta::DefineEvent(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, szEvent, dwEventFlags, tkEventType, mdAddOn, mdRemoveOn, mdFire, rmdOtherMethods, pmdEvent));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && td != mdTypeDefNil);
+ _ASSERTE(IsNilToken(tkEventType) || TypeFromToken(tkEventType) == mdtTypeDef ||
+ TypeFromToken(tkEventType) == mdtTypeRef || TypeFromToken(tkEventType) == mdtTypeSpec);
+ _ASSERTE(TypeFromToken(mdAddOn) == mdtMethodDef && mdAddOn != mdMethodDefNil);
+ _ASSERTE(TypeFromToken(mdRemoveOn) == mdtMethodDef && mdRemoveOn != mdMethodDefNil);
+ _ASSERTE(IsNilToken(mdFire) || TypeFromToken(mdFire) == mdtMethodDef);
+ _ASSERTE(szEvent && pmdEvent);
+
+ hr = _DefineEvent(td, szEvent, dwEventFlags, tkEventType, pmdEvent);
+ if (hr != S_OK)
+ goto ErrExit;
+
+ IfFailGo(_SetEventProps2(*pmdEvent, mdAddOn, mdRemoveOn, mdFire, rmdOtherMethods, IsENCOn()));
+ IfFailGo(UpdateENCLog(*pmdEvent));
+ErrExit:
+
+ STOP_MD_PERF(DefineEvent);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineEvent
+
+//*****************************************************************************
+// Set the ClassLayout information.
+//
+// If a row already exists for this class in the layout table, the layout
+// information is overwritten.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetClassLayout(
+ mdTypeDef td, // [IN] typedef
+ DWORD dwPackSize, // [IN] packing size specified as 1, 2, 4, 8, or 16
+ COR_FIELD_OFFSET rFieldOffsets[], // [IN] array of layout specification
+ ULONG ulClassSize) // [IN] size of the class
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK; // A result.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ int index = 0; // Loop control.
+
+ LOG((LOGMD, "MD RegMeta::SetClassLayout(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, dwPackSize, rFieldOffsets, ulClassSize));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+
+ // Create entries in the FieldLayout table.
+ if (rFieldOffsets)
+ {
+ mdFieldDef tkfd;
+ // Iterate the list of fields...
+ for (index = 0; rFieldOffsets[index].ridOfField != mdFieldDefNil; index++)
+ {
+ if (rFieldOffsets[index].ulOffset != ULONG_MAX)
+ {
+ tkfd = TokenFromRid(rFieldOffsets[index].ridOfField, mdtFieldDef);
+
+ IfFailGo(_SetFieldOffset(tkfd, rFieldOffsets[index].ulOffset));
+ }
+ }
+ }
+
+ IfFailGo(_SetClassLayout(td, dwPackSize, ulClassSize));
+
+ErrExit:
+
+ STOP_MD_PERF(SetClassLayout);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetClassLayout
+
+//*****************************************************************************
+// Helper function to set a class layout for a given class.
+//*****************************************************************************
+HRESULT RegMeta::_SetClassLayout( // S_OK or error.
+ mdTypeDef td, // [IN] The class.
+ ULONG dwPackSize, // [IN] The packing size.
+ ULONG ulClassSize) // [IN, OPTIONAL] The class size.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK; // A result.
+ ClassLayoutRec *pClassLayout; // A classlayout record.
+ RID iClassLayout = 0; // RID of classlayout record.
+
+ // See if a ClassLayout record already exists for the given TypeDef.
+ IfFailGo(m_pStgdb->m_MiniMd.FindClassLayoutHelper(td, &iClassLayout));
+
+ if (InvalidRid(iClassLayout))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddClassLayoutRecord(&pClassLayout, &iClassLayout));
+ // Set the Parent entry.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ClassLayout, ClassLayoutRec::COL_Parent,
+ pClassLayout, td));
+ IfFailGo( m_pStgdb->m_MiniMd.AddClassLayoutToHash(iClassLayout) );
+ }
+ else
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetClassLayoutRecord(iClassLayout, &pClassLayout));
+ }
+
+ // Set the data.
+ if (dwPackSize != ULONG_MAX)
+ pClassLayout->SetPackingSize(static_cast<USHORT>(dwPackSize));
+ if (ulClassSize != ULONG_MAX)
+ pClassLayout->SetClassSize(ulClassSize);
+
+ // Create the log record for the non-token record.
+ IfFailGo(UpdateENCLog2(TBL_ClassLayout, iClassLayout));
+
+ErrExit:
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::_SetClassLayout
+
+//*****************************************************************************
+// Helper function to set a field offset for a given field def.
+//*****************************************************************************
+HRESULT RegMeta::_SetFieldOffset( // S_OK or error.
+ mdFieldDef fd, // [IN] The field.
+ ULONG ulOffset) // [IN] The offset of the field.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr;
+ FieldLayoutRec * pFieldLayoutRec=0; // A FieldLayout record.
+ RID iFieldLayoutRec=0; // RID of a FieldLayout record.
+
+ // See if an entry already exists for the Field in the FieldLayout table.
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldLayoutHelper(fd, &iFieldLayoutRec));
+ if (InvalidRid(iFieldLayoutRec))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddFieldLayoutRecord(&pFieldLayoutRec, &iFieldLayoutRec));
+ // Set the Field entry.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldLayout, FieldLayoutRec::COL_Field,
+ pFieldLayoutRec, fd));
+ IfFailGo( m_pStgdb->m_MiniMd.AddFieldLayoutToHash(iFieldLayoutRec) );
+ }
+ else
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldLayoutRecord(iFieldLayoutRec, &pFieldLayoutRec));
+ }
+
+ // Set the offset.
+ pFieldLayoutRec->SetOffSet(ulOffset);
+
+ // Create the log record for the non-token record.
+ IfFailGo(UpdateENCLog2(TBL_FieldLayout, iFieldLayoutRec));
+
+ErrExit:
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::_SetFieldOffset
+
+//*****************************************************************************
+// Delete the ClassLayout information.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DeleteClassLayout(
+ mdTypeDef td) // [IN] typdef token
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ClassLayoutRec *pClassLayoutRec;
+ TypeDefRec *pTypeDefRec;
+ FieldLayoutRec *pFieldLayoutRec;
+ RID iClassLayoutRec;
+ RID iFieldLayoutRec;
+ RID ridStart;
+ RID ridEnd;
+ RID ridCur;
+ ULONG index;
+
+ LOG((LOGMD, "MD RegMeta::DeleteClassLayout(0x%08x)\n", td));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(!m_bSaveOptimized && "Cannot change records after PreSave() and before Save().");
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && !IsNilToken(td));
+
+ // Get the ClassLayout record.
+ IfFailGo(m_pStgdb->m_MiniMd.FindClassLayoutHelper(td, &iClassLayoutRec));
+ if (InvalidRid(iClassLayoutRec))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetClassLayoutRecord(iClassLayoutRec, &pClassLayoutRec));
+
+ // Clear the parent.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ClassLayout,
+ ClassLayoutRec::COL_Parent,
+ pClassLayoutRec, mdTypeDefNil));
+
+ // Create the log record for the non-token record.
+ IfFailGo(UpdateENCLog2(TBL_ClassLayout, iClassLayoutRec));
+
+ // Delete all the corresponding FieldLayout records if there are any.
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+ ridStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pTypeDefRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(td), &ridEnd));
+
+ for (index = ridStart; index < ridEnd; index++)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(index, &ridCur));
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldLayoutHelper(TokenFromRid(ridCur, mdtFieldDef), &iFieldLayoutRec));
+ if (InvalidRid(iFieldLayoutRec))
+ continue;
+ else
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldLayoutRecord(iFieldLayoutRec, &pFieldLayoutRec));
+ // Set the Field entry.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldLayout, FieldLayoutRec::COL_Field,
+ pFieldLayoutRec, mdFieldDefNil));
+ // Create the log record for the non-token record.
+ IfFailGo(UpdateENCLog2(TBL_FieldLayout, iFieldLayoutRec));
+ }
+ }
+ErrExit:
+ STOP_MD_PERF(DeleteClassLayout);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::DeleteClassLayout
+
+//*****************************************************************************
+// Set the field's native type.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetFieldMarshal(
+ mdToken tk, // [IN] given a fieldDef or paramDef token
+ PCCOR_SIGNATURE pvNativeType, // [IN] native type specification
+ ULONG cbNativeType) // [IN] count of bytes of pvNativeType
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ LOG((LOGMD, "MD RegMeta::SetFieldMarshal(0x%08x, 0x%08x, 0x%08x)\n",
+ tk, pvNativeType, cbNativeType));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ hr = _SetFieldMarshal(tk, pvNativeType, cbNativeType);
+
+ErrExit:
+ STOP_MD_PERF(SetFieldMarshal);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetFieldMarshal
+
+HRESULT RegMeta::_SetFieldMarshal(
+ mdToken tk, // [IN] given a fieldDef or paramDef token
+ PCCOR_SIGNATURE pvNativeType, // [IN] native type specification
+ ULONG cbNativeType) // [IN] count of bytes of pvNativeType
+{
+ HRESULT hr = S_OK;
+ FieldMarshalRec *pFieldMarshRec;
+ RID iFieldMarshRec = 0; // initialize to invalid rid
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtParamDef);
+ _ASSERTE(!IsNilToken(tk));
+
+ // turn on the HasFieldMarshal
+ if (TypeFromToken(tk) == mdtFieldDef)
+ {
+ FieldRec *pFieldRec;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pFieldRec));
+ pFieldRec->AddFlags(fdHasFieldMarshal);
+ }
+ else // TypeFromToken(tk) == mdtParamDef
+ {
+ ParamRec *pParamRec;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(tk), &pParamRec));
+ pParamRec->AddFlags(pdHasFieldMarshal);
+ }
+ IfFailGo(UpdateENCLog(tk));
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldMarshalHelper(tk, &iFieldMarshRec));
+ if (InvalidRid(iFieldMarshRec))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddFieldMarshalRecord(&pFieldMarshRec, &iFieldMarshRec));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldMarshal, FieldMarshalRec::COL_Parent, pFieldMarshRec, tk));
+ IfFailGo( m_pStgdb->m_MiniMd.AddFieldMarshalToHash(iFieldMarshRec) );
+ }
+ else
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldMarshalRecord(iFieldMarshRec, &pFieldMarshRec));
+ }
+
+ // Set data.
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_FieldMarshal, FieldMarshalRec::COL_NativeType, pFieldMarshRec,
+ pvNativeType, cbNativeType));
+
+ // Create the log record for the non-token record.
+ IfFailGo(UpdateENCLog2(TBL_FieldMarshal, iFieldMarshRec));
+
+ErrExit:
+
+ return hr;
+} // RegMeta::_SetFieldMarshal
+
+
+//*****************************************************************************
+// Delete the FieldMarshal record for the given token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DeleteFieldMarshal(
+ mdToken tk) // [IN] fieldDef or paramDef token to be deleted.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ FieldMarshalRec *pFieldMarshRec;
+ RID iFieldMarshRec;
+
+
+
+ LOG((LOGMD, "MD RegMeta::DeleteFieldMarshal(0x%08x)\n", tk));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtParamDef);
+ _ASSERTE(!IsNilToken(tk));
+ _ASSERTE(!m_bSaveOptimized && "Cannot delete records after PreSave() and before Save().");
+
+ // Get the FieldMarshal record.
+ IfFailGo(m_pStgdb->m_MiniMd.FindFieldMarshalHelper(tk, &iFieldMarshRec));
+ if (InvalidRid(iFieldMarshRec))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldMarshalRecord(iFieldMarshRec, &pFieldMarshRec));
+ // Clear the parent token from the FieldMarshal record.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldMarshal,
+ FieldMarshalRec::COL_Parent, pFieldMarshRec, mdFieldDefNil));
+
+ // turn off the HasFieldMarshal
+ if (TypeFromToken(tk) == mdtFieldDef)
+ {
+ FieldRec *pFieldRec;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pFieldRec));
+ pFieldRec->RemoveFlags(fdHasFieldMarshal);
+ }
+ else // TypeFromToken(tk) == mdtParamDef
+ {
+ ParamRec *pParamRec;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(tk), &pParamRec));
+ pParamRec->RemoveFlags(pdHasFieldMarshal);
+ }
+
+ // Update the ENC log for the parent token.
+ IfFailGo(UpdateENCLog(tk));
+ // Create the log record for the non-token record.
+ IfFailGo(UpdateENCLog2(TBL_FieldMarshal, iFieldMarshRec));
+
+ErrExit:
+ STOP_MD_PERF(DeleteFieldMarshal);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::DeleteFieldMarshal
+
+//*****************************************************************************
+// Define a new permission set for an object.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefinePermissionSet(
+ mdToken tk, // [IN] the object to be decorated.
+ DWORD dwAction, // [IN] CorDeclSecurity.
+ void const *pvPermission, // [IN] permission blob.
+ ULONG cbPermission, // [IN] count of bytes of pvPermission.
+ mdPermission *ppm) // [OUT] returned permission token.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::DefinePermissionSet(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ tk, dwAction, pvPermission, cbPermission, ppm));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ IfFailGo(_DefinePermissionSet(tk, dwAction, pvPermission, cbPermission, ppm));
+
+ErrExit:
+ STOP_MD_PERF(DefinePermissionSet);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::DefinePermissionSet
+
+
+//*****************************************************************************
+// Define a new permission set for an object.
+//*****************************************************************************
+HRESULT RegMeta::_DefinePermissionSet(
+ mdToken tk, // [IN] the object to be decorated.
+ DWORD dwAction, // [IN] CorDeclSecurity.
+ void const *pvPermission, // [IN] permission blob.
+ ULONG cbPermission, // [IN] count of bytes of pvPermission.
+ mdPermission *ppm) // [OUT] returned permission token.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+ DeclSecurityRec *pDeclSec = NULL;
+ RID iDeclSec;
+ short sAction = static_cast<short>(dwAction); // To match with the type in DeclSecurityRec.
+ mdPermission tkPerm; // New permission token.
+
+ _ASSERTE(TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtMethodDef ||
+ TypeFromToken(tk) == mdtAssembly);
+
+ // Check for valid Action.
+ if (sAction == 0 || sAction > dclMaximumValue)
+ IfFailGo(E_INVALIDARG);
+
+ if (CheckDups(MDDupPermission))
+ {
+ hr = ImportHelper::FindPermission(&(m_pStgdb->m_MiniMd), tk, sAction, &tkPerm);
+
+ if (SUCCEEDED(hr))
+ {
+ // Set output parameter.
+ if (ppm)
+ *ppm = tkPerm;
+ if (IsENCOn())
+ IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(tkPerm), &pDeclSec));
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ // Create a new record.
+ if (!pDeclSec)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddDeclSecurityRecord(&pDeclSec, &iDeclSec));
+ tkPerm = TokenFromRid(iDeclSec, mdtPermission);
+
+ // Set output parameter.
+ if (ppm)
+ *ppm = tkPerm;
+
+ // Save parent and action information.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_DeclSecurity, DeclSecurityRec::COL_Parent, pDeclSec, tk));
+ pDeclSec->SetAction(sAction);
+
+ // Turn on the internal security flag on the parent.
+ if (TypeFromToken(tk) == mdtTypeDef)
+ IfFailGo(_TurnInternalFlagsOn(tk, tdHasSecurity));
+ else if (TypeFromToken(tk) == mdtMethodDef)
+ IfFailGo(_TurnInternalFlagsOn(tk, mdHasSecurity));
+ IfFailGo(UpdateENCLog(tk));
+ }
+
+ IfFailGo(_SetPermissionSetProps(tkPerm, sAction, pvPermission, cbPermission));
+ IfFailGo(UpdateENCLog(tkPerm));
+ErrExit:
+
+ STOP_MD_PERF(DefinePermissionSet);
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::_DefinePermissionSet
+
+
+
+//*****************************************************************************
+// Set the RVA of a methoddef
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetRVA( // [IN] S_OK or error.
+ mdToken md, // [IN] Member for which to set offset
+ ULONG ulRVA) // [IN] The offset#endif
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::SetRVA(0x%08x, 0x%08x)\n",
+ md, ulRVA));
+ START_MD_PERF();
+
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+ IfFailGo(_SetRVA(md, ulRVA, ULONG_MAX)); // 0xbaad
+
+ErrExit:
+ STOP_MD_PERF(SetRVA);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetRVA
+
+//*****************************************************************************
+// Given a signature, return a token to the user. If there isn't an existing
+// token, create a new record. This should more appropriately be called
+// DefineSignature.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetTokenFromSig( // [IN] S_OK or error.
+ PCCOR_SIGNATURE pvSig, // [IN] Signature to define.
+ ULONG cbSig, // [IN] Size of signature data.
+ mdSignature *pmsig) // [OUT] returned signature token.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::GetTokenFromSig(0x%08x, 0x%08x, 0x%08x)\n",
+ pvSig, cbSig, pmsig));
+ START_MD_PERF();
+
+ LOCKWRITE();
+
+ _ASSERTE(pmsig);
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+ IfFailGo(_GetTokenFromSig(pvSig, cbSig, pmsig));
+
+ErrExit:
+ STOP_MD_PERF(GetTokenFromSig);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::GetTokenFromSig
+
+//*****************************************************************************
+// Define and set a ModuleRef record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineModuleRef( // S_OK or error.
+ LPCWSTR szName, // [IN] DLL name
+ mdModuleRef *pmur) // [OUT] returned module ref token
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::DefineModuleRef(%S, 0x%08x)\n", MDSTR(szName), pmur));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ hr = _DefineModuleRef(szName, pmur);
+
+ErrExit:
+ STOP_MD_PERF(DefineModuleRef);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineModuleRef
+
+HRESULT RegMeta::_DefineModuleRef( // S_OK or error.
+ LPCWSTR szName, // [IN] DLL name
+ mdModuleRef *pmur) // [OUT] returned module ref token
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+ ModuleRefRec *pModuleRef = 0; // The ModuleRef record.
+ RID iModuleRef; // Rid of new ModuleRef record.
+ LPUTF8 szUTF8Name;
+ UTF8STR((LPCWSTR)szName, szUTF8Name);
+
+ _ASSERTE(szName && pmur);
+
+ // See if the given ModuleRef already exists. If it exists just return.
+ // Else create a new record.
+ if (CheckDups(MDDupModuleRef))
+ {
+ hr = ImportHelper::FindModuleRef(&(m_pStgdb->m_MiniMd), szUTF8Name, pmur);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ IfFailGo(m_pStgdb->m_MiniMd.GetModuleRefRecord(RidFromToken(*pmur), &pModuleRef));
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ if (!pModuleRef)
+ {
+ // Create new record and set the values.
+ IfFailGo(m_pStgdb->m_MiniMd.AddModuleRefRecord(&pModuleRef, &iModuleRef));
+
+ // Set the output parameter.
+ *pmur = TokenFromRid(iModuleRef, mdtModuleRef);
+ }
+
+ // Save the data.
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_ModuleRef, ModuleRefRec::COL_Name,
+ pModuleRef, szUTF8Name));
+ IfFailGo(UpdateENCLog(*pmur));
+
+ErrExit:
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::_DefineModuleRef
+
+//*****************************************************************************
+// Set the parent for the specified MemberRef.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetParent( // S_OK or error.
+ mdMemberRef mr, // [IN] Token for the ref to be fixed up.
+ mdToken tk) // [IN] The ref parent.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MemberRefRec *pMemberRef;
+
+ LOG((LOGMD, "MD RegMeta::SetParent(0x%08x, 0x%08x)\n",
+ mr, tk));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ _ASSERTE(TypeFromToken(mr) == mdtMemberRef);
+ _ASSERTE(IsNilToken(tk) || TypeFromToken(tk) == mdtTypeRef || TypeFromToken(tk) == mdtTypeDef ||
+ TypeFromToken(tk) == mdtModuleRef || TypeFromToken(tk) == mdtMethodDef);
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(mr), &pMemberRef));
+
+ // If the token is nil set it to to m_tdModule.
+ tk = IsNilToken(tk) ? m_tdModule : tk;
+
+ // Set the parent.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MemberRef, MemberRefRec::COL_Class, pMemberRef, tk));
+
+ // Add the updated MemberRef to the hash.
+ IfFailGo(m_pStgdb->m_MiniMd.AddMemberRefToHash(mr) );
+
+ IfFailGo(UpdateENCLog(mr));
+
+ErrExit:
+
+ STOP_MD_PERF(SetParent);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetParent
+
+//*****************************************************************************
+// Define an TypeSpec token given a type description.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetTokenFromTypeSpec( // [IN] S_OK or error.
+ PCCOR_SIGNATURE pvSig, // [IN] Signature to define.
+ ULONG cbSig, // [IN] Size of signature data.
+ mdTypeSpec *ptypespec) // [OUT] returned signature token.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ TypeSpecRec *pTypeSpecRec;
+ RID iRec;
+
+ LOG((LOGMD, "MD RegMeta::GetTokenFromTypeSpec(0x%08x, 0x%08x, 0x%08x)\n",
+ pvSig, cbSig, ptypespec));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ _ASSERTE(ptypespec);
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ if (CheckDups(MDDupTypeSpec))
+ {
+ hr = ImportHelper::FindTypeSpec(&(m_pStgdb->m_MiniMd), pvSig, cbSig, ptypespec);
+ if (SUCCEEDED(hr))
+ {
+ //@GENERICS: Generalizing from similar code in this file, should we not set
+ // hr = META_S_DUPLICATE;
+ // here?
+ goto ErrExit;
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ // Create a new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddTypeSpecRecord(&pTypeSpecRec, &iRec));
+
+ // Set output parameter.
+ *ptypespec = TokenFromRid(iRec, mdtTypeSpec);
+
+ // Set the signature field
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(
+ TBL_TypeSpec,
+ TypeSpecRec::COL_Signature,
+ pTypeSpecRec,
+ pvSig,
+ cbSig));
+ IfFailGo(UpdateENCLog(*ptypespec));
+
+ErrExit:
+
+ STOP_MD_PERF(GetTokenFromTypeSpec);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::GetTokenFromTypeSpec
+
+//*****************************************************************************
+// This API defines a user literal string to be stored in the MetaData section.
+// The token for this string has embedded in it the offset into the BLOB pool
+// where the string is stored in UNICODE format. An additional byte is padded
+// at the end to indicate whether the string has any characters that are >= 0x80.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineUserString( // S_OK or error.
+ LPCWSTR szString, // [IN] User literal string.
+ ULONG cchString, // [IN] Length of string.
+ mdString *pstk) // [OUT] String token.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ UINT32 nIndex; // Index into the user string heap.
+ CQuickBytes qb; // For storing the string with the byte prefix.
+ ULONG i; // Loop counter.
+ BOOL bIs80Plus = false; // Is there an 80+ WCHAR.
+ ULONG ulMemSize; // Size of memory taken by the string passed in.
+ PBYTE pb; // Pointer into memory allocated by qb.
+ WCHAR c; // Temporary used during comparison;
+
+
+
+ LOG((LOGMD, "MD RegMeta::DefineUserString(0x%08x, 0x%08x, 0x%08x)\n",
+ szString, cchString, pstk));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(pstk && szString && cchString != ULONG_MAX);
+
+ //
+ // Walk the entire string looking for characters that would block us from doing
+ // a fast comparison of the string. These characters include anything greater than
+ // 0x80 or an apostrophe or a hyphen. Apostrophe and hyphen are excluded because
+ // they would prevent words like coop and co-op from sorting together in a culture-aware
+ // comparison. We also need to exclude some set of the control characters. This check
+ // is more restrictive
+ //
+ for (i=0; i<cchString; i++) {
+ c = szString[i];
+ if (c>=0x80 || HighCharHelper::IsHighChar((int)c)) {
+ bIs80Plus = true;
+ break;
+ }
+ }
+
+ // Copy over the string to memory.
+ ulMemSize = cchString * sizeof(WCHAR);
+ IfFailGo(qb.ReSizeNoThrow(ulMemSize + 1));
+ pb = reinterpret_cast<PBYTE>(qb.Ptr());
+ memcpy(pb, szString, ulMemSize);
+ SwapStringLength((WCHAR *) pb, cchString);
+ // Set the last byte of memory to indicate whether there is a 80+ character.
+ *(pb + ulMemSize) = bIs80Plus ? 1 : 0;
+
+ IfFailGo(m_pStgdb->m_MiniMd.PutUserString(
+ MetaData::DataBlob(pb, ulMemSize + 1),
+ &nIndex));
+
+ // Fail if the offset requires the high byte which is reserved for the token ID.
+ if (nIndex & 0xff000000)
+ IfFailGo(META_E_STRINGSPACE_FULL);
+ else
+ *pstk = TokenFromRid(nIndex, mdtString);
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ STOP_MD_PERF(DefineUserString);
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineUserString
+
+//*****************************************************************************
+// Delete a token.
+// We only allow deleting a subset of tokens at this moment. These are TypeDef,
+// MethodDef, FieldDef, Event, Property, and CustomAttribute. Except
+// CustomAttribute, all the other tokens are named. We reserved a special
+// name COR_DELETED_NAME_A to indicating a named record is deleted when
+// xxRTSpecialName is set.
+//*****************************************************************************
+
+STDMETHODIMP RegMeta::DeleteToken(
+ mdToken tkObj) // [IN] The token to be deleted
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::DeleteToken(0x%08x)\n", tkObj));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ if (!IsValidToken(tkObj))
+ IfFailGo( E_INVALIDARG );
+
+ // make sure that MetaData scope is opened for incremental compilation
+ if (!m_pStgdb->m_MiniMd.HasDelete())
+ {
+ _ASSERTE( !"You cannot call delete token when you did not open the scope with proper Update flags in the SetOption!");
+ IfFailGo( E_INVALIDARG );
+ }
+
+ _ASSERTE(!m_bSaveOptimized && "Cannot delete records after PreSave() and before Save().");
+
+ switch ( TypeFromToken(tkObj) )
+ {
+ case mdtTypeDef:
+ {
+ TypeDefRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tkObj), &pRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeDef, TypeDefRec::COL_Name, pRecord, COR_DELETED_NAME_A));
+ pRecord->AddFlags(tdSpecialName | tdRTSpecialName);
+ break;
+ }
+ case mdtMethodDef:
+ {
+ MethodRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkObj), &pRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Method, MethodRec::COL_Name, pRecord, COR_DELETED_NAME_A));
+ pRecord->AddFlags(mdSpecialName | mdRTSpecialName);
+ break;
+ }
+ case mdtFieldDef:
+ {
+ FieldRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tkObj), &pRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Field, FieldRec::COL_Name, pRecord, COR_DELETED_NAME_A));
+ pRecord->AddFlags(fdSpecialName | fdRTSpecialName);
+ break;
+ }
+ case mdtEvent:
+ {
+ EventRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(tkObj), &pRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Event, EventRec::COL_Name, pRecord, COR_DELETED_NAME_A));
+ pRecord->AddEventFlags(evSpecialName | evRTSpecialName);
+ break;
+ }
+ case mdtProperty:
+ {
+ PropertyRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(tkObj), &pRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Property, PropertyRec::COL_Name, pRecord, COR_DELETED_NAME_A));
+ pRecord->AddPropFlags(prSpecialName | prRTSpecialName);
+ break;
+ }
+ case mdtExportedType:
+ {
+ ExportedTypeRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(RidFromToken(tkObj), &pRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_ExportedType, ExportedTypeRec::COL_TypeName, pRecord, COR_DELETED_NAME_A));
+ break;
+ }
+ case mdtCustomAttribute:
+ {
+ mdToken tkParent;
+ CustomAttributeRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(tkObj), &pRecord));
+
+ // replace the parent column of the custom value record to a nil token.
+ tkParent = m_pStgdb->m_MiniMd.getParentOfCustomAttribute(pRecord);
+ tkParent = TokenFromRid( mdTokenNil, TypeFromToken(tkParent) );
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Parent, pRecord, tkParent));
+
+ // now the CustomAttribute table is no longer sorted
+ m_pStgdb->m_MiniMd.SetSorted(TBL_CustomAttribute, false);
+ break;
+ }
+ case mdtGenericParam:
+ {
+ mdToken tkParent;
+ GenericParamRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamRecord(RidFromToken(tkObj), &pRecord));
+
+ // replace the Parent column of the GenericParam record with a nil token.
+ tkParent = m_pStgdb->m_MiniMd.getOwnerOfGenericParam(pRecord);
+ tkParent = TokenFromRid( mdTokenNil, TypeFromToken(tkParent) );
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_GenericParam, GenericParamRec::COL_Owner,
+ pRecord, tkParent));
+
+ // now the GenericParam table is no longer sorted
+ m_pStgdb->m_MiniMd.SetSorted(TBL_GenericParam, false);
+ break;
+ }
+ case mdtGenericParamConstraint:
+ {
+ GenericParamConstraintRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamConstraintRecord(RidFromToken(tkObj), &pRecord));
+
+ // replace the Param column of the GenericParamConstraint record with zero RID.
+ IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_GenericParamConstraint,
+ GenericParamConstraintRec::COL_Owner,pRecord, 0));
+ // now the GenericParamConstraint table is no longer sorted
+ m_pStgdb->m_MiniMd.SetSorted(TBL_GenericParamConstraint, false);
+ break;
+ }
+ case mdtPermission:
+ {
+ mdToken tkParent;
+ mdToken tkNil;
+ DeclSecurityRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(tkObj), &pRecord));
+
+ // Replace the parent column of the permission record with a nil tokne.
+ tkParent = m_pStgdb->m_MiniMd.getParentOfDeclSecurity(pRecord);
+ tkNil = TokenFromRid( mdTokenNil, TypeFromToken(tkParent) );
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_DeclSecurity, DeclSecurityRec::COL_Parent, pRecord, tkNil ));
+
+ // The table is no longer sorted.
+ m_pStgdb->m_MiniMd.SetSorted(TBL_DeclSecurity, false);
+
+ // If the parent has no more security attributes, turn off the "has security" bit.
+ HCORENUM hEnum = 0;
+ mdPermission rPerms[1];
+ ULONG cPerms = 0;
+ EnumPermissionSets(&hEnum, tkParent, 0 /* all actions */, rPerms, 1, &cPerms);
+ CloseEnum(hEnum);
+ if (cPerms == 0)
+ {
+ void *pRow;
+ ULONG ixTbl;
+ // Get the row for the parent object.
+ ixTbl = m_pStgdb->m_MiniMd.GetTblForToken(tkParent);
+ _ASSERTE(ixTbl >= 0 && ixTbl <= m_pStgdb->m_MiniMd.GetCountTables());
+ IfFailGo(m_pStgdb->m_MiniMd.getRow(ixTbl, RidFromToken(tkParent), &pRow));
+
+ switch (TypeFromToken(tkParent))
+ {
+ case mdtTypeDef:
+ reinterpret_cast<TypeDefRec*>(pRow)->RemoveFlags(tdHasSecurity);
+ break;
+ case mdtMethodDef:
+ reinterpret_cast<MethodRec*>(pRow)->RemoveFlags(mdHasSecurity);
+ break;
+ case mdtAssembly:
+ // No security bit.
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ _ASSERTE(!"Bad token type!");
+ IfFailGo( E_INVALIDARG );
+ break;
+ }
+
+ ErrExit:
+
+ STOP_MD_PERF(DeleteToken);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::DeleteToken
+
+//*****************************************************************************
+// Set the properties on the given TypeDef token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetTypeDefProps( // S_OK or error.
+ mdTypeDef td, // [IN] The TypeDef.
+ DWORD dwTypeDefFlags, // [IN] TypeDef flags.
+ mdToken tkExtends, // [IN] Base TypeDef or TypeRef.
+ mdToken rtkImplements[]) // [IN] Implemented interfaces.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::SetTypeDefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, dwTypeDefFlags, tkExtends, rtkImplements));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ hr = _SetTypeDefProps(td, dwTypeDefFlags, tkExtends, rtkImplements);
+
+ErrExit:
+
+ STOP_MD_PERF(SetTypeDefProps);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetTypeDefProps
+
+
+//*****************************************************************************
+// Define a Nested Type.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineNestedType( // S_OK or error.
+ LPCWSTR szTypeDef, // [IN] Name of TypeDef
+ DWORD dwTypeDefFlags, // [IN] CustomAttribute flags
+ mdToken tkExtends, // [IN] extends this TypeDef or typeref
+ mdToken rtkImplements[], // [IN] Implements interfaces
+ mdTypeDef tdEncloser, // [IN] TypeDef token of the enclosing type.
+ mdTypeDef *ptd) // [OUT] Put TypeDef token here
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::DefineNestedType(%S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ MDSTR(szTypeDef), dwTypeDefFlags, tkExtends,
+ rtkImplements, tdEncloser, ptd));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(tdEncloser) == mdtTypeDef && !IsNilToken(tdEncloser));
+ _ASSERTE(IsTdNested(dwTypeDefFlags));
+
+ IfFailGo(_DefineTypeDef(szTypeDef, dwTypeDefFlags,
+ tkExtends, rtkImplements, tdEncloser, ptd));
+
+ErrExit:
+ STOP_MD_PERF(DefineNestedType);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineNestedType
+
+//*****************************************************************************
+// Define a formal type parameter for the given TypeDef or MethodDef token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineGenericParam( // S_OK or error.
+ mdToken tkOwner, // [IN] TypeDef or MethodDef
+ ULONG ulParamSeq, // [IN] Index of the type parameter
+ DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance)
+ LPCWSTR szName, // [IN] Name
+ DWORD reserved, // [IN] For future use
+ mdToken rtkConstraints[], // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec)
+ mdGenericParam *pgp) // [OUT] Put GenericParam token here
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ mdToken tkRet = mdGenericParamNil;
+ mdToken tkOwnerType = TypeFromToken(tkOwner);
+
+ LOG((LOGMD, "RegMeta::DefineGenericParam(0x%08x, %d, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ tkOwner, ulParamSeq, dwParamFlags, szName, reserved, rtkConstraints, pgp));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ if (reserved != 0)
+ IfFailGo(META_E_BAD_INPUT_PARAMETER);
+
+ // See if this version of the metadata can do Generics
+ if (!m_pStgdb->m_MiniMd.SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+ if ((tkOwnerType == mdtTypeDef) || (tkOwnerType == mdtMethodDef))
+ {
+ // 1. Find/create GP (unique tkOwner+ulParamSeq) = tkRet
+ GenericParamRec *pGenericParam = NULL;
+ RID iGenericParam,rid;
+ RID ridStart;
+ RID ridEnd;
+
+ // See if this GenericParam has already been defined.
+ if (CheckDups(MDDupGenericParam))
+ {
+ // Enumerate any GenericParams for the parent, looking for this sequence number.
+ IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamsForToken(tkOwner, &ridStart, &ridEnd));
+ for (rid = ridStart; rid < ridEnd; rid++)
+ {
+ iGenericParam = m_pStgdb->m_MiniMd.GetGenericParamRid(rid);
+ IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamRecord(iGenericParam, &pGenericParam));
+ // Is this the desired GenericParam #?
+ if (pGenericParam->GetNumber() == (USHORT)ulParamSeq)
+ {
+ tkRet = TokenFromRid(iGenericParam,mdtGenericParam);
+ // This is a duplicate. If not ENC, just return 'DUPLICATE'. If ENC, overwrite.
+ if (!IsENCOn())
+ {
+ IfFailGo(META_S_DUPLICATE);
+ }
+ break;
+ }
+ }
+ }
+ else
+ { // Clear rid, ridStart, ridEnd, so we no we didn't find one.
+ rid = ridStart = ridEnd = 0;
+ }
+
+ // If none was found, create one.
+ if(rid >= ridEnd)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddGenericParamRecord(&pGenericParam, &iGenericParam));
+ pGenericParam->SetNumber((USHORT)ulParamSeq);
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_GenericParam, GenericParamRec::COL_Owner,
+ pGenericParam, tkOwner));
+ tkRet = TokenFromRid(iGenericParam,mdtGenericParam);
+ }
+
+ // 2. Set its props
+ IfFailGo(_SetGenericParamProps(tkRet, pGenericParam, dwParamFlags, szName, reserved ,rtkConstraints));
+ IfFailGo(UpdateENCLog(tkRet));
+ }
+ else
+ hr = META_E_BAD_INPUT_PARAMETER;
+
+ErrExit:
+
+ if(pgp != NULL)
+ *pgp = tkRet;
+ STOP_MD_PERF(DefineGenericParam);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineGenericParam
+
+//*****************************************************************************
+// Set props of a formal type parameter.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetGenericParamProps( // S_OK or error.
+ mdGenericParam gp, // [IN] GenericParam
+ DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance)
+ LPCWSTR szName, // [IN] Optional name
+ DWORD reserved, // [IN] For future use (e.g. non-type parameters)
+ mdToken rtkConstraints[]) // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec)
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::SetGenericParamProps(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ gp, dwParamFlags,szName,reserved,rtkConstraints));
+ START_MD_PERF();
+
+ if (reserved != 0)
+ IfFailGo(META_E_BAD_INPUT_PARAMETER);
+
+ // See if this version of the metadata can do Generics
+ if (!m_pStgdb->m_MiniMd.SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+ if (TypeFromToken(gp) == mdtGenericParam)
+ {
+ GenericParamRec *pGenericParam;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamRecord(RidFromToken(gp), &pGenericParam));
+ IfFailGo(_SetGenericParamProps(gp,pGenericParam,dwParamFlags,szName,reserved,rtkConstraints));
+ IfFailGo(UpdateENCLog(gp));
+ }
+ else
+ hr = META_E_BAD_INPUT_PARAMETER;
+
+ErrExit:
+ STOP_MD_PERF(SetGenericParamProps);
+
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetGenericParamProps
+
+//*****************************************************************************
+// Set props of a formal type parameter (internal).
+//*****************************************************************************
+HRESULT RegMeta::_SetGenericParamProps( // S_OK or error.
+ mdGenericParam tkGP, // [IN] Formal parameter token
+ GenericParamRec *pGenericParam, // [IN] GenericParam record ptr
+ DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance)
+ LPCWSTR szName, // [IN] Optional name
+ DWORD reserved, // [IN] For future use (e.g. non-type parameters)
+ mdToken rtkConstraints[]) // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec)
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ if (pGenericParam != NULL)
+ {
+ // If there is a name, set it.
+ if ((szName != NULL) && (*szName != 0))
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_GenericParam, GenericParamRec::COL_Name,
+ pGenericParam, szName));
+
+ // If there are new flags, set them.
+ if (dwParamFlags != (DWORD) -1)
+ pGenericParam->SetFlags((USHORT)dwParamFlags);
+
+ // If there is a new array of constraints, apply it.
+ if (rtkConstraints != NULL)
+ {
+ //Clear existing constraints
+ GenericParamConstraintRec* pGPCRec;
+ RID ridGPC;
+ RID rid;
+ RID ridStart;
+ RID ridEnd;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamConstraintsForToken(tkGP, &ridStart, &ridEnd));
+ for (rid = ridStart; rid < ridEnd; rid++)
+ {
+ ridGPC = m_pStgdb->m_MiniMd.GetGenericParamConstraintRid(rid);
+ IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamConstraintRecord(ridGPC, &pGPCRec));
+ IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_GenericParamConstraint,
+ GenericParamConstraintRec::COL_Owner,
+ pGPCRec, 0));
+ IfFailGo(UpdateENCLog(TokenFromRid(ridGPC,mdtGenericParamConstraint)));
+ }
+
+ //Emit new constraints
+ mdToken* ptk;
+ for (ptk = rtkConstraints; (ptk != NULL)&&(RidFromToken(*ptk)!=0); ptk++)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddGenericParamConstraintRecord(&pGPCRec, &ridGPC));
+ IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_GenericParamConstraint,
+ GenericParamConstraintRec::COL_Owner,
+ pGPCRec, RidFromToken(tkGP)));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_GenericParamConstraint,
+ GenericParamConstraintRec::COL_Constraint,
+ pGPCRec, *ptk));
+ IfFailGo(UpdateENCLog(TokenFromRid(ridGPC,mdtGenericParamConstraint)));
+ }
+ }
+ }
+ else
+ hr = META_E_BAD_INPUT_PARAMETER;
+
+ErrExit:
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::_SetGenericParamProps
+
+//*****************************************************************************
+// Create and set a MethodSpec record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineMethodSpec( // S_OK or error
+ mdToken tkImport, // [IN] MethodDef or MemberRef
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodSpec *pmi) // [OUT] method instantiation token
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MethodSpecRec *pRecord = 0; // The MethodSpec record.
+ RID iRecord; // RID of new MethodSpec record.
+
+
+ LOG((LOGMD, "MD RegMeta::DefineMethodSpec(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ tkImport, pvSigBlob, cbSigBlob, pmi));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ // See if this version of the metadata can do Generics
+ if (!m_pStgdb->m_MiniMd.SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ // Check that it is a method, or at least memberref.
+ if ((TypeFromToken(tkImport) != mdtMethodDef) && (TypeFromToken(tkImport) != mdtMemberRef))
+ IfFailGo(META_E_BAD_INPUT_PARAMETER);
+
+ // Must have a signature, and someplace to return the token.
+ if ((pvSigBlob == NULL) || (cbSigBlob == 0) || (pmi == NULL))
+ IfFailGo(META_E_BAD_INPUT_PARAMETER);
+
+ // If the MethodSpec already exists, just return the token, else
+ // create a new record.
+ if (CheckDups(MDDupMethodSpec))
+ {
+ hr = ImportHelper::FindMethodSpecByMethodAndInstantiation(&(m_pStgdb->m_MiniMd), tkImport,pvSigBlob, cbSigBlob, pmi);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn()) //GENERICS: is this correct? Do we really want to support ENC of MethodSpecs?
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodSpecRecord(RidFromToken(*pmi), &pRecord));
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND) // MemberRef exists
+ IfFailGo(hr);
+ }
+
+
+ if (!pRecord)
+ { // Create the record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddMethodSpecRecord(&pRecord, &iRecord));
+
+ /*GENERICS: do we need to do anything like this?
+ Probably not, since SetMemberDefDirty is for ref to def optimization, and there are no method spec "refs".
+ // record the more defs are introduced.
+ SetMemberDefDirty(true);
+ */
+
+ // Give token to caller.
+ *pmi = TokenFromRid(iRecord, mdtMethodSpec);
+ }
+
+ // Save row data.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodSpec, MethodSpecRec::COL_Method, pRecord, tkImport));
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_MethodSpec, MethodSpecRec::COL_Instantiation, pRecord,
+ pvSigBlob, cbSigBlob));
+ /*@GENERICS: todo: update MethodSpec hash table */
+ /* IfFailGo(m_pStgdb->m_MiniMd.AddMemberRefToHash(*pmi) ); */
+
+ IfFailGo(UpdateENCLog(*pmi));
+
+ErrExit:
+
+ STOP_MD_PERF(DefineMethodSpec);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineMethodSpec
+
+//*****************************************************************************
+// Set the properties on the given Method token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetMethodProps( // S_OK or error.
+ mdMethodDef md, // [IN] The MethodDef.
+ DWORD dwMethodFlags, // [IN] Method attributes.
+ ULONG ulCodeRVA, // [IN] Code RVA.
+ DWORD dwImplFlags) // [IN] MethodImpl flags.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::SetMethodProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ md, dwMethodFlags, ulCodeRVA, dwImplFlags));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ if (dwMethodFlags != ULONG_MAX)
+ {
+ // Make sure no one sets the reserved bits on the way in.
+ _ASSERTE((dwMethodFlags & (mdReservedMask&~mdRTSpecialName)) == 0);
+ dwMethodFlags &= (~mdReservedMask);
+ }
+
+ hr = _SetMethodProps(md, dwMethodFlags, ulCodeRVA, dwImplFlags);
+
+ErrExit:
+
+ STOP_MD_PERF(SetMethodProps);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetMethodProps
+
+//*****************************************************************************
+// Set the properties on the given Event token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetEventProps( // S_OK or error.
+ mdEvent ev, // [IN] The event token.
+ DWORD dwEventFlags, // [IN] CorEventAttr.
+ mdToken tkEventType, // [IN] A reference (mdTypeRef or mdTypeRef) to the Event class.
+ mdMethodDef mdAddOn, // [IN] Add method.
+ mdMethodDef mdRemoveOn, // [IN] Remove method.
+ mdMethodDef mdFire, // [IN] Fire method.
+ mdMethodDef rmdOtherMethods[]) // [IN] Array of other methods associate with the event.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::SetEventProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ ev, dwEventFlags, tkEventType, mdAddOn, mdRemoveOn, mdFire, rmdOtherMethods));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ IfFailGo(_SetEventProps1(ev, dwEventFlags, tkEventType));
+ IfFailGo(_SetEventProps2(ev, mdAddOn, mdRemoveOn, mdFire, rmdOtherMethods, true));
+
+ErrExit:
+
+ STOP_MD_PERF(SetEventProps);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetEventProps
+
+//*****************************************************************************
+// Set the properties on the given Permission token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetPermissionSetProps( // S_OK or error.
+ mdToken tk, // [IN] The object to be decorated.
+ DWORD dwAction, // [IN] CorDeclSecurity.
+ void const *pvPermission, // [IN] Permission blob.
+ ULONG cbPermission, // [IN] Count of bytes of pvPermission.
+ mdPermission *ppm) // [OUT] Permission token.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ USHORT sAction = static_cast<USHORT>(dwAction); // Corresponding DeclSec field is a USHORT.
+ mdPermission tkPerm;
+
+ LOG((LOGMD, "MD RegMeta::SetPermissionSetProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ tk, dwAction, pvPermission, cbPermission, ppm));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtMethodDef ||
+ TypeFromToken(tk) == mdtAssembly);
+
+ // Check for valid Action.
+ if (dwAction == ULONG_MAX || dwAction == 0 || dwAction > dclMaximumValue)
+ IfFailGo(E_INVALIDARG);
+
+ IfFailGo(ImportHelper::FindPermission(&(m_pStgdb->m_MiniMd), tk, sAction, &tkPerm));
+ if (ppm)
+ *ppm = tkPerm;
+ IfFailGo(_SetPermissionSetProps(tkPerm, dwAction, pvPermission, cbPermission));
+ErrExit:
+
+ STOP_MD_PERF(SetPermissionSetProps);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::SetPermissionSetProps
+
+//*****************************************************************************
+// This routine sets the p-invoke information for the specified Field or Method.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefinePinvokeMap( // Return code.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD dwMappingFlags, // [IN] Flags used for mapping.
+ LPCWSTR szImportName, // [IN] Import name.
+ mdModuleRef mrImportDLL) // [IN] ModuleRef token for the target DLL.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::DefinePinvokeMap(0x%08x, 0x%08x, %S, 0x%08x)\n",
+ tk, dwMappingFlags, MDSTR(szImportName), mrImportDLL));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ hr = _DefinePinvokeMap(tk, dwMappingFlags, szImportName, mrImportDLL);
+
+ErrExit:
+
+ STOP_MD_PERF(DefinePinvokeMap);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefinePinvokeMap
+
+//*****************************************************************************
+// Internal worker function for setting p-invoke info.
+//*****************************************************************************
+HRESULT RegMeta::_DefinePinvokeMap( // Return hresult.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD dwMappingFlags, // [IN] Flags used for mapping.
+ LPCWSTR szImportName, // [IN] Import name.
+ mdModuleRef mrImportDLL) // [IN] ModuleRef token for the target DLL.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ ImplMapRec *pRecord;
+ ULONG iRecord;
+ bool bDupFound = false;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtMethodDef);
+ _ASSERTE(TypeFromToken(mrImportDLL) == mdtModuleRef);
+ _ASSERTE(RidFromToken(tk) && RidFromToken(mrImportDLL) && szImportName);
+
+ // Turn on the quick lookup flag.
+ if (TypeFromToken(tk) == mdtMethodDef)
+ {
+ if (CheckDups(MDDupMethodDef))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord));
+ if (! InvalidRid(iRecord))
+ bDupFound = true;
+ }
+ MethodRec *pMethod;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethod));
+ pMethod->AddFlags(mdPinvokeImpl);
+ }
+ else // TypeFromToken(tk) == mdtFieldDef
+ {
+ if (CheckDups(MDDupFieldDef))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord));
+ if (!InvalidRid(iRecord))
+ bDupFound = true;
+ }
+ FieldRec *pField;
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pField));
+ pField->AddFlags(fdPinvokeImpl);
+ }
+
+ // Create a new record.
+ if (bDupFound)
+ {
+ if (IsENCOn())
+ IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(RidFromToken(iRecord), &pRecord));
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else
+ {
+ IfFailGo(UpdateENCLog(tk));
+ IfFailGo(m_pStgdb->m_MiniMd.AddImplMapRecord(&pRecord, &iRecord));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ImplMap,
+ ImplMapRec::COL_MemberForwarded, pRecord, tk));
+ IfFailGo( m_pStgdb->m_MiniMd.AddImplMapToHash(iRecord) );
+
+ }
+
+ // If no module, create a dummy, empty module.
+ if (IsNilToken(mrImportDLL))
+ {
+ hr = ImportHelper::FindModuleRef(&m_pStgdb->m_MiniMd, "", &mrImportDLL);
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(_DefineModuleRef(W(""), &mrImportDLL));
+ }
+
+ // Set the data.
+ if (dwMappingFlags != ULONG_MAX)
+ pRecord->SetMappingFlags(static_cast<USHORT>(dwMappingFlags));
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_ImplMap, ImplMapRec::COL_ImportName,
+ pRecord, szImportName));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ImplMap,
+ ImplMapRec::COL_ImportScope, pRecord, mrImportDLL));
+
+ IfFailGo(UpdateENCLog2(TBL_ImplMap, iRecord));
+
+ErrExit:
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefinePinvokeMap
+
+//*****************************************************************************
+// This routine sets the p-invoke information for the specified Field or Method.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetPinvokeMap( // Return code.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD dwMappingFlags, // [IN] Flags used for mapping.
+ LPCWSTR szImportName, // [IN] Import name.
+ mdModuleRef mrImportDLL) // [IN] ModuleRef token for the target DLL.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ImplMapRec *pRecord;
+ ULONG iRecord;
+
+ LOG((LOGMD, "MD RegMeta::SetPinvokeMap(0x%08x, 0x%08x, %S, 0x%08x)\n",
+ tk, dwMappingFlags, MDSTR(szImportName), mrImportDLL));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtMethodDef);
+ _ASSERTE(RidFromToken(tk));
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord));
+
+ if (InvalidRid(iRecord))
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+ else
+ IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(iRecord, &pRecord));
+
+ // Set the data.
+ if (dwMappingFlags != ULONG_MAX)
+ pRecord->SetMappingFlags(static_cast<USHORT>(dwMappingFlags));
+ if (szImportName)
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_ImplMap, ImplMapRec::COL_ImportName,
+ pRecord, szImportName));
+ if (! IsNilToken(mrImportDLL))
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ImplMap, ImplMapRec::COL_ImportScope,
+ pRecord, mrImportDLL));
+
+ IfFailGo(UpdateENCLog2(TBL_ImplMap, iRecord));
+
+ErrExit:
+
+ STOP_MD_PERF(SetPinvokeMap);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetPinvokeMap
+
+//*****************************************************************************
+// This routine deletes the p-invoke record for the specified Field or Method.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DeletePinvokeMap( // Return code.
+ mdToken tk) // [IN]FieldDef or MethodDef.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ImplMapRec *pRecord;
+ RID iRecord;
+
+ LOG((LOGMD, "MD RegMeta::DeletePinvokeMap(0x%08x)\n", tk));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtMethodDef);
+ _ASSERTE(!IsNilToken(tk));
+ _ASSERTE(!m_bSaveOptimized && "Cannot delete records after PreSave() and before Save().");
+
+ // Get the PinvokeMap record.
+ IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord));
+ if (InvalidRid(iRecord))
+ {
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+ }
+ IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(iRecord, &pRecord));
+
+ // Clear the MemberForwarded token from the PinvokeMap record.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ImplMap,
+ ImplMapRec::COL_MemberForwarded, pRecord, mdFieldDefNil));
+
+ // turn off the PinvokeImpl bit.
+ if (TypeFromToken(tk) == mdtFieldDef)
+ {
+ FieldRec *pFieldRec;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pFieldRec));
+ pFieldRec->RemoveFlags(fdPinvokeImpl);
+ }
+ else // TypeFromToken(tk) == mdtMethodDef
+ {
+ MethodRec *pMethodRec;
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethodRec));
+ pMethodRec->RemoveFlags(mdPinvokeImpl);
+ }
+
+ // Update the ENC log for the parent token.
+ IfFailGo(UpdateENCLog(tk));
+ // Create the log record for the non-token record.
+ IfFailGo(UpdateENCLog2(TBL_ImplMap, iRecord));
+
+ErrExit:
+ STOP_MD_PERF(DeletePinvokeMap);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::DeletePinvokeMap
+
+//*****************************************************************************
+// Create and define a new FieldDef record.
+//*****************************************************************************
+HRESULT RegMeta::DefineField( // S_OK or error.
+ mdTypeDef td, // Parent TypeDef
+ LPCWSTR szName, // Name of member
+ DWORD dwFieldFlags, // Member attributes
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] constant value
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdFieldDef *pmd) // [OUT] Put member token here
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ FieldRec *pRecord = NULL; // The new record.
+ RID iRecord; // RID of new record.
+ LPUTF8 szNameUtf8;
+ UTF8STR(szName, szNameUtf8);
+
+ LOG((LOGMD, "MD: RegMeta::DefineField(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, MDSTR(szName), dwFieldFlags, pvSigBlob, cbSigBlob, dwCPlusTypeFlag, pValue, cchValue, pmd));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ _ASSERTE(pmd);
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+ IsGlobalMethodParent(&td);
+
+ // Validate flags.
+ if (dwFieldFlags != ULONG_MAX)
+ {
+ // fdHasFieldRVA is settable, but not re-settable by applications.
+ _ASSERTE((dwFieldFlags & (fdReservedMask&~(fdHasFieldRVA|fdRTSpecialName))) == 0);
+ dwFieldFlags &= ~(fdReservedMask&~fdHasFieldRVA);
+ }
+
+ // See if this field has already been defined as a forward reference
+ // from a MemberRef. If so, then update the data to match what we know now.
+ if (CheckDups(MDDupFieldDef))
+ {
+
+ hr = ImportHelper::FindField(&(m_pStgdb->m_MiniMd),
+ td,
+ szNameUtf8,
+ pvSigBlob,
+ cbSigBlob,
+ pmd);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(*pmd), &pRecord));
+ }
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailGo(hr);
+ }
+ }
+
+ // Create a new record.
+ if (pRecord == NULL)
+ {
+ // Create the field record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddFieldRecord(&pRecord, &iRecord));
+
+ // Set output parameter pmd.
+ *pmd = TokenFromRid(iRecord, mdtFieldDef);
+
+ // Add to parent's list of child records.
+ IfFailGo(m_pStgdb->m_MiniMd.AddFieldToTypeDef(RidFromToken(td), iRecord));
+
+ IfFailGo(UpdateENCLog(td, CMiniMdRW::eDeltaFieldCreate));
+
+ // record the more defs are introduced.
+ SetMemberDefDirty(true);
+ }
+
+ // Set the Field properties.
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Field, FieldRec::COL_Name, pRecord, szNameUtf8));
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Field, FieldRec::COL_Signature, pRecord,
+ pvSigBlob, cbSigBlob));
+
+ // Check to see if it is value__ for enum type
+ // <TODO>@FUTURE: shouldn't we have checked the type containing the field to be a Enum type first of all?</TODO>
+ // value__ is defined in corhdr.h. However, corhdr.h does not have the
+ // the W() macro we need (since it's distributed to windows). We substitute the values of the
+ // macro in the code below to work around this issue.
+ // #define COR_ENUM_FIELD_NAME_W L"value__"
+
+ if (!wcscmp(szName, W("value__")))
+ {
+ dwFieldFlags |= fdRTSpecialName | fdSpecialName;
+ }
+ SetCallerDefine();
+ IfFailGo(_SetFieldProps(*pmd, dwFieldFlags, dwCPlusTypeFlag, pValue, cchValue));
+ IfFailGo(m_pStgdb->m_MiniMd.AddMemberDefToHash(*pmd, td) );
+
+ErrExit:
+ SetCallerExternal();
+
+ STOP_MD_PERF(DefineField);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineField
+
+//*****************************************************************************
+// Define and set a Property record.
+//*****************************************************************************
+HRESULT RegMeta::DefineProperty(
+ mdTypeDef td, // [IN] the class/interface on which the property is being defined
+ LPCWSTR szProperty, // [IN] Name of the property
+ DWORD dwPropFlags, // [IN] CorPropertyAttr
+ PCCOR_SIGNATURE pvSig, // [IN] the required type signature
+ ULONG cbSig, // [IN] the size of the type signature blob
+ DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] constant value
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdMethodDef mdSetter, // [IN] optional setter of the property
+ mdMethodDef mdGetter, // [IN] optional getter of the property
+ mdMethodDef rmdOtherMethods[], // [IN] an optional array of other methods
+ mdProperty *pmdProp) // [OUT] output property token
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ PropertyRec *pPropRec = NULL;
+ RID iPropRec;
+ PropertyMapRec *pPropMap;
+ RID iPropMap;
+ LPUTF8 szUTF8Property;
+ UTF8STR(szProperty, szUTF8Property);
+
+ LOG((LOGMD, "MD RegMeta::DefineProperty(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, szProperty, dwPropFlags, pvSig, cbSig, dwCPlusTypeFlag, pValue, cchValue, mdSetter, mdGetter,
+ rmdOtherMethods, pmdProp));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && td != mdTypeDefNil &&
+ szProperty && pvSig && cbSig && pmdProp);
+
+ if (CheckDups(MDDupProperty))
+ {
+ hr = ImportHelper::FindProperty(&(m_pStgdb->m_MiniMd), td, szUTF8Property, pvSig, cbSig, pmdProp);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(*pmdProp), &pPropRec));
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ if (! pPropRec)
+ {
+ // Create a new map if one doesn't exist already, else retrieve the existing one.
+ // The property map must be created before the PropertyRecord, the new property
+ // map will be pointing past the first property record.
+ IfFailGo(m_pStgdb->m_MiniMd.FindPropertyMapFor(RidFromToken(td), &iPropMap));
+ if (InvalidRid(iPropMap))
+ {
+ // Create new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddPropertyMapRecord(&pPropMap, &iPropMap));
+ // Set parent.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_PropertyMap,
+ PropertyMapRec::COL_Parent, pPropMap, td));
+ IfFailGo(UpdateENCLog2(TBL_PropertyMap, iPropMap));
+ }
+ else
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyMapRecord(iPropMap, &pPropMap));
+ }
+
+ // Create a new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddPropertyRecord(&pPropRec, &iPropRec));
+
+ // Set output parameter.
+ *pmdProp = TokenFromRid(iPropRec, mdtProperty);
+
+ // Add Property to the PropertyMap.
+ IfFailGo(m_pStgdb->m_MiniMd.AddPropertyToPropertyMap(RidFromToken(iPropMap), iPropRec));
+
+ IfFailGo(UpdateENCLog2(TBL_PropertyMap, iPropMap, CMiniMdRW::eDeltaPropertyCreate));
+ }
+
+ // Save the data.
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Property, PropertyRec::COL_Type, pPropRec,
+ pvSig, cbSig));
+ IfFailGo( m_pStgdb->m_MiniMd.PutString(TBL_Property, PropertyRec::COL_Name,
+ pPropRec, szUTF8Property) );
+
+ SetCallerDefine();
+ IfFailGo(_SetPropertyProps(*pmdProp, dwPropFlags, dwCPlusTypeFlag, pValue, cchValue, mdSetter,
+ mdGetter, rmdOtherMethods));
+
+ // Add the <property token, typedef token> to the lookup table
+ if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Property))
+ IfFailGo( m_pStgdb->m_MiniMd.AddPropertyToLookUpTable(*pmdProp, td) );
+
+ErrExit:
+ SetCallerExternal();
+
+ STOP_MD_PERF(DefineProperty);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineProperty
+
+//*****************************************************************************
+// Create a record in the Param table. Any set of name, flags, or default value
+// may be set.
+//*****************************************************************************
+HRESULT RegMeta::DefineParam(
+ mdMethodDef md, // [IN] Owning method
+ ULONG ulParamSeq, // [IN] Which param
+ LPCWSTR szName, // [IN] Optional param name
+ DWORD dwParamFlags, // [IN] Optional param flags
+ DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] constant value
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdParamDef *ppd) // [OUT] Put param token here
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ RID iRecord;
+ ParamRec *pRecord = 0;
+
+ LOG((LOGMD, "MD RegMeta::DefineParam(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ md, ulParamSeq, MDSTR(szName), dwParamFlags, dwCPlusTypeFlag, pValue, cchValue, ppd));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ _ASSERTE(TypeFromToken(md) == mdtMethodDef && md != mdMethodDefNil &&
+ ulParamSeq != ULONG_MAX && ppd);
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ // Retrieve or create the Param row.
+ if (CheckDups(MDDupParamDef))
+ {
+ hr = _FindParamOfMethod(md, ulParamSeq, ppd);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(*ppd), &pRecord));
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ if (!pRecord)
+ {
+ // Create the Param record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddParamRecord(&pRecord, &iRecord));
+
+ // Set the output parameter.
+ *ppd = TokenFromRid(iRecord, mdtParamDef);
+
+ // Set sequence number.
+ pRecord->SetSequence(static_cast<USHORT>(ulParamSeq));
+
+ // Add to the parent's list of child records.
+ IfFailGo(m_pStgdb->m_MiniMd.AddParamToMethod(RidFromToken(md), iRecord));
+
+ IfFailGo(UpdateENCLog(md, CMiniMdRW::eDeltaParamCreate));
+ }
+
+ SetCallerDefine();
+ // Set the properties.
+ IfFailGo(_SetParamProps(*ppd, szName, dwParamFlags, dwCPlusTypeFlag, pValue, cchValue));
+
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+ SetCallerExternal();
+
+ STOP_MD_PERF(DefineParam);
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::DefineParam
+
+//*****************************************************************************
+// Set the properties on the given Field token.
+//*****************************************************************************
+HRESULT RegMeta::SetFieldProps( // S_OK or error.
+ mdFieldDef fd, // [IN] The FieldDef.
+ DWORD dwFieldFlags, // [IN] Field attributes.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchValue) // [IN] size of constant value (string, in wide chars).
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ LOG((LOGMD, "MD: RegMeta::SetFieldProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ fd, dwFieldFlags, dwCPlusTypeFlag, pValue, cchValue));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ // Validate flags.
+ if (dwFieldFlags != ULONG_MAX)
+ {
+ // fdHasFieldRVA is settable, but not re-settable by applications.
+ _ASSERTE((dwFieldFlags & (fdReservedMask&~(fdHasFieldRVA|fdRTSpecialName))) == 0);
+ dwFieldFlags &= ~(fdReservedMask&~fdHasFieldRVA);
+ }
+
+ hr = _SetFieldProps(fd, dwFieldFlags, dwCPlusTypeFlag, pValue, cchValue);
+
+ErrExit:
+
+ STOP_MD_PERF(SetFieldProps);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetFieldProps
+
+//*****************************************************************************
+// Set the properties on the given Property token.
+//*****************************************************************************
+HRESULT RegMeta::SetPropertyProps( // S_OK or error.
+ mdProperty pr, // [IN] Property token.
+ DWORD dwPropFlags, // [IN] CorPropertyAttr.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdMethodDef mdSetter, // [IN] Setter of the property.
+ mdMethodDef mdGetter, // [IN] Getter of the property.
+ mdMethodDef rmdOtherMethods[]) // [IN] Array of other methods.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ LOG((LOGMD, "MD RegMeta::SetPropertyProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ pr, dwPropFlags, dwCPlusTypeFlag, pValue, cchValue, mdSetter, mdGetter,
+ rmdOtherMethods));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ hr = _SetPropertyProps(pr, dwPropFlags, dwCPlusTypeFlag, pValue, cchValue, mdSetter, mdGetter, rmdOtherMethods);
+
+ErrExit:
+
+ STOP_MD_PERF(SetPropertyProps);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetPropertyProps
+
+
+//*****************************************************************************
+// This routine sets properties on the given Param token.
+//*****************************************************************************
+HRESULT RegMeta::SetParamProps( // Return code.
+ mdParamDef pd, // [IN] Param token.
+ LPCWSTR szName, // [IN] Param name.
+ DWORD dwParamFlags, // [IN] Param flags.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for value type. selected ELEMENT_TYPE_*.
+ void const *pValue, // [OUT] Constant value.
+ ULONG cchValue) // [IN] size of constant value (string, in wide chars).
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::SetParamProps(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ pd, MDSTR(szName), dwParamFlags, dwCPlusTypeFlag, pValue, cchValue));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ hr = _SetParamProps(pd, szName, dwParamFlags, dwCPlusTypeFlag, pValue, cchValue);
+
+ErrExit:
+
+ STOP_MD_PERF(SetParamProps);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::SetParamProps
+
+//*****************************************************************************
+// Apply edit and continue changes to this metadata.
+//*****************************************************************************
+STDMETHODIMP RegMeta::ApplyEditAndContinue( // S_OK or error.
+ IUnknown *pUnk) // [IN] Metadata from the delta PE.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ IMetaDataImport2 *pImport=0; // Interface on the delta metadata.
+ RegMeta *pDeltaMD=0; // The delta metadata.
+ CMiniMdRW *mdDelta = NULL;
+ CMiniMdRW *mdBase = NULL;
+
+ // Get the MiniMd on the delta.
+ IfFailGo(pUnk->QueryInterface(IID_IMetaDataImport2, (void**)&pImport));
+
+ pDeltaMD = static_cast<RegMeta*>(pImport);
+
+ mdDelta = &(pDeltaMD->m_pStgdb->m_MiniMd);
+ mdBase = &(m_pStgdb->m_MiniMd);
+
+ IfFailGo(mdBase->ConvertToRW());
+ IfFailGo(mdBase->ApplyDelta(*mdDelta));
+
+ErrExit:
+ if (pImport)
+ pImport->Release();
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::ApplyEditAndContinue
+
+#endif //FEATURE_METADATA_EMIT
diff --git a/src/md/compiler/filtermanager.cpp b/src/md/compiler/filtermanager.cpp
new file mode 100644
index 0000000000..c8e9ac3e07
--- /dev/null
+++ b/src/md/compiler/filtermanager.cpp
@@ -0,0 +1,1458 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// FilterManager.cpp
+//
+
+//
+// contains utility code to MD directory
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "filtermanager.h"
+
+#define IsGlobalTypeDef(td) (td == TokenFromRid(mdtTypeDef, 1))
+
+//*****************************************************************************
+// Walk up to the containing tree and
+// mark the transitive closure of the root token
+//*****************************************************************************
+HRESULT FilterManager::Mark(mdToken tk)
+{
+ HRESULT hr = NOERROR;
+ mdTypeDef td;
+
+ // We hard coded System.Object as mdTypeDefNil
+ // The backing Field of property can be NULL as well.
+ if (RidFromToken(tk) == mdTokenNil)
+ goto ErrExit;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ switch ( TypeFromToken(tk) )
+ {
+ case mdtTypeDef:
+ IfFailGo( MarkTypeDef(tk) );
+ break;
+
+ case mdtMethodDef:
+ // Get the typedef containing the MethodDef and mark the whole type
+ IfFailGo( m_pMiniMd->FindParentOfMethodHelper(tk, &td) );
+
+ // Global function so only mark the function itself and the typedef.
+ // Don't call MarkTypeDef. That will trigger all of the global methods/fields
+ // marked.
+ //
+ if (IsGlobalTypeDef(td))
+ {
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeDef(td) );
+ IfFailGo( MarkMethod(tk) );
+ }
+ else
+ {
+ IfFailGo( MarkTypeDef(td) );
+ }
+ break;
+
+ case mdtFieldDef:
+ // Get the typedef containing the FieldDef and mark the whole type
+ IfFailGo( m_pMiniMd->FindParentOfFieldHelper(tk, &td) );
+ if (IsGlobalTypeDef(td))
+ {
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeDef(td) );
+ IfFailGo( MarkField(tk) );
+ }
+ else
+ {
+ IfFailGo( MarkTypeDef(td) );
+ }
+ break;
+
+ case mdtMemberRef:
+ IfFailGo( MarkMemberRef(tk) );
+ break;
+
+ case mdtTypeRef:
+ IfFailGo( MarkTypeRef(tk) );
+ break;
+
+ case mdtTypeSpec:
+ IfFailGo( MarkTypeSpec(tk) );
+ break;
+ case mdtSignature:
+ IfFailGo( MarkStandAloneSig(tk) );
+ break;
+
+ case mdtModuleRef:
+ IfFailGo( MarkModuleRef(tk) );
+ break;
+
+ case mdtAssemblyRef:
+ IfFailGo( MarkAssemblyRef(tk) );
+ break;
+
+ case mdtModule:
+ IfFailGo( MarkModule(tk) );
+ break;
+
+ case mdtString:
+ IfFailGo( MarkUserString(tk) );
+ break;
+
+ case mdtBaseType:
+ // don't need to mark any base type.
+ break;
+
+ case mdtAssembly:
+ IfFailGo( MarkAssembly(tk) );
+ break;
+
+ case mdtMethodSpec:
+ IfFailGo( MarkMethodSpec(tk) );
+ break;
+
+ case mdtProperty:
+ case mdtEvent:
+ case mdtParamDef:
+ case mdtInterfaceImpl:
+ default:
+ _ASSERTE(!" unknown type!");
+ hr = E_INVALIDARG;
+ break;
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::Mark()
+
+
+
+//*****************************************************************************
+// marking only module property
+//*****************************************************************************
+HRESULT FilterManager::MarkAssembly(mdAssembly as)
+{
+ HRESULT hr = NOERROR;
+
+ if (hasAssemblyBeenMarked == false)
+ {
+ hasAssemblyBeenMarked = true;
+ IfFailGo( MarkCustomAttributesWithParentToken(as) );
+ IfFailGo( MarkDeclSecuritiesWithParentToken(as) );
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkAssembly()
+
+
+//*****************************************************************************
+// marking only module property
+//*****************************************************************************
+HRESULT FilterManager::MarkModule(mdModule mo)
+{
+ HRESULT hr = NOERROR;
+
+ if (hasModuleBeenMarked == false)
+ {
+ hasModuleBeenMarked = true;
+ IfFailGo( MarkCustomAttributesWithParentToken(mo) );
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkModule()
+
+
+//*****************************************************************************
+// cascading Mark of a CustomAttribute
+//*****************************************************************************
+HRESULT FilterManager::MarkCustomAttribute(mdCustomAttribute cv)
+{
+ HRESULT hr = NOERROR;
+ CustomAttributeRec *pRec;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkCustomAttribute( cv ) );
+
+ // Mark the type (and any family) of the CustomAttribue.
+ IfFailGo(m_pMiniMd->GetCustomAttributeRecord(RidFromToken(cv), &pRec));
+ IfFailGo( Mark(m_pMiniMd->getTypeOfCustomAttribute(pRec)) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkCustomAttribute()
+
+
+//*****************************************************************************
+// cascading Mark of a DeclSecurity
+//*****************************************************************************
+HRESULT FilterManager::MarkDeclSecurity(mdPermission pe)
+{
+ HRESULT hr = NOERROR;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkDeclSecurity( pe ) );
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkDeclSecurity()
+
+
+
+//*****************************************************************************
+// cascading Mark of a signature
+//*****************************************************************************
+HRESULT FilterManager::MarkStandAloneSig(mdSignature sig)
+{
+ HRESULT hr = NOERROR;
+ StandAloneSigRec *pRec;
+ ULONG cbSize;
+ ULONG cbUsed;
+ PCCOR_SIGNATURE pbSig;
+ IHostFilter *pFilter = m_pMiniMd->GetHostFilter();
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if TypeRef is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsSignatureMarked(sig))
+ goto ErrExit;
+
+ // To mark the signature, we will need to mark
+ // all of the embedded TypeRef or TypeDef
+ //
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkSignature( sig ) );
+
+ if (pFilter)
+ pFilter->MarkToken(sig);
+
+ // Walk the signature and mark all of the embedded types
+ IfFailGo(m_pMiniMd->GetStandAloneSigRecord(RidFromToken(sig), &pRec));
+ IfFailGo(m_pMiniMd->getSignatureOfStandAloneSig(pRec, &pbSig, &cbSize));
+ IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) );
+
+ IfFailGo( MarkCustomAttributesWithParentToken(sig) );
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkStandAloneSig()
+
+
+
+//*****************************************************************************
+// cascading Mark of a TypeSpec
+//*****************************************************************************
+HRESULT FilterManager::MarkTypeSpec(mdTypeSpec ts)
+{
+ HRESULT hr = NOERROR;
+ TypeSpecRec *pRec;
+ ULONG cbSize;
+ ULONG cbUsed;
+ PCCOR_SIGNATURE pbSig;
+ IHostFilter *pFilter = m_pMiniMd->GetHostFilter();
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if TypeRef is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsTypeSpecMarked(ts))
+ goto ErrExit;
+
+ // To mark the TypeSpec, we will need to mark
+ // all of the embedded TypeRef or TypeDef
+ //
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeSpec( ts ) );
+
+ if (pFilter)
+ pFilter->MarkToken(ts);
+
+ // Walk the signature and mark all of the embedded types
+ IfFailGo(m_pMiniMd->GetTypeSpecRecord(RidFromToken(ts), &pRec));
+ IfFailGo(m_pMiniMd->getSignatureOfTypeSpec(pRec, &pbSig, &cbSize));
+ IfFailGo( MarkFieldSignature(pbSig, cbSize, &cbUsed) );
+ IfFailGo( MarkCustomAttributesWithParentToken(ts) );
+
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkTypeSpec()
+
+
+
+
+//*****************************************************************************
+// cascading Mark of a TypeRef
+//*****************************************************************************
+HRESULT FilterManager::MarkTypeRef(mdTypeRef tr)
+{
+ HRESULT hr = NOERROR;
+ TOKENMAP *tkMap;
+ mdTypeDef td;
+ IHostFilter *pFilter = m_pMiniMd->GetHostFilter();
+ TypeRefRec *pRec;
+ mdToken parentTk;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if TypeRef is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsTypeRefMarked(tr))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeRef( tr ) );
+
+ if (pFilter)
+ pFilter->MarkToken(tr);
+
+ IfFailGo(m_pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pRec));
+ parentTk = m_pMiniMd->getResolutionScopeOfTypeRef(pRec);
+ if ( RidFromToken(parentTk) )
+ {
+ IfFailGo( Mark( parentTk ) );
+ }
+
+ tkMap = m_pMiniMd->GetTypeRefToTypeDefMap();
+ PREFIX_ASSUME(tkMap != NULL);
+ td = *(tkMap->Get(RidFromToken(tr)));
+ if ( td != mdTokenNil )
+ {
+ // TypeRef is referring to a TypeDef within the same module.
+ // Mark the TypeDef as well.
+ //
+ IfFailGo( Mark(td) );
+ }
+
+ IfFailGo( MarkCustomAttributesWithParentToken(tr) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkTypeRef()
+
+
+//*****************************************************************************
+// cascading Mark of a MemberRef
+//*****************************************************************************
+HRESULT FilterManager::MarkMemberRef(mdMemberRef mr)
+{
+ HRESULT hr = NOERROR;
+ MemberRefRec *pRec;
+ ULONG cbSize;
+ ULONG cbUsed;
+ PCCOR_SIGNATURE pbSig;
+ IHostFilter *pFilter = m_pMiniMd->GetHostFilter();
+ mdToken md;
+ TOKENMAP *tkMap;
+ mdToken tkParent;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if MemberRef is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsMemberRefMarked(mr))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkMemberRef( mr ) );
+
+ if (pFilter)
+ pFilter->MarkToken(mr);
+
+ IfFailGo(m_pMiniMd->GetMemberRefRecord(RidFromToken(mr), &pRec));
+
+ // we want to mark the parent of MemberRef as well
+ tkParent = m_pMiniMd->getClassOfMemberRef(pRec);
+
+ // If the parent is the global TypeDef, mark only the TypeDef itself (low-level function).
+ // Other parents, do the transitive mark (ie, the high-level function).
+ //
+ if (IsGlobalTypeDef(tkParent))
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeDef( tkParent ) );
+ else
+ IfFailGo( Mark( tkParent ) );
+
+ // Walk the signature and mark all of the embedded types
+ IfFailGo(m_pMiniMd->getSignatureOfMemberRef(pRec, &pbSig, &cbSize));
+ IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) );
+
+ tkMap = m_pMiniMd->GetMemberRefToMemberDefMap();
+ PREFIX_ASSUME(tkMap != NULL);
+ md = *(tkMap->Get(RidFromToken(mr))); // can be fielddef or methoddef
+ if ( RidFromToken(md) != mdTokenNil )
+ {
+ // MemberRef is referring to either a FieldDef or MethodDef.
+ // If it is referring to MethodDef, we have fix the parent of MemberRef to be the MethodDef.
+ // However, if it is mapped to a FieldDef, the parent column does not track this information.
+ // Therefore we need to mark it explicitly.
+ //
+ IfFailGo( Mark(md) );
+ }
+
+ IfFailGo( MarkCustomAttributesWithParentToken(mr) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkMemberRef()
+
+
+//*****************************************************************************
+// cascading Mark of a UserString
+//*****************************************************************************
+HRESULT FilterManager::MarkUserString(mdString str)
+{
+ HRESULT hr = NOERROR;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if UserString is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsUserStringMarked(str))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkUserString( str ) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkUserString()
+
+
+//*****************************************************************************
+// Mark of a new UserString
+//*****************************************************************************
+HRESULT FilterManager::MarkNewUserString(mdString str)
+{
+ HRESULT hr = NOERROR;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkNewUserString( str ) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkUserString()
+
+
+//*****************************************************************************
+// cascading Mark of a MethodSpec
+//*****************************************************************************
+HRESULT FilterManager::MarkMethodSpec(mdMethodSpec ms)
+{
+ HRESULT hr = NOERROR;
+ MethodSpecRec *pRec;
+ ULONG cbSize;
+ ULONG cbUsed;
+ PCCOR_SIGNATURE pbSig;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if MethodSpec is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsMethodSpecMarked(ms))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkMethodSpec( ms ) );
+
+ // Mark MethodRef or MethodDef and embedded TypeRef and TypeDef tokens
+
+ IfFailGo(m_pMiniMd->GetMethodSpecRecord(RidFromToken(ms), &pRec));
+
+ IfFailGo( Mark(m_pMiniMd->getMethodOfMethodSpec(pRec)) );
+
+ IfFailGo(m_pMiniMd->getInstantiationOfMethodSpec(pRec, &pbSig, &cbSize));
+ IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkMethodSpec()
+
+
+//*****************************************************************************
+// cascading Mark of a ModuleRef
+//*****************************************************************************
+HRESULT FilterManager::MarkModuleRef(mdModuleRef mr)
+{
+ HRESULT hr = NOERROR;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if ModuleRef is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsModuleRefMarked(mr))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkModuleRef( mr ) );
+ IfFailGo( MarkCustomAttributesWithParentToken(mr) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkModuleRef()
+
+
+//*****************************************************************************
+// cascading Mark of a AssemblyRef
+//*****************************************************************************
+HRESULT FilterManager::MarkAssemblyRef(mdAssemblyRef ar)
+{
+ HRESULT hr = NOERROR;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if ModuleREf is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsAssemblyRefMarked(ar))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkAssemblyRef( ar ) );
+ IfFailGo( MarkCustomAttributesWithParentToken(ar) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkAssemblyRef()
+
+
+//*****************************************************************************
+// cascading Mark of all of the custom values associated with a token
+//*****************************************************************************
+HRESULT FilterManager::MarkCustomAttributesWithParentToken(mdToken tkParent)
+{
+ HRESULT hr = NOERROR;
+ RID ridStart, ridEnd;
+ RID index;
+ CustomAttributeRec *pRec;
+
+ if ( m_pMiniMd->IsSorted( TBL_CustomAttribute ) )
+ {
+ // table is sorted. ridStart to ridEnd - 1 are all CustomAttribute
+ // associated with tkParent
+ //
+ IfFailGo(m_pMiniMd->getCustomAttributeForToken(tkParent, &ridEnd, &ridStart));
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo( MarkCustomAttribute( TokenFromRid(index, mdtCustomAttribute) ) );
+ }
+ }
+ else
+ {
+ // table scan is needed
+ ridStart = 1;
+ ridEnd = m_pMiniMd->getCountCustomAttributes() + 1;
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(m_pMiniMd->GetCustomAttributeRecord(index, &pRec));
+ if ( tkParent == m_pMiniMd->getParentOfCustomAttribute(pRec) )
+ {
+ // This CustomAttribute is associated with tkParent
+ IfFailGo( MarkCustomAttribute( TokenFromRid(index, mdtCustomAttribute) ) );
+ }
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkCustomAttributesWithParentToken()
+
+
+//*****************************************************************************
+// cascading Mark of all securities associated with a token
+//*****************************************************************************
+HRESULT FilterManager::MarkDeclSecuritiesWithParentToken(mdToken tkParent)
+{
+ HRESULT hr = NOERROR;
+ RID ridStart, ridEnd;
+ RID index;
+ DeclSecurityRec *pRec;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ if ( m_pMiniMd->IsSorted( TBL_DeclSecurity ) )
+ {
+ // table is sorted. ridStart to ridEnd - 1 are all DeclSecurity
+ // associated with tkParent
+ //
+ IfFailGo(m_pMiniMd->getDeclSecurityForToken(tkParent, &ridEnd, &ridStart));
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkDeclSecurity( TokenFromRid(index, mdtPermission) ) );
+ }
+ }
+ else
+ {
+ // table scan is needed
+ ridStart = 1;
+ ridEnd = m_pMiniMd->getCountDeclSecuritys() + 1;
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(m_pMiniMd->GetDeclSecurityRecord(index, &pRec));
+ if ( tkParent == m_pMiniMd->getParentOfDeclSecurity(pRec) )
+ {
+ // This DeclSecurity is associated with tkParent
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkDeclSecurity( TokenFromRid(index, mdtPermission) ) );
+ }
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkDeclSecuritiesWithParentToken()
+
+
+//*****************************************************************************
+// cascading Mark of all MemberRefs associated with a parent token
+//*****************************************************************************
+HRESULT FilterManager::MarkMemberRefsWithParentToken(mdToken tk)
+{
+ HRESULT hr = NOERROR;
+ RID ulEnd;
+ RID index;
+ mdToken tkParent;
+ MemberRefRec *pRec;
+
+ ulEnd = m_pMiniMd->getCountMemberRefs();
+
+ for (index = 1; index <= ulEnd; index ++ )
+ {
+ // memberRef table is not sorted. Table scan is needed.
+ IfFailGo(m_pMiniMd->GetMemberRefRecord(index, &pRec));
+ tkParent = m_pMiniMd->getClassOfMemberRef(pRec);
+ if ( tk == tkParent )
+ {
+ IfFailGo( MarkMemberRef( TokenFromRid(index, mdtMemberRef) ) );
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkMemberRefsWithParentToken()
+
+
+//*****************************************************************************
+// cascading Mark of a ParamDef token
+//*****************************************************************************
+HRESULT FilterManager::MarkParam(mdParamDef pd)
+{
+ HRESULT hr;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkParam( pd ) );
+
+ IfFailGo( MarkCustomAttributesWithParentToken(pd) );
+ // Parameter does not have declsecurity
+ // IfFailGo( MarkDeclSecuritiesWithParentToken(pd) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkParam()
+
+
+//*****************************************************************************
+// cascading Mark of a method token
+//*****************************************************************************
+HRESULT FilterManager::MarkMethod(mdMethodDef md)
+{
+ HRESULT hr = NOERROR;
+ MethodRec *pRec;
+ ULONG cbSize;
+ ULONG cbUsed;
+ PCCOR_SIGNATURE pbSig;
+ ULONG i, iCount;
+ ImplMapRec *pImplMapRec = NULL;
+ mdMethodDef mdImp;
+ mdModuleRef mrImp;
+ IHostFilter *pFilter = m_pMiniMd->GetHostFilter();
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if MethodDef is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsMethodMarked(md))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkMethod( md ) );
+ if (pFilter)
+ pFilter->MarkToken(md);
+
+ IfFailGo( MarkParamsWithParentToken(md) );
+
+ // mark any GenericParam of this Method
+ IfFailGo( MarkGenericParamWithParentToken(md) );
+
+
+ // Walk the signature and mark all of the embedded types
+ IfFailGo(m_pMiniMd->GetMethodRecord(RidFromToken(md), &pRec));
+ IfFailGo(m_pMiniMd->getSignatureOfMethod(pRec, &pbSig, &cbSize));
+ IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) );
+
+ iCount = m_pMiniMd->getCountImplMaps();
+
+ // loop through all ImplMaps and find the Impl map associated with this method def tokens
+ // and mark the Module Ref tokens in the entries
+ //
+ for (i = 1; i <= iCount; i++)
+ {
+ IfFailGo(m_pMiniMd->GetImplMapRecord(i, &pImplMapRec));
+
+ // Get the MethodDef that the impl map is associated with
+ mdImp = m_pMiniMd->getMemberForwardedOfImplMap(pImplMapRec);
+
+ if (mdImp != md)
+ {
+ // Impl Map entry does not associated with the method def that we are marking
+ continue;
+ }
+
+ // Get the ModuleRef token
+ mrImp = m_pMiniMd->getImportScopeOfImplMap(pImplMapRec);
+ IfFailGo( Mark(mrImp) );
+ }
+
+ // We should not mark all of the memberref with the parent of this methoddef token.
+ // Because not all of the call sites are needed.
+ //
+ // IfFailGo( MarkMemberRefsWithParentToken(md) );
+ IfFailGo( MarkCustomAttributesWithParentToken(md) );
+ IfFailGo( MarkDeclSecuritiesWithParentToken(md) );
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkMethod()
+
+
+//*****************************************************************************
+// cascading Mark of a field token
+//*****************************************************************************
+HRESULT FilterManager::MarkField(mdFieldDef fd)
+{
+ HRESULT hr = NOERROR;
+ FieldRec *pRec;
+ ULONG cbSize;
+ ULONG cbUsed;
+ PCCOR_SIGNATURE pbSig;
+ IHostFilter *pFilter = m_pMiniMd->GetHostFilter();
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if FieldDef is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsFieldMarked(fd))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkField( fd ) );
+ if (pFilter)
+ pFilter->MarkToken(fd);
+
+ // We should not mark all of the MemberRef with the parent of this FieldDef token.
+ // Because not all of the call sites are needed.
+ //
+
+ // Walk the signature and mark all of the embedded types
+ IfFailGo(m_pMiniMd->GetFieldRecord(RidFromToken(fd), &pRec));
+ IfFailGo(m_pMiniMd->getSignatureOfField(pRec, &pbSig, &cbSize));
+ IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) );
+
+ IfFailGo( MarkCustomAttributesWithParentToken(fd) );
+ // IfFailGo( MarkDeclSecuritiesWithParentToken(fd) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkField()
+
+
+//*****************************************************************************
+// cascading Mark of an event token
+//*****************************************************************************
+HRESULT FilterManager::MarkEvent(mdEvent ev)
+{
+ HRESULT hr = NOERROR;
+ EventRec *pRec;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if Event is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsEventMarked(ev))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkEvent( ev ) );
+
+ // mark the event type as well
+ IfFailGo(m_pMiniMd->GetEventRecord(RidFromToken(ev), &pRec));
+ IfFailGo( Mark(m_pMiniMd->getEventTypeOfEvent(pRec)) );
+
+ // Note that we don't need to mark the MethodSemantics. Because the association of MethodSemantics
+ // is marked. The Method column can only store MethodDef, ie the MethodDef has the same parent as
+ // this Event.
+
+ IfFailGo( MarkCustomAttributesWithParentToken(ev) );
+ // IfFailGo( MarkDeclSecuritiesWithParentToken(ev) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkEvent()
+
+
+
+//*****************************************************************************
+// cascading Mark of a Property token
+//*****************************************************************************
+HRESULT FilterManager::MarkProperty(mdProperty pr)
+{
+ HRESULT hr = NOERROR;
+ PropertyRec *pRec;
+ ULONG cbSize;
+ ULONG cbUsed;
+ PCCOR_SIGNATURE pbSig;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if Property is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsPropertyMarked(pr))
+ goto ErrExit;
+
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkProperty( pr ) );
+
+ // marking the backing field, event changing and event changed
+ IfFailGo(m_pMiniMd->GetPropertyRecord(RidFromToken(pr), &pRec));
+
+ // Walk the signature and mark all of the embedded types
+ IfFailGo(m_pMiniMd->getTypeOfProperty(pRec, &pbSig, &cbSize));
+ IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) );
+
+ // Note that we don't need to mark the MethodSemantics. Because the association of MethodSemantics
+ // is marked. The Method column can only store MethodDef, ie the MethodDef has the same parent as
+ // this Property.
+
+ IfFailGo( MarkCustomAttributesWithParentToken(pr) );
+ // IfFailGo( MarkDeclSecuritiesWithParentToken(pr) );
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkProperty()
+
+//*****************************************************************************
+// cascading Mark of all ParamDef associated with a methoddef
+//*****************************************************************************
+HRESULT FilterManager::MarkParamsWithParentToken(mdMethodDef md)
+{
+ HRESULT hr = NOERROR;
+ RID ulStart, ulEnd;
+ RID index;
+ MethodRec *pMethodRec;
+
+ IfFailGo(m_pMiniMd->GetMethodRecord(RidFromToken(md), &pMethodRec));
+
+ // figure out the start rid and end rid of the parameter list of this methoddef
+ ulStart = m_pMiniMd->getParamListOfMethod(pMethodRec);
+ IfFailGo(m_pMiniMd->getEndParamListOfMethod(RidFromToken(md), &ulEnd));
+ for (index = ulStart; index < ulEnd; index ++ )
+ {
+ RID rid;
+ IfFailGo(m_pMiniMd->GetParamRid(index, &rid));
+ IfFailGo(MarkParam(TokenFromRid(
+ rid,
+ mdtParamDef)));
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkParamsWithParentToken()
+
+
+//*****************************************************************************
+// cascading Mark of all methods associated with a TypeDef token
+//*****************************************************************************
+HRESULT FilterManager::MarkMethodsWithParentToken(mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ RID ulStart, ulEnd;
+ RID index;
+ TypeDefRec *pTypeDefRec;
+
+ IfFailGo(m_pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+ ulStart = m_pMiniMd->getMethodListOfTypeDef( pTypeDefRec );
+ IfFailGo(m_pMiniMd->getEndMethodListOfTypeDef(RidFromToken(td), &ulEnd));
+ for ( index = ulStart; index < ulEnd; index ++ )
+ {
+ RID rid;
+ IfFailGo(m_pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(MarkMethod(TokenFromRid(
+ rid,
+ mdtMethodDef)));
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkMethodsWithParentToken()
+
+
+//*****************************************************************************
+// cascading Mark of all MethodImpls associated with a TypeDef token
+//*****************************************************************************
+HRESULT FilterManager::MarkMethodImplsWithParentToken(mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ RID index;
+ mdToken tkBody;
+ mdToken tkDecl;
+ MethodImplRec *pMethodImplRec;
+ HENUMInternal hEnum;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ memset(&hEnum, 0, sizeof(HENUMInternal));
+ IfFailGo( m_pMiniMd->FindMethodImplHelper(td, &hEnum) );
+
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&index))
+ {
+ IfFailGo(m_pMiniMd->GetMethodImplRecord(index, &pMethodImplRec));
+ IfFailGo(m_pMiniMd->GetFilterTable()->MarkMethodImpl(index));
+
+ tkBody = m_pMiniMd->getMethodBodyOfMethodImpl(pMethodImplRec);
+ IfFailGo( Mark(tkBody) );
+
+ tkDecl = m_pMiniMd->getMethodDeclarationOfMethodImpl(pMethodImplRec);
+ IfFailGo( Mark(tkDecl) );
+ }
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+ return hr;
+} // HRESULT FilterManager::MarkMethodImplsWithParentToken()
+
+
+//*****************************************************************************
+// cascading Mark of all fields associated with a TypeDef token
+//*****************************************************************************
+HRESULT FilterManager::MarkFieldsWithParentToken(mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ RID ulStart, ulEnd;
+ RID index;
+ TypeDefRec *pTypeDefRec;
+
+ IfFailGo(m_pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+ ulStart = m_pMiniMd->getFieldListOfTypeDef( pTypeDefRec );
+ IfFailGo(m_pMiniMd->getEndFieldListOfTypeDef(RidFromToken(td), &ulEnd));
+ for ( index = ulStart; index < ulEnd; index ++ )
+ {
+ RID rid;
+ IfFailGo(m_pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo(MarkField(TokenFromRid(
+ rid,
+ mdtFieldDef)));
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkFieldsWithParentToken()
+
+
+//*****************************************************************************
+// cascading Mark of all events associated with a TypeDef token
+//*****************************************************************************
+HRESULT FilterManager::MarkEventsWithParentToken(
+ mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ RID ridEventMap;
+ RID ulStart, ulEnd;
+ RID index;
+ EventMapRec *pEventMapRec;
+
+ // get the starting/ending rid of Events of this typedef
+ IfFailGo(m_pMiniMd->FindEventMapFor(RidFromToken(td), &ridEventMap));
+ if ( !InvalidRid(ridEventMap) )
+ {
+ IfFailGo(m_pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec));
+ ulStart = m_pMiniMd->getEventListOfEventMap( pEventMapRec );
+ IfFailGo(m_pMiniMd->getEndEventListOfEventMap(ridEventMap, &ulEnd));
+ for ( index = ulStart; index < ulEnd; index ++ )
+ {
+ RID rid;
+ IfFailGo(m_pMiniMd->GetEventRid(index, &rid));
+ IfFailGo(MarkEvent(TokenFromRid(
+ rid,
+ mdtEvent)));
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkEventsWithParentToken()
+
+
+
+//*****************************************************************************
+// cascading Mark of all properties associated with a TypeDef token
+//*****************************************************************************
+HRESULT FilterManager::MarkPropertiesWithParentToken(
+ mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ RID ridPropertyMap;
+ RID ulStart, ulEnd;
+ RID index;
+ PropertyMapRec *pPropertyMapRec;
+
+ // get the starting/ending rid of properties of this typedef
+ IfFailGo(m_pMiniMd->FindPropertyMapFor(RidFromToken(td), &ridPropertyMap));
+ if ( !InvalidRid(ridPropertyMap) )
+ {
+ IfFailGo(m_pMiniMd->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec));
+ ulStart = m_pMiniMd->getPropertyListOfPropertyMap( pPropertyMapRec );
+ IfFailGo(m_pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ulEnd));
+ for ( index = ulStart; index < ulEnd; index ++ )
+ {
+ RID rid;
+ IfFailGo(m_pMiniMd->GetPropertyRid(index, &rid));
+ IfFailGo(MarkProperty(TokenFromRid(
+ rid,
+ mdtProperty)));
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkPropertiesWithParentToken()
+
+
+//*****************************************************************************
+// cascading Mark of all GenericPar associated with a TypeDef or MethodDef token
+//*****************************************************************************
+HRESULT FilterManager::MarkGenericParamWithParentToken(
+ mdToken tk)
+{
+ HRESULT hr = NOERROR;
+ RID ulStart, ulEnd;
+ RID index;
+ GenericParamRec *pGenericParamRec;
+ mdToken constraint;
+ HENUMInternal hEnum; // To enumerate constraints.
+
+ // Enumerate the GenericPar
+ //@todo: Handle the unsorted case.
+ IfFailGo( m_pMiniMd->GetGenericParamsForToken(tk, &ulStart, &ulEnd) );
+
+ for (; ulStart < ulEnd; ++ulStart)
+ {
+ index = m_pMiniMd->GetGenericParamRid(ulStart);
+ IfFailGo(m_pMiniMd->GetGenericParamRecord(index, &pGenericParamRec));
+
+ RID ridConstraint;
+ IfFailGo( m_pMiniMd->FindGenericParamConstraintHelper(TokenFromRid(ulStart, mdtGenericParam), &hEnum) );
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &ridConstraint))
+ {
+ // Get the constraint.
+ GenericParamConstraintRec *pRec;
+ IfFailGo(m_pMiniMd->GetGenericParamConstraintRecord(RidFromToken(ridConstraint), &pRec));
+ constraint = m_pMiniMd->getConstraintOfGenericParamConstraint(pRec);
+
+ // Mark it.
+ IfFailGo( Mark(constraint) );
+ }
+ HENUMInternal::ClearEnum(&hEnum);
+ }
+
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+
+ return hr;
+} // HRESULT FilterManager::MarkGenericParamWithParentToken()
+
+
+//*****************************************************************************
+// cascading Mark of an TypeDef token
+//*****************************************************************************
+HRESULT FilterManager::MarkInterfaceImpls(
+ mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ ULONG ridStart, ridEnd;
+ ULONG i;
+ InterfaceImplRec *pRec;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ if ( m_pMiniMd->IsSorted(TBL_InterfaceImpl) )
+ {
+ IfFailGo(m_pMiniMd->getInterfaceImplsForTypeDef(RidFromToken(td), &ridEnd, &ridStart));
+ }
+ else
+ {
+ ridStart = 1;
+ ridEnd = m_pMiniMd->getCountInterfaceImpls() + 1;
+ }
+
+ // Search for the interfaceimpl with the parent of td
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ IfFailGo(m_pMiniMd->GetInterfaceImplRecord(i, &pRec));
+ if ( td != m_pMiniMd->getClassOfInterfaceImpl(pRec) )
+ continue;
+
+ // found an InterfaceImpl associate with td. Mark the interface row and the interfaceimpl type
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkInterfaceImpl(TokenFromRid(i, mdtInterfaceImpl)) );
+ IfFailGo( MarkCustomAttributesWithParentToken(TokenFromRid(i, mdtInterfaceImpl)) );
+ // IfFailGo( MarkDeclSecuritiesWithParentToken(TokenFromRid(i, mdtInterfaceImpl)) );
+ IfFailGo( Mark(m_pMiniMd->getInterfaceOfInterfaceImpl(pRec)) );
+ }
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkInterfaceImpls()
+
+//*****************************************************************************
+// cascading Mark of an TypeDef token
+//*****************************************************************************
+HRESULT FilterManager::MarkTypeDef(
+ mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ TypeDefRec *pRec;
+ IHostFilter *pFilter = m_pMiniMd->GetHostFilter();
+ DWORD dwFlags;
+ RID iNester;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if TypeDef is already marked, just return
+ if (m_pMiniMd->GetFilterTable()->IsTypeDefMarked(td))
+ goto ErrExit;
+
+ // Mark the TypeDef first to avoid duplicate marking
+ IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeDef(td) );
+ if (pFilter)
+ pFilter->MarkToken(td);
+
+ // We don't need to mark InterfaceImpl but we need to mark the
+ // TypeDef/TypeRef associated with InterfaceImpl.
+ IfFailGo( MarkInterfaceImpls(td) );
+
+ // mark the base class
+ IfFailGo(m_pMiniMd->GetTypeDefRecord(RidFromToken(td), &pRec));
+ IfFailGo( Mark(m_pMiniMd->getExtendsOfTypeDef(pRec)) );
+
+ // mark all of the children of this TypeDef
+ IfFailGo( MarkMethodsWithParentToken(td) );
+ IfFailGo( MarkMethodImplsWithParentToken(td) );
+ IfFailGo( MarkFieldsWithParentToken(td) );
+ IfFailGo( MarkEventsWithParentToken(td) );
+ IfFailGo( MarkPropertiesWithParentToken(td) );
+
+ // mark any GenericParam of this TypeDef
+ IfFailGo( MarkGenericParamWithParentToken(td) );
+
+ // mark custom value and permission
+ IfFailGo( MarkCustomAttributesWithParentToken(td) );
+ IfFailGo( MarkDeclSecuritiesWithParentToken(td) );
+
+ // If the class is a Nested class mark the parent, recursively.
+ dwFlags = m_pMiniMd->getFlagsOfTypeDef(pRec);
+ if (IsTdNested(dwFlags))
+ {
+ NestedClassRec *pNestClassRec;
+ IfFailGo(m_pMiniMd->FindNestedClassHelper(td, &iNester));
+ if (InvalidRid(iNester))
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+ IfFailGo(m_pMiniMd->GetNestedClassRecord(iNester, &pNestClassRec));
+ IfFailGo(MarkTypeDef(m_pMiniMd->getEnclosingClassOfNestedClass(pNestClassRec)));
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT FilterManager::MarkTypeDef()
+
+
+//*****************************************************************************
+// walk signature and mark tokens embedded in the signature
+//*****************************************************************************
+
+#define VALIDATE_SIGNATURE_LEN(x) \
+ do{ cb = (x); \
+ cbUsed += cb; pbSig += cb; \
+ if (cbUsed > cbSig) IfFailGo(META_E_BAD_SIGNATURE); \
+ }while (0)
+
+#define VALIDATE_SIGNATURE_LEN_HR(x) \
+ do{ IfFailGo(x); \
+ cbUsed += cb; pbSig += cb; \
+ if (cbUsed > cbSig) IfFailGo(META_E_BAD_SIGNATURE); \
+ }while (0)
+
+HRESULT FilterManager::MarkSignature(
+ PCCOR_SIGNATURE pbSig, // [IN] point to the current byte to visit in the signature
+ ULONG cbSig, // [IN] count of bytes available.
+ ULONG *pcbUsed) // [OUT] count of bytes consumed.
+{
+ HRESULT hr = NOERROR; // A result.
+ ULONG cArg = 0; // count of arguments in the signature
+ ULONG cTypes = 0; // Count of argument types in the signature.
+ ULONG cb; // Bytes used in a sig element.
+ ULONG cbUsed = 0; // Total bytes consumed.
+ ULONG callingconv = IMAGE_CEE_CS_CALLCONV_MAX;
+
+ // calling convention
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &callingconv) );
+
+ if ((callingconv & IMAGE_CEE_CS_CALLCONV_MASK) >= IMAGE_CEE_CS_CALLCONV_MAX)
+ IfFailGo(META_E_BAD_SIGNATURE);
+
+ // Field signature is a single element.
+ if (isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_FIELD))
+ {
+ // It is a FieldDef
+ VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) );
+ }
+ else
+ {
+ // If Generic call, get count of type parameters.
+ //@TODO: where are the type params?
+ if (callingconv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &cTypes) );
+ }
+
+ // Count of arguments passed in call.
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &cArg) );
+
+ // Mark the return type, if there is one (LocalVarSig and GenericInst don't have return types).
+ if ( !( isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) || isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_GENERICINST)) )
+ { // process the return type
+ VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) );
+ }
+
+ // Iterate over the arguments, and mark each one.
+ while (cArg--)
+ {
+ VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) );
+ }
+ }
+
+ErrExit:
+ *pcbUsed = cbUsed;
+ return hr;
+} // HRESULT FilterManager::MarkSignature()
+
+
+//*****************************************************************************
+// walk one type and mark tokens embedded in the signature
+//*****************************************************************************
+HRESULT FilterManager::MarkFieldSignature(
+ PCCOR_SIGNATURE pbSig, // [IN] point to the current byte to visit in the signature
+ ULONG cbSig, // [IN] count of bytes available.
+ ULONG *pcbUsed) // [OUT] count of bytes consumed.
+{
+ HRESULT hr = NOERROR; // A result.
+ ULONG cb; // Bytes in one signature element.
+ ULONG cbUsed = 0; // Total bytes consumed from signature.
+ CorElementType ulElementType; // ELEMENT_TYPE_xxx from signature.
+ ULONG ulData; // Some data (like a count) from the signature.
+ ULONG ulTemp; // Unused data.
+ mdToken token; // A token from the signature.
+ int iData; // Integer data from signature.
+
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressElementType(pbSig, &ulElementType) );
+
+ // Skip the modifiers...
+ while (CorIsModifierElementType((CorElementType) ulElementType))
+ {
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressElementType(pbSig, &ulElementType) );
+ }
+
+ // Examine the signature element
+ switch (ulElementType)
+ {
+ case ELEMENT_TYPE_SZARRAY:
+ // syntax: SZARRAY <BaseType>
+
+ // conver the base type for the SZARRAY or GENERICARRAY
+ VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) );
+ break;
+
+ case ELEMENT_TYPE_CMOD_REQD:
+ case ELEMENT_TYPE_CMOD_OPT:
+ // syntax: {CMOD_REQD|CMOD_OPT} <token> <signature>
+
+ // now get the embedded token
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressToken(pbSig, &token) );
+
+ // Mark the token
+ IfFailGo( Mark(token) );
+
+ // mark the base type
+ VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) );
+ break;
+
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ // syntax: VAR <index>
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) );
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ // syntax: ARRAY BaseType <rank> [i size_1... size_i] [j lowerbound_1 ... lowerbound_j]
+
+ VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) );
+
+ // Parse for the rank
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) );
+
+ // if rank == 0, we are done
+ if (ulData == 0)
+ break;
+
+ // Any size of dimension specified?
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) );
+
+ // Consume sizes of dimension.
+ while (ulData--)
+ {
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulTemp) );
+ }
+
+ // Any lower bounds specified?
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) );
+
+ // Consume lower bounds.
+ while (ulData--)
+ {
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressSignedInt(pbSig, &iData) );
+ }
+
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ // function pointer is followed by another complete signature
+ VALIDATE_SIGNATURE_LEN_HR( MarkSignature(pbSig, cbSig - cbUsed, &cb) );
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ // syntax: {CLASS | VALUECLASS} <token>
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressToken(pbSig, &token) );
+
+ // Mark it.
+ IfFailGo( Mark(token) );
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ // syntax: ELEMENT_TYPE_GEENRICINST <ELEMENT_TYPE_CLASS | ELEMENT_TYPE_VALUECLASS> <token> <n> <n params>
+ VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) );
+
+ // Get the number of generic parameters
+ VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) );
+
+ // Get the generic parameters
+ while (ulData--)
+ {
+ VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) );
+ }
+ break;
+
+ default:
+ // If valid element (I4, etc), great. Otherwise, return error.
+ if ((ulElementType >= ELEMENT_TYPE_MAX) ||
+ (ulElementType == ELEMENT_TYPE_PTR) ||
+ (ulElementType == ELEMENT_TYPE_BYREF) ||
+ (ulElementType == ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED))
+ {
+ IfFailGo(META_E_BAD_SIGNATURE);
+ }
+ break;
+ }
+
+ErrExit:
+ *pcbUsed = cbUsed;
+ return hr;
+} // HRESULT FilterManager::MarkFieldSignature()
+
+
+
+//*****************************************************************************
+//
+// Unmark the TypeDef
+//
+//*****************************************************************************
+HRESULT FilterManager::UnmarkTypeDef(
+ mdTypeDef td)
+{
+ HRESULT hr = NOERROR;
+ TypeDefRec *pTypeDefRec;
+ RID ridStart, ridEnd;
+ RID index;
+ CustomAttributeRec *pCARec;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL);
+
+ // if TypeDef is already unmarked, just return
+ if (m_pMiniMd->GetFilterTable()->IsTypeDefMarked(td) == false)
+ goto ErrExit;
+
+ // Mark the TypeDef first to avoid duplicate marking
+ IfFailGo( m_pMiniMd->GetFilterTable()->UnmarkTypeDef(td) );
+
+ // Don't need to unmark InterfaceImpl because the TypeDef is unmarked that will make
+ // the InterfaceImpl automatically unmarked.
+
+ // unmark all of the children of this TypeDef
+ IfFailGo(m_pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+
+ // unmark the methods
+ ridStart = m_pMiniMd->getMethodListOfTypeDef(pTypeDefRec);
+ IfFailGo(m_pMiniMd->getEndMethodListOfTypeDef(RidFromToken(td), &ridEnd));
+ for ( index = ridStart; index < ridEnd; index ++ )
+ {
+ RID rid;
+ IfFailGo(m_pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(m_pMiniMd->GetFilterTable()->UnmarkMethod(TokenFromRid(
+ rid,
+ mdtMethodDef)));
+ }
+
+ // unmark the fields
+ ridStart = m_pMiniMd->getFieldListOfTypeDef(pTypeDefRec);
+ IfFailGo(m_pMiniMd->getEndFieldListOfTypeDef(RidFromToken(td), &ridEnd));
+ for ( index = ridStart; index < ridEnd; index ++ )
+ {
+ RID rid;
+ IfFailGo(m_pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo(m_pMiniMd->GetFilterTable()->UnmarkField(TokenFromRid(
+ rid,
+ mdtFieldDef)));
+ }
+
+ // unmark custom value
+ if ( m_pMiniMd->IsSorted( TBL_CustomAttribute ) )
+ {
+ // table is sorted. ridStart to ridEnd - 1 are all CustomAttribute
+ // associated with tkParent
+ //
+ IfFailGo(m_pMiniMd->getCustomAttributeForToken(td, &ridEnd, &ridStart));
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo( m_pMiniMd->GetFilterTable()->UnmarkCustomAttribute( TokenFromRid(index, mdtCustomAttribute) ) );
+ }
+ }
+ else
+ {
+ // table scan is needed
+ ridStart = 1;
+ ridEnd = m_pMiniMd->getCountCustomAttributes() + 1;
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(m_pMiniMd->GetCustomAttributeRecord(index, &pCARec));
+ if ( td == m_pMiniMd->getParentOfCustomAttribute(pCARec) )
+ {
+ // This CustomAttribute is associated with tkParent
+ IfFailGo( m_pMiniMd->GetFilterTable()->UnmarkCustomAttribute( TokenFromRid(index, mdtCustomAttribute) ) );
+ }
+ }
+ }
+
+ // We don't support nested type!!
+
+ErrExit:
+ return hr;
+
+} // HRESULT FilterManager::UnmarkTypeDef()
+
+
diff --git a/src/md/compiler/filtermanager.h b/src/md/compiler/filtermanager.h
new file mode 100644
index 0000000000..374afb7dfd
--- /dev/null
+++ b/src/md/compiler/filtermanager.h
@@ -0,0 +1,87 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// FilterManager.h
+//
+
+//
+// Contains utility code for MD directory
+//
+//*****************************************************************************
+#ifndef __FilterManager__h__
+#define __FilterManager__h__
+
+
+
+
+//*********************************************************************
+// FilterManager Class
+//*********************************************************************
+class FilterManager
+{
+public:
+ FilterManager(CMiniMdRW *pMiniMd) {m_pMiniMd = pMiniMd; hasModuleBeenMarked = false; hasAssemblyBeenMarked = false;}
+ ~FilterManager() {};
+
+ HRESULT Mark(mdToken tk);
+
+ // Unmark helper
+ HRESULT UnmarkTypeDef(mdTypeDef td);
+ HRESULT MarkNewUserString(mdString str);
+
+
+private:
+ HRESULT MarkCustomAttribute(mdCustomAttribute cv);
+ HRESULT MarkDeclSecurity(mdPermission pe);
+ HRESULT MarkStandAloneSig(mdSignature sig);
+ HRESULT MarkTypeSpec(mdTypeSpec ts);
+ HRESULT MarkTypeRef(mdTypeRef tr);
+ HRESULT MarkMemberRef(mdMemberRef mr);
+ HRESULT MarkModuleRef(mdModuleRef mr);
+ HRESULT MarkAssemblyRef(mdAssemblyRef ar);
+ HRESULT MarkModule(mdModule mo);
+ HRESULT MarkAssembly(mdAssembly as);
+ HRESULT MarkInterfaceImpls(mdTypeDef td);
+ HRESULT MarkUserString(mdString str);
+
+ HRESULT MarkMethodSpec(mdMethodSpec ms);
+
+ HRESULT MarkCustomAttributesWithParentToken(mdToken tkParent);
+ HRESULT MarkDeclSecuritiesWithParentToken(mdToken tkParent);
+ HRESULT MarkMemberRefsWithParentToken(mdToken tk);
+
+ HRESULT MarkParam(mdParamDef pd);
+ HRESULT MarkMethod(mdMethodDef md);
+ HRESULT MarkField(mdFieldDef fd);
+ HRESULT MarkEvent(mdEvent ev);
+ HRESULT MarkProperty(mdProperty pr);
+
+ HRESULT MarkParamsWithParentToken(mdMethodDef md);
+ HRESULT MarkMethodsWithParentToken(mdTypeDef td);
+ HRESULT MarkMethodImplsWithParentToken(mdTypeDef td);
+ HRESULT MarkFieldsWithParentToken(mdTypeDef td);
+ HRESULT MarkEventsWithParentToken(mdTypeDef td);
+ HRESULT MarkPropertiesWithParentToken(mdTypeDef td);
+
+ HRESULT MarkGenericParamWithParentToken(mdToken tk);
+
+
+ HRESULT MarkTypeDef(mdTypeDef td);
+
+
+ // <TODO>We don't want to keep track the debug info with bits because these are going away...</TODO>
+ HRESULT MarkMethodDebugInfo(mdMethodDef md);
+
+ // walk the signature and mark all of the embedded TypeDef or TypeRef
+ HRESULT MarkSignature(PCCOR_SIGNATURE pbSig, ULONG cbSig, ULONG *pcbUsed);
+ HRESULT MarkFieldSignature(PCCOR_SIGNATURE pbSig, ULONG cbSig, ULONG *pcbUsed);
+
+
+private:
+ CMiniMdRW *m_pMiniMd;
+ bool hasModuleBeenMarked;
+ bool hasAssemblyBeenMarked;
+};
+
+#endif // __FilterManager__h__
diff --git a/src/md/compiler/helper.cpp b/src/md/compiler/helper.cpp
new file mode 100644
index 0000000000..0727176f07
--- /dev/null
+++ b/src/md/compiler/helper.cpp
@@ -0,0 +1,445 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// Helper.cpp
+//
+
+//
+// Implementation of some internal APIs from code:IMetaDataHelper and code:IMetaDataEmitHelper.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "importhelper.h"
+#include "mdlog.h"
+
+#if defined(FEATURE_METADATA_EMIT) || defined(FEATURE_METADATA_INTERNAL_APIS)
+
+//*****************************************************************************
+// translating signature from one scope to another scope
+//
+// Implements public API code:IMetaDataEmit::TranslateSigWithScope.
+// Implements internal API code:IMetaDataHelper::TranslateSigWithScope.
+//*****************************************************************************
+STDMETHODIMP RegMeta::TranslateSigWithScope( // S_OK or error.
+ IMetaDataAssemblyImport *pAssemImport, // [IN] importing assembly interface
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ IMetaDataImport *pImport, // [IN] importing interface
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit *pAssemEmit,// [IN] emit assembly interface
+ IMetaDataEmit *pEmit, // [IN] emit interface
+ PCOR_SIGNATURE pvTranslatedSig, // [OUT] buffer to hold translated signature
+ ULONG cbTranslatedSigMax,
+ ULONG *pcbTranslatedSig) // [OUT] count of bytes in the translated signature
+{
+#ifdef FEATURE_METADATA_EMIT
+ HRESULT hr = S_OK;
+
+ IMDCommon *pAssemImportMDCommon = NULL;
+ IMDCommon *pImportMDCommon = NULL;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ RegMeta *pRegMetaAssemEmit = static_cast<RegMeta*>(pAssemEmit);
+ RegMeta *pRegMetaEmit = NULL;
+
+ CQuickBytes qkSigEmit;
+ ULONG cbEmit;
+
+ pRegMetaEmit = static_cast<RegMeta*>(pEmit);
+
+ {
+ // This function can cause new TypeRef being introduced.
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(pvTranslatedSig && pcbTranslatedSig);
+
+ if (pAssemImport)
+ {
+ IfFailGo(pAssemImport->QueryInterface(IID_IMDCommon, (void**)&pAssemImportMDCommon));
+ }
+ IMetaModelCommon *pAssemImportMetaModelCommon = pAssemImportMDCommon ? pAssemImportMDCommon->GetMetaModelCommon() : 0;
+
+ IfFailGo(pImport->QueryInterface(IID_IMDCommon, (void**)&pImportMDCommon));
+ IMetaModelCommon *pImportMetaModelCommon = pImportMDCommon->GetMetaModelCommon();
+
+ IfFailGo( ImportHelper::MergeUpdateTokenInSig( // S_OK or error.
+ pRegMetaAssemEmit ? &(pRegMetaAssemEmit->m_pStgdb->m_MiniMd) : 0, // The assembly emit scope.
+ &(pRegMetaEmit->m_pStgdb->m_MiniMd), // The emit scope.
+ pAssemImportMetaModelCommon, // Assembly where the signature is from.
+ pbHashValue, // Hash value for the import assembly.
+ cbHashValue, // Size in bytes.
+ pImportMetaModelCommon, // The scope where signature is from.
+ pbSigBlob, // signature from the imported scope
+ NULL, // Internal OID mapping structure.
+ &qkSigEmit, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit
+ memcpy(pvTranslatedSig, qkSigEmit.Ptr(), cbEmit > cbTranslatedSigMax ? cbTranslatedSigMax :cbEmit );
+ *pcbTranslatedSig = cbEmit;
+ if (cbEmit > cbTranslatedSigMax)
+ hr = CLDB_S_TRUNCATION;
+ }
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ if (pAssemImportMDCommon)
+ pAssemImportMDCommon->Release();
+ if (pImportMDCommon)
+ pImportMDCommon->Release();
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT
+} // RegMeta::TranslateSigWithScope
+
+#endif //FEATURE_METADATA_EMIT || FEATURE_METADATA_INTERNAL_APIS
+
+#if defined(FEATURE_METADATA_EMIT) && defined(FEATURE_METADATA_INTERNAL_APIS)
+
+//*****************************************************************************
+// Helper : Set ResolutionScope of a TypeRef
+//
+// Implements internal API code:IMetaDataEmitHelper::SetResolutionScopeHelper.
+//*****************************************************************************
+HRESULT RegMeta::SetResolutionScopeHelper( // Return hresult.
+ mdTypeRef tr, // [IN] TypeRef record to update
+ mdToken rs) // [IN] new ResolutionScope
+{
+ HRESULT hr = NOERROR;
+ TypeRefRec * pTypeRef;
+
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeRefRecord(RidFromToken(tr), &pTypeRef));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_TypeRef, TypeRefRec::COL_ResolutionScope, pTypeRef, rs));
+
+ErrExit:
+ return hr;
+} // RegMeta::SetResolutionScopeHelper
+
+
+//*****************************************************************************
+// Helper : Set offset of a ManifestResource
+//
+// Implements internal API code:IMetaDataEmitHelper::SetManifestResourceOffsetHelper.
+//*****************************************************************************
+HRESULT
+RegMeta::SetManifestResourceOffsetHelper(
+ mdManifestResource mr, // [IN] The manifest token
+ ULONG ulOffset) // [IN] new offset
+{
+ HRESULT hr = NOERROR;
+ ManifestResourceRec * pRec;
+
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(RidFromToken(mr), &pRec));
+ pRec->SetOffset(ulOffset);
+
+ErrExit:
+ return hr;
+} // RegMeta::SetManifestResourceOffsetHelper
+
+//*******************************************************************************
+//
+// Following APIs are used by reflection emit.
+//
+//*******************************************************************************
+
+//*******************************************************************************
+// helper to define method semantics
+//
+// Implements internal API code:IMetaDataEmitHelper::DefineMethodSemanticsHelper.
+//*******************************************************************************
+HRESULT RegMeta::DefineMethodSemanticsHelper(
+ mdToken tkAssociation, // [IN] property or event token
+ DWORD dwFlags, // [IN] semantics
+ mdMethodDef md) // [IN] method to associated with
+{
+ HRESULT hr;
+ LOCKWRITE();
+ hr = _DefineMethodSemantics((USHORT) dwFlags, md, tkAssociation, false);
+
+ErrExit:
+ return hr;
+} // RegMeta::DefineMethodSemantics
+
+//*******************************************************************************
+// helper to set field layout
+//
+// Implements internal API code:IMetaDataEmitHelper::SetFieldLayoutHelper.
+//*******************************************************************************
+HRESULT RegMeta::SetFieldLayoutHelper( // Return hresult.
+ mdFieldDef fd, // [IN] field to associate the layout info
+ ULONG ulOffset) // [IN] the offset for the field
+{
+ HRESULT hr;
+ FieldLayoutRec *pFieldLayoutRec;
+ RID iFieldLayoutRec;
+
+ LOCKWRITE();
+
+ if (ulOffset == ULONG_MAX)
+ {
+ // invalid argument
+ IfFailGo( E_INVALIDARG );
+ }
+
+ // create a field layout record
+ IfFailGo(m_pStgdb->m_MiniMd.AddFieldLayoutRecord(&pFieldLayoutRec, &iFieldLayoutRec));
+
+ // Set the Field entry.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(
+ TBL_FieldLayout,
+ FieldLayoutRec::COL_Field,
+ pFieldLayoutRec,
+ fd));
+ pFieldLayoutRec->SetOffSet(ulOffset);
+ IfFailGo( m_pStgdb->m_MiniMd.AddFieldLayoutToHash(iFieldLayoutRec) );
+
+ErrExit:
+
+ return hr;
+} // RegMeta::SetFieldLayout
+
+//*******************************************************************************
+// helper to define event
+//
+// Implements internal API code:IMetaDataEmitHelper::DefineEventHelper.
+//*******************************************************************************
+STDMETHODIMP RegMeta::DefineEventHelper( // Return hresult.
+ mdTypeDef td, // [IN] the class/interface on which the event is being defined
+ LPCWSTR szEvent, // [IN] Name of the event
+ DWORD dwEventFlags, // [IN] CorEventAttr
+ mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef) to the Event class
+ mdEvent *pmdEvent) // [OUT] output event token
+{
+ HRESULT hr = S_OK;
+ LOG((LOGMD, "MD RegMeta::DefineEventHelper(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, szEvent, dwEventFlags, tkEventType, pmdEvent));
+
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ hr = _DefineEvent(td, szEvent, dwEventFlags, tkEventType, pmdEvent);
+
+ErrExit:
+ return hr;
+} // RegMeta::DefineEvent
+
+
+//*******************************************************************************
+// helper to add a declarative security blob to a class or method
+//
+// Implements internal API code:IMetaDataEmitHelper::AddDeclarativeSecurityHelper.
+//*******************************************************************************
+STDMETHODIMP RegMeta::AddDeclarativeSecurityHelper(
+ mdToken tk, // [IN] Parent token (typedef/methoddef)
+ DWORD dwAction, // [IN] Security action (CorDeclSecurity)
+ void const *pValue, // [IN] Permission set blob
+ DWORD cbValue, // [IN] Byte count of permission set blob
+ mdPermission*pmdPermission) // [OUT] Output permission token
+{
+ HRESULT hr = S_OK;
+ DeclSecurityRec *pDeclSec = NULL;
+ RID iDeclSec;
+ short sAction = static_cast<short>(dwAction);
+ mdPermission tkPerm;
+
+ LOG((LOGMD, "MD RegMeta::AddDeclarativeSecurityHelper(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ tk, dwAction, pValue, cbValue, pmdPermission));
+
+ LOCKWRITE();
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtMethodDef || TypeFromToken(tk) == mdtAssembly);
+
+ // Check for valid Action.
+ if (sAction == 0 || sAction > dclMaximumValue)
+ IfFailGo(E_INVALIDARG);
+
+ if (CheckDups(MDDupPermission))
+ {
+ hr = ImportHelper::FindPermission(&(m_pStgdb->m_MiniMd), tk, sAction, &tkPerm);
+
+ if (SUCCEEDED(hr))
+ {
+ // Set output parameter.
+ if (pmdPermission)
+ *pmdPermission = tkPerm;
+ if (IsENCOn())
+ IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(tkPerm), &pDeclSec));
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ // Create a new record.
+ if (!pDeclSec)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddDeclSecurityRecord(&pDeclSec, &iDeclSec));
+ tkPerm = TokenFromRid(iDeclSec, mdtPermission);
+
+ // Set output parameter.
+ if (pmdPermission)
+ *pmdPermission = tkPerm;
+
+ // Save parent and action information.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_DeclSecurity, DeclSecurityRec::COL_Parent, pDeclSec, tk));
+ pDeclSec->SetAction(sAction);
+
+ // Turn on the internal security flag on the parent.
+ if (TypeFromToken(tk) == mdtTypeDef)
+ IfFailGo(_TurnInternalFlagsOn(tk, tdHasSecurity));
+ else if (TypeFromToken(tk) == mdtMethodDef)
+ IfFailGo(_TurnInternalFlagsOn(tk, mdHasSecurity));
+ IfFailGo(UpdateENCLog(tk));
+ }
+
+ // Write the blob into the record.
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_DeclSecurity, DeclSecurityRec::COL_PermissionSet,
+ pDeclSec, pValue, cbValue));
+
+ IfFailGo(UpdateENCLog(tkPerm));
+
+ErrExit:
+
+ return hr;
+} // RegMeta::AddDeclarativeSecurityHelper
+
+
+//*******************************************************************************
+// helper to set type's extends column
+//
+// Implements internal API code:IMetaDataEmitHelper::SetTypeParent.
+//*******************************************************************************
+HRESULT RegMeta::SetTypeParent( // Return hresult.
+ mdTypeDef td, // [IN] Type definition
+ mdToken tkExtends) // [IN] parent type
+{
+ HRESULT hr;
+ TypeDefRec *pRec;
+
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pRec));
+ IfFailGo( m_pStgdb->m_MiniMd.PutToken(TBL_TypeDef, TypeDefRec::COL_Extends, pRec, tkExtends) );
+
+ErrExit:
+ return hr;
+} // RegMeta::SetTypeParent
+
+
+//*******************************************************************************
+// helper to set type's extends column
+//
+// Implements internal API code:IMetaDataEmitHelper::AddInterfaceImpl.
+//*******************************************************************************
+HRESULT RegMeta::AddInterfaceImpl( // Return hresult.
+ mdTypeDef td, // [IN] Type definition
+ mdToken tkInterface) // [IN] interface type
+{
+ HRESULT hr;
+ InterfaceImplRec *pRec;
+ RID ii;
+
+ LOCKWRITE();
+ hr = ImportHelper::FindInterfaceImpl(&(m_pStgdb->m_MiniMd), td, tkInterface, (mdInterfaceImpl *)&ii);
+ if (hr == S_OK)
+ goto ErrExit;
+ IfFailGo(m_pStgdb->m_MiniMd.AddInterfaceImplRecord(&pRec, &ii));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken( TBL_InterfaceImpl, InterfaceImplRec::COL_Class, pRec, td));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken( TBL_InterfaceImpl, InterfaceImplRec::COL_Interface, pRec, tkInterface));
+
+ErrExit:
+ return hr;
+} // RegMeta::AddInterfaceImpl
+
+#endif //FEATURE_METADATA_EMIT && FEATURE_METADATA_INTERNAL_APIS
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+//*****************************************************************************
+// Helper : get metadata information
+//
+// Implements internal API code:IMetaDataHelper::GetMetadata.
+//*****************************************************************************
+STDMETHODIMP
+RegMeta::GetMetadata(
+ ULONG ulSelect, // [IN] Selector.
+ void ** ppData) // [OUT] Put pointer to data here.
+{
+
+ REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+
+ switch (ulSelect)
+ {
+ case 0:
+ *ppData = &m_pStgdb->m_MiniMd;
+ break;
+ case 1:
+ *ppData = (void*)g_CodedTokens;
+ break;
+ case 2:
+ *ppData = (void*)g_Tables;
+ break;
+ default:
+ *ppData = 0;
+ break;
+ }
+
+ return S_OK;
+} // RegMeta::GetMetadata
+
+//*******************************************************************************
+// helper to change MVID
+//
+// Implements internal API code:IMDInternalEmit::ChangeMvid.
+//*******************************************************************************
+HRESULT RegMeta::ChangeMvid( // S_OK or error.
+ REFGUID newMvid) // GUID to use as the MVID
+{
+ return GetMiniMd()->ChangeMvid(newMvid);
+}
+
+//*******************************************************************************
+// Helper to change MDUpdateMode value to updateMode.
+//
+// Implements internal API code:IMDInternalEmit::SetMDUpdateMode.
+//*******************************************************************************
+HRESULT
+RegMeta::SetMDUpdateMode(
+ ULONG updateMode,
+ ULONG * pPreviousUpdateMode)
+{
+ HRESULT hr;
+
+ OptionValue optionValue;
+ IfFailGo(m_pStgdb->m_MiniMd.GetOption(&optionValue));
+ if (pPreviousUpdateMode != NULL)
+ {
+ *pPreviousUpdateMode = optionValue.m_UpdateMode;
+ }
+ optionValue.m_UpdateMode = updateMode;
+ IfFailGo(m_pStgdb->m_MiniMd.SetOption(&optionValue));
+
+ErrExit:
+ return hr;
+} // RegMeta::SetMDUpdateMode
+
+#endif //FEATURE_METADATA_INTERNAL_APIS
diff --git a/src/md/compiler/import.cpp b/src/md/compiler/import.cpp
new file mode 100644
index 0000000000..9d1dfc8116
--- /dev/null
+++ b/src/md/compiler/import.cpp
@@ -0,0 +1,3809 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// Import.cpp
+//
+
+//
+// Methods of code:RegMeta class which implement public API interfaces:
+// * code:IMetaDataImport, and
+// * code:IMetaDataImport2.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "metadata.h"
+#include "corerror.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "corpriv.h"
+#include "importhelper.h"
+#include "mdlog.h"
+#include "mdperf.h"
+#include "stgio.h"
+
+//*****************************************************************************
+// Enumerate over all the Methods in a TypeDef.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumMembers( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStartMethod;
+ ULONG ridEndMethod;
+ ULONG ridStartField;
+ ULONG ridEndField;
+ ULONG index;
+ ULONG indexField;
+ TypeDefRec *pRec;
+ HENUMInternal *pEnum = *ppmdEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumMembers(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, cl, rMembers, cMax, pcTokens));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ if ( IsGlobalMethodParentTk(cl) )
+ {
+ cl = m_tdModule;
+ }
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(cl), &pRec));
+
+ ridStartMethod = m_pStgdb->m_MiniMd.getMethodListOfTypeDef(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(cl), &ridEndMethod));
+
+ ridStartField = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(cl), &ridEndField));
+
+
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) );
+
+ // add all methods to the dynamic array
+ for (index = ridStartMethod; index < ridEndMethod; index++ )
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtMethodDef)));
+ }
+
+ // add all fields to the dynamic array
+ for (indexField = ridStartField; indexField < ridEndField; indexField++ )
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetFieldRid(indexField, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtFieldDef)));
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMembers, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumMembers);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumMembers()
+
+//*****************************************************************************
+// Enumerate over all the Methods in a TypeDef that has szName
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumMembersWithName( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG index;
+ TypeDefRec *pRec;
+ MethodRec *pMethod;
+ FieldRec *pField;
+ HENUMInternal *pEnum = *ppmdEnum;
+ LPUTF8 szNameUtf8;
+ UTF8STR(szName, szNameUtf8);
+ LPCUTF8 szNameUtf8Tmp;
+
+ LOG((LOGMD, "MD RegMeta::EnumMembersWithName(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, cl, MDSTR(szName), rMembers, cMax, pcTokens));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // create the enumerator
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) );
+
+ if ( IsGlobalMethodParentTk(cl) )
+ {
+ cl = m_tdModule;
+ }
+
+ // get the range of method rids given a typedef
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(cl), &pRec));
+ ridStart = pMiniMd->getMethodListOfTypeDef(pRec);
+ IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(cl), &ridEnd));
+
+ for (index = ridStart; index < ridEnd; index++ )
+ {
+ if (szNameUtf8 == NULL)
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtMethodDef)));
+ }
+ else
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(pMiniMd->GetMethodRecord(rid, &pMethod));
+ IfFailGo(pMiniMd->getNameOfMethod(pMethod, &szNameUtf8Tmp));
+ if ( strcmp(szNameUtf8Tmp, szNameUtf8) == 0 )
+ {
+ IfFailGo(pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(rid, mdtMethodDef)));
+ }
+ }
+ }
+
+ ridStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(cl), &ridEnd));
+
+ for (index = ridStart; index < ridEnd; index++ )
+ {
+ if (szNameUtf8 == NULL)
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(rid, mdtFieldDef)));
+ }
+ else
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo(pMiniMd->GetFieldRecord(rid, &pField));
+ IfFailGo(pMiniMd->getNameOfField(pField, &szNameUtf8Tmp));
+ if ( strcmp(szNameUtf8Tmp, szNameUtf8) == 0 )
+ {
+ IfFailGo(pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtFieldDef)));
+ }
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMembers, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumMembersWithName);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumMembersWithName()
+
+//*****************************************************************************
+// enumerating through methods given a Typedef and the flag
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumMethods(
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdMethodDef rMethods[], // [OUT] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ TypeDefRec *pRec;
+ HENUMInternal *pEnum = *ppmdEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumMethods(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, td, rMethods, cMax, pcTokens));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // Check for mdTypeDefNil (representing <Module>).
+ // If so, this will map it to its token.
+ //
+ if ( IsGlobalMethodParentTk(td) )
+ {
+ td = m_tdModule;
+ }
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pRec));
+ ridStart = m_pStgdb->m_MiniMd.getMethodListOfTypeDef(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(td), &ridEnd));
+
+ if (pMiniMd->HasIndirectTable(TBL_Method) || pMiniMd->HasDelete())
+ {
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) );
+
+ // add all methods to the dynamic array
+ for (ULONG index = ridStart; index < ridEnd; index++ )
+ {
+ if (pMiniMd->HasDelete() &&
+ ((m_OptionValue.m_ImportOption & MDImportOptionAllMethodDefs) == 0))
+ {
+ MethodRec *pMethRec;
+ RID rid;
+ IfFailGo(pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(pMiniMd->GetMethodRecord(rid, &pMethRec));
+ LPCSTR szMethodName;
+ IfFailGo(pMiniMd->getNameOfMethod(pMethRec, &szMethodName));
+ if (IsMdRTSpecialName(pMethRec->GetFlags()) && IsDeletedName(szMethodName) )
+ {
+ continue;
+ }
+ }
+ RID rid;
+ IfFailGo(pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtMethodDef)));
+ }
+ }
+ else
+ {
+ IfFailGo( HENUMInternal::CreateSimpleEnum( mdtMethodDef, ridStart, ridEnd, &pEnum) );
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethods, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumMethods);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumMethods()
+
+
+
+
+//*****************************************************************************
+// Enumerate over all the methods with szName in a TypeDef.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumMethodsWithName( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdMethodDef rMethods[], // [OU] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG index;
+ TypeDefRec *pRec;
+ MethodRec *pMethod;
+ HENUMInternal *pEnum = *ppmdEnum;
+ LPUTF8 szNameUtf8;
+ UTF8STR(szName, szNameUtf8);
+ LPCUTF8 szNameUtf8Tmp;
+
+ LOG((LOGMD, "MD RegMeta::EnumMethodsWithName(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, cl, MDSTR(szName), rMethods, cMax, pcTokens));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // Check for mdTypeDefNil (representing <Module>).
+ // If so, this will map it to its token.
+ //
+ if ( IsGlobalMethodParentTk(cl) )
+ {
+ cl = m_tdModule;
+ }
+
+
+ // create the enumerator
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) );
+
+ // get the range of method rids given a typedef
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(cl), &pRec));
+ ridStart = pMiniMd->getMethodListOfTypeDef(pRec);
+ IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(cl), &ridEnd));
+
+ for (index = ridStart; index < ridEnd; index++ )
+ {
+ if ( szNameUtf8 == NULL )
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtMethodDef)));
+ }
+ else
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(pMiniMd->GetMethodRecord(rid, &pMethod));
+ IfFailGo(pMiniMd->getNameOfMethod(pMethod, &szNameUtf8Tmp));
+ if ( strcmp(szNameUtf8Tmp, szNameUtf8) == 0 )
+ {
+ IfFailGo(pMiniMd->GetMethodRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtMethodDef)));
+ }
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethods, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumMethodsWithName);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumMethodsWithName()
+
+
+
+//*****************************************************************************
+// Enumerate over all the fields in a TypeDef and a flag.
+//*****************************************************************************
+STDMETHODIMP
+RegMeta::EnumFields(
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdFieldDef rFields[], // [OUT] Put FieldDefs here.
+ ULONG cMax, // [IN] Max FieldDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **>(phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ TypeDefRec *pRec;
+ HENUMInternal *pEnum = *ppmdEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumFields(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, td, rFields, cMax, pcTokens));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (pEnum == NULL)
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // Check for mdTypeDefNil (representing <Module>).
+ // If so, this will map it to its token.
+ //
+ if (IsGlobalMethodParentTk(td))
+ {
+ td = m_tdModule;
+ }
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pRec));
+ ridStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(td), &ridEnd));
+
+ if (pMiniMd->HasIndirectTable(TBL_Field) || pMiniMd->HasDelete())
+ {
+ IfFailGo(HENUMInternal::CreateDynamicArrayEnum(mdtFieldDef, &pEnum));
+
+ // add all methods to the dynamic array
+ for (ULONG index = ridStart; index < ridEnd; index++)
+ {
+ if (pMiniMd->HasDelete() &&
+ ((m_OptionValue.m_ImportOption & MDImportOptionAllFieldDefs) == 0))
+ {
+ FieldRec *pFieldRec;
+ RID rid;
+ IfFailGo(pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo(pMiniMd->GetFieldRecord(rid, &pFieldRec));
+ LPCUTF8 szFieldName;
+ IfFailGo(pMiniMd->getNameOfField(pFieldRec, &szFieldName));
+ if (IsFdRTSpecialName(pFieldRec->GetFlags()) && IsDeletedName(szFieldName))
+ {
+ continue;
+ }
+ }
+ RID rid;
+ IfFailGo(pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtFieldDef)));
+ }
+ }
+ else
+ {
+ IfFailGo(HENUMInternal::CreateSimpleEnum(mdtFieldDef, ridStart, ridEnd, &pEnum));
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rFields, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumFields);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumFields
+
+
+
+//*****************************************************************************
+// Enumerate over all the fields with szName in a TypeDef.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumFieldsWithName( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdFieldDef rFields[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG index;
+ TypeDefRec *pRec;
+ FieldRec *pField;
+ HENUMInternal *pEnum = *ppmdEnum;
+ LPUTF8 szNameUtf8;
+ UTF8STR(szName, szNameUtf8);
+ LPCUTF8 szNameUtf8Tmp;
+
+ LOG((LOGMD, "MD RegMeta::EnumFields(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, cl, MDSTR(szName), rFields, cMax, pcTokens));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // Check for mdTypeDefNil (representing <Module>).
+ // If so, this will map it to its token.
+ //
+ if ( IsGlobalMethodParentTk(cl) )
+ {
+ cl = m_tdModule;
+ }
+
+ // create the enumerator
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) );
+
+ // get the range of field rids given a typedef
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(cl), &pRec));
+ ridStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(cl), &ridEnd));
+
+ for (index = ridStart; index < ridEnd; index++ )
+ {
+ if ( szNameUtf8 == NULL )
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtFieldDef)));
+ }
+ else
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo(pMiniMd->GetFieldRecord(rid, &pField));
+ IfFailGo(pMiniMd->getNameOfField(pField, &szNameUtf8Tmp));
+ if ( strcmp(szNameUtf8Tmp, szNameUtf8) == 0 )
+ {
+ IfFailGo(pMiniMd->GetFieldRid(index, &rid));
+ IfFailGo( HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtFieldDef) ) );
+ }
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rFields, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumFieldsWithName);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumFieldsWithName()
+
+
+//*****************************************************************************
+// Enumerate over the ParamDefs in a Method.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumParams( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdParamDef rParams[], // [OUT] Put ParamDefs here.
+ ULONG cMax, // [IN] Max ParamDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ MethodRec *pRec;
+ HENUMInternal *pEnum = *ppmdEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumParams(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, mb, rParams, cMax, pcTokens));
+ START_MD_PERF();
+ LOCKREAD();
+
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(mb), &pRec));
+ ridStart = m_pStgdb->m_MiniMd.getParamListOfMethod(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndParamListOfMethod(RidFromToken(mb), &ridEnd));
+
+ if (pMiniMd->HasIndirectTable(TBL_Param))
+ {
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtParamDef, &pEnum) );
+
+ // add all methods to the dynamic array
+ for (ULONG index = ridStart; index < ridEnd; index++ )
+ {
+ RID rid;
+ IfFailGo(pMiniMd->GetParamRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtParamDef)));
+ }
+ }
+ else
+ {
+ IfFailGo( HENUMInternal::CreateSimpleEnum( mdtParamDef, ridStart, ridEnd, &pEnum) );
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rParams, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumParams);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumParams()
+
+
+
+//*****************************************************************************
+// Enumerate the MemberRefs given the parent token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumMemberRefs( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tkParent, // [IN] Parent token to scope the enumeration.
+ mdMemberRef rMemberRefs[], // [OUT] Put MemberRefs here.
+ ULONG cMax, // [IN] Max MemberRefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridEnd;
+ ULONG index;
+ MemberRefRec *pRec;
+ HENUMInternal *pEnum = *ppmdEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumMemberRefs(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, tkParent, rMemberRefs, cMax, pcTokens));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ mdToken tk;
+
+ // Check for mdTypeDefNil (representing <Module>).
+ // If so, this will map it to its token.
+ //
+ IsGlobalMethodParent(&tkParent);
+
+ // create the enumerator
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMemberRef, &pEnum) );
+
+ // get the range of field rids given a typedef
+ ridEnd = pMiniMd->getCountMemberRefs();
+
+ for (index = 1; index <= ridEnd; index++ )
+ {
+ IfFailGo(pMiniMd->GetMemberRefRecord(index, &pRec));
+ tk = pMiniMd->getClassOfMemberRef(pRec);
+ if ( tk == tkParent )
+ {
+ // add the matched ones to the enumerator
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtMemberRef) ) );
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMemberRefs, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumMemberRefs);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumMemberRefs()
+
+
+//*****************************************************************************
+// Enumerate methodimpls given a typedef
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumMethodImpls( // S_OK, S_FALSE, or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdToken rMethodBody[], // [OUT] Put Method Body tokens here.
+ mdToken rMethodDecl[], // [OUT] Put Method Declaration tokens here.
+ ULONG cMax, // [IN] Max tokens to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ MethodImplRec *pRec;
+ HENUMInternal *pEnum = *ppmdEnum;
+ HENUMInternal hEnum;
+
+
+ LOG((LOGMD, "MD RegMeta::EnumMethodImpls(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, td, rMethodBody, rMethodDecl, cMax, pcTokens));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ memset(&hEnum, 0, sizeof(HENUMInternal));
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ mdToken tkMethodBody;
+ mdToken tkMethodDecl;
+ RID ridCur;
+
+ // Get the range of rids.
+ IfFailGo( pMiniMd->FindMethodImplHelper(td, &hEnum) );
+
+ // Create the enumerator, DynamicArrayEnum does not use the token type.
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( (TBL_MethodImpl << 24), &pEnum) );
+
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur))
+ {
+ // Get the MethodBody and MethodDeclaration tokens for the current
+ // MethodImpl record.
+ IfFailGo(pMiniMd->GetMethodImplRecord(ridCur, &pRec));
+ tkMethodBody = pMiniMd->getMethodBodyOfMethodImpl(pRec);
+ tkMethodDecl = pMiniMd->getMethodDeclarationOfMethodImpl(pRec);
+
+ // Add the Method body/declaration pairs to the Enum
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, tkMethodBody ) );
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, tkMethodDecl ) );
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethodBody, rMethodDecl, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+ HENUMInternal::ClearEnum(&hEnum);
+
+ STOP_MD_PERF(EnumMethodImpls);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumMethodImpls()
+
+
+//*****************************************************************************
+// Enumerate over PermissionSets. Optionally limit to an object and/or an
+// action.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumPermissionSets( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] if !NIL, token to scope the enumeration.
+ DWORD dwActions, // [IN] if !0, return only these actions.
+ mdPermission rPermission[], // [OUT] Put Permissions here.
+ ULONG cMax, // [IN] Max Permissions to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG index;
+ DeclSecurityRec *pRec;
+ HENUMInternal *pEnum = *ppmdEnum;
+ bool fCompareParent = false;
+ mdToken typ = TypeFromToken(tk);
+ mdToken tkParent;
+
+ LOG((LOGMD, "MD RegMeta::EnumPermissionSets(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, tk, dwActions, rPermission, cMax, pcTokens));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if ( pEnum == 0 )
+ {
+ // Does this token type even have security?
+ if (tk != 0 &&
+ !(typ == mdtTypeDef || typ == mdtMethodDef || typ == mdtAssembly))
+ {
+ if (pcTokens)
+ *pcTokens = 0;
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ if (!IsNilToken(tk))
+ {
+ // parent is provided for lookup
+ if ( pMiniMd->IsSorted( TBL_DeclSecurity ) )
+ {
+ IfFailGo(pMiniMd->getDeclSecurityForToken(tk, &ridEnd, &ridStart));
+ }
+ else
+ {
+ // table is not sorted. So we have to do a table scan
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountDeclSecuritys() + 1;
+ fCompareParent = true;
+ }
+ }
+ else
+ {
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountDeclSecuritys() + 1;
+ }
+
+ if (IsDclActionNil(dwActions) && !fCompareParent && !m_pStgdb->m_MiniMd.HasDelete())
+ {
+ // create simple enumerator
+ IfFailGo( HENUMInternal::CreateSimpleEnum( mdtPermission, ridStart, ridEnd, &pEnum) );
+ }
+ else
+ {
+ // create the dynamic enumerator
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtPermission, &pEnum) );
+
+ for (index = ridStart; index < ridEnd; index++ )
+ {
+ IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pRec));
+ tkParent = pMiniMd->getParentOfDeclSecurity(pRec);
+ if ( (fCompareParent && tk != tkParent) ||
+ IsNilToken(tkParent) )
+ {
+ // We need to compare parent token and they are not equal so skip
+ // over this row.
+ //
+ continue;
+ }
+ if ( IsDclActionNil(dwActions) ||
+ ( (DWORD)(pMiniMd->getActionOfDeclSecurity(pRec))) == dwActions )
+ {
+ // If we don't need to compare the action, just add to the enum.
+ // Or we need to compare the action and the action values are equal, add to enum as well.
+ //
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtPermission) ) );
+ }
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rPermission, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumPermissionSets);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumPermissionSets()
+
+
+//*****************************************************************************
+// Find a given member in a TypeDef (typically a class).
+//*****************************************************************************
+STDMETHODIMP RegMeta::FindMember(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdToken *pmb) // [OUT] matching memberdef
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ LOG((LOGMD, "MD RegMeta::FindMember(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, MDSTR(szName), pvSigBlob, cbSigBlob, pmb));
+
+ START_MD_PERF();
+
+ // Don't lock this function. All of the functions that it calls are public APIs. keep it that way.
+
+ // try to match with method first of all
+ hr = FindMethod(
+ td,
+ szName,
+ pvSigBlob,
+ cbSigBlob,
+ pmb);
+
+ if ( hr == CLDB_E_RECORD_NOTFOUND )
+ {
+ // now try field table
+ IfFailGo( FindField(
+ td,
+ szName,
+ pvSigBlob,
+ cbSigBlob,
+ pmb) );
+ }
+ErrExit:
+ STOP_MD_PERF(FindMember);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::FindMember()
+
+
+
+//*****************************************************************************
+// Find a given member in a TypeDef (typically a class).
+//*****************************************************************************
+STDMETHODIMP RegMeta::FindMethod(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmb) // [OUT] matching memberdef
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ LPUTF8 szNameUtf8;
+ UTF8STR(szName, szNameUtf8);
+
+ LOG((LOGMD, "MD RegMeta::FindMethod(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, MDSTR(szName), pvSigBlob, cbSigBlob, pmb));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (szName == NULL)
+ IfFailGo(E_INVALIDARG);
+ PREFIX_ASSUME(szName != NULL);
+
+ // If this is a global method, then use the <Module> typedef as parent.
+ IsGlobalMethodParent(&td);
+
+ IfFailGo(ImportHelper::FindMethod(pMiniMd,
+ td,
+ szNameUtf8,
+ pvSigBlob,
+ cbSigBlob,
+ pmb));
+
+ErrExit:
+ STOP_MD_PERF(FindMethod);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::FindMethod
+
+
+//*****************************************************************************
+// Find a given member in a TypeDef (typically a class).
+//*****************************************************************************
+STDMETHODIMP
+RegMeta::FindField(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdFieldDef *pmb) // [OUT] matching memberdef
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "MD RegMeta::FindField(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, MDSTR(szName), pvSigBlob, cbSigBlob, pmb));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (szName == NULL)
+ IfFailGo(E_INVALIDARG);
+
+ LPUTF8 szNameUtf8;
+ UTF8STR(szName, szNameUtf8);
+
+ // If this is a global method, then use the <Module> typedef as parent.
+ IsGlobalMethodParent(&td);
+
+ IfFailGo(ImportHelper::FindField(pMiniMd,
+ td,
+ szNameUtf8,
+ pvSigBlob,
+ cbSigBlob,
+ pmb));
+
+ErrExit:
+ STOP_MD_PERF(FindField);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::FindField
+
+
+//*****************************************************************************
+// Find a given MemberRef in a TypeRef (typically a class). If no TypeRef
+// is specified, the query will be for a random member in the scope.
+//*****************************************************************************
+STDMETHODIMP RegMeta::FindMemberRef(
+ mdToken tkPar, // [IN] given parent token.
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMemberRef *pmr) // [OUT] matching memberref
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ LPUTF8 szNameUtf8;
+ UTF8STR(szName, szNameUtf8);
+
+ LOG((LOGMD, "MD RegMeta::FindMemberRef(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n",
+ tkPar, MDSTR(szName), pvSigBlob, cbSigBlob, pmr));
+
+
+
+ START_MD_PERF();
+
+ // <TODO>@todo: Can this causing building hash table? If so, should this consider the write lock?</TODO>
+ LOCKREAD();
+
+ // get the range of field rids given a typedef
+ _ASSERTE(TypeFromToken(tkPar) == mdtTypeRef || TypeFromToken(tkPar) == mdtMethodDef ||
+ TypeFromToken(tkPar) == mdtModuleRef || TypeFromToken(tkPar) == mdtTypeDef ||
+ TypeFromToken(tkPar) == mdtTypeSpec);
+
+ // Set parent to global class m_tdModule if mdTokenNil is passed.
+ if (IsNilToken(tkPar))
+ tkPar = m_tdModule;
+
+ IfFailGo( ImportHelper::FindMemberRef(pMiniMd, tkPar, szNameUtf8, pvSigBlob, cbSigBlob, pmr) );
+
+ErrExit:
+
+ STOP_MD_PERF(FindMemberRef);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::FindMemberRef()
+
+
+//*****************************************************************************
+// Return the property of a MethodDef
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetMethodProps(
+ mdMethodDef mb, // The method for which to get props.
+ mdTypeDef *pClass, // Put method's class here.
+ __out_ecount_opt (cchMethod) LPWSTR szMethod, // Put method's name here.
+ ULONG cchMethod, // Size of szMethod buffer in wide chars.
+ ULONG *pchMethod, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG *pulCodeRVA, // [OUT] codeRVA
+ DWORD *pdwImplFlags) // [OUT] Impl. Flags
+{
+ HRESULT hr = NOERROR;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MethodRec *pMethodRec;
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "MD RegMeta::GetMethodProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ mb, pClass, szMethod, cchMethod, pchMethod, pdwAttr, ppvSigBlob, pcbSigBlob,
+ pulCodeRVA, pdwImplFlags));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mb) == mdtMethodDef);
+
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(mb), &pMethodRec));
+
+ if (pClass)
+ {
+ // caller wants parent typedef
+ IfFailGo( pMiniMd->FindParentOfMethodHelper(mb, pClass) );
+
+ if ( IsGlobalMethodParentToken(*pClass) )
+ {
+ // If the parent of Method is the <Module>, return mdTypeDefNil instead.
+ *pClass = mdTypeDefNil;
+ }
+
+ }
+ if (ppvSigBlob || pcbSigBlob)
+ {
+ // caller wants signature information
+ PCCOR_SIGNATURE pvSigTmp;
+ ULONG cbSig;
+ IfFailGo(pMiniMd->getSignatureOfMethod(pMethodRec, &pvSigTmp, &cbSig));
+ if ( ppvSigBlob )
+ *ppvSigBlob = pvSigTmp;
+ if ( pcbSigBlob)
+ *pcbSigBlob = cbSig;
+ }
+ if ( pdwAttr )
+ {
+ *pdwAttr = pMiniMd->getFlagsOfMethod(pMethodRec);
+ }
+ if ( pulCodeRVA )
+ {
+ *pulCodeRVA = pMiniMd->getRVAOfMethod(pMethodRec);
+ }
+ if ( pdwImplFlags )
+ {
+ *pdwImplFlags = (DWORD )pMiniMd->getImplFlagsOfMethod(pMethodRec);
+ }
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szMethod || pchMethod)
+ {
+ IfFailGo( pMiniMd->getNameOfMethod(pMethodRec, szMethod, cchMethod, pchMethod) );
+ }
+
+ErrExit:
+ STOP_MD_PERF(GetMethodProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetMethodProps()
+
+
+//*****************************************************************************
+// Return the property of a MemberRef
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetMemberRefProps( // S_OK or error.
+ mdMemberRef mr, // [IN] given memberref
+ mdToken *ptk, // [OUT] Put classref or classdef here.
+ __out_ecount_opt (cchMember) LPWSTR szMember, // [OUT] buffer to fill for member's name
+ ULONG cchMember, // [IN] the count of char of szMember
+ ULONG *pchMember, // [OUT] actual count of char in member name
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to meta data blob value
+ ULONG *pbSig) // [OUT] actual size of signature blob
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ MemberRefRec *pMemberRefRec;
+
+ LOG((LOGMD, "MD RegMeta::GetMemberRefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ mr, ptk, szMember, cchMember, pchMember, ppvSigBlob, pbSig));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mr) == mdtMemberRef);
+
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(mr), &pMemberRefRec));
+
+ if (ptk)
+ {
+ *ptk = pMiniMd->getClassOfMemberRef(pMemberRefRec);
+ if ( IsGlobalMethodParentToken(*ptk) )
+ {
+ // If the parent of MemberRef is the <Module>, return mdTypeDefNil instead.
+ *ptk = mdTypeDefNil;
+ }
+
+ }
+ if (ppvSigBlob || pbSig)
+ {
+ // caller wants signature information
+ PCCOR_SIGNATURE pvSigTmp;
+ ULONG cbSig;
+ IfFailGo(pMiniMd->getSignatureOfMemberRef(pMemberRefRec, &pvSigTmp, &cbSig));
+ if ( ppvSigBlob )
+ *ppvSigBlob = pvSigTmp;
+ if ( pbSig)
+ *pbSig = cbSig;
+ }
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szMember || pchMember)
+ {
+ IfFailGo( pMiniMd->getNameOfMemberRef(pMemberRefRec, szMember, cchMember, pchMember) );
+ }
+
+ErrExit:
+
+ STOP_MD_PERF(GetMemberRefProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetMemberRefProps()
+
+
+//*****************************************************************************
+// enumerate Property tokens for a typedef
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumProperties( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdProperty rProperties[], // [OUT] Put Properties here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcProperties) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart = 0;
+ ULONG ridEnd = 0;
+ ULONG ridMax = 0;
+ HENUMInternal *pEnum = *ppmdEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumProperties(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, td, rProperties, cMax, pcProperties));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (IsNilToken(td))
+ {
+ if (pcProperties)
+ *pcProperties = 0;
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ RID ridPropertyMap;
+ PropertyMapRec *pPropertyMapRec;
+
+ // get the starting/ending rid of properties of this typedef
+ IfFailGo(pMiniMd->FindPropertyMapFor(RidFromToken(td), &ridPropertyMap));
+ if (!InvalidRid(ridPropertyMap))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec));
+ ridStart = pMiniMd->getPropertyListOfPropertyMap(pPropertyMapRec);
+ IfFailGo(pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd));
+ ridMax = pMiniMd->getCountPropertys() + 1;
+ if(ridStart == 0) ridStart = 1;
+ if(ridEnd > ridMax) ridEnd = ridMax;
+ if(ridStart > ridEnd) ridStart=ridEnd;
+ }
+
+ if (pMiniMd->HasIndirectTable(TBL_Property) || pMiniMd->HasDelete())
+ {
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtProperty, &pEnum) );
+
+ // add all methods to the dynamic array
+ for (ULONG index = ridStart; index < ridEnd; index++ )
+ {
+ if (pMiniMd->HasDelete() &&
+ ((m_OptionValue.m_ImportOption & MDImportOptionAllProperties) == 0))
+ {
+ PropertyRec *pRec;
+ RID rid;
+ IfFailGo(pMiniMd->GetPropertyRid(index, &rid));
+ IfFailGo(pMiniMd->GetPropertyRecord(rid, &pRec));
+ LPCUTF8 szPropertyName;
+ IfFailGo(pMiniMd->getNameOfProperty(pRec, &szPropertyName));
+ if (IsPrRTSpecialName(pRec->GetPropFlags()) && IsDeletedName(szPropertyName))
+ {
+ continue;
+ }
+ }
+ RID rid;
+ IfFailGo(pMiniMd->GetPropertyRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtProperty)));
+ }
+ }
+ else
+ {
+ IfFailGo( HENUMInternal::CreateSimpleEnum( mdtProperty, ridStart, ridEnd, &pEnum) );
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rProperties, pcProperties);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+
+ STOP_MD_PERF(EnumProperties);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+
+} // STDMETHODIMP RegMeta::EnumProperties()
+
+
+//*****************************************************************************
+// enumerate event tokens for a typedef
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumEvents( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdEvent rEvents[], // [OUT] Put events here.
+ ULONG cMax, // [IN] Max events to put.
+ ULONG *pcEvents) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart = 0;
+ ULONG ridEnd = 0;
+ ULONG ridMax = 0;
+ HENUMInternal *pEnum = *ppmdEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumEvents(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, td, rEvents, cMax, pcEvents));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ RID ridEventMap;
+ EventMapRec *pEventMapRec;
+
+ // get the starting/ending rid of properties of this typedef
+ IfFailGo(pMiniMd->FindEventMapFor(RidFromToken(td), &ridEventMap));
+ if (!InvalidRid(ridEventMap))
+ {
+ IfFailGo(pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec));
+ ridStart = pMiniMd->getEventListOfEventMap(pEventMapRec);
+ IfFailGo(pMiniMd->getEndEventListOfEventMap(ridEventMap, &ridEnd));
+ ridMax = pMiniMd->getCountEvents() + 1;
+ if(ridStart == 0) ridStart = 1;
+ if(ridEnd > ridMax) ridEnd = ridMax;
+ if(ridStart > ridEnd) ridStart=ridEnd;
+ }
+
+ if (pMiniMd->HasIndirectTable(TBL_Event) || pMiniMd->HasDelete())
+ {
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtEvent, &pEnum) );
+
+ // add all methods to the dynamic array
+ for (ULONG index = ridStart; index < ridEnd; index++ )
+ {
+ if (pMiniMd->HasDelete() &&
+ ((m_OptionValue.m_ImportOption & MDImportOptionAllEvents) == 0))
+ {
+ EventRec *pRec;
+ RID rid;
+ IfFailGo(pMiniMd->GetEventRid(index, &rid));
+ IfFailGo(pMiniMd->GetEventRecord(rid, &pRec));
+ LPCSTR szEventName;
+ IfFailGo(pMiniMd->getNameOfEvent(pRec, &szEventName));
+ if (IsEvRTSpecialName(pRec->GetEventFlags()) && IsDeletedName(szEventName))
+ {
+ continue;
+ }
+ }
+ RID rid;
+ IfFailGo(pMiniMd->GetEventRid(index, &rid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(rid, mdtEvent)));
+ }
+ }
+ else
+ {
+ IfFailGo( HENUMInternal::CreateSimpleEnum( mdtEvent, ridStart, ridEnd, &pEnum) );
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rEvents, pcEvents);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+
+ STOP_MD_PERF(EnumEvents);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumEvents()
+
+
+
+//*****************************************************************************
+// return the properties of an event token
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetEventProps( // S_OK, S_FALSE, or error.
+ mdEvent ev, // [IN] event token
+ mdTypeDef *pClass, // [OUT] typedef containing the event declarion.
+ LPCWSTR szEvent, // [OUT] Event name
+ ULONG cchEvent, // [IN] the count of wchar of szEvent
+ ULONG *pchEvent, // [OUT] actual count of wchar for event's name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType, // [OUT] EventType class
+ mdMethodDef *pmdAddOn, // [OUT] AddOn method of the event
+ mdMethodDef *pmdRemoveOn, // [OUT] RemoveOn method of the event
+ mdMethodDef *pmdFire, // [OUT] Fire method of the event
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the event
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod) // [OUT] total number of other method of this event
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ EventRec *pRec;
+ HENUMInternal hEnum;
+
+ LOG((LOGMD, "MD RegMeta::GetEventProps(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ ev, pClass, MDSTR(szEvent), cchEvent, pchEvent, pdwEventFlags, ptkEventType,
+ pmdAddOn, pmdRemoveOn, pmdFire, rmdOtherMethod, cMax, pcOtherMethod));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(ev) == mdtEvent);
+
+ memset(&hEnum, 0, sizeof(HENUMInternal));
+ IfFailGo(pMiniMd->GetEventRecord(RidFromToken(ev), &pRec));
+
+ if ( pClass )
+ {
+ // find the event map entry corresponding to this event
+ IfFailGo( pMiniMd->FindParentOfEventHelper( ev, pClass ) );
+ }
+ if ( pdwEventFlags )
+ {
+ *pdwEventFlags = pMiniMd->getEventFlagsOfEvent(pRec);
+ }
+ if ( ptkEventType )
+ {
+ *ptkEventType = pMiniMd->getEventTypeOfEvent(pRec);
+ }
+ {
+ MethodSemanticsRec *pSemantics;
+ RID ridCur;
+ ULONG cCurOtherMethod = 0;
+ ULONG ulSemantics;
+ mdMethodDef tkMethod;
+
+ // initialize output parameters
+ if (pmdAddOn)
+ *pmdAddOn = mdMethodDefNil;
+ if (pmdRemoveOn)
+ *pmdRemoveOn = mdMethodDefNil;
+ if (pmdFire)
+ *pmdFire = mdMethodDefNil;
+
+ IfFailGo( pMiniMd->FindMethodSemanticsHelper(ev, &hEnum) );
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur))
+ {
+ IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics));
+ ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics);
+ tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef );
+ switch (ulSemantics)
+ {
+ case msAddOn:
+ if (pmdAddOn) *pmdAddOn = tkMethod;
+ break;
+ case msRemoveOn:
+ if (pmdRemoveOn) *pmdRemoveOn = tkMethod;
+ break;
+ case msFire:
+ if (pmdFire) *pmdFire = tkMethod;
+ break;
+ case msOther:
+ if (cCurOtherMethod < cMax)
+ rmdOtherMethod[cCurOtherMethod] = tkMethod;
+ cCurOtherMethod++;
+ break;
+ default:
+ _ASSERTE(!"BadKind!");
+ }
+ }
+
+ // set the output parameter
+ if (pcOtherMethod)
+ *pcOtherMethod = cCurOtherMethod;
+ }
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szEvent || pchEvent)
+ {
+ IfFailGo( pMiniMd->getNameOfEvent(pRec, (LPWSTR) szEvent, cchEvent, pchEvent) );
+ }
+
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+ STOP_MD_PERF(GetEventProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetEventProps()
+
+
+//*****************************************************************************
+// given a method, return an arra of event/property tokens for each accessor role
+// it is defined to have
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumMethodSemantics( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdToken rEventProp[], // [OUT] Put Event/Property here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcEventProp) // [OUT] Put # put here.
+{
+ HRESULT hr = NOERROR;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridEnd;
+ ULONG index;
+ HENUMInternal *pEnum = *ppmdEnum;
+ MethodSemanticsRec *pRec;
+
+ LOG((LOGMD, "MD RegMeta::EnumMethodSemantics(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, mb, rEventProp, cMax, pcEventProp));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // create the enumerator
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( (DWORD) -1, &pEnum) );
+
+ // get the range of method rids given a typedef
+ ridEnd = pMiniMd->getCountMethodSemantics();
+
+ for (index = 1; index <= ridEnd; index++ )
+ {
+ IfFailGo(pMiniMd->GetMethodSemanticsRecord(index, &pRec));
+ if ( pMiniMd->getMethodOfMethodSemantics(pRec) == mb )
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, pMiniMd->getAssociationOfMethodSemantics(pRec) ) );
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rEventProp, pcEventProp);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+
+ STOP_MD_PERF(EnumMethodSemantics);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumMethodSemantics()
+
+
+
+//*****************************************************************************
+// return the role flags for the method/propevent pair
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetMethodSemantics( // S_OK, S_FALSE, or error.
+ mdMethodDef mb, // [IN] method token
+ mdToken tkEventProp, // [IN] event/property token.
+ DWORD *pdwSemanticsFlags) // [OUT] the role flags for the method/propevent pair
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ MethodSemanticsRec *pRec;
+ ULONG ridCur;
+ HENUMInternal hEnum;
+
+ LOG((LOGMD, "MD RegMeta::GetMethodSemantics(0x%08x, 0x%08x, 0x%08x)\n",
+ mb, tkEventProp, pdwSemanticsFlags));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mb) == mdtMethodDef);
+ _ASSERTE( pdwSemanticsFlags );
+
+ *pdwSemanticsFlags = 0;
+ memset(&hEnum, 0, sizeof(HENUMInternal));
+
+ // loop through all methods associated with this tkEventProp
+ IfFailGo( pMiniMd->FindMethodSemanticsHelper(tkEventProp, &hEnum) );
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur))
+ {
+ IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pRec));
+ if ( pMiniMd->getMethodOfMethodSemantics(pRec) == mb )
+ {
+ // we findd the match
+ *pdwSemanticsFlags = pMiniMd->getSemanticOfMethodSemantics(pRec);
+ goto ErrExit;
+ }
+ }
+
+ IfFailGo( CLDB_E_RECORD_NOTFOUND );
+
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+ STOP_MD_PERF(GetMethodSemantics);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetMethodSemantics()
+
+
+
+//*****************************************************************************
+// return the class layout information
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetClassLayout(
+ mdTypeDef td, // [IN] give typedef
+ DWORD *pdwPackSize, // [OUT] 1, 2, 4, 8, or 16
+ COR_FIELD_OFFSET rFieldOffset[], // [OUT] field offset array
+ ULONG cMax, // [IN] size of the array
+ ULONG *pcFieldOffset, // [OUT] needed array size
+ ULONG *pulClassSize) // [OUT] the size of the class
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ ClassLayoutRec *pRec;
+ RID ridClassLayout;
+ int bLayout=0; // Was any layout information found?
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+
+ LOG((LOGMD, "MD RegMeta::GetClassLayout(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ td, pdwPackSize, rFieldOffset, cMax, pcFieldOffset, pulClassSize));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ IfFailGo(pMiniMd->FindClassLayoutHelper(td, &ridClassLayout));
+
+ if (InvalidRid(ridClassLayout))
+ { // Nothing specified - return default values of 0.
+ if ( pdwPackSize )
+ *pdwPackSize = 0;
+ if ( pulClassSize )
+ *pulClassSize = 0;
+ }
+ else
+ {
+ IfFailGo(pMiniMd->GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec));
+ if ( pdwPackSize )
+ *pdwPackSize = pMiniMd->getPackingSizeOfClassLayout(pRec);
+ if ( pulClassSize )
+ *pulClassSize = pMiniMd->getClassSizeOfClassLayout(pRec);
+ bLayout = 1;
+ }
+
+ // fill the layout array
+ if (rFieldOffset || pcFieldOffset)
+ {
+ ULONG iFieldOffset = 0;
+ ULONG ridFieldStart;
+ ULONG ridFieldEnd;
+ ULONG ridFieldLayout;
+ ULONG ulOffset;
+ TypeDefRec *pTypeDefRec;
+ FieldLayoutRec *pLayout2Rec;
+ mdFieldDef fd;
+
+ // record for this typedef in TypeDef Table
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+
+ // find the starting and end field for this typedef
+ ridFieldStart = pMiniMd->getFieldListOfTypeDef(pTypeDefRec);
+ IfFailGo(pMiniMd->getEndFieldListOfTypeDef(RidFromToken(td), &ridFieldEnd));
+
+ // loop through the field table
+
+ for(; ridFieldStart < ridFieldEnd; ridFieldStart++)
+ {
+ // Calculate the field token.
+ RID rid;
+ IfFailGo(pMiniMd->GetFieldRid(ridFieldStart, &rid));
+ fd = TokenFromRid(rid, mdtFieldDef);
+
+ // Calculate the FieldLayout rid for the current field.
+ IfFailGo(pMiniMd->FindFieldLayoutHelper(fd, &ridFieldLayout));
+
+ // Calculate the offset.
+ if (InvalidRid(ridFieldLayout))
+ ulOffset = (ULONG) -1;
+ else
+ {
+ // get the FieldLayout record.
+ IfFailGo(pMiniMd->GetFieldLayoutRecord(ridFieldLayout, &pLayout2Rec));
+ ulOffset = pMiniMd->getOffSetOfFieldLayout(pLayout2Rec);
+ bLayout = 1;
+ }
+
+ // fill in the field layout if output buffer still has space.
+ if (cMax > iFieldOffset && rFieldOffset)
+ {
+ rFieldOffset[iFieldOffset].ridOfField = fd;
+ rFieldOffset[iFieldOffset].ulOffset = ulOffset;
+ }
+
+ // advance the index to the buffer.
+ iFieldOffset++;
+ }
+
+ if (bLayout && pcFieldOffset)
+ *pcFieldOffset = iFieldOffset;
+ }
+
+ if (!bLayout)
+ hr = CLDB_E_RECORD_NOTFOUND;
+
+ErrExit:
+ STOP_MD_PERF(GetClassLayout);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetClassLayout()
+
+
+
+//*****************************************************************************
+// return the native type of a field
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetFieldMarshal(
+ mdToken tk, // [IN] given a field's memberdef
+ PCCOR_SIGNATURE *ppvNativeType, // [OUT] native type of this field
+ ULONG *pcbNativeType) // [OUT] the count of bytes of *ppvNativeType
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ RID rid;
+ FieldMarshalRec *pFieldMarshalRec;
+
+
+ _ASSERTE(ppvNativeType != NULL && pcbNativeType != NULL);
+
+ LOG((LOGMD, "MD RegMeta::GetFieldMarshal(0x%08x, 0x%08x, 0x%08x)\n",
+ tk, ppvNativeType, pcbNativeType));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(tk) == mdtParamDef || TypeFromToken(tk) == mdtFieldDef);
+
+ // find the row containing the marshal definition for tk
+ IfFailGo(pMiniMd->FindFieldMarshalHelper(tk, &rid));
+ if (InvalidRid(rid))
+ {
+ IfFailGo( CLDB_E_RECORD_NOTFOUND );
+ }
+ IfFailGo(pMiniMd->GetFieldMarshalRecord(rid, &pFieldMarshalRec));
+
+ // get the native type
+ IfFailGo(pMiniMd->getNativeTypeOfFieldMarshal(pFieldMarshalRec, ppvNativeType, pcbNativeType));
+
+ErrExit:
+ STOP_MD_PERF(GetFieldMarshal);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetFieldMarshal()
+
+
+
+//*****************************************************************************
+// return the RVA and implflag for MethodDef or FieldDef token
+//*****************************************************************************
+STDMETHODIMP
+RegMeta::GetRVA(
+ mdToken tk, // Member for which to set offset
+ ULONG *pulCodeRVA, // The offset
+ DWORD *pdwImplFlags) // the implementation flags
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "MD RegMeta::GetRVA(0x%08x, 0x%08x, 0x%08x)\n",
+ tk, pulCodeRVA, pdwImplFlags));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (TypeFromToken(tk) == mdtMethodDef)
+ {
+ if (tk == mdMethodDefNil)
+ { // Backward compatibility with CLR 2.0 implementation
+ if (pulCodeRVA != NULL)
+ *pulCodeRVA = 0;
+ if (pdwImplFlags != NULL)
+ *pdwImplFlags = 0;
+
+ hr = S_OK;
+ goto ErrExit;
+ }
+
+ // MethodDef token
+ MethodRec *pMethodRec;
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tk), &pMethodRec));
+
+ if (pulCodeRVA != NULL)
+ {
+ *pulCodeRVA = pMiniMd->getRVAOfMethod(pMethodRec);
+ }
+ if (pdwImplFlags != NULL)
+ {
+ *pdwImplFlags = pMiniMd->getImplFlagsOfMethod(pMethodRec);
+ }
+ }
+ else
+ { // FieldDef token or invalid type of token (not mdtMethodDef)
+ ULONG iRecord;
+
+ IfFailGo(pMiniMd->FindFieldRVAHelper(tk, &iRecord));
+
+ if (InvalidRid(iRecord))
+ {
+ if (pulCodeRVA != NULL)
+ *pulCodeRVA = 0;
+
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+ }
+
+ FieldRVARec *pFieldRVARec;
+ IfFailGo(pMiniMd->GetFieldRVARecord(iRecord, &pFieldRVARec));
+
+ if (pulCodeRVA != NULL)
+ {
+ *pulCodeRVA = pMiniMd->getRVAOfFieldRVA(pFieldRVARec);
+ }
+ if (pdwImplFlags != NULL)
+ {
+ *pdwImplFlags = 0;
+ }
+ }
+ErrExit:
+ STOP_MD_PERF(GetRVA);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetRVA
+
+
+
+//*****************************************************************************
+// Get the Action and Permissions blob for a given PermissionSet.
+//*****************************************************************************
+STDMETHODIMP RegMeta::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;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::GetPermissionSetProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ pm, pdwAction, ppvPermission, pcbPermission));
+
+ CMiniMdRW *pMiniMd = NULL;
+ DeclSecurityRec *pRecord = NULL;
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+ IfFailGo(pMiniMd->GetDeclSecurityRecord(RidFromToken(pm), &pRecord));
+
+ _ASSERTE(TypeFromToken(pm) == mdtPermission && RidFromToken(pm));
+
+ // If you want the BLOB, better get the BLOB size as well.
+ _ASSERTE(!ppvPermission || pcbPermission);
+
+ if (pdwAction)
+ *pdwAction = pMiniMd->getActionOfDeclSecurity(pRecord);
+
+ if (ppvPermission != NULL)
+ {
+ IfFailGo(pMiniMd->getPermissionSetOfDeclSecurity(pRecord, (const BYTE **)ppvPermission, pcbPermission));
+ }
+
+ErrExit:
+
+ STOP_MD_PERF(GetPermissionSetProps);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // STDMETHODIMP RegMeta::GetPermissionSetProps()
+
+
+
+//*****************************************************************************
+// Given a signature token, get return a pointer to the signature to the caller.
+//
+//<TODO>@FUTURE: for short term we have a problem where there is no way to get a
+// fixed up address for a blob and do Merge at the same time. So we've created
+// this dummy table called StandAloneSig which you hand out a rid for. This
+// makes finding the sig an extra indirection that is not required. The
+// Model Compression save code needs to map the token into a byte offset in
+// the heap. Perhaps we can have another mdt* type to switch on the difference.
+// But ultimately it has to simply be "pBlobHeapBase + RidFromToken(mdSig)".</TODO>
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetSigFromToken( // S_OK or error.
+ mdSignature mdSig, // [IN] Signature token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token.
+ ULONG *pcbSig) // [OUT] return size of signature.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ StandAloneSigRec *pRec;
+
+ LOG((LOGMD, "MD RegMeta::GetSigFromToken(0x%08x, 0x%08x, 0x%08x)\n",
+ mdSig, ppvSig, pcbSig));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(mdSig) == mdtSignature);
+ _ASSERTE(ppvSig && pcbSig);
+
+ IfFailGo(pMiniMd->GetStandAloneSigRecord(RidFromToken(mdSig), &pRec));
+ IfFailGo(pMiniMd->getSignatureOfStandAloneSig(pRec, ppvSig, pcbSig));
+
+
+ErrExit:
+
+ STOP_MD_PERF(GetSigFromToken);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetSigFromToken()
+
+
+//*******************************************************************************
+// return the ModuleRef properties
+//*******************************************************************************
+STDMETHODIMP RegMeta::GetModuleRefProps( // S_OK or error.
+ mdModuleRef mur, // [IN] moduleref token.
+ __out_ecount_opt (cchName) LPWSTR szName, // [OUT] buffer to fill with the moduleref name.
+ ULONG cchName, // [IN] size of szName in wide characters.
+ ULONG *pchName) // [OUT] actual count of characters in the name.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ ModuleRefRec *pModuleRefRec;
+
+
+
+ LOG((LOGMD, "MD RegMeta::GetModuleRefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ mur, szName, cchName, pchName));
+ START_MD_PERF();
+ LOCKREAD();
+
+ IfFailGo(pMiniMd->GetModuleRefRecord(RidFromToken(mur), &pModuleRefRec));
+
+ _ASSERTE(TypeFromToken(mur) == mdtModuleRef);
+
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szName || pchName)
+ {
+ IfFailGo( pMiniMd->getNameOfModuleRef(pModuleRefRec, szName, cchName, pchName) );
+ }
+
+ErrExit:
+
+ STOP_MD_PERF(GetModuleRefProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetModuleRefProps()
+
+
+
+//*******************************************************************************
+// enumerating through all of the ModuleRefs
+//*******************************************************************************
+STDMETHODIMP RegMeta::EnumModuleRefs( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdModuleRef rModuleRefs[], // [OUT] put modulerefs here.
+ ULONG cMax, // [IN] max memberrefs to put.
+ ULONG *pcModuleRefs) // [OUT] put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumModuleRefs(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, rModuleRefs, cMax, pcModuleRefs));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (*ppmdEnum == NULL)
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // create the enumerator
+ IfFailGo(HENUMInternal::CreateSimpleEnum(
+ mdtModuleRef,
+ 1,
+ pMiniMd->getCountModuleRefs() + 1,
+ &pEnum));
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppmdEnum;
+ }
+
+ // we can only fill the minimun of what caller asked for or what we have left
+ IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rModuleRefs, pcModuleRefs));
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumModuleRefs);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumModuleRefs()
+
+
+//*******************************************************************************
+// return properties regarding a TypeSpec
+//*******************************************************************************
+STDMETHODIMP RegMeta::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;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ TypeSpecRec *pRec = NULL;
+
+ LOG((LOGMD, "MD RegMeta::GetTypeSpecFromToken(0x%08x, 0x%08x, 0x%08x)\n",
+ typespec, ppvSig, pcbSig));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(typespec) == mdtTypeSpec);
+ _ASSERTE(ppvSig && pcbSig);
+
+ IfFailGo(pMiniMd->GetTypeSpecRecord(RidFromToken(typespec), &pRec));
+ IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRec, ppvSig, pcbSig));
+
+ErrExit:
+
+ STOP_MD_PERF(GetTypeSpecFromToken);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // STDMETHODIMP RegMeta::GetTypeSpecFromToken()
+
+
+//*****************************************************************************
+// For those items that have a name, retrieve a direct pointer to the name
+// off of the heap. This reduces copies made for the caller.
+//*****************************************************************************
+#define NAME_FROM_TOKEN_TYPE(RecType, TokenType) \
+ case mdt ## TokenType: \
+ { \
+ RecType ## Rec *pRecord; \
+ IfFailGo(pMiniMd->Get ## RecType ## Record(RidFromToken(tk), &pRecord)); \
+ IfFailGo(pMiniMd->getNameOf ## RecType (pRecord, pszUtf8NamePtr)); \
+ } \
+ break;
+#define NAME_FROM_TOKEN(RecType) NAME_FROM_TOKEN_TYPE(RecType, RecType)
+
+STDMETHODIMP RegMeta::GetNameFromToken( // S_OK or error.
+ mdToken tk, // [IN] Token to get name from. Must have a name.
+ MDUTF8CSTR *pszUtf8NamePtr) // [OUT] Return pointer to UTF8 name in heap.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "MD RegMeta::GetNameFromToken(0x%08x, 0x%08x)\n",
+ tk, pszUtf8NamePtr));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(pszUtf8NamePtr);
+
+ switch (TypeFromToken(tk))
+ {
+ NAME_FROM_TOKEN(Module);
+ NAME_FROM_TOKEN(TypeRef);
+ NAME_FROM_TOKEN(TypeDef);
+ NAME_FROM_TOKEN_TYPE(Field, FieldDef);
+ NAME_FROM_TOKEN_TYPE(Method, MethodDef);
+ NAME_FROM_TOKEN_TYPE(Param, ParamDef);
+ NAME_FROM_TOKEN(MemberRef);
+ NAME_FROM_TOKEN(Event);
+ NAME_FROM_TOKEN(Property);
+ NAME_FROM_TOKEN(ModuleRef);
+
+ default:
+ hr = E_INVALIDARG;
+ }
+
+ErrExit:
+
+ STOP_MD_PERF(GetNameFromToken);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return (hr);
+} // RegMeta::GetNameFromToken
+
+
+//*****************************************************************************
+// Get the symbol binding data back from the module if it is there. It is
+// stored as a custom value.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumUnresolvedMethods( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken rMethods[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+#ifdef FEATURE_METADATA_EMIT
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal ** ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG iCountTypeDef; // Count of TypeDefs.
+ ULONG ulStart, ulEnd; // Bounds of methods on a given TypeDef.
+ ULONG index; // For counting methods on a TypeDef.
+ ULONG indexTypeDef; // For counting TypeDefs.
+ bool bIsInterface; // Is a given TypeDef an interface?
+ HENUMInternal * pEnum = *ppmdEnum; // Enum we're working with.
+ CMiniMdRW * pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "MD RegMeta::EnumUnresolvedMethods(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, rMethods, cMax, pcTokens));
+
+ START_MD_PERF();
+
+ // take the write lock. Because we should have not have two EnumUnresolvedMethods being called at the
+ // same time. Ref to Def map may be calculated incorrectly.
+ LOCKWRITE();
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ MethodRec *pMethodRec;
+ TypeDefRec *pTypeDefRec;
+
+ // make sure our ref to def optimization is up to date
+ IfFailGo( RefToDefOptimization() );
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( (DWORD) -1, &pEnum) );
+
+ // Loop through all of the methoddef except global functions.
+ // If methoddef has RVA 0 and not miRuntime, mdAbstract, mdVirtual, mdNative,
+ // we will fill it into the enumerator.
+ //
+ iCountTypeDef = pMiniMd->getCountTypeDefs();
+
+ for (indexTypeDef = 2; indexTypeDef <= iCountTypeDef; indexTypeDef ++ )
+ {
+ IfFailGo(pMiniMd->GetTypeDefRecord(indexTypeDef, &pTypeDefRec));
+
+ // If the type is an interface, check the static methods.
+ bIsInterface = IsTdInterface(pTypeDefRec->GetFlags());
+
+ ulStart = pMiniMd->getMethodListOfTypeDef(pTypeDefRec);
+ IfFailGo(pMiniMd->getEndMethodListOfTypeDef(indexTypeDef, &ulEnd));
+
+ // always report errors even with any unimplemented methods
+ for (index = ulStart; index < ulEnd; index++)
+ {
+ RID methodRid;
+ IfFailGo(pMiniMd->GetMethodRid(index, &methodRid));
+ IfFailGo(pMiniMd->GetMethodRecord(methodRid, &pMethodRec));
+
+ // If the type is an interface, and the method is not static, on to next.
+ if (bIsInterface && !IsMdStatic(pMethodRec->GetFlags()))
+ continue;
+
+ if ( IsMiForwardRef(pMethodRec->GetImplFlags()) )
+ {
+ if ( IsMdPinvokeImpl(pMethodRec->GetFlags()) )
+ {
+ continue;
+ }
+ if ( IsMiRuntime(pMethodRec->GetImplFlags()) || IsMiInternalCall(pMethodRec->GetImplFlags()))
+ {
+ continue;
+ }
+
+ if (IsMdAbstract(pMethodRec->GetFlags()))
+ continue;
+
+ // If a methoddef has RVA 0 and it is not an abstract or virtual method.
+ // Nor it is a runtime generated method nore a native method, then we add it
+ // to the unresolved list.
+ //
+ IfFailGo(pMiniMd->GetMethodRid(index, &methodRid));
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(methodRid, mdtMethodDef)));
+
+ LOG((LOGMD, "MD adding unresolved MethodDef: token=%08x, flags=%08x, impl flags=%08x\n",
+ TokenFromRid(methodRid, mdtMethodDef),
+ pMethodRec->GetFlags(), pMethodRec->GetImplFlags()));
+ }
+ }
+ }
+
+ MemberRefRec *pMemberRefRec;
+ ULONG iCount;
+
+ // loop through MemberRef tables and find all of the unsats
+ iCount = pMiniMd->getCountMemberRefs();
+ for (index = 1; index <= iCount; index++ )
+ {
+ mdToken defToken;
+ mdMemberRef refToken = TokenFromRid(index, mdtMemberRef);
+ IfFailGo(pMiniMd->GetMemberRefRecord(index, &pMemberRefRec));
+ pMiniMd->GetTokenRemapManager()->ResolveRefToDef(refToken, &defToken);
+
+ if ( pMiniMd->getClassOfMemberRef(pMemberRefRec) == m_tdModule && defToken == refToken )
+ {
+ // unresovled externals reference if parent token is not resolved and this ref token does not
+ // map to any def token (can be MethodDef or FieldDef).
+ //
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, refToken) );
+
+ LOG((LOGMD, "MD adding unresolved MemberRef: token=%08x, doesn't have a proper parent\n",
+ refToken ));
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethods, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumUnresolvedMethods);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT
+} // RegMeta::EnumUnresolvedMethods
+
+//*****************************************************************************
+// Return the User string given the token. The offset into the Blob pool where
+// the string is stored in Unicode is embedded inside the token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetUserString( // S_OK or error.
+ mdString stk, // [IN] String token.
+ __out_ecount_opt(cchStringSize) LPWSTR wszString, // [OUT] Copy of string.
+ ULONG cchStringSize, // [IN] Max chars of room in szString.
+ ULONG *pcchStringSize) // [OUT] How many chars in actual string.
+{
+ HRESULT hr = S_OK;
+ ULONG cchStringSize_Dummy;
+ MetaData::DataBlob userString;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::GetUserString(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ stk, wszString, cchStringSize, pcchStringSize));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ // Get the string data.
+ IfFailGo(m_pStgdb->m_MiniMd.GetUserString(RidFromToken(stk), &userString));
+ // Want to get whole characters, followed by byte to indicate whether there
+ // are extended characters (>= 0x80).
+ if ((userString.GetSize() % sizeof(WCHAR)) == 0)
+ {
+ Debug_ReportError("User strings should have 1 byte terminator (either 0x00 or 0x80).");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Strip off the last byte.
+ if (!userString.TruncateBySize(1))
+ {
+ Debug_ReportInternalError("There's a bug, because previous % 2 check didn't return 0.");
+ IfFailGo(METADATA_E_INTERNAL_ERROR);
+ }
+
+ // Convert bytes to characters.
+ if (pcchStringSize == NULL)
+ {
+ pcchStringSize = &cchStringSize_Dummy;
+ }
+ *pcchStringSize = userString.GetSize() / sizeof(WCHAR);
+
+ // Copy the string back to the caller.
+ if ((wszString != NULL) && (cchStringSize > 0))
+ {
+ ULONG cbStringSize = cchStringSize * sizeof(WCHAR);
+ memcpy(
+ wszString,
+ userString.GetDataPointer(),
+ min(userString.GetSize(), cbStringSize));
+ if (cbStringSize < userString.GetSize())
+ {
+ if ((wszString != NULL) && (cchStringSize > 0))
+ { // null-terminate the truncated output string
+ wszString[cchStringSize - 1] = W('\0');
+ }
+
+ hr = CLDB_S_TRUNCATION;
+ }
+ }
+
+ ErrExit:
+ STOP_MD_PERF(GetUserString);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetUserString
+
+//*****************************************************************************
+// Return contents of Pinvoke given the forwarded member token.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetPinvokeMap( // S_OK or error.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ __out_ecount_opt (cchImportName) LPWSTR szImportName, // [OUT] Import name.
+ ULONG cchImportName, // [IN] Size of the name buffer.
+ ULONG *pchImportName, // [OUT] Actual number of characters stored.
+ mdModuleRef *pmrImportDLL) // [OUT] ModuleRef token for the target DLL.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ImplMapRec * pRecord;
+ ULONG iRecord;
+
+ LOG((LOGMD, "MD RegMeta::GetPinvokeMap(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ tk, pdwMappingFlags, szImportName, cchImportName, pchImportName, pmrImportDLL));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(tk) == mdtFieldDef ||
+ TypeFromToken(tk) == mdtMethodDef);
+
+ IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord));
+ if (InvalidRid(iRecord))
+ {
+ IfFailGo( CLDB_E_RECORD_NOTFOUND );
+ }
+ else
+ IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(iRecord, &pRecord));
+
+ if (pdwMappingFlags)
+ *pdwMappingFlags = m_pStgdb->m_MiniMd.getMappingFlagsOfImplMap(pRecord);
+ if (pmrImportDLL)
+ *pmrImportDLL = m_pStgdb->m_MiniMd.getImportScopeOfImplMap(pRecord);
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szImportName || pchImportName)
+ IfFailGo(m_pStgdb->m_MiniMd.getImportNameOfImplMap(pRecord, szImportName, cchImportName, pchImportName));
+ErrExit:
+ STOP_MD_PERF(GetPinvokeMap);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetPinvokeMap()
+
+//*****************************************************************************
+// Enumerate through all the local sigs.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumSignatures( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdModuleRef rSignatures[], // [OUT] put signatures here.
+ ULONG cmax, // [IN] max signatures to put.
+ ULONG *pcSignatures) // [OUT] put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppsigEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumSignatures(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, rSignatures, cmax, pcSignatures));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (*ppsigEnum == NULL)
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // create the enumerator.
+ IfFailGo(HENUMInternal::CreateSimpleEnum(
+ mdtSignature,
+ 1,
+ pMiniMd->getCountStandAloneSigs() + 1,
+ &pEnum));
+
+ // set the output parameter
+ *ppsigEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppsigEnum;
+ }
+
+ // we can only fill the minimum of what caller asked for or what we have left.
+ IfFailGo(HENUMInternal::EnumWithCount(pEnum, cmax, rSignatures, pcSignatures));
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppsigEnum);
+
+ STOP_MD_PERF(EnumSignatures);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumSignatures
+
+
+//*****************************************************************************
+// Enumerate through all the TypeSpec
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumTypeSpecs( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdTypeSpec rTypeSpecs[], // [OUT] put TypeSpecs here.
+ ULONG cmax, // [IN] max TypeSpecs to put.
+ ULONG *pcTypeSpecs) // [OUT] put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumTypeSpecs(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, rTypeSpecs, cmax, pcTypeSpecs));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (*ppEnum == NULL)
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // create the enumerator.
+ IfFailGo(HENUMInternal::CreateSimpleEnum(
+ mdtTypeSpec,
+ 1,
+ pMiniMd->getCountTypeSpecs() + 1,
+ &pEnum));
+
+ // set the output parameter
+ *ppEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppEnum;
+ }
+
+ // we can only fill the minimum of what caller asked for or what we have left.
+ IfFailGo(HENUMInternal::EnumWithCount(pEnum, cmax, rTypeSpecs, pcTypeSpecs));
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppEnum);
+
+ STOP_MD_PERF(EnumTypeSpecs);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumTypeSpecs
+
+
+//*****************************************************************************
+// Enumerate through all the User Strings.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumUserStrings( // S_OK or error.
+ HCORENUM *phEnum, // [IN/OUT] pointer to the enum.
+ mdString rStrings[], // [OUT] put Strings here.
+ ULONG cmax, // [IN] max Strings to put.
+ ULONG *pcStrings) // [OUT] put # put here.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum = *ppEnum;
+
+ LOG((LOGMD, "MD RegMeta::EnumUserStrings(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, rStrings, cmax, pcStrings));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (pEnum == NULL)
+ {
+ // instantiating a new ENUM.
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ IfFailGo(HENUMInternal::CreateDynamicArrayEnum(mdtString, &pEnum));
+
+ // Add all strings to the dynamic array
+ 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;
+ }
+ // Add the user string into dynamic array
+ IfFailGo(HENUMInternal::AddElementToEnum(
+ pEnum,
+ TokenFromRid(nIndex, mdtString)));
+
+ // Process next user string in the heap
+ nIndex = nNextIndex;
+ }
+
+ // set the output parameter.
+ *ppEnum = pEnum;
+ }
+
+ // fill the output token buffer.
+ hr = HENUMInternal::EnumWithCount(pEnum, cmax, rStrings, pcStrings);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppEnum);
+
+
+ STOP_MD_PERF(EnumUserStrings);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumUserStrings
+
+
+//*****************************************************************************
+// This routine gets the param token given a method and index of the parameter.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetParamForMethodIndex( // S_OK or error.
+ mdMethodDef md, // [IN] Method token.
+ ULONG ulParamSeq, // [IN] Parameter sequence.
+ mdParamDef *ppd) // [IN] Put Param token here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ LOG((LOGMD, "MD RegMeta::GetParamForMethodIndex(0x%08x, 0x%08x, 0x%08x)\n",
+ md, ulParamSeq, ppd));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE((TypeFromToken(md) == mdtMethodDef) && (ulParamSeq != ULONG_MAX) && (ppd != NULL));
+
+ IfFailGo(_FindParamOfMethod(md, ulParamSeq, ppd));
+ErrExit:
+
+ STOP_MD_PERF(GetParamForMethodIndex);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetParamForMethodIndex()
+
+//*****************************************************************************
+// Return the property of a MethodDef or a FieldDef
+//*****************************************************************************
+HRESULT RegMeta::GetMemberProps(
+ mdToken mb, // The member for which to get props.
+ mdTypeDef *pClass, // Put member's class here.
+ __out_ecount_opt (cchMember) LPWSTR szMember, // Put member's name here.
+ ULONG cchMember, // Size of szMember buffer in wide chars.
+ ULONG *pchMember, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG *pulCodeRVA, // [OUT] codeRVA
+ DWORD *pdwImplFlags, // [OUT] Impl. Flags
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppValue, // [OUT] constant value
+ ULONG *pchValue) // [OUT] size of constant value, string only, wide chars
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::GetMemberProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ mb, pClass, szMember, cchMember, pchMember, pdwAttr, ppvSigBlob, pcbSigBlob,
+ pulCodeRVA, pdwImplFlags, pdwCPlusTypeFlag, ppValue, pchValue));
+
+
+
+ START_MD_PERF();
+
+ _ASSERTE(TypeFromToken(mb) == mdtMethodDef || TypeFromToken(mb) == mdtFieldDef);
+
+ // No need to lock this function. It is calling public APIs. Keep it that way.
+
+ if (TypeFromToken(mb) == mdtMethodDef)
+ {
+ // It is a Method
+ IfFailGo( GetMethodProps(
+ mb,
+ pClass,
+ szMember,
+ cchMember,
+ pchMember,
+ pdwAttr,
+ ppvSigBlob,
+ pcbSigBlob,
+ pulCodeRVA,
+ pdwImplFlags) );
+ }
+ else
+ {
+ // It is a Field
+ IfFailGo( GetFieldProps(
+ mb,
+ pClass,
+ szMember,
+ cchMember,
+ pchMember,
+ pdwAttr,
+ ppvSigBlob,
+ pcbSigBlob,
+ pdwCPlusTypeFlag,
+ ppValue,
+ pchValue) );
+ }
+ErrExit:
+ STOP_MD_PERF(GetMemberProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetMemberProps()
+
+//*****************************************************************************
+// Return the property of a FieldDef
+//*****************************************************************************
+HRESULT RegMeta::GetFieldProps(
+ mdFieldDef fd, // The field for which to get props.
+ mdTypeDef *pClass, // Put field's class here.
+ __out_ecount_opt (cchField) LPWSTR szField, // Put field's name here.
+ ULONG cchField, // Size of szField buffer in wide chars.
+ ULONG *pchField, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppValue, // [OUT] constant value
+ ULONG *pchValue) // [OUT] size of constant value, string only, wide chars
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ FieldRec *pFieldRec;
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "MD RegMeta::GetFieldProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ fd, pClass, szField, cchField, pchField, pdwAttr, ppvSigBlob, pcbSigBlob, pdwCPlusTypeFlag,
+ ppValue, pchValue));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(fd) == mdtFieldDef);
+
+ IfFailGo(pMiniMd->GetFieldRecord(RidFromToken(fd), &pFieldRec));
+
+ if (pClass)
+ {
+ // caller wants parent typedef
+ IfFailGo( pMiniMd->FindParentOfFieldHelper(fd, pClass) );
+
+ if ( IsGlobalMethodParentToken(*pClass) )
+ {
+ // If the parent of Field is the <Module>, return mdTypeDefNil instead.
+ *pClass = mdTypeDefNil;
+ }
+ }
+ if (ppvSigBlob || pcbSigBlob)
+ {
+ // caller wants signature information
+ PCCOR_SIGNATURE pvSigTmp;
+ ULONG cbSig;
+ IfFailGo(pMiniMd->getSignatureOfField(pFieldRec, &pvSigTmp, &cbSig));
+ if ( ppvSigBlob )
+ *ppvSigBlob = pvSigTmp;
+ if ( pcbSigBlob)
+ *pcbSigBlob = cbSig;
+ }
+ if ( pdwAttr )
+ {
+ *pdwAttr = pMiniMd->getFlagsOfField(pFieldRec);
+ }
+ if ( pdwCPlusTypeFlag || ppValue || pchValue)
+ {
+ // get the constant value
+ ULONG cbValue;
+ RID rid;
+ IfFailGo(pMiniMd->FindConstantHelper(fd, &rid));
+
+ if (pchValue)
+ *pchValue = 0;
+
+ if (InvalidRid(rid))
+ {
+ // There is no constant value associate with it
+ if (pdwCPlusTypeFlag)
+ *pdwCPlusTypeFlag = ELEMENT_TYPE_VOID;
+
+ if ( ppValue )
+ *ppValue = NULL;
+ }
+ else
+ {
+ ConstantRec *pConstantRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(rid, &pConstantRec));
+ DWORD dwType;
+
+ // get the type of constant value
+ dwType = pMiniMd->getTypeOfConstant(pConstantRec);
+ if ( pdwCPlusTypeFlag )
+ *pdwCPlusTypeFlag = dwType;
+
+ // get the value blob
+ if (ppValue != NULL)
+ {
+ IfFailGo(pMiniMd->getValueOfConstant(pConstantRec, (const BYTE **)ppValue, &cbValue));
+ if (pchValue && dwType == ELEMENT_TYPE_STRING)
+ *pchValue = cbValue / sizeof(WCHAR);
+ }
+ }
+ }
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szField || pchField)
+ {
+ IfFailGo( pMiniMd->getNameOfField(pFieldRec, szField, cchField, pchField) );
+ }
+
+ErrExit:
+ STOP_MD_PERF(GetFieldProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetFieldProps()
+
+//*****************************************************************************
+// return the properties of a property token
+//*****************************************************************************
+HRESULT RegMeta::GetPropertyProps( // S_OK, S_FALSE, or error.
+ mdProperty prop, // [IN] property token
+ mdTypeDef *pClass, // [OUT] typedef containing the property declarion.
+ LPCWSTR szProperty, // [OUT] Property name
+ ULONG cchProperty, // [IN] the count of wchar of szProperty
+ ULONG *pchProperty, // [OUT] actual count of wchar for property name
+ DWORD *pdwPropFlags, // [OUT] property flags.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob
+ ULONG *pbSig, // [OUT] count of bytes in *ppvSig
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppDefaultValue, // [OUT] constant value
+ ULONG *pchDefaultValue, // [OUT] size of constant value, string only, wide chars
+ mdMethodDef *pmdSetter, // [OUT] setter method of the property
+ mdMethodDef *pmdGetter, // [OUT] getter method of the property
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the property
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod) // [OUT] total number of other method of this property
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd;
+ PropertyRec *pRec;
+ HENUMInternal hEnum;
+
+ LOG((LOGMD, "MD RegMeta::GetPropertyProps(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, "
+ "0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, "
+ "0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, "
+ "0x%08x)\n",
+ prop, pClass, MDSTR(szProperty), cchProperty, pchProperty,
+ pdwPropFlags, ppvSig, pbSig, pdwCPlusTypeFlag, ppDefaultValue,
+ pchDefaultValue, pmdSetter, pmdGetter, rmdOtherMethod, cMax,
+ pcOtherMethod));
+
+
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(prop) == mdtProperty);
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ memset(&hEnum, 0, sizeof(HENUMInternal));
+ IfFailGo(pMiniMd->GetPropertyRecord(RidFromToken(prop), &pRec));
+
+ if ( pClass )
+ {
+ // find the property map entry corresponding to this property
+ IfFailGo( pMiniMd->FindParentOfPropertyHelper( prop, pClass) );
+ }
+ if ( pdwPropFlags )
+ {
+ *pdwPropFlags = pMiniMd->getPropFlagsOfProperty(pRec);
+ }
+ if ( ppvSig || pbSig )
+ {
+ // caller wants the signature
+ //
+ ULONG cbSig;
+ PCCOR_SIGNATURE pvSig;
+ IfFailGo(pMiniMd->getTypeOfProperty(pRec, &pvSig, &cbSig));
+ if ( ppvSig )
+ {
+ *ppvSig = pvSig;
+ }
+ if ( pbSig )
+ {
+ *pbSig = cbSig;
+ }
+ }
+ if ( pdwCPlusTypeFlag || ppDefaultValue || pchDefaultValue)
+ {
+ // get the constant value
+ ULONG cbValue;
+ RID rid;
+ IfFailGo(pMiniMd->FindConstantHelper(prop, &rid));
+
+ if (pchDefaultValue)
+ *pchDefaultValue = 0;
+
+ if (InvalidRid(rid))
+ {
+ // There is no constant value associate with it
+ if (pdwCPlusTypeFlag)
+ *pdwCPlusTypeFlag = ELEMENT_TYPE_VOID;
+
+ if ( ppDefaultValue )
+ *ppDefaultValue = NULL;
+ }
+ else
+ {
+ ConstantRec *pConstantRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(rid, &pConstantRec));
+ DWORD dwType;
+
+ // get the type of constant value
+ dwType = pMiniMd->getTypeOfConstant(pConstantRec);
+ if ( pdwCPlusTypeFlag )
+ *pdwCPlusTypeFlag = dwType;
+
+ // get the value blob
+ if (ppDefaultValue != NULL)
+ {
+ IfFailGo(pMiniMd->getValueOfConstant(pConstantRec, (const BYTE **)ppDefaultValue, &cbValue));
+ if (pchDefaultValue && dwType == ELEMENT_TYPE_STRING)
+ *pchDefaultValue = cbValue / sizeof(WCHAR);
+ }
+ }
+ }
+ {
+ MethodSemanticsRec *pSemantics;
+ RID ridCur;
+ ULONG cCurOtherMethod = 0;
+ ULONG ulSemantics;
+ mdMethodDef tkMethod;
+
+ // initialize output parameters
+ if (pmdSetter)
+ *pmdSetter = mdMethodDefNil;
+ if (pmdGetter)
+ *pmdGetter = mdMethodDefNil;
+
+ IfFailGo( pMiniMd->FindMethodSemanticsHelper(prop, &hEnum) );
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur))
+ {
+ IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics));
+ ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics);
+ tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef );
+ switch (ulSemantics)
+ {
+ case msSetter:
+ if (pmdSetter) *pmdSetter = tkMethod;
+ break;
+ case msGetter:
+ if (pmdGetter) *pmdGetter = tkMethod;
+ break;
+ case msOther:
+ if (cCurOtherMethod < cMax)
+ rmdOtherMethod[cCurOtherMethod] = tkMethod;
+ cCurOtherMethod ++;
+ break;
+ default:
+ _ASSERTE(!"BadKind!");
+ }
+ }
+
+ // set the output parameter
+ if (pcOtherMethod)
+ *pcOtherMethod = cCurOtherMethod;
+ }
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szProperty || pchProperty)
+ {
+ IfFailGo( pMiniMd->getNameOfProperty(pRec, (LPWSTR) szProperty, cchProperty, pchProperty) );
+ }
+
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+ STOP_MD_PERF(GetPropertyProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetPropertyProps()
+
+
+//*****************************************************************************
+// This routine gets the properties for the given Param token.
+//*****************************************************************************
+HRESULT RegMeta::GetParamProps( // S_OK or error.
+ mdParamDef pd, // [IN]The Parameter.
+ mdMethodDef *pmd, // [OUT] Parent Method token.
+ ULONG *pulSequence, // [OUT] Parameter sequence.
+ __out_ecount_opt (cchName) LPWSTR szName, // [OUT] Put name here.
+ ULONG cchName, // [OUT] Size of name buffer.
+ ULONG *pchName, // [OUT] Put actual size of name here.
+ DWORD *pdwAttr, // [OUT] Put flags here.
+ DWORD *pdwCPlusTypeFlag, // [OUT] Flag for value type. selected ELEMENT_TYPE_*.
+ UVCP_CONSTANT *ppValue, // [OUT] Constant value.
+ ULONG *pchValue) // [OUT] size of constant value, string only, wide chars
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ParamRec *pParamRec;
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ LOG((LOGMD, "MD RegMeta::GetParamProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ pd, pmd, pulSequence, szName, cchName, pchName, pdwAttr, pdwCPlusTypeFlag, ppValue, pchValue));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(pd) == mdtParamDef);
+
+ IfFailGo(pMiniMd->GetParamRecord(RidFromToken(pd), &pParamRec));
+
+ if (pmd)
+ {
+ IfFailGo(pMiniMd->FindParentOfParamHelper(pd, pmd));
+ _ASSERTE(TypeFromToken(*pmd) == mdtMethodDef);
+ }
+ if (pulSequence)
+ *pulSequence = pMiniMd->getSequenceOfParam(pParamRec);
+ if (pdwAttr)
+ {
+ *pdwAttr = pMiniMd->getFlagsOfParam(pParamRec);
+ }
+ if ( pdwCPlusTypeFlag || ppValue || pchValue)
+ {
+ // get the constant value
+ ULONG cbValue;
+ RID rid;
+ IfFailGo(pMiniMd->FindConstantHelper(pd, &rid));
+
+ if (pchValue)
+ *pchValue = 0;
+
+ if (InvalidRid(rid))
+ {
+ // There is no constant value associate with it
+ if (pdwCPlusTypeFlag)
+ *pdwCPlusTypeFlag = ELEMENT_TYPE_VOID;
+
+ if ( ppValue )
+ *ppValue = NULL;
+ }
+ else
+ {
+ ConstantRec *pConstantRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(rid, &pConstantRec));
+ DWORD dwType;
+
+ // get the type of constant value
+ dwType = pMiniMd->getTypeOfConstant(pConstantRec);
+ if ( pdwCPlusTypeFlag )
+ *pdwCPlusTypeFlag = dwType;
+
+ // get the value blob
+ if (ppValue != NULL)
+ {
+ IfFailGo(pMiniMd->getValueOfConstant(pConstantRec, (const BYTE **)ppValue, &cbValue));
+ if (pchValue && dwType == ELEMENT_TYPE_STRING)
+ *pchValue = cbValue / sizeof(WCHAR);
+ }
+ }
+ }
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szName || pchName)
+ IfFailGo( pMiniMd->getNameOfParam(pParamRec, szName, cchName, pchName) );
+
+ErrExit:
+ STOP_MD_PERF(GetParamProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetParamProps()
+
+//*****************************************************************************
+// This routine gets the properties for the given GenericParam token.
+//*****************************************************************************
+HRESULT RegMeta::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)
+ __out_ecount_opt (cchName) LPWSTR szName, // [OUT] The name
+ ULONG cchName, // [IN] Size of name buffer
+ ULONG *pchName) // [OUT] Actual size of name
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ GenericParamRec *pGenericParamRec;
+ CMiniMdRW *pMiniMd = NULL;
+ RID ridRD = RidFromToken(rd);
+
+
+ LOG((LOGMD, "MD RegMeta::GetGenericParamProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ rd, pulSequence, pdwAttr, ptOwner, reserved, szName, cchName, pchName));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // See if this version of the metadata can do Generics
+ if (!pMiniMd->SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+
+ if((TypeFromToken(rd) == mdtGenericParam) && (ridRD != 0))
+ {
+ IfFailGo(pMiniMd->GetGenericParamRecord(RidFromToken(rd), &pGenericParamRec));
+
+ if (pulSequence)
+ *pulSequence = pMiniMd->getNumberOfGenericParam(pGenericParamRec);
+ if (pdwAttr)
+ *pdwAttr = pMiniMd->getFlagsOfGenericParam(pGenericParamRec);
+ if (ptOwner)
+ *ptOwner = pMiniMd->getOwnerOfGenericParam(pGenericParamRec);
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (pchName || szName)
+ IfFailGo(pMiniMd->getNameOfGenericParam(pGenericParamRec, szName, cchName, pchName));
+ }
+ else
+ hr = META_E_BAD_INPUT_PARAMETER;
+
+ErrExit:
+ STOP_MD_PERF(GetGenericParamProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetGenericParamProps()
+
+//*****************************************************************************
+// This routine gets the properties for the given GenericParamConstraint token.
+//*****************************************************************************
+HRESULT RegMeta::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;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ GenericParamConstraintRec *pGPCRec;
+ CMiniMdRW *pMiniMd = NULL;
+ RID ridRD = RidFromToken(rd);
+
+ LOG((LOGMD, "MD RegMeta::GetGenericParamConstraintProps(0x%08x, 0x%08x, 0x%08x)\n",
+ rd, ptGenericParam, ptkConstraintType));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // See if this version of the metadata can do Generics
+ if (!pMiniMd->SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+
+ if((TypeFromToken(rd) == mdtGenericParamConstraint) && (ridRD != 0))
+ {
+ IfFailGo(pMiniMd->GetGenericParamConstraintRecord(ridRD, &pGPCRec));
+
+ if (ptGenericParam)
+ *ptGenericParam = TokenFromRid(pMiniMd->getOwnerOfGenericParamConstraint(pGPCRec),mdtGenericParam);
+ if (ptkConstraintType)
+ *ptkConstraintType = pMiniMd->getConstraintOfGenericParamConstraint(pGPCRec);
+ }
+ else
+ hr = META_E_BAD_INPUT_PARAMETER;
+
+ErrExit:
+ STOP_MD_PERF(GetGenericParamConstraintProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetGenericParamConstraintProps()
+
+//*****************************************************************************
+// This routine gets the properties for the given MethodSpec token.
+//*****************************************************************************
+HRESULT RegMeta::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;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MethodSpecRec *pMethodSpecRec;
+ CMiniMdRW *pMiniMd = NULL;
+
+ LOG((LOGMD, "MD RegMeta::GetMethodSpecProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ mi, tkParent, ppvSigBlob, pcbSigBlob));
+ START_MD_PERF();
+ LOCKREAD();
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+
+ // See if this version of the metadata can do Generics
+ if (!pMiniMd->SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+ _ASSERTE(TypeFromToken(mi) == mdtMethodSpec && RidFromToken(mi));
+
+ IfFailGo(pMiniMd->GetMethodSpecRecord(RidFromToken(mi), &pMethodSpecRec));
+
+ if (tkParent)
+ *tkParent = pMiniMd->getMethodOfMethodSpec(pMethodSpecRec);
+
+ if (ppvSigBlob || pcbSigBlob)
+ {
+ // caller wants signature information
+ PCCOR_SIGNATURE pvSigTmp;
+ ULONG cbSig;
+ IfFailGo(pMiniMd->getInstantiationOfMethodSpec(pMethodSpecRec, &pvSigTmp, &cbSig));
+ if ( ppvSigBlob )
+ *ppvSigBlob = pvSigTmp;
+ if ( pcbSigBlob)
+ *pcbSigBlob = cbSig;
+ }
+
+
+ErrExit:
+
+ STOP_MD_PERF(GetMethodSpecProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetMethodSpecProps()
+
+//*****************************************************************************
+// This routine gets the type and machine of the PE file the scope is opened on.
+//*****************************************************************************
+HRESULT RegMeta::GetPEKind( // S_OK or error.
+ DWORD *pdwPEKind, // [OUT] The kind of PE (0 - not a PE)
+ DWORD *pdwMachine) // [OUT] Machine as defined in NT header
+{
+ HRESULT hr = NOERROR;
+ MAPPINGTYPE mt = MTYPE_NOMAPPING;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "MD RegMeta::GetPEKind(0x%08x, 0x%08x)\n",pdwPEKind,pdwMachine));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+
+ if (m_pStgdb->m_pStgIO != NULL)
+ mt = m_pStgdb->m_pStgIO->GetMemoryMappedType();
+
+ hr = m_pStgdb->GetPEKind(mt, pdwPEKind, pdwMachine);
+
+ ErrExit:
+
+ STOP_MD_PERF(GetPEKind);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // HRESULT RegMeta::GetPEKind()
+
+//*****************************************************************************
+// 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.
+//*****************************************************************************
+HRESULT RegMeta::GetVersionString( // S_OK or error.
+ __out_ecount_opt (cchBufSize) LPWSTR pwzBuf, // [OUT] Put version string here.
+ DWORD cchBufSize, // [in] size of the buffer, in wide chars
+ DWORD *pchBufSize) // [out] Size of the version string, wide chars, including terminating nul.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+ REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+
+ DWORD cch; // Length of WideChar string.
+ LPCSTR pVer; // Pointer to version string.
+
+ LOG((LOGMD, "MD RegMeta::GetVersionString(0x%08x, 0x%08x, 0x%08x)\n",pwzBuf,cchBufSize,pchBufSize));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ 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);
+ // Attempt to convert into caller's buffer.
+ cch = WszMultiByteToWideChar(CP_UTF8,0, pVer,-1, pwzBuf,cchBufSize);
+ // Did the string fit?
+ if (cch == 0)
+ { // No, didn't fit. Find out space required.
+ cch = WszMultiByteToWideChar(CP_UTF8,0, pVer,-1, pwzBuf,0);
+ // NUL terminate string.
+ if (cchBufSize > 0)
+ pwzBuf[cchBufSize-1] = W('\0');
+ // Truncation return code.
+ hr = CLDB_S_TRUNCATION;
+ }
+ }
+ else
+ { // No string.
+ if (cchBufSize > 0)
+ *pwzBuf = W('\0');
+ cch = 0;
+ }
+
+ if (pchBufSize)
+ *pchBufSize = cch;
+
+ErrExit:
+
+ STOP_MD_PERF(GetVersionString);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // HRESULT RegMeta::GetVersionString()
+
+//*****************************************************************************
+// This routine gets the parent class for the nested class.
+//*****************************************************************************
+HRESULT RegMeta::GetNestedClassProps( // S_OK or error.
+ mdTypeDef tdNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptdEnclosingClass) // [OUT] EnclosingClass token.
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ NestedClassRec *pRecord;
+ ULONG iRecord;
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+
+ LOG((LOGMD, "MD RegMeta::GetNestedClassProps(0x%08x, 0x%08x)\n",
+ tdNestedClass, ptdEnclosingClass));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ // If not a typedef -- return error.
+ if (TypeFromToken(tdNestedClass) != mdtTypeDef)
+ {
+ IfFailGo(META_E_INVALID_TOKEN_TYPE); // PostError(META_E_INVALID_TOKEN_TYPE, tdNestedClass));
+ }
+
+ _ASSERTE(TypeFromToken(tdNestedClass) && !IsNilToken(tdNestedClass) && ptdEnclosingClass);
+
+ IfFailGo(pMiniMd->FindNestedClassHelper(tdNestedClass, &iRecord));
+
+ if (InvalidRid(iRecord))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+
+ IfFailGo(pMiniMd->GetNestedClassRecord(iRecord, &pRecord));
+
+ _ASSERTE(tdNestedClass == pMiniMd->getNestedClassOfNestedClass(pRecord));
+ *ptdEnclosingClass = pMiniMd->getEnclosingClassOfNestedClass(pRecord);
+
+ErrExit:
+ STOP_MD_PERF(GetNestedClassProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetNestedClassProps()
+
+//*****************************************************************************
+// Given a signature, parse it for custom modifier with calling convention.
+//*****************************************************************************
+HRESULT RegMeta::GetNativeCallConvFromSig( // S_OK or error.
+ void const *pvSig, // [IN] Pointer to signature.
+ ULONG cbSig, // [IN] Count of signature bytes.
+ ULONG *pCallConv) // [OUT] Put calling conv here (see CorPinvokemap).
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ PCCOR_SIGNATURE pvSigBlob = reinterpret_cast<PCCOR_SIGNATURE>(pvSig);
+ ULONG cbTotal = 0; // total of number bytes for return type + all fixed arguments
+ ULONG cbCur = 0; // index through the pvSigBlob
+ ULONG cb;
+ ULONG cArg;
+ ULONG cTyArg = 0;
+ ULONG callingconv;
+ ULONG cArgsIndex;
+ ULONG callConv = pmCallConvWinapi; // The calling convention.
+
+
+
+
+ *pCallConv = pmCallConvWinapi;
+
+ // remember the number of bytes to represent the calling convention
+ cb = CorSigUncompressData (pvSigBlob, &callingconv);
+ if (cb == ((ULONG)(-1)))
+ {
+ hr = CORSEC_E_INVALID_IMAGE_FORMAT;
+ goto ErrExit;
+ }
+ cbCur += cb;
+
+ // remember the number of bytes to represent the type parameter count
+ if (callingconv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ cb= CorSigUncompressData (&pvSigBlob[cbCur], &cTyArg);
+ if (cb == ((ULONG)(-1)))
+ {
+ hr = CORSEC_E_INVALID_IMAGE_FORMAT;
+ goto ErrExit;
+ }
+ cbCur += cb;
+ }
+
+
+ // remember number of bytes to represent the arg counts
+ cb= CorSigUncompressData (&pvSigBlob[cbCur], &cArg);
+ if (cb == ((ULONG)(-1)))
+ {
+ hr = CORSEC_E_INVALID_IMAGE_FORMAT;
+ goto ErrExit;
+ }
+
+ cbCur += cb;
+
+ // Look at the return type.
+ hr = _SearchOneArgForCallConv( &pvSigBlob[cbCur], &cb, &callConv);
+ if (hr == (HRESULT)-1)
+ {
+ *pCallConv = callConv;
+ hr = S_OK;
+ goto ErrExit;
+ }
+ IfFailGo(hr);
+ cbCur += cb;
+ cbTotal += cb;
+
+ // loop through argument until we found ELEMENT_TYPE_SENTINEL or run
+ // out of arguments
+ for (cArgsIndex = 0; cArgsIndex < cArg; cArgsIndex++)
+ {
+ _ASSERTE(cbCur < cbSig);
+ hr = _SearchOneArgForCallConv( &pvSigBlob[cbCur], &cb, &callConv);
+ if (hr == (HRESULT)-1)
+ {
+ *pCallConv = callConv;
+ hr = S_OK;
+ goto ErrExit;
+ }
+ IfFailGo(hr);
+ cbTotal += cb;
+ cbCur += cb;
+ }
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::GetNativeCallConvFromSig()
+
+//*****************************************************************************
+// Helper used by GetNativeCallingConvFromSig.
+//*****************************************************************************
+HRESULT RegMeta::_CheckCmodForCallConv( // S_OK, -1 if found, or error.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature to check.
+ ULONG *pcbTotal, // [OUT] Put bytes consumed here.
+ ULONG *pCallConv) // [OUT] If found, put calling convention here.
+{
+ ULONG cbTotal = 0; // Bytes consumed.
+ mdToken tk; // Token for callconv.
+ HRESULT hr = NOERROR; // A result.
+ LPCUTF8 szName=0; // Callconv name.
+ LPCUTF8 szNamespace=0; // Callconv namespace.
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ _ASSERTE(pcbTotal);
+
+
+
+
+ // count the bytes for the token compression
+ cbTotal += CorSigUncompressToken(&pbSig[cbTotal], &tk);
+
+ // workaround to skip nil tokens and TypeSpec tokens.
+ if (IsNilToken(tk) || TypeFromToken(tk) == mdtTypeSpec)
+ {
+ *pcbTotal = cbTotal;
+ goto ErrExit;
+ }
+
+ // See if this token is a calling convention.
+ if (TypeFromToken(tk) == mdtTypeRef)
+ {
+ TypeRefRec *pTypeRefRec;
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tk), &pTypeRefRec));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace));
+ }
+ else
+ if (TypeFromToken(tk) == mdtTypeDef)
+ {
+ TypeDefRec *pTypeDefRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tk), &pTypeDefRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace));
+ }
+
+ if ((szNamespace && szName) &&
+ (strcmp(szNamespace, CMOD_CALLCONV_NAMESPACE) == 0 ||
+ strcmp(szNamespace, CMOD_CALLCONV_NAMESPACE_OLD) == 0) )
+ {
+ // Set the hr to -1, which is an unspecified 'error'. This will percolate
+ // back up to the caller, where the 'error' should be recognized.
+ hr=(HRESULT)-1;
+ if (strcmp(szName, CMOD_CALLCONV_NAME_CDECL) == 0)
+ *pCallConv = pmCallConvCdecl;
+ else
+ if (strcmp(szName, CMOD_CALLCONV_NAME_STDCALL) == 0)
+ *pCallConv = pmCallConvStdcall;
+ else
+ if (strcmp(szName, CMOD_CALLCONV_NAME_THISCALL) == 0)
+ *pCallConv = pmCallConvThiscall;
+ else
+ if (strcmp(szName, CMOD_CALLCONV_NAME_FASTCALL) == 0)
+ *pCallConv = pmCallConvFastcall;
+ else
+ hr = S_OK; // keep looking
+ IfFailGo(hr);
+ }
+ *pcbTotal = cbTotal;
+
+ErrExit:
+
+ return hr;
+} // HRESULT RegMeta::_CheckCmodForCallConv()
+
+//*****************************************************************************
+// Helper used by GetNativeCallingConvFromSig.
+//*****************************************************************************
+HRESULT RegMeta::_SearchOneArgForCallConv(// S_OK, -1 if found, or error.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature to check.
+ ULONG *pcbTotal, // [OUT] Put bytes consumed here.
+ ULONG *pCallConv) // [OUT] If found, put calling convention here.
+{
+ ULONG cb;
+ ULONG cbTotal = 0;
+ CorElementType ulElementType;
+ ULONG ulData;
+ ULONG ulTemp;
+ int iData;
+ mdToken tk;
+ ULONG cArg;
+ ULONG callingconv;
+ ULONG cArgsIndex;
+ HRESULT hr = NOERROR;
+
+ _ASSERTE(pcbTotal);
+
+ cbTotal += CorSigUncompressElementType(&pbSig[cbTotal], &ulElementType);
+ while (CorIsModifierElementType(ulElementType) || ulElementType == ELEMENT_TYPE_SENTINEL)
+ {
+ cbTotal += CorSigUncompressElementType(&pbSig[cbTotal], &ulElementType);
+ }
+ switch (ulElementType)
+ {
+ case ELEMENT_TYPE_SZARRAY:
+ // skip over base type
+ IfFailGo( _SearchOneArgForCallConv(&pbSig[cbTotal], &cb, pCallConv) );
+ cbTotal += cb;
+ break;
+
+ case ELEMENT_TYPE_VAR :
+ case ELEMENT_TYPE_MVAR :
+ // skip over index
+ cbTotal += CorSigUncompressData(&pbSig[cbTotal], &ulData);
+ break;
+
+ case ELEMENT_TYPE_GENERICINST :
+ // skip over generic type
+ IfFailGo( _SearchOneArgForCallConv(&pbSig[cbTotal], &cb, pCallConv) );
+ cbTotal += cb;
+
+ // skip over number of parameters
+ cbTotal += CorSigUncompressData(&pbSig[cbTotal], &cArg);
+
+ // loop through type parameters
+ for (cArgsIndex = 0; cArgsIndex < cArg; cArgsIndex++)
+ {
+ IfFailGo( _SearchOneArgForCallConv( &pbSig[cbTotal], &cb, pCallConv) );
+ cbTotal += cb;
+ }
+
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ cbTotal += CorSigUncompressData (&pbSig[cbTotal], &callingconv);
+
+ // remember number of bytes to represent the arg counts
+ cbTotal += CorSigUncompressData (&pbSig[cbTotal], &cArg);
+
+ // how many bytes to represent the return type
+ IfFailGo( _SearchOneArgForCallConv( &pbSig[cbTotal], &cb, pCallConv) );
+ cbTotal += cb;
+
+ // loop through argument
+ for (cArgsIndex = 0; cArgsIndex < cArg; cArgsIndex++)
+ {
+ IfFailGo( _SearchOneArgForCallConv( &pbSig[cbTotal], &cb, pCallConv) );
+ cbTotal += cb;
+ }
+
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ // syntax : ARRAY BaseType <rank> [i size_1... size_i] [j lowerbound_1 ... lowerbound_j]
+
+ // skip over base type
+ IfFailGo( _SearchOneArgForCallConv(&pbSig[cbTotal], &cb, pCallConv) );
+ cbTotal += cb;
+
+ // Parse for the rank
+ cbTotal += CorSigUncompressData(&pbSig[cbTotal], &ulData);
+
+ // if rank == 0, we are done
+ if (ulData == 0)
+ break;
+
+ // any size of dimension specified?
+ cbTotal += CorSigUncompressData(&pbSig[cbTotal], &ulData);
+ while (ulData--)
+ {
+ cbTotal += CorSigUncompressData(&pbSig[cbTotal], &ulTemp);
+ }
+
+ // any lower bound specified?
+ cbTotal = CorSigUncompressData(&pbSig[cbTotal], &ulData);
+
+ while (ulData--)
+ {
+ cbTotal += CorSigUncompressSignedInt(&pbSig[cbTotal], &iData);
+ }
+
+ break;
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ // count the bytes for the token compression
+ cbTotal += CorSigUncompressToken(&pbSig[cbTotal], &tk);
+ break;
+ case ELEMENT_TYPE_CMOD_REQD:
+ case ELEMENT_TYPE_CMOD_OPT:
+ // Check for the calling convention.
+ IfFailGo(_CheckCmodForCallConv(&pbSig[cbTotal], &cb, pCallConv));
+ cbTotal += cb;
+ // skip over base type
+ IfFailGo( _SearchOneArgForCallConv(&pbSig[cbTotal], &cb, pCallConv) );
+ cbTotal += cb;
+ break;
+ default:
+ break;
+ }
+ *pcbTotal = cbTotal;
+
+ErrExit:
+
+ return hr;
+} // HRESULT RegMeta::_SearchOneArgForCallConv()
diff --git a/src/md/compiler/importhelper.cpp b/src/md/compiler/importhelper.cpp
new file mode 100644
index 0000000000..abebe1e805
--- /dev/null
+++ b/src/md/compiler/importhelper.cpp
@@ -0,0 +1,3415 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// ImportHelper.cpp
+//
+
+//
+// contains utility code to MD directory
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "importhelper.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "strongname.h"
+#include "sstring.h"
+
+#define COM_RUNTIME_LIBRARY "ComRuntimeLibrary"
+
+
+//*******************************************************************************
+// Find the MethodSpec by Method and Instantiation
+//*******************************************************************************
+//@GENERICS: todo: look in hashtable (cf. MetaModelRW.cpp) if necessary
+HRESULT ImportHelper::FindMethodSpecByMethodAndInstantiation(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ /*mdMethodDefOrRef*/ mdToken tkMethod, // [IN] MethodSpec method field
+ PCCOR_SIGNATURE pInstantiation, // [IN] MethodSpec instantiation (a signature)
+ ULONG cbInstantiation, // [IN] Size of instantiation.
+ mdMethodSpec *pMethodSpec, // [OUT] Put the MethodSpec token here.
+ RID rid /* = 0*/) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr;
+ MethodSpecRec *pRecord;
+ /*mdMethodDefOrRef*/ mdToken tkMethodTmp;
+ PCCOR_SIGNATURE pInstantiationTmp;
+ ULONG cbInstantiationTmp;
+ ULONG cMethodSpecs;
+ ULONG i;
+
+ _ASSERTE(pMethodSpec);
+
+ cMethodSpecs = pMiniMd->getCountMethodSpecs();
+
+ // linear scan through the MethodSpec table
+ for (i=1; i <= cMethodSpecs; ++i)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailRet(pMiniMd->GetMethodSpecRecord(i, &pRecord));
+
+ tkMethodTmp = pMiniMd->getMethodOfMethodSpec(pRecord);
+ if ((tkMethodTmp != tkMethod))
+ continue;
+
+ //@GENERICS: not sure what is meant by duplicate here: identical sig pointers or sig pointer contents?
+ IfFailRet(pMiniMd->getInstantiationOfMethodSpec(pRecord, &pInstantiationTmp, &cbInstantiationTmp));
+ if (cbInstantiationTmp != cbInstantiation || memcmp(pInstantiation, pInstantiationTmp, cbInstantiation))
+ continue;
+
+ // Matching record found.
+ *pMethodSpec = TokenFromRid(i, mdtMethodSpec);
+ return S_OK;
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindMethodSpecByMethodAndInstantiation()
+
+
+//*******************************************************************************
+// Find the GenericParam by owner and constraint
+//*******************************************************************************
+//@GENERICS: todo: look in hashtable (cf. MetaModelRW.cpp) if necessary
+HRESULT ImportHelper::FindGenericParamConstraintByOwnerAndConstraint(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdGenericParam tkOwner, // [IN] GenericParamConstraint Owner
+ mdToken tkConstraint, // [IN] GenericParamConstraint Constraint
+ mdGenericParamConstraint *pGenericParamConstraint,// [OUT] Put the GenericParam token here.
+ RID rid /* = 0*/) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr;
+ GenericParamConstraintRec *pRecord;
+ mdGenericParam tkOwnerTmp;
+ mdToken tkConstraintTmp;
+ ULONG cGenericParamConstraints;
+
+ ULONG i;
+
+ _ASSERTE(pGenericParamConstraint);
+
+ cGenericParamConstraints = pMiniMd->getCountGenericParamConstraints();
+
+ // linear scan through the GenericParam table
+ for (i=1; i <= cGenericParamConstraints; ++i)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailRet(pMiniMd->GetGenericParamConstraintRecord(i, &pRecord));
+
+ tkOwnerTmp = pMiniMd->getOwnerOfGenericParamConstraint(pRecord);
+ tkConstraintTmp = pMiniMd->getConstraintOfGenericParamConstraint(pRecord);
+
+ if ((tkOwnerTmp != tkOwner) || (tkConstraintTmp != tkConstraint))
+ continue;
+
+ // Matching record found.
+ *pGenericParamConstraint = TokenFromRid(i, mdtGenericParamConstraint);
+ return S_OK;
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindGenericParamConstraintByOwnerAndConstraint()
+
+//*******************************************************************************
+// Find the GenericParam by owner and name or number
+//*******************************************************************************
+//<REVISIT_TODO> @GENERICS: todo: look in hashtable (cf. MetaModelRW.cpp) if necessary </REVISIT_TODO>
+HRESULT ImportHelper::FindGenericParamByOwner(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkOwner, // [IN] GenericParam Owner
+ LPCUTF8 szUTF8Name, // [IN] GeneriParam Name, may be NULL if not used for search
+ ULONG *pNumber, // [IN] GeneriParam Number, may be NULL if not used for search
+ mdGenericParam *pGenericParam, // [OUT] Put the GenericParam token here.
+ RID rid /* = 0*/) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr;
+ GenericParamRec *pRecord;
+ mdToken tkOwnerTmp;
+ ULONG cGenericParams;
+ LPCUTF8 szCurName;
+ ULONG curNumber;
+ ULONG i;
+
+ _ASSERTE(pGenericParam);
+
+ cGenericParams = pMiniMd->getCountGenericParams();
+
+ // linear scan through the GenericParam table
+ for (i=1; i <= cGenericParams; ++i)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailRet(pMiniMd->GetGenericParamRecord(i, &pRecord));
+
+ tkOwnerTmp = pMiniMd->getOwnerOfGenericParam(pRecord);
+ if ( tkOwnerTmp != tkOwner)
+ continue;
+
+ // if the name is significant, try to match it
+ if (szUTF8Name)
+ {
+ IfFailRet(pMiniMd->getNameOfGenericParam(pRecord, &szCurName));
+ if (strcmp(szCurName, szUTF8Name))
+ continue;
+ }
+
+ // if the number is significant, try to match it
+ if (pNumber)
+ { curNumber = pMiniMd->getNumberOfGenericParam(pRecord);
+ if (*pNumber != curNumber)
+ continue;
+ }
+
+ // Matching record found.
+ *pGenericParam = TokenFromRid(i, mdtGenericParam);
+ return S_OK;
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindGenericParamByOwner()
+
+//*******************************************************************************
+// Find a Method given a parent, name and signature.
+//*******************************************************************************
+HRESULT ImportHelper::FindMethod(
+ CMiniMdRW * pMiniMd, // [IN] the minimd to lookup
+ mdTypeDef td, // [IN] parent.
+ LPCUTF8 szName, // [IN] MethodDef name.
+ PCCOR_SIGNATURE pSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdMethodDef * pmb, // [OUT] Put the MethodDef token here.
+ RID rid, // = 0 // [IN] Optional rid to be ignored.
+ PSIGCOMPARE pSignatureCompare, // = NULL // [IN] Optional Routine to compare signatures
+ void * pCompareContext) // = NULL // [IN] Optional context for the compare function
+{
+ HRESULT hr = S_OK;
+ ULONG ridStart; // Start of td's methods.
+ ULONG ridEnd; // End of td's methods.
+ ULONG index; // Loop control.
+ TypeDefRec *pRec; // A TypeDef Record.
+ MethodRec *pMethod; // A MethodDef Record.
+ LPCUTF8 szNameUtf8Tmp; // A found MethodDef's name.
+ PCCOR_SIGNATURE pSigTmp; // A found MethodDef's signature.
+ ULONG cbSigTmp; // Size of a found MethodDef's signature.
+ PCCOR_SIGNATURE pvSigTemp = pSig; // For use in parsing a signature.
+ CQuickBytes qbSig; // Struct to build a non-varargs signature.
+ CMiniMdRW::HashSearchResult rtn;
+
+ if (cbSig)
+ { // check to see if this is a vararg signature
+ if (isCallConv(CorSigUncompressCallingConv(pvSigTemp), IMAGE_CEE_CS_CALLCONV_VARARG))
+ { // Get the fix part of VARARG signature
+ IfFailGo(_GetFixedSigOfVarArg(pSig, cbSig, &qbSig, &cbSig));
+ pSig = (PCCOR_SIGNATURE) qbSig.Ptr();
+ }
+ }
+
+ *pmb = TokenFromRid(rid, mdtMethodDef); // to know what to ignore
+ rtn = pMiniMd->FindMemberDefFromHash(td, szName, pSig, cbSig, pmb);
+ if (rtn == CMiniMdRW::Found)
+ {
+ goto ErrExit;
+ }
+ else if (rtn == CMiniMdRW::NotFound)
+ {
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+ }
+ _ASSERTE(rtn == CMiniMdRW::NoTable);
+
+ *pmb = mdMethodDefNil;
+
+ // get the range of method rids given a typedef
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pRec));
+ ridStart = pMiniMd->getMethodListOfTypeDef(pRec);
+ IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(td), &ridEnd));
+ // Iterate over the methods.
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ RID methodRID;
+ IfFailGo(pMiniMd->GetMethodRid(index, &methodRID));
+ // For the call from Validator ignore the rid passed in.
+ if (methodRID != rid)
+ {
+ // Get the method and its name.
+ IfFailGo(pMiniMd->GetMethodRecord(methodRID, &pMethod));
+ IfFailGo(pMiniMd->getNameOfMethod(pMethod, &szNameUtf8Tmp));
+
+ // If name matches what was requested...
+ if ( strcmp(szNameUtf8Tmp, szName) == 0 )
+ {
+ if (cbSig && pSig)
+ {
+ IfFailGo(pMiniMd->getSignatureOfMethod(pMethod, &pSigTmp, &cbSigTmp));
+
+ // If the caller did not provide a custom compare routine
+ // then we use memcmp to match the signatures
+ //
+ if (pSignatureCompare == NULL)
+ {
+ if (cbSigTmp != cbSig || memcmp(pSig, pSigTmp, cbSig))
+ continue;
+ }
+ else
+ {
+ // Call the custom compare routine
+ //
+ if (!pSignatureCompare(pSigTmp, cbSigTmp, pSig, cbSig, pCompareContext))
+ continue;
+ }
+ }
+ // Ignore PrivateScope methods.
+ if (IsMdPrivateScope(pMiniMd->getFlagsOfMethod(pMethod)))
+ continue;
+
+ // Found method.
+ *pmb = TokenFromRid(methodRID, mdtMethodDef);
+ goto ErrExit;
+ }
+ }
+ }
+
+ // record not found
+ *pmb = mdMethodDefNil;
+ hr = CLDB_E_RECORD_NOTFOUND;
+
+ErrExit:
+ return hr;
+} // ImportHelper::FindMethod
+
+//*******************************************************************************
+// Find a Field given a parent, name and signature.
+//*******************************************************************************
+HRESULT ImportHelper::FindField(
+ CMiniMdRW * pMiniMd, // [IN] the minimd to lookup
+ mdTypeDef td, // [IN] parent.
+ LPCUTF8 szName, // [IN] FieldDef name.
+ PCCOR_SIGNATURE pSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdFieldDef * pfd, // [OUT] Put the FieldDef token here.
+ RID rid) // = 0 // [IN] Optional rid to be ignored.
+{
+ HRESULT hr = S_OK; // A result.
+ ULONG ridStart; // Start of td's methods.
+ ULONG ridEnd; // End of td's methods.
+ ULONG index; // Loop control.
+ TypeDefRec *pRec; // A TypeDef Record.
+ FieldRec *pField; // A FieldDef Record.
+ LPCUTF8 szNameUtf8Tmp; // A found FieldDef's name.
+ PCCOR_SIGNATURE pSigTmp; // A found FieldDef's signature.
+ ULONG cbSigTmp; // Size of a found FieldDef's signature.
+ CMiniMdRW::HashSearchResult rtn;
+
+ *pfd = TokenFromRid(rid,mdtFieldDef); // to know what to ignore
+ rtn = pMiniMd->FindMemberDefFromHash(td, szName, pSig, cbSig, pfd);
+ if (rtn == CMiniMdRW::Found)
+ {
+ goto ErrExit;
+ }
+ else if (rtn == CMiniMdRW::NotFound)
+ {
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+ }
+ _ASSERTE(rtn == CMiniMdRW::NoTable);
+
+ *pfd = mdFieldDefNil;
+
+ // get the range of method rids given a typedef
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pRec));
+ ridStart = pMiniMd->getFieldListOfTypeDef(pRec);
+ IfFailGo(pMiniMd->getEndFieldListOfTypeDef(RidFromToken(td), &ridEnd));
+
+ // Iterate over the methods.
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ RID fieldRID;
+ IfFailGo(pMiniMd->GetFieldRid(index, &fieldRID));
+ // For the call from Validator ignore the rid passed in.
+ if (fieldRID != rid)
+ {
+ // Get the field and its name.
+ IfFailGo(pMiniMd->GetFieldRecord(fieldRID, &pField));
+ IfFailGo(pMiniMd->getNameOfField(pField, &szNameUtf8Tmp));
+
+ // If name matches what was requested...
+ if ( strcmp(szNameUtf8Tmp, szName) == 0 )
+ {
+ // Check signature if specified.
+ if (cbSig && pSig)
+ {
+ IfFailGo(pMiniMd->getSignatureOfField(pField, &pSigTmp, &cbSigTmp));
+ if (cbSigTmp != cbSig || memcmp(pSig, pSigTmp, cbSig))
+ continue;
+ }
+ // Ignore PrivateScope fields.
+ if (IsFdPrivateScope(pMiniMd->getFlagsOfField(pField)))
+ continue;
+ // Field found.
+ *pfd = TokenFromRid(fieldRID, mdtFieldDef);
+ goto ErrExit;
+ }
+ }
+ }
+
+ // record not found
+ *pfd = mdFieldDefNil;
+ hr = CLDB_E_RECORD_NOTFOUND;
+
+ErrExit:
+ return hr;
+} // ImportHelper::FindField
+
+//*******************************************************************************
+// Find a Member given a parent, name and signature.
+//*******************************************************************************
+HRESULT ImportHelper::FindMember(
+ CMiniMdRW * pMiniMd, // [IN] the minimd to lookup
+ mdTypeDef td, // [IN] parent.
+ LPCUTF8 szName, // [IN] Member name.
+ PCCOR_SIGNATURE pSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdToken * ptk) // [OUT] Put the token here.
+{
+ HRESULT hr;
+
+ if (cbSig == 0)
+ {
+ Debug_ReportError("Invalid signature size 0.");
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+
+ // determine if it is ref to MethodDef or FieldDef
+ if ((pSig[0] & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_FIELD)
+ {
+ hr = FindMethod(pMiniMd, td, szName, pSig, cbSig, ptk);
+ }
+ else
+ {
+ hr = FindField(pMiniMd, td, szName, pSig, cbSig, ptk);
+ }
+
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ *ptk = mdTokenNil;
+
+ return hr;
+} // ImportHelper::FindMember
+
+
+//*******************************************************************************
+// Find the memberref given name, sig, and parent
+//*******************************************************************************
+HRESULT ImportHelper::FindMemberRef(
+ CMiniMdRW * pMiniMd, // [IN] the minimd to lookup
+ mdToken tkParent, // [IN] the parent token
+ LPCUTF8 szName, // [IN] memberref name
+ const COR_SIGNATURE * pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdMemberRef * pmr, // [OUT] Put the MemberRef token found
+ RID rid, // = 0 // [IN] Optional rid to be ignored.
+ HashSearchOption fCreateHash) // = DoNotCreateHash // [IN] Should we create hash first? (Optimize for multiple calls vs. single isolated call)
+{
+ ULONG cMemberRefRecs;
+ MemberRefRec * pMemberRefRec;
+ LPCUTF8 szNameTmp = 0;
+ const COR_SIGNATURE * pbSigTmp; // Signature.
+ ULONG cbSigTmp; // Size of signature.
+ mdToken tkParentTmp; // the parent token
+ HRESULT hr = NOERROR;
+ CMiniMdRW::HashSearchResult rtn;
+
+ if ((szName == NULL) || (pmr == NULL))
+ {
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+ }
+
+ if (fCreateHash == CreateHash)
+ { // Caller asked for creating hash to optimize for multiple calls
+ IfFailGo(pMiniMd->CreateMemberRefHash());
+ }
+
+ *pmr = TokenFromRid(rid, mdtMemberRef); // to know what to ignore
+ rtn = pMiniMd->FindMemberRefFromHash(tkParent, szName, pbSig, cbSig, pmr);
+ if (rtn == CMiniMdRW::Found)
+ {
+ goto ErrExit;
+ }
+ else if (rtn == CMiniMdRW::NotFound)
+ {
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+ }
+ _ASSERTE(rtn == CMiniMdRW::NoTable);
+
+ *pmr = mdMemberRefNil;
+
+ cMemberRefRecs = pMiniMd->getCountMemberRefs();
+
+ // Search for the MemberRef
+ for (ULONG i = 1; i <= cMemberRefRecs; i++)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailGo(pMiniMd->GetMemberRefRecord(i, &pMemberRefRec));
+ if (!IsNilToken(tkParent))
+ {
+ // given a valid parent
+ tkParentTmp = pMiniMd->getClassOfMemberRef(pMemberRefRec);
+ if (tkParentTmp != tkParent)
+ {
+ // if parent is specified and not equal to the current row,
+ // try the next row.
+ continue;
+ }
+ }
+ if ((szName != NULL) && (*szName != 0))
+ {
+ // name is specified
+ IfFailGo(pMiniMd->getNameOfMemberRef(pMemberRefRec, &szNameTmp));
+ if (strcmp(szName, szNameTmp) != 0)
+ {
+ // Name is not equal. Try next row.
+ continue;
+ }
+ }
+ if ((cbSig != 0) && (pbSig != NULL))
+ {
+ // signature is specifed
+ IfFailGo(pMiniMd->getSignatureOfMemberRef(pMemberRefRec, &pbSigTmp, &cbSigTmp));
+ if (cbSigTmp != cbSig)
+ continue;
+ if (memcmp( pbSig, pbSigTmp, cbSig ) != 0)
+ continue;
+ }
+
+ // we found a match
+ *pmr = TokenFromRid(i, mdtMemberRef);
+ return S_OK;
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+ return hr;
+} // ImportHelper::FindMemberRef
+
+
+
+//*******************************************************************************
+// Find duplicate StandAloneSig
+//*******************************************************************************
+HRESULT ImportHelper::FindStandAloneSig(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ const COR_SIGNATURE *pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdSignature *psa) // [OUT] Put the StandAloneSig token found
+{
+ HRESULT hr;
+ ULONG cRecs;
+ StandAloneSigRec *pRec;
+ const COR_SIGNATURE *pbSigTmp; // Signature.
+ ULONG cbSigTmp; // Size of signature.
+
+
+ _ASSERTE(cbSig && psa);
+ *psa = mdSignatureNil;
+
+ cRecs = pMiniMd->getCountStandAloneSigs();
+
+ // Search for the StandAloneSignature
+ for (ULONG i = 1; i <= cRecs; i++)
+ {
+ IfFailRet(pMiniMd->GetStandAloneSigRecord(i, &pRec));
+ IfFailRet(pMiniMd->getSignatureOfStandAloneSig(pRec, &pbSigTmp, &cbSigTmp));
+ if (cbSigTmp != cbSig)
+ continue;
+ if (memcmp( pbSig, pbSigTmp, cbSig ) != 0)
+ continue;
+
+ // we found a match
+ *psa = TokenFromRid(i, mdtSignature);
+ return S_OK;
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindStandAloneSig()
+
+//*******************************************************************************
+// Find duplicate TypeSpec
+//*******************************************************************************
+HRESULT
+ImportHelper::FindTypeSpec(
+ CMiniMdRW * pMiniMd, // [IN] the minimd to lookup
+ const COR_SIGNATURE * pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdTypeSpec * pTypeSpec) // [OUT] Put the TypeSpec token found
+{
+ HRESULT hr;
+ ULONG cRecs;
+ TypeSpecRec * pRec;
+ const COR_SIGNATURE * pbSigTmp; // Signature.
+ ULONG cbSigTmp; // Size of signature.
+
+ // cbSig can be 0
+ _ASSERTE(pTypeSpec != NULL);
+ *pTypeSpec = mdSignatureNil;
+
+ cRecs = pMiniMd->getCountTypeSpecs();
+
+ // Search for the TypeSpec
+ for (ULONG i = 1; i <= cRecs; i++)
+ {
+ IfFailRet(pMiniMd->GetTypeSpecRecord(i, &pRec));
+ IfFailRet(pMiniMd->getSignatureOfTypeSpec(pRec, &pbSigTmp, &cbSigTmp));
+ if (cbSigTmp != cbSig)
+ continue;
+ if (memcmp(pbSig, pbSigTmp, cbSig) != 0)
+ continue;
+
+ // we found a match
+ *pTypeSpec = TokenFromRid(i, mdtTypeSpec);
+ return S_OK;
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // ImportHelper::FindTypeSpec
+
+
+//*******************************************************************************
+// Find the MethodImpl
+//*******************************************************************************
+HRESULT ImportHelper::FindMethodImpl(
+ CMiniMdRW *pMiniMd, // [IN] The MiniMd to lookup.
+ mdTypeDef tkClass, // [IN] The parent TypeDef token.
+ mdMethodDef tkBody, // [IN] Method body token.
+ mdMethodDef tkDecl, // [IN] Method declaration token.
+ RID *pRid) // [OUT] Put the MethodImpl rid here
+{
+ HRESULT hr;
+ MethodImplRec *pMethodImplRec; // MethodImpl record.
+ ULONG cMethodImplRecs; // Count of MethodImpl records.
+ mdTypeDef tkClassTmp; // Parent TypeDef token.
+ mdToken tkBodyTmp; // Method body token.
+ mdToken tkDeclTmp; // Method declaration token.
+
+ _ASSERTE(TypeFromToken(tkClass) == mdtTypeDef);
+ _ASSERTE(TypeFromToken(tkBody) == mdtMemberRef || TypeFromToken(tkBody) == mdtMethodDef);
+ _ASSERTE(TypeFromToken(tkDecl) == mdtMemberRef || TypeFromToken(tkDecl) == mdtMethodDef);
+ _ASSERTE(!IsNilToken(tkClass) && !IsNilToken(tkBody) && !IsNilToken(tkDecl));
+
+ if (pRid)
+ *pRid = 0;
+
+ cMethodImplRecs = pMiniMd->getCountMethodImpls();
+
+ // Search for the MethodImpl.
+ for (ULONG i = 1; i <= cMethodImplRecs; i++)
+ {
+ IfFailRet(pMiniMd->GetMethodImplRecord(i, &pMethodImplRec));
+
+ // match the parent column
+ tkClassTmp = pMiniMd->getClassOfMethodImpl(pMethodImplRec);
+ if (tkClassTmp != tkClass)
+ continue;
+
+ // match the method body column
+ tkBodyTmp = pMiniMd->getMethodBodyOfMethodImpl(pMethodImplRec);
+ if (tkBodyTmp != tkBody)
+ continue;
+
+ // match the method declaration column
+ tkDeclTmp = pMiniMd->getMethodDeclarationOfMethodImpl(pMethodImplRec);
+ if (tkDeclTmp != tkDecl)
+ continue;
+
+ // we found a match
+ if (pRid)
+ *pRid = i;
+ return S_OK;
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindMethodImpl()
+
+//*******************************************************************************
+// Find the TypeRef given the fully qualified name and the assembly name
+//*******************************************************************************
+HRESULT ImportHelper::FindCustomAttributeCtorByName(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ LPCUTF8 szAssemblyName, // [IN] Assembly Name.
+ LPCUTF8 szNamespace, // [IN] TypeRef Namespace.
+ LPCUTF8 szName, // [IN] TypeRef Name.
+ mdTypeDef *ptk, // [OUT] Put the TypeRef token here.
+ RID rid /* = 0*/) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr;
+ ULONG cRecs; // Count of records.
+ AssemblyRefRec *pRec; // Current record being looked at.
+ LPCUTF8 szTmp; // Temp string.
+ mdTypeRef tkCAType;
+
+ cRecs = pMiniMd->getCountAssemblyRefs();
+ // Search for the AssemblyRef record.
+ for (ULONG i = 1; i <= cRecs; i++)
+ {
+ IfFailRet(pMiniMd->GetAssemblyRefRecord(i, &pRec));
+
+ IfFailRet(pMiniMd->getNameOfAssemblyRef(pRec, &szTmp));
+ if (!strcmp(szTmp, szAssemblyName) &&
+ (SUCCEEDED(FindTypeRefByName(pMiniMd, TokenFromRid(i, mdtAssemblyRef), szNamespace, szName, &tkCAType, rid))) &&
+ (SUCCEEDED(FindMemberRef(pMiniMd, tkCAType, COR_CTOR_METHOD_NAME, NULL, 0 ,ptk))))
+ {
+ return S_OK;
+ }
+ }
+
+ return CLDB_E_RECORD_NOTFOUND;
+}
+
+//*******************************************************************************
+// Find the TypeRef given the fully qualified name.
+//*******************************************************************************
+HRESULT ImportHelper::FindTypeRefByName(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkResolutionScope, // [IN] Resolution scope for the TypeRef.
+ LPCUTF8 szNamespace, // [IN] TypeRef Namespace.
+ LPCUTF8 szName, // [IN] TypeRef Name.
+ mdTypeRef *ptk, // [OUT] Put the TypeRef token here.
+ RID rid /* = 0*/) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr=S_OK; // A result.
+ ULONG cTypeRefRecs; // Count of TypeRefs to scan.
+ TypeRefRec *pTypeRefRec; // A TypeRef record.
+ LPCUTF8 szNameTmp; // A TypeRef's Name.
+ LPCUTF8 szNamespaceTmp; // A TypeRef's Namespace.
+ mdToken tkResTmp; // TypeRef's resolution scope.
+ ULONG i; // Loop control.
+
+ _ASSERTE(szName && ptk);
+ *ptk = mdTypeRefNil;
+
+ // Treat no namespace as empty string.
+ if (!szNamespace)
+ szNamespace = "";
+
+ if (pMiniMd->m_pNamedItemHash)
+ {
+ // If hash is build, go through the hash table
+ TOKENHASHENTRY *p; // Hash entry from chain.
+ ULONG iHash; // Item's hash value.
+ int pos; // Position in hash chain.
+
+ // Hash the data.
+ iHash = pMiniMd->HashNamedItem(0, szName);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pMiniMd->m_pNamedItemHash->FindFirst(iHash, pos);
+ p;
+ p = pMiniMd->m_pNamedItemHash->FindNext(pos))
+ {
+
+ // name hash can hold more than one kind of token
+ if (TypeFromToken(p->tok) != (ULONG)mdtTypeRef)
+ {
+ continue;
+ }
+
+ // skip this one if asked
+ if (RidFromToken(p->tok) == rid)
+ continue;
+
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(p->tok), &pTypeRefRec));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szNameTmp));
+ if (strcmp(szName, szNameTmp) || strcmp(szNamespace, szNamespaceTmp))
+ {
+ // if the name space is not equal, then check the next one.
+ continue;
+ }
+ tkResTmp = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec);
+
+ if (tkResTmp == tkResolutionScope ||
+ (IsNilToken(tkResTmp) && IsNilToken(tkResolutionScope)))
+ {
+ // we found a match
+ *ptk = p->tok;
+ return S_OK;
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ cTypeRefRecs = pMiniMd->getCountTypeRefs();
+
+ // Search for the TypeRef.
+ for (i = 1; i <= cTypeRefRecs; i++)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailGo(pMiniMd->GetTypeRefRecord(i, &pTypeRefRec));
+
+ // See if the Resolution scopes match.
+ tkResTmp = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec);
+ if (IsNilToken(tkResTmp))
+ {
+ if (!IsNilToken(tkResolutionScope))
+ continue;
+ }
+ else if (tkResTmp != tkResolutionScope)
+ continue;
+
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp));
+ if (strcmp(szNamespace, szNamespaceTmp))
+ continue;
+
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szNameTmp));
+ if (! strcmp(szName, szNameTmp))
+ {
+ *ptk = TokenFromRid(i, mdtTypeRef);
+ return S_OK;
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+ }
+ErrExit:
+ return hr;
+} // HRESULT ImportHelper::FindTypeRefByName()
+
+
+//*******************************************************************************
+// Find the ModuleRef given the name, guid and mvid.
+//*******************************************************************************
+HRESULT ImportHelper::FindModuleRef(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ LPCUTF8 szUTF8Name, // [IN] ModuleRef name.
+ mdModuleRef *pmur, // [OUT] Put the ModuleRef token here.
+ RID rid /* = 0*/) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr;
+ ModuleRefRec *pModuleRef;
+ ULONG cModuleRefs;
+ LPCUTF8 szCurName;
+ ULONG i;
+
+ _ASSERTE(pmur);
+ _ASSERTE(szUTF8Name);
+
+ cModuleRefs = pMiniMd->getCountModuleRefs();
+
+ // linear scan through the ModuleRef table
+ for (i=1; i <= cModuleRefs; ++i)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailRet(pMiniMd->GetModuleRefRecord(i, &pModuleRef));
+
+ if (szUTF8Name != NULL)
+ {
+ IfFailRet(pMiniMd->getNameOfModuleRef(pModuleRef, &szCurName));
+ if (strcmp(szCurName, szUTF8Name))
+ continue;
+ }
+ // Matching record found.
+ *pmur = TokenFromRid(i, mdtModuleRef);
+ return S_OK;
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindModuleRef()
+
+
+
+//*******************************************************************************
+// Find the TypeDef given the type and namespace name
+//*******************************************************************************
+HRESULT
+ImportHelper::FindTypeDefByName(
+ CMiniMdRW * pMiniMd, // [IN] the minimd to lookup
+ LPCUTF8 szTypeDefNamespace, // [IN] Full qualified TypeRef name.
+ LPCUTF8 szTypeDefName, // [IN] Full qualified TypeRef name.
+ mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef/Module for Enclosing class.
+ mdTypeDef * ptkTypeDef, // [OUT] Put the TypeRef token here.
+ RID ridIgnore) // =0 // [IN] Optional rid to be ignored.
+{
+ ULONG cTypeDefRecs;
+ TypeDefRec * pTypeDefRec;
+ LPCUTF8 szName;
+ LPCUTF8 szNamespace;
+ DWORD dwFlags;
+ HRESULT hr = S_OK;
+
+ _ASSERTE((szTypeDefName != NULL) && (ptkTypeDef != NULL));
+ _ASSERTE((TypeFromToken(tkEnclosingClass) == mdtTypeDef) ||
+ (TypeFromToken(tkEnclosingClass) == mdtTypeRef) ||
+ (tkEnclosingClass == TokenFromRid(1, mdtModule)) ||
+ IsNilToken(tkEnclosingClass));
+
+ *ptkTypeDef = mdTypeDefNil;
+
+ cTypeDefRecs = pMiniMd->getCountTypeDefs();
+
+ // Treat no namespace as empty string.
+ if (szTypeDefNamespace == NULL)
+ szTypeDefNamespace = "";
+
+ if (tkEnclosingClass == TokenFromRid(1, mdtModule))
+ { // Module scope is the same as no scope (used in .winmd files as TypeRef scope for self-references)
+ tkEnclosingClass = mdTokenNil;
+ }
+
+ // Get TypeDef of the tkEnclosingClass passed in
+ if (TypeFromToken(tkEnclosingClass) == mdtTypeRef)
+ {
+ // Resolve the TypeRef to a TypeDef
+ TypeRefRec * pTypeRefRec;
+ mdToken tkResolutionScope;
+ LPCUTF8 szTypeRefName;
+ LPCUTF8 szTypeRefNamespace;
+
+ IfFailRet(pMiniMd->GetTypeRefRecord(RidFromToken(tkEnclosingClass), &pTypeRefRec));
+ tkResolutionScope = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec);
+ IfFailRet(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szTypeRefName));
+ IfFailRet(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szTypeRefNamespace));
+
+ if (tkEnclosingClass == tkResolutionScope && !strcmp(szTypeDefName, szTypeRefName) &&
+ ((szTypeDefNamespace == nullptr && szTypeRefNamespace == nullptr) ||
+ (szTypeDefNamespace != nullptr && szTypeRefNamespace != nullptr && !strcmp(szTypeDefNamespace, szTypeRefNamespace))))
+ {
+ //
+ // This defensive workaround works around a feature of DotFuscator that adds a bad type-ref
+ // which causes tools like ILDASM to crash. The type-ref's parent is set to itself
+ // which causes this function to recurse infinitely. A side-effect is that during Ngen we
+ // parse all the type-refs in an assembly and Ngen also hangs infinitely.
+ // This workaround is necessary because several popular gaming libraries experience hangs
+ // and we need binary compatibility in Apollo.
+ //
+ return CLDB_E_FILE_CORRUPT;
+ }
+
+ // Update tkEnclosingClass to TypeDef
+ IfFailRet(FindTypeDefByName(
+ pMiniMd,
+ szTypeRefNamespace,
+ szTypeRefName,
+ (TypeFromToken(tkResolutionScope) == mdtTypeRef) ? tkResolutionScope : mdTokenNil,
+ &tkEnclosingClass));
+ _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef);
+ }
+
+ // Search for the TypeDef
+ for (ULONG i = 1; i <= cTypeDefRecs; i++)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == ridIgnore)
+ continue;
+
+ IfFailRet(pMiniMd->GetTypeDefRecord(i, &pTypeDefRec));
+
+ dwFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec);
+
+ if (!IsTdNested(dwFlags) && !IsNilToken(tkEnclosingClass))
+ {
+ // If the class is not Nested and EnclosingClass passed in is not nil
+ continue;
+ }
+ else if (IsTdNested(dwFlags) && IsNilToken(tkEnclosingClass))
+ {
+ // If the class is nested and EnclosingClass passed is nil
+ continue;
+ }
+ else if (!IsNilToken(tkEnclosingClass))
+ {
+ _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef);
+
+ RID iNestedClassRec;
+ NestedClassRec * pNestedClassRec;
+ mdTypeDef tkEnclosingClassTmp;
+
+ IfFailRet(pMiniMd->FindNestedClassHelper(TokenFromRid(i, mdtTypeDef), &iNestedClassRec));
+ if (InvalidRid(iNestedClassRec))
+ continue;
+ IfFailRet(pMiniMd->GetNestedClassRecord(iNestedClassRec, &pNestedClassRec));
+ tkEnclosingClassTmp = pMiniMd->getEnclosingClassOfNestedClass(pNestedClassRec);
+ if (tkEnclosingClass != tkEnclosingClassTmp)
+ continue;
+ }
+
+ IfFailRet(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szName));
+ if (strcmp(szTypeDefName, szName) == 0)
+ {
+ IfFailRet(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace));
+ if (strcmp(szTypeDefNamespace, szNamespace) == 0)
+ {
+ *ptkTypeDef = TokenFromRid(i, mdtTypeDef);
+ return S_OK;
+ }
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // ImportHelper::FindTypeDefByName
+
+//*******************************************************************************
+// Find the InterfaceImpl given the typedef and implemented interface
+//*******************************************************************************
+HRESULT ImportHelper::FindInterfaceImpl(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkClass, // [IN] TypeDef of the type
+ mdToken tkInterface, // [IN] could be typedef/typeref
+ mdInterfaceImpl *ptk, // [OUT] Put the interface token here.
+ RID rid /* = 0*/) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr;
+ ULONG ridStart, ridEnd;
+ ULONG i;
+ InterfaceImplRec *pInterfaceImplRec;
+
+ _ASSERTE(ptk);
+ *ptk = mdInterfaceImplNil;
+ if ( pMiniMd->IsSorted(TBL_InterfaceImpl) )
+ {
+ IfFailRet(pMiniMd->getInterfaceImplsForTypeDef(RidFromToken(tkClass), &ridEnd, &ridStart));
+ }
+ else
+ {
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountInterfaceImpls() + 1;
+ }
+
+ // Search for the interfaceimpl
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailRet(pMiniMd->GetInterfaceImplRecord(i, &pInterfaceImplRec));
+ if ( tkClass != pMiniMd->getClassOfInterfaceImpl(pInterfaceImplRec) )
+ continue;
+ if ( tkInterface == pMiniMd->getInterfaceOfInterfaceImpl(pInterfaceImplRec) )
+ {
+ *ptk = TokenFromRid(i, mdtInterfaceImpl);
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindInterfaceImpl()
+
+
+
+//*******************************************************************************
+// Find the Permission by parent and action
+//*******************************************************************************
+HRESULT ImportHelper::FindPermission(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkParent, // [IN] Token with the Permission
+ USHORT usAction, // [IN] The action of the permission
+ mdPermission *ppm) // [OUT] Put permission token here
+{
+ HRESULT hr;
+ DeclSecurityRec *pRec;
+ ULONG ridStart, ridEnd;
+ ULONG i;
+ mdToken tkParentTmp;
+
+ _ASSERTE(ppm);
+
+ if ( pMiniMd->IsSorted(TBL_DeclSecurity) )
+ {
+
+ IfFailRet(pMiniMd->getDeclSecurityForToken(tkParent, &ridEnd, &ridStart));
+ }
+ else
+ {
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountDeclSecuritys() + 1;
+ }
+ // loop through all permission
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ IfFailRet(pMiniMd->GetDeclSecurityRecord(i, &pRec));
+ tkParentTmp = pMiniMd->getParentOfDeclSecurity(pRec);
+ if ( tkParentTmp != tkParent )
+ continue;
+ if (pRec->GetAction() == usAction)
+ {
+ *ppm = TokenFromRid(i, mdtPermission);
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindPermission()
+
+
+//*****************************************************************************
+// find a property record
+//*****************************************************************************
+HRESULT ImportHelper::FindProperty(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkTypeDef, // [IN] typedef token
+ LPCUTF8 szName, // [IN] name of the property
+ const COR_SIGNATURE *pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdProperty *ppr) // [OUT] Property token
+{
+ HRESULT hr;
+ RID ridPropertyMap;
+ PropertyMapRec *pPropertyMapRec;
+ PropertyRec *pRec;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+ LPCUTF8 szNameTmp;
+ PCCOR_SIGNATURE pbSigTmp;
+ ULONG cbSigTmp;
+ ULONG pr;
+
+ IfFailRet(pMiniMd->FindPropertyMapFor(RidFromToken(tkTypeDef), &ridPropertyMap));
+ if ( !InvalidRid(ridPropertyMap) )
+ {
+ IfFailRet(pMiniMd->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec));
+ ridStart = pMiniMd->getPropertyListOfPropertyMap(pPropertyMapRec);
+ IfFailRet(pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd));
+
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // get the property rid
+ IfFailRet(pMiniMd->GetPropertyRid(i, &pr));
+ IfFailRet(pMiniMd->GetPropertyRecord(pr, &pRec));
+ IfFailRet(pMiniMd->getNameOfProperty(pRec, &szNameTmp));
+ IfFailRet(pMiniMd->getTypeOfProperty(pRec, &pbSigTmp, &cbSigTmp));
+ if ( strcmp (szName, szNameTmp) != 0 )
+ continue;
+ if ( cbSig != 0 && (cbSigTmp != cbSig || memcmp(pbSig, pbSigTmp, cbSig) != 0 ) )
+ continue;
+ *ppr = TokenFromRid( i, mdtProperty );
+ return S_OK;
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+} // HRESULT ImportHelper::FindProperty()
+
+
+
+
+//*****************************************************************************
+// find an Event record
+//*****************************************************************************
+HRESULT ImportHelper::FindEvent(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkTypeDef, // [IN] typedef token
+ LPCUTF8 szName, // [IN] name of the event
+ mdProperty *pev) // [OUT] Event token
+{
+ HRESULT hr;
+ RID ridEventMap;
+ EventMapRec *pEventMapRec;
+ EventRec *pRec;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+ LPCUTF8 szNameTmp;
+ ULONG ev;
+
+ IfFailRet(pMiniMd->FindEventMapFor(RidFromToken(tkTypeDef), &ridEventMap));
+ if ( !InvalidRid(ridEventMap) )
+ {
+ IfFailRet(pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec));
+ ridStart = pMiniMd->getEventListOfEventMap(pEventMapRec);
+ IfFailRet(pMiniMd->getEndEventListOfEventMap(ridEventMap, &ridEnd));
+
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // get the Event rid
+ IfFailRet(pMiniMd->GetEventRid(i, &ev));
+
+ // get the event row
+ IfFailRet(pMiniMd->GetEventRecord(ev, &pRec));
+ IfFailRet(pMiniMd->getNameOfEvent(pRec, &szNameTmp));
+ if ( strcmp (szName, szNameTmp) == 0)
+ {
+ *pev = TokenFromRid( ev, mdtEvent );
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+} // HRESULT ImportHelper::FindEvent()
+
+
+
+//*****************************************************************************
+// find an custom value record given by parent and type token. This will always return
+// the first one that is found regardless duplicated.
+//*****************************************************************************
+HRESULT ImportHelper::FindCustomAttributeByToken(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkParent, // [IN] the parent that custom value is associated with
+ mdToken tkType, // [IN] type of the CustomAttribute
+ const void *pCustBlob, // [IN] custom attribute blob
+ ULONG cbCustBlob, // [IN] size of the blob.
+ mdCustomAttribute *pcv) // [OUT] CustomAttribute token
+{
+ HRESULT hr;
+ CustomAttributeRec *pRec;
+ ULONG ridStart, ridEnd;
+ ULONG i;
+ mdToken tkParentTmp;
+ mdToken tkTypeTmp;
+ const void *pCustBlobTmp;
+ ULONG cbCustBlobTmp;
+
+ _ASSERTE(pcv);
+ *pcv = mdCustomAttributeNil;
+ if ( pMiniMd->IsSorted(TBL_CustomAttribute) )
+ {
+ IfFailRet(pMiniMd->FindCustomAttributeFor(
+ RidFromToken(tkParent),
+ TypeFromToken(tkParent),
+ tkType,
+ (RID *)pcv));
+ if (InvalidRid(*pcv))
+ {
+ return S_FALSE;
+ }
+ else if (pCustBlob)
+ {
+ IfFailRet(pMiniMd->GetCustomAttributeRecord(RidFromToken(*pcv), &pRec));
+ IfFailRet(pMiniMd->getValueOfCustomAttribute(pRec, (const BYTE **)&pCustBlobTmp, &cbCustBlobTmp));
+ if (cbCustBlob == cbCustBlobTmp &&
+ !memcmp(pCustBlob, pCustBlobTmp, cbCustBlob))
+ {
+ return S_OK;
+ }
+ }
+ else
+ {
+ return S_OK;
+ }
+ }
+ else
+ {
+ CLookUpHash *pHashTable = pMiniMd->m_pLookUpHashs[TBL_CustomAttribute];
+
+ if (pHashTable)
+ {
+ // table is not sorted but hash is built
+ // We want to create dynmaic array to hold the dynamic enumerator.
+ TOKENHASHENTRY *p;
+ ULONG iHash;
+ int pos;
+
+ // Hash the data.
+ iHash = pMiniMd->HashCustomAttribute(tkParent);
+
+ // Go through every entry in the hash chain looking for ours.
+ for (p = pHashTable->FindFirst(iHash, pos);
+ p;
+ p = pHashTable->FindNext(pos))
+ {
+ IfFailRet(pMiniMd->GetCustomAttributeRecord(RidFromToken(p->tok), &pRec));
+
+ tkParentTmp = pMiniMd->getParentOfCustomAttribute(pRec);
+ if (tkParentTmp != tkParent)
+ continue;
+
+ tkTypeTmp = pMiniMd->getTypeOfCustomAttribute(pRec);
+ if (tkType != tkTypeTmp)
+ continue;
+ if (pCustBlob != NULL)
+ {
+ IfFailRet(pMiniMd->getValueOfCustomAttribute(pRec, (const BYTE **)&pCustBlobTmp, &cbCustBlobTmp));
+ if (cbCustBlob == cbCustBlobTmp &&
+ !memcmp(pCustBlob, pCustBlobTmp, cbCustBlob))
+ {
+ *pcv = TokenFromRid(p->tok, mdtCustomAttribute);
+ return S_OK;
+ }
+ }
+ else
+ return S_OK;
+ }
+ }
+ else
+ {
+ // linear scan
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountCustomAttributes() + 1;
+
+ // loop through all custom values
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ IfFailRet(pMiniMd->GetCustomAttributeRecord(i, &pRec));
+
+ tkParentTmp = pMiniMd->getParentOfCustomAttribute(pRec);
+ if ( tkParentTmp != tkParent )
+ continue;
+
+ tkTypeTmp = pMiniMd->getTypeOfCustomAttribute(pRec);
+ if (tkType != tkTypeTmp)
+ continue;
+
+ if (pCustBlob != NULL)
+ {
+ IfFailRet(pMiniMd->getValueOfCustomAttribute(pRec, (const BYTE **)&pCustBlobTmp, &cbCustBlobTmp));
+ if (cbCustBlob == cbCustBlobTmp &&
+ !memcmp(pCustBlob, pCustBlobTmp, cbCustBlob))
+ {
+ *pcv = TokenFromRid(i, mdtCustomAttribute);
+ return S_OK;
+ }
+ }
+ else
+ return S_OK;
+ }
+ }
+ // fall through
+ }
+ return S_FALSE;
+} // ImportHelper::FindCustomAttributeByToken
+
+//*****************************************************************************
+// Helper function to lookup and retrieve a CustomAttribute.
+//*****************************************************************************
+HRESULT ImportHelper::GetCustomAttributeByName( // S_OK or error.
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) // [OUT] Put size of data here.
+{
+ return pMiniMd->CommonGetCustomAttributeByName(tkObj, szName, ppData, pcbData);
+} // ImportHelper::GetCustomAttributeByName
+
+#ifdef FEATURE_METADATA_EMIT
+
+//*******************************************************************************
+// Find an AssemblyRef record given the name.
+//*******************************************************************************
+HRESULT ImportHelper::FindAssemblyRef(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup.
+ LPCUTF8 szName, // [IN] Name.
+ LPCUTF8 szLocale, // [IN] Locale.
+ const void *pbPublicKeyOrToken, // [IN] Public key or token (based on flags).
+ ULONG cbPublicKeyOrToken, // [IN] Byte count of public key or token.
+ USHORT usMajorVersion, // [IN] Major version.
+ USHORT usMinorVersion, // [IN] Minor version.
+ USHORT usBuildNumber, // [IN] Build number.
+ USHORT usRevisionNumber, // [IN] Revision number.
+ DWORD dwFlags, // [IN] Flags.
+ mdAssemblyRef *pmar) // [OUT] returned AssemblyRef token.
+{
+ HRESULT hr;
+ ULONG cRecs; // Count of records.
+ AssemblyRefRec *pRec; // Current record being looked at.
+ LPCUTF8 szTmp; // Temp string.
+ const void *pbTmp; // Temp blob.
+ ULONG cbTmp; // Temp byte count.
+ DWORD dwTmp; // Temp flags.
+ const void *pbToken = NULL; // Token version of public key.
+ ULONG cbToken = 0; // Count of bytes in token.
+#if !defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) || defined(DACCESS_COMPILE)
+ const void *pbTmpToken; // Token version of public key.
+ ULONG cbTmpToken; // Count of bytes in token.
+ bool fMatch; // Did public key or tokens match?
+#endif // !FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE
+
+ // Handle special cases upfront.
+ if (!szLocale)
+ szLocale = "";
+ if (!pbPublicKeyOrToken)
+ cbPublicKeyOrToken = 0;
+
+ if (!IsAfPublicKey(dwFlags))
+ {
+ pbToken = pbPublicKeyOrToken;
+ cbToken = cbPublicKeyOrToken;
+ }
+
+ _ASSERTE(pMiniMd && szName && pmar);
+ *pmar = 0;
+
+ cRecs = pMiniMd->getCountAssemblyRefs();
+
+ // Search for the AssemblyRef record.
+ for (ULONG i = 1; i <= cRecs; i++)
+ {
+ IfFailRet(pMiniMd->GetAssemblyRefRecord(i, &pRec));
+
+ IfFailRet(pMiniMd->getNameOfAssemblyRef(pRec, &szTmp));
+ if (strcmp(szTmp, szName))
+ continue;
+
+ IfFailRet(pMiniMd->getLocaleOfAssemblyRef(pRec, &szTmp));
+ if (strcmp(szTmp, szLocale))
+ continue;
+
+ if (pRec->GetMajorVersion() != usMajorVersion)
+ continue;
+ if (pRec->GetMinorVersion() != usMinorVersion)
+ continue;
+
+ // We'll "unify" all versions of mscorlib and Microsoft.VisualC... so if this
+ // is one of those, we won't do the version check beyond the major/minor
+
+ LPCUTF8 szAssemblyRefName;
+ IfFailRet(pMiniMd->getNameOfAssemblyRef(pRec, &szAssemblyRefName));
+ if (SString::_stricmp(szAssemblyRefName, "mscorlib") &&
+ SString::_stricmp(szAssemblyRefName, "microsoft.visualc"))
+ {
+ if (pRec->GetBuildNumber() != usBuildNumber)
+ continue;
+ if (pRec->GetRevisionNumber() != usRevisionNumber)
+ continue;
+ }
+
+ IfFailRet(pMiniMd->getPublicKeyOrTokenOfAssemblyRef(pRec, (const BYTE **)&pbTmp, &cbTmp));
+
+ if ((cbPublicKeyOrToken && !cbTmp) ||
+ (!cbPublicKeyOrToken && cbTmp))
+ continue;
+
+ if (cbTmp)
+ {
+ // Either ref may be using either a full public key or a token
+ // (determined by the ref flags). Must cope with all variations.
+ dwTmp = pMiniMd->getFlagsOfAssemblyRef(pRec);
+ if (IsAfPublicKey(dwTmp) == IsAfPublicKey(dwFlags))
+ {
+ // Easy case, they're both in the same form.
+ if (cbTmp != cbPublicKeyOrToken || memcmp(pbTmp, pbPublicKeyOrToken, cbTmp))
+ continue;
+ }
+ else if (IsAfPublicKey(dwTmp))
+ {
+#if defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) && !defined(DACCESS_COMPILE)
+ return E_FAIL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE
+ // Need to compress target public key to see if it matches.
+ if (!StrongNameTokenFromPublicKey((BYTE*)pbTmp,
+ cbTmp,
+ (BYTE**)&pbTmpToken,
+ &cbTmpToken))
+ {
+ return StrongNameErrorInfo();
+ }
+ fMatch = cbTmpToken == cbPublicKeyOrToken && !memcmp(pbTmpToken, pbPublicKeyOrToken, cbTmpToken);
+ StrongNameFreeBuffer((BYTE*)pbTmpToken);
+ if (!fMatch)
+ continue;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE
+ }
+ else
+ {
+ // Need to compress out public key to see if it matches. We
+ // cache the result of this for further iterations.
+ if (!pbToken)
+ {
+#if defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) && !defined(DACCESS_COMPILE)
+ return E_FAIL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE
+ if (!StrongNameTokenFromPublicKey((BYTE*)pbPublicKeyOrToken,
+ cbPublicKeyOrToken,
+ (BYTE**)&pbToken,
+ &cbToken))
+ {
+ return StrongNameErrorInfo();
+ }
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE
+ }
+ if (cbTmp != cbToken || memcmp(pbTmp, pbToken, cbToken))
+ continue;
+ }
+ }
+
+ if (pbToken && IsAfPublicKey(dwFlags))
+ {
+#if !defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) || defined(DACCESS_COMPILE)
+ StrongNameFreeBuffer((BYTE*)pbToken);
+#endif
+ }
+ *pmar = TokenFromRid(i, mdtAssemblyRef);
+ return S_OK;
+ }
+ if (pbToken && IsAfPublicKey(dwFlags))
+ {
+#if !defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) || defined(DACCESS_COMPILE)
+ StrongNameFreeBuffer((BYTE*)pbToken);
+#endif
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // ImportHelper::FindAssemblyRef
+
+//*******************************************************************************
+// Find a File record given the name.
+//*******************************************************************************
+HRESULT ImportHelper::FindFile(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup.
+ LPCUTF8 szName, // [IN] name for the File.
+ mdFile *pmf, // [OUT] returned File token.
+ RID rid /* = 0 */) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr;
+ ULONG cRecs; // Count of records.
+ FileRec *pRec; // Current record being looked at.
+
+ LPCUTF8 szNameTmp;
+
+ _ASSERTE(pMiniMd && szName && pmf);
+ *pmf = 0;
+
+ cRecs = pMiniMd->getCountFiles();
+
+ // Search for the File record.
+ for (ULONG i = 1; i <= cRecs; i++)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailRet(pMiniMd->GetFileRecord(i, &pRec));
+
+ IfFailRet(pMiniMd->getNameOfFile(pRec, &szNameTmp));
+ if (!strcmp(szNameTmp, szName))
+ {
+ *pmf = TokenFromRid(i, mdtFile);
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // ImportHelper::FindFile
+
+#endif //FEATURE_METADATA_EMIT
+
+//*******************************************************************************
+// Find a ExportedType record given the name.
+//*******************************************************************************
+HRESULT ImportHelper::FindExportedType(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup.
+ LPCUTF8 szNamespace, // [IN] namespace for the ExportedType.
+ LPCUTF8 szName, // [IN] name for the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] token for the enclosing type.
+ mdExportedType *pmct, // [OUT] returned ExportedType token.
+ RID rid /* = 0 */) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr;
+ ULONG cRecs; // Count of records.
+ ExportedTypeRec *pRec; // Current record being looked at.
+ mdToken tkImpl;
+ LPCUTF8 szNamespaceTmp;
+ LPCUTF8 szNameTmp;
+
+ _ASSERTE(pMiniMd && szName && pmct);
+ *pmct = 0;
+
+ // Treat no namespace as empty string.
+ if (!szNamespace)
+ szNamespace = "";
+
+ cRecs = pMiniMd->getCountExportedTypes();
+
+ // Search for the ExportedType record.
+ for (ULONG i = 1; i <= cRecs; i++)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailRet(pMiniMd->GetExportedTypeRecord(i, &pRec));
+
+ // Handle the case of nested vs. non-nested classes.
+ tkImpl = pMiniMd->getImplementationOfExportedType(pRec);
+ if (TypeFromToken(tkImpl) == mdtExportedType && !IsNilToken(tkImpl))
+ {
+ // Current ExportedType being looked at is a nested type, so
+ // comparing the implementation token.
+ if (tkImpl != tkEnclosingType)
+ continue;
+ }
+ else if (TypeFromToken(tkEnclosingType) == mdtExportedType &&
+ !IsNilToken(tkEnclosingType))
+ {
+ // ExportedType passed in is nested but the current ExportedType is not.
+ continue;
+ }
+
+ IfFailRet(pMiniMd->getTypeNamespaceOfExportedType(pRec, &szNamespaceTmp));
+ if (strcmp(szNamespaceTmp, szNamespace))
+ continue;
+
+ IfFailRet(pMiniMd->getTypeNameOfExportedType(pRec, &szNameTmp));
+ if (!strcmp(szNameTmp, szName))
+ {
+ *pmct = TokenFromRid(i, mdtExportedType);
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindExportedType()
+
+//*******************************************************************************
+// Find a ManifestResource record given the name.
+//*******************************************************************************
+HRESULT ImportHelper::FindManifestResource(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup.
+ LPCUTF8 szName, // [IN] name for the ManifestResource.
+ mdManifestResource *pmmr, // [OUT] returned ManifestResource token.
+ RID rid /* = 0 */) // [IN] Optional rid to be ignored.
+{
+ HRESULT hr;
+ ULONG cRecs; // Count of records.
+ ManifestResourceRec *pRec; // Current record being looked at.
+
+ LPCUTF8 szNameTmp;
+
+ _ASSERTE(pMiniMd && szName && pmmr);
+ *pmmr = 0;
+
+ cRecs = pMiniMd->getCountManifestResources();
+
+ // Search for the ManifestResource record.
+ for (ULONG i = 1; i <= cRecs; i++)
+ {
+ // For the call from Validator ignore the rid passed in.
+ if (i == rid)
+ continue;
+
+ IfFailRet(pMiniMd->GetManifestResourceRecord(i, &pRec));
+
+ IfFailRet(pMiniMd->getNameOfManifestResource(pRec, &szNameTmp));
+ if (!strcmp(szNameTmp, szName))
+ {
+ *pmmr = TokenFromRid(i, mdtManifestResource);
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT ImportHelper::FindManifestResource()
+
+#ifdef FEATURE_METADATA_EMIT
+
+//****************************************************************************
+// Convert tokens contained in an element type
+//****************************************************************************
+HRESULT
+ImportHelper::MergeUpdateTokenInFieldSig(
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope.
+ CMiniMdRW *pMiniMdEmit, // [IN] The emit scope.
+ IMetaModelCommon *pCommonAssemImport,// [IN] Assembly scope where the signature is from.
+ const void *pbHashValue, // [IN] Hash value for the import assembly.
+ ULONG cbHashValue, // [IN] Size in bytes for the hash value.
+ IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope.
+ PCCOR_SIGNATURE pbSigImp, // signature from the imported scope
+ MDTOKENMAP *ptkMap, // Internal OID mapping structure.
+ CQuickBytes *pqkSigEmit, // [OUT] buffer for translated signature
+ ULONG cbStartEmit, // [IN] start point of buffer to write to
+ ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp
+ ULONG *pcbEmit) // [OUT] total number of bytes write to pqkSigEmit
+{
+
+ HRESULT hr; // A result.
+ ULONG cb; // count of bytes
+ ULONG cb1; // count of bytes
+ ULONG cb2; // count of bytes
+ ULONG cbSubTotal;
+ ULONG cbImp;
+ ULONG cbEmit;
+ ULONG cbSrcTotal = 0; // count of bytes consumed in the imported signature
+ ULONG cbDestTotal = 0; // count of bytes for the new signature
+ ULONG ulElementType = 0; // place holder for expanded data
+ ULONG ulData;
+ ULONG ulTemp;
+ mdToken tkRidFrom; // Original rid
+ mdToken tkRidTo; // new rid
+ int iData;
+ CQuickArray<mdToken> cqaNesters; // Array of Nester tokens.
+ CQuickArray<LPCUTF8> cqaNesterNamespaces; // Array of Nester Namespaces.
+ CQuickArray<LPCUTF8> cqaNesterNames; // Array of Nester names.
+
+ _ASSERTE(pcbEmit);
+
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulElementType);
+ cbSrcTotal += cb;
+
+ // count numbers of modifiers
+ while (CorIsModifierElementType((CorElementType) ulElementType))
+ {
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulElementType);
+ cbSrcTotal += cb;
+ }
+
+ // copy ELEMENT_TYPE_* over
+ cbDestTotal = cbSrcTotal;
+ IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal));
+ memcpy(((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit, pbSigImp, cbDestTotal);
+ switch (ulElementType)
+ {
+ case ELEMENT_TYPE_SZARRAY:
+ // syntax : SZARRAY <BaseType>
+
+ // conver the base type for the SZARRAY or GENERICARRAY
+ IfFailGo(MergeUpdateTokenInFieldSig(
+ pMiniMdAssemEmit, // The assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ pCommonAssemImport, // The assembly scope where the signature is from.
+ pbHashValue, // Hash value for the import assembly.
+ cbHashValue, // Size in bytes for the hash value.
+ pCommonImport, // scope to merge into the emit scope.
+ &pbSigImp[cbSrcTotal], // from the imported scope
+ ptkMap, // OID mapping structure.
+ pqkSigEmit, // [OUT] buffer for translated signature
+ cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to
+ &cbImp, // [OUT] total number of bytes consumed from pbSigImp
+ &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit
+ cbSrcTotal += cbImp;
+ cbDestTotal += cbEmit;
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ {
+ // syntax : WITH (ELEMENT_TYPE_CLASS | ELEMENT_TYPE_VALUECLASS) <BaseType>
+
+ IfFailGo(MergeUpdateTokenInFieldSig(
+ pMiniMdAssemEmit, // The assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ pCommonAssemImport, // The assembly scope where the signature is from.
+ pbHashValue, // Hash value for the import assembly.
+ cbHashValue, // Size in bytes for the hash value.
+ pCommonImport, // scope to merge into the emit scope.
+ &pbSigImp[cbSrcTotal], // from the imported scope
+ ptkMap, // OID mapping structure.
+ pqkSigEmit, // [OUT] buffer for translated signature
+ cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to
+ &cbImp, // [OUT] total number of bytes consumed from pbSigImp
+ &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit
+ cbSrcTotal += cbImp;
+ cbDestTotal += cbEmit;
+
+ // copy over the number of arguments
+ ULONG nargs;
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &nargs);
+
+ IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cb));
+ cb1 = CorSigCompressData(nargs, ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit + cbDestTotal);
+ _ASSERTE(cb == cb1);
+
+ cbSrcTotal += cb;
+ cbDestTotal += cb1;
+
+ for (ULONG narg = 0; narg < nargs; narg++) {
+ IfFailGo(MergeUpdateTokenInFieldSig(
+ pMiniMdAssemEmit, // The assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ pCommonAssemImport, // The assembly scope where the signature is from.
+ pbHashValue, // Hash value for the import assembly.
+ cbHashValue, // Size in bytes for the hash value.
+ pCommonImport, // The scope to merge into the emit scope.
+ &pbSigImp[cbSrcTotal], // signature from the imported scope
+ ptkMap, // Internal OID mapping structure.
+ pqkSigEmit, // [OUT] buffer for translated signature
+ cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to
+ &cbImp, // [OUT] total number of bytes consumed from pbSigImp
+ &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit
+ cbSrcTotal += cbImp;
+ cbDestTotal += cbEmit;
+ }
+ }
+
+ break;
+
+ case ELEMENT_TYPE_MVAR:
+ case ELEMENT_TYPE_VAR:
+ // syntax : VAR <n>
+ // syntax : MVAR <n>
+
+ // after the VAR or MVAR there is an integer indicating which type variable
+ //
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulData);
+
+ IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cb));
+ cb1 = CorSigCompressData(ulData, ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit + cbDestTotal);
+ _ASSERTE(cb == cb1);
+
+ cbSrcTotal += cb;
+ cbDestTotal += cb1;
+
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ // syntax : ARRAY BaseType <rank> [i size_1... size_i] [j lowerbound_1 ... lowerbound_j]
+
+ // conver the base type for the MDARRAY
+ IfFailGo(MergeUpdateTokenInFieldSig(
+ pMiniMdAssemEmit, // The assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ pCommonAssemImport, // The assembly scope where the signature is from.
+ pbHashValue, // Hash value for the import assembly.
+ cbHashValue, // Size in bytes for the hash value.
+ pCommonImport, // The scope to merge into the emit scope.
+ &pbSigImp[cbSrcTotal], // signature from the imported scope
+ ptkMap, // Internal OID mapping structure.
+ pqkSigEmit, // [OUT] buffer for translated signature
+ cbStartEmit + cbSrcTotal, // [IN] start point of buffer to write to
+ &cbImp, // [OUT] total number of bytes consumed from pbSigImp
+ &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit
+ cbSrcTotal += cbImp;
+ cbDestTotal += cbEmit;
+
+ // Parse for the rank
+ cbSubTotal = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulData);
+
+ // if rank == 0, we are done
+ if (ulData != 0)
+ {
+ // any size of dimension specified?
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal + cbSubTotal], &ulData);
+ cbSubTotal += cb;
+
+ while (ulData--)
+ {
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal + cbSubTotal], &ulTemp);
+ cbSubTotal += cb;
+ }
+
+ // any lower bound specified?
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal + cbSubTotal], &ulData);
+ cbSubTotal += cb;
+
+ while (ulData--)
+ {
+ cb = CorSigUncompressSignedInt(&pbSigImp[cbSrcTotal + cbSubTotal], &iData);
+ cbSubTotal += cb;
+ }
+ }
+
+ // cbSubTotal is now the number of bytes still left to move over
+ // cbSrcTotal is where bytes start on the pbSigImp to be copied over
+ // cbStartEmit + cbDestTotal is where the destination of copy
+
+ IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cbSubTotal));
+ memcpy(((BYTE *)pqkSigEmit->Ptr())+cbStartEmit + cbDestTotal, &pbSigImp[cbSrcTotal], cbSubTotal);
+
+ cbSrcTotal = cbSrcTotal + cbSubTotal;
+ cbDestTotal = cbDestTotal + cbSubTotal;
+
+ break;
+ case ELEMENT_TYPE_FNPTR:
+ // function pointer is followed by another complete signature
+ IfFailGo(MergeUpdateTokenInSig(
+ pMiniMdAssemEmit, // The assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ pCommonAssemImport, // The assembly scope where the signature is from.
+ pbHashValue, // Hash value for the import assembly.
+ cbHashValue, // Size in bytes for the hash value.
+ pCommonImport, // The scope to merge into the emit scope.
+ &pbSigImp[cbSrcTotal], // signature from the imported scope
+ ptkMap, // Internal OID mapping structure.
+ pqkSigEmit, // [OUT] buffer for translated signature
+ cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to
+ &cbImp, // [OUT] total number of bytes consumed from pbSigImp
+ &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit
+ cbSrcTotal += cbImp;
+ cbDestTotal += cbEmit;
+ break;
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_CMOD_REQD:
+ case ELEMENT_TYPE_CMOD_OPT:
+
+ // syntax for CLASS = ELEMENT_TYPE_CLASS <rid>
+ // syntax for VALUE_CLASS = ELEMENT_TYPE_VALUECLASS <rid>
+
+ // now get the embedded typeref token
+ cb = CorSigUncompressToken(&pbSigImp[cbSrcTotal], &tkRidFrom);
+
+ // Map the ulRidFrom to ulRidTo
+ if (ptkMap)
+ {
+ // mdtBaseType does not record in the map. It is unique across modules
+ if ( TypeFromToken(tkRidFrom) == mdtBaseType )
+ {
+ tkRidTo = tkRidFrom;
+ }
+ else
+ {
+ IfFailGo( ptkMap->Remap(tkRidFrom, &tkRidTo) );
+ }
+ }
+ else
+ {
+ // If the token is a TypeDef or a TypeRef, get/create the
+ // ResolutionScope for the outermost TypeRef.
+ if (TypeFromToken(tkRidFrom) == mdtTypeDef)
+ {
+ IfFailGo(ImportTypeDef(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ pbHashValue,
+ cbHashValue,
+ pCommonImport,
+ tkRidFrom,
+ true, // Optimize to TypeDef if emit and import scopes are identical.
+ &tkRidTo));
+ }
+ else if (TypeFromToken(tkRidFrom) == mdtTypeRef)
+ {
+ IfFailGo(ImportTypeRef(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ pbHashValue,
+ cbHashValue,
+ pCommonImport,
+ tkRidFrom,
+ &tkRidTo));
+ }
+ else if ( TypeFromToken(tkRidFrom) == mdtTypeSpec )
+ {
+ // copy over the TypeSpec
+ PCCOR_SIGNATURE pvTypeSpecSig;
+ ULONG cbTypeSpecSig;
+ CQuickBytes qkTypeSpecSigEmit;
+ ULONG cbTypeSpecEmit;
+
+ IfFailGo(pCommonImport->CommonGetTypeSpecProps(
+ tkRidFrom,
+ &pvTypeSpecSig,
+ &cbTypeSpecSig));
+
+ // Translate the typespec signature before look up
+ IfFailGo(MergeUpdateTokenInFieldSig(
+ pMiniMdAssemEmit, // The assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ pCommonAssemImport, // The assembly scope where the signature is from.
+ pbHashValue, // Hash value for the import assembly.
+ cbHashValue, // Size in bytes for the hash value.
+ pCommonImport, // The scope to merge into the emit scope.
+ pvTypeSpecSig, // signature from the imported scope
+ ptkMap, // Internal OID mapping structure.
+ &qkTypeSpecSigEmit, // [OUT] buffer for translated signature
+ 0, // start from first byte of TypeSpec signature
+ 0, // don't care how many bytes are consumed
+ &cbTypeSpecEmit) ); // [OUT] total number of bytes write to pqkSigEmit
+
+ hr = FindTypeSpec(pMiniMdEmit,
+ (PCCOR_SIGNATURE) (qkTypeSpecSigEmit.Ptr()),
+ cbTypeSpecEmit,
+ &tkRidTo);
+
+ if ( hr == CLDB_E_RECORD_NOTFOUND )
+ {
+ // Create TypeSpec record.
+ TypeSpecRec *pRecEmit;
+
+ IfFailGo(pMiniMdEmit->AddTypeSpecRecord(&pRecEmit, (RID *)&tkRidTo));
+
+ IfFailGo(pMiniMdEmit->PutBlob(
+ TBL_TypeSpec,
+ TypeSpecRec::COL_Signature,
+ pRecEmit,
+ (PCCOR_SIGNATURE) (qkTypeSpecSigEmit.Ptr()),
+ cbTypeSpecEmit));
+ tkRidTo = TokenFromRid( tkRidTo, mdtTypeSpec );
+ IfFailGo(pMiniMdEmit->UpdateENCLog(tkRidTo));
+ }
+ IfFailGo( hr );
+ }
+ else
+ {
+ _ASSERTE( TypeFromToken(tkRidFrom) == mdtBaseType );
+
+ // base type is unique across module
+ tkRidTo = tkRidFrom;
+ }
+ }
+
+ // How many bytes the new rid will consume?
+ cb1 = CorSigCompressToken(tkRidTo, &ulData);
+
+ // ensure buffer is big enough
+ IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cb1));
+
+ // store the new token
+ cb2 = CorSigCompressToken(
+ tkRidTo,
+ (ULONG *)( ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit + cbDestTotal) );
+
+ // inconsistency on CorSigCompressToken and CorSigUncompressToken
+ _ASSERTE(cb1 == cb2);
+
+ cbSrcTotal = cbSrcTotal + cb;
+ cbDestTotal = cbDestTotal + cb1;
+
+ if ( ulElementType == ELEMENT_TYPE_CMOD_REQD ||
+ ulElementType == ELEMENT_TYPE_CMOD_OPT)
+ {
+ // need to skip over the base type
+ IfFailGo(MergeUpdateTokenInFieldSig(
+ pMiniMdAssemEmit, // The assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ pCommonAssemImport, // The assembly scope where the signature is from.
+ pbHashValue, // Hash value for the import assembly.
+ cbHashValue, // Size in bytes for the hash value.
+ pCommonImport, // The scope to merge into the emit scope.
+ &pbSigImp[cbSrcTotal], // signature from the imported scope
+ ptkMap, // Internal OID mapping structure.
+ pqkSigEmit, // [OUT] buffer for translated signature
+ cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to
+ &cbImp, // [OUT] total number of bytes consumed from pbSigImp
+ &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit
+ cbSrcTotal += cbImp;
+ cbDestTotal += cbEmit;
+ }
+
+ break;
+ default:
+ _ASSERTE(cbSrcTotal == cbDestTotal);
+
+ if ((ulElementType >= ELEMENT_TYPE_MAX) ||
+ (ulElementType == ELEMENT_TYPE_PTR) ||
+ (ulElementType == ELEMENT_TYPE_BYREF) ||
+ (ulElementType == ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED))
+ {
+ IfFailGo(META_E_BAD_SIGNATURE);
+ }
+ break;
+ }
+ if (pcbImp)
+ *pcbImp = cbSrcTotal;
+ *pcbEmit = cbDestTotal;
+
+ErrExit:
+ return hr;
+} // ImportHelper::MergeUpdateTokenInFieldSig
+
+#endif //FEATURE_METADATA_EMIT
+
+//****************************************************************************
+// convert tokens contained in a COM+ signature
+//****************************************************************************
+HRESULT ImportHelper::MergeUpdateTokenInSig(// S_OK or error.
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope.
+ CMiniMdRW *pMiniMdEmit, // [IN] The emit scope.
+ IMetaModelCommon *pCommonAssemImport,// [IN] Assembly scope where the signature is from.
+ const void *pbHashValue, // [IN] Hash value for the import assembly.
+ ULONG cbHashValue, // [IN] Size in bytes for the hash value.
+ IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope.
+ PCCOR_SIGNATURE pbSigImp, // signature from the imported scope
+ MDTOKENMAP *ptkMap, // Internal OID mapping structure.
+ CQuickBytes *pqkSigEmit, // [OUT] translated signature
+ ULONG cbStartEmit, // [IN] start point of buffer to write to
+ ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp
+ ULONG *pcbEmit) // [OUT] total number of bytes write to pqkSigEmit
+{
+#ifdef FEATURE_METADATA_EMIT
+ HRESULT hr = NOERROR; // A result.
+ ULONG cb; // count of bytes
+ ULONG cb1;
+ ULONG cbSrcTotal = 0; // count of bytes consumed in the imported signature
+ ULONG cbDestTotal = 0; // count of bytes for the new signature
+ ULONG cbEmit; // count of bytes consumed in the imported signature
+ ULONG cbImp; // count of bytes for the new signature
+ ULONG cArg = 0; // count of arguments in the signature
+ ULONG cTyArg = 0;
+ ULONG callingconv = 0; // calling convention from signature
+
+ _ASSERTE(pcbEmit && pqkSigEmit && pbSigImp);
+
+ // calling convention
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &callingconv);
+ _ASSERTE((callingconv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX);
+
+ // skip over calling convention
+ cbSrcTotal += cb;
+
+ if (isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_FIELD))
+ {
+ // It is a FieldRef
+ cb1 = CorSigCompressData(callingconv, ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit);
+
+ // compression and uncompression better match
+ _ASSERTE(cb == cb1);
+
+ cbDestTotal = cbSrcTotal = cb;
+ IfFailGo(MergeUpdateTokenInFieldSig(
+ pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ pbHashValue,
+ cbHashValue,
+ pCommonImport,
+ &pbSigImp[cbSrcTotal],
+ ptkMap,
+ pqkSigEmit, // output buffer to hold the new sig for the field
+ cbStartEmit + cbDestTotal, // number of bytes already in pqkSigDest
+ &cbImp, // number of bytes consumed from imported signature
+ &cbEmit)); // number of bytes write to the new signature
+ *pcbEmit = cbDestTotal + cbEmit;
+ }
+ else
+ {
+
+ // It is a MethodRef
+ // count of type arguments
+ if (callingconv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &cTyArg);
+ cbSrcTotal += cb;
+ }
+
+ // count of argument
+ cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &cArg);
+ cbSrcTotal += cb;
+
+ // move over the calling convention and the count of arguments
+ IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbSrcTotal));
+ memcpy(((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit, pbSigImp, cbSrcTotal);
+ cbDestTotal = cbSrcTotal;
+
+ if ( !( isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) || isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_GENERICINST)) )
+ {
+ // LocalVar sig does not have return type
+ // process the return type
+ IfFailGo(MergeUpdateTokenInFieldSig(
+ pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ pbHashValue,
+ cbHashValue,
+ pCommonImport,
+ &pbSigImp[cbSrcTotal],
+ ptkMap,
+ pqkSigEmit, // output buffer to hold the new sig for the field
+ cbStartEmit + cbDestTotal, // number of bytes already in pqkSigDest
+ &cbImp, // number of bytes consumed from imported signature
+ &cbEmit)); // number of bytes write to the new signature
+
+ // advance the count
+ cbSrcTotal += cbImp;
+ cbDestTotal += cbEmit;
+ }
+
+
+ while (cArg)
+ {
+ // process every argument
+ IfFailGo(MergeUpdateTokenInFieldSig(
+ pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ pbHashValue,
+ cbHashValue,
+ pCommonImport,
+ &pbSigImp[cbSrcTotal],
+ ptkMap,
+ pqkSigEmit, // output buffer to hold the new sig for the field
+ cbStartEmit + cbDestTotal,
+ &cbImp, // number of bytes consumed from imported signature
+ &cbEmit)); // number of bytes write to the new signature
+ cbSrcTotal += cbImp;
+ cbDestTotal += cbEmit;
+ cArg--;
+ }
+
+ // total of number of bytes consumed from imported signature
+ if (pcbImp)
+ *pcbImp = cbSrcTotal;
+
+ // total number of bytes emitted by this function call to the emitting signature
+ *pcbEmit = cbDestTotal;
+ }
+
+ErrExit:
+ return hr;
+#else //!FEATURE_METADATA_EMIT
+ // This code should be called only with public emit APIs
+ _ASSERTE_MSG(FALSE, "This method should not be reachable");
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT
+} // ImportHelper::MergeUpdateTokenInSig
+
+//****************************************************************************
+// Given a TypeDef or a TypeRef, return the Nesting hierarchy. The first
+// element in the returned array always refers to the class token passed and
+// the nesting hierarchy expands outwards from there.
+//****************************************************************************
+HRESULT ImportHelper::GetNesterHierarchy(
+ IMetaModelCommon *pCommon, // Scope in which to find the hierarchy.
+ mdToken tk, // TypeDef/TypeRef whose hierarchy is needed.
+ CQuickArray<mdToken> &cqaNesters, // Array of Nesters.
+ CQuickArray<LPCUTF8> &cqaNamespaces, // Names of the nesters.
+ CQuickArray<LPCUTF8> &cqaNames) // Namespaces of the nesters.
+{
+ _ASSERTE(pCommon &&
+ (TypeFromToken(tk) == mdtTypeDef ||
+ TypeFromToken(tk) == mdtTypeRef) &&
+ !IsNilToken(tk));
+
+ if (TypeFromToken(tk) == mdtTypeDef)
+ {
+ return GetTDNesterHierarchy(pCommon,
+ tk,
+ cqaNesters,
+ cqaNamespaces,
+ cqaNames);
+ }
+ else
+ {
+ return GetTRNesterHierarchy(pCommon,
+ tk,
+ cqaNesters,
+ cqaNamespaces,
+ cqaNames);
+ }
+} // HRESULT ImportHelper::GetNesterHierarchy()
+
+//****************************************************************************
+// Get Nesting hierarchy given a TypeDef.
+//****************************************************************************
+HRESULT ImportHelper::GetTDNesterHierarchy(
+ IMetaModelCommon *pCommon, // Scope in which to find the hierarchy.
+ mdTypeDef td, // TypeDef whose hierarchy is needed.
+ CQuickArray<mdTypeDef> &cqaTdNesters,// Array of Nesters.
+ CQuickArray<LPCUTF8> &cqaNamespaces, // Namespaces of the nesters.
+ CQuickArray<LPCUTF8> &cqaNames) // Names of the nesters.
+{
+ LPCUTF8 szName, szNamespace;
+ DWORD dwFlags;
+ mdTypeDef tdNester;
+ ULONG ulNesters;
+ HRESULT hr = NOERROR;
+
+ _ASSERTE(pCommon &&
+ TypeFromToken(td) == mdtTypeDef &&
+ !IsNilToken(td));
+
+ // Set current Nester index to 0.
+ ulNesters = 0;
+ // The first element in the hierarchy is the TypeDef itself.
+ tdNester = td;
+ // Bogus initialization to kick off the while loop.
+ dwFlags = tdNestedPublic;
+ // Loop as long as the TypeDef is a Nested TypeDef.
+ while (IsTdNested(dwFlags))
+ {
+ if (InvalidRid(tdNester))
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+ // Get the name and namespace for the TypeDef.
+ IfFailGo(pCommon->CommonGetTypeDefProps(
+ tdNester,
+ &szNamespace,
+ &szName,
+ &dwFlags,
+ NULL,
+ NULL));
+
+ // Update the dynamic arrays.
+ ulNesters++;
+
+ IfFailGo(cqaTdNesters.ReSizeNoThrow(ulNesters));
+ cqaTdNesters[ulNesters-1] = tdNester;
+
+ IfFailGo(cqaNamespaces.ReSizeNoThrow(ulNesters));
+ cqaNamespaces[ulNesters-1] = szNamespace;
+
+ IfFailGo(cqaNames.ReSizeNoThrow(ulNesters));
+ cqaNames[ulNesters-1] = szName;
+
+ IfFailGo(pCommon->CommonGetEnclosingClassOfTypeDef(tdNester, &tdNester));
+ }
+ // Outermost class must have enclosing of Nil.
+ _ASSERTE(IsNilToken(tdNester));
+ErrExit:
+ return hr;
+} // HRESULT ImportHelper::GetTDNesterHierarchy()
+
+
+//****************************************************************************
+// Get Nesting hierarchy given a TypeRef.
+//****************************************************************************
+HRESULT ImportHelper::GetTRNesterHierarchy(
+ IMetaModelCommon *pCommon, // [IN] Scope in which to find the hierarchy.
+ mdTypeRef tr, // [IN] TypeRef whose hierarchy is needed.
+ CQuickArray<mdTypeRef> &cqaTrNesters,// [OUT] Array of Nesters.
+ CQuickArray<LPCUTF8> &cqaNamespaces, // [OUT] Namespaces of the nesters.
+ CQuickArray<LPCUTF8> &cqaNames) // [OUT] Names of the nesters.
+{
+ LPCUTF8 szNamespace;
+ LPCUTF8 szName;
+ mdTypeRef trNester;
+ mdToken tkResolutionScope;
+ ULONG ulNesters;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(pCommon &&
+ TypeFromToken(tr) == mdtTypeRef &&
+ !IsNilToken(tr));
+
+ // Set current Nester index to 0.
+ ulNesters = 0;
+ // The first element in the hierarchy is the TypeRef itself.
+ trNester = tr;
+ // Loop as long as the TypeRef is a Nested TypeRef.
+ while (TypeFromToken(trNester) == mdtTypeRef && !IsNilToken(trNester))
+ {
+ // Get the name and namespace for the TypeDef.
+ IfFailGo(pCommon->CommonGetTypeRefProps(
+ trNester,
+ &szNamespace,
+ &szName,
+ &tkResolutionScope));
+
+ // Update the dynamic arrays.
+ ulNesters++;
+
+ IfFailGo(cqaTrNesters.ReSizeNoThrow(ulNesters));
+ cqaTrNesters[ulNesters-1] = trNester;
+
+ IfFailGo(cqaNamespaces.ReSizeNoThrow(ulNesters));
+ cqaNamespaces[ulNesters-1] = szNamespace;
+
+ IfFailGo(cqaNames.ReSizeNoThrow(ulNesters));
+ cqaNames[ulNesters-1] = szName;
+
+ trNester = tkResolutionScope;
+ }
+ErrExit:
+ return hr;
+} // HRESULT ImportHelper::GetTRNesterHierarchy()
+
+//****************************************************************************
+// Create the Nesting hierarchy given the array of TypeRef names. The first
+// TypeRef in the array is the innermost TypeRef.
+//****************************************************************************
+HRESULT ImportHelper::CreateNesterHierarchy(
+ CMiniMdRW *pMiniMdEmit, // [IN] Emit scope to create the Nesters in.
+ CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Nester namespaces.
+ CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Nester names.
+ mdToken tkResolutionScope, // [IN] ResolutionScope for the innermost TypeRef.
+ mdTypeRef *ptr) // [OUT] Token for the innermost TypeRef.
+{
+ TypeRefRec *pRecEmit;
+ ULONG iRecord;
+ LPCUTF8 szName;
+ LPCUTF8 szNamespace;
+ mdTypeRef trNester;
+ mdTypeRef trCur;
+ ULONG ulNesters;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(cqaNesterNames.Size() == cqaNesterNamespaces.Size() &&
+ cqaNesterNames.Size());
+
+ // Initialize the output parameter.
+ *ptr = mdTypeRefNil;
+
+ // Get count of Nesters in the hierarchy.
+ ulNesters = (ULONG)cqaNesterNames.Size();
+
+ // For each nester try to find the corresponding TypeRef in the emit scope.
+ // For the outermost TypeRef, ResolutionScope is what's passed in.
+ if (tkResolutionScope == mdTokenNil)
+ trNester = mdTypeRefNil;
+ else
+ trNester = tkResolutionScope;
+ ULONG ulCurNester;
+ for (ulCurNester = ulNesters-1; ulCurNester != (ULONG) -1; ulCurNester--)
+ {
+ hr = FindTypeRefByName(pMiniMdEmit,
+ trNester,
+ cqaNesterNamespaces[ulCurNester],
+ cqaNesterNames[ulCurNester],
+ &trCur);
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ break;
+ else
+ IfFailGo(hr);
+ trNester = trCur;
+ }
+ if (SUCCEEDED(hr))
+ *ptr = trNester;
+ else if ( hr == CLDB_E_RECORD_NOTFOUND )
+ {
+ // Create TypeRef records for the part of the hierarchy for which
+ // TypeRefs are not already present.
+ for (;ulCurNester != (ULONG) -1; ulCurNester--)
+ {
+ szName = cqaNesterNames[ulCurNester];
+ szNamespace = cqaNesterNamespaces[ulCurNester];
+
+ IfFailGo(pMiniMdEmit->AddTypeRefRecord(&pRecEmit, &iRecord));
+ if (szNamespace && szNamespace[0] != '\0')
+ {
+ // only put the namespace if it is not an empty string and not NULL
+ IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Namespace,
+ pRecEmit, szNamespace));
+ }
+ IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Name,
+ pRecEmit, szName));
+ IfFailGo(pMiniMdEmit->PutToken(TBL_TypeRef,
+ TypeRefRec::COL_ResolutionScope, pRecEmit, trNester));
+
+ trNester = TokenFromRid(iRecord, mdtTypeRef);
+ IfFailGo(pMiniMdEmit->UpdateENCLog(trNester));
+
+ // Hash the name.
+ IfFailGo(pMiniMdEmit->AddNamedItemToHash(TBL_TypeRef, trNester, szName, 0));
+ }
+ *ptr = trNester;
+ }
+ else
+ IfFailGo(hr);
+ErrExit:
+ return hr;
+} // ImportHelper::CreateNesterHierarchy
+
+//****************************************************************************
+// Given the arrays of names and namespaces for the Nested Type hierarchy,
+// find the innermost TypeRef token. The arrays start with the innermost
+// TypeRefs and go outwards.
+//****************************************************************************
+HRESULT ImportHelper::FindNestedTypeRef(
+ CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef.
+ CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Names.
+ CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Namespaces.
+ mdToken tkResolutionScope, // [IN] Resolution scope for the outermost TypeRef.
+ mdTypeRef *ptr) // [OUT] Inner most TypeRef token.
+{
+ ULONG ulNesters;
+ ULONG ulCurNester;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(cqaNesterNames.Size() == cqaNesterNamespaces.Size() &&
+ cqaNesterNames.Size());
+
+ // Set the output parameter to Nil token.
+ *ptr = mdTokenNil;
+
+ // Get count in the hierarchy, the give TypeDef included.
+ ulNesters = (ULONG)cqaNesterNames.Size();
+
+ // For each nester try to find the corresponding TypeRef in
+ // the emit scope. For the outermost TypeDef enclosing class is Nil.
+ for (ulCurNester = ulNesters-1; ulCurNester != (ULONG) -1; ulCurNester--)
+ {
+ IfFailGo(FindTypeRefByName(pMiniMd,
+ tkResolutionScope,
+ cqaNesterNamespaces[ulCurNester],
+ cqaNesterNames[ulCurNester],
+ &tkResolutionScope));
+ }
+ *ptr = tkResolutionScope;
+ErrExit:
+ return hr;
+} // HRESULT ImportHelper::FindNestedTypeRef()
+
+
+//****************************************************************************
+// Given the arrays of names and namespaces for the Nested Type hierarchy,
+// find the innermost TypeDef token. The arrays start with the innermost
+// TypeDef and go outwards.
+//****************************************************************************
+HRESULT ImportHelper::FindNestedTypeDef(
+ CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef.
+ CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Namespaces.
+ CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Names.
+ mdTypeDef tdNester, // [IN] Enclosing class for the Outermost TypeDef.
+ mdTypeDef *ptd) // [OUT] Inner most TypeRef token.
+{
+ ULONG ulNesters;
+ ULONG ulCurNester;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(cqaNesterNames.Size() == cqaNesterNamespaces.Size() &&
+ cqaNesterNames.Size());
+
+ // Set the output parameter to Nil token.
+ *ptd = mdTokenNil;
+
+ // Get count in the hierarchy, the give TypeDef included.
+ ulNesters = (ULONG)cqaNesterNames.Size();
+
+ // For each nester try to find the corresponding TypeRef in
+ // the emit scope. For the outermost TypeDef enclosing class is Nil.
+ for (ulCurNester = ulNesters-1; ulCurNester != (ULONG) -1; ulCurNester--)
+ {
+ IfFailGo(FindTypeDefByName(pMiniMd,
+ cqaNesterNamespaces[ulCurNester],
+ cqaNesterNames[ulCurNester],
+ tdNester,
+ &tdNester));
+ }
+ *ptd = tdNester;
+ErrExit:
+ return hr;
+} // ImportHelper::FindNestedTypeDef
+
+#ifdef FEATURE_METADATA_EMIT
+
+//****************************************************************************
+// Given the TypeDef and the corresponding assembly and module import scopes,
+// create a corresponding TypeRef in the given emit scope.
+//****************************************************************************
+HRESULT
+ImportHelper::ImportTypeDef(
+ CMiniMdRW * pMiniMdAssemEmit, // [IN] Assembly emit scope.
+ CMiniMdRW * pMiniMdEmit, // [IN] Module emit scope.
+ IMetaModelCommon * pCommonAssemImport, // [IN] Assembly import scope.
+ const void * pbHashValue, // [IN] Hash value for import assembly.
+ ULONG cbHashValue, // [IN] Size in bytes of hash value.
+ IMetaModelCommon * pCommonImport, // [IN] Module import scope.
+ mdTypeDef tdImport, // [IN] Imported TypeDef.
+ bool bReturnTd, // [IN] If the import and emit scopes are identical, return the TypeDef.
+ mdToken * ptkType) // [OUT] Output token for the imported type in the emit scope.
+{
+ CQuickArray<mdTypeDef> cqaNesters;
+ CQuickArray<LPCUTF8> cqaNesterNames;
+ CQuickArray<LPCUTF8> cqaNesterNamespaces;
+ GUID nullguid = GUID_NULL;
+ GUID MvidAssemImport = nullguid;
+ GUID MvidAssemEmit = nullguid;
+ GUID MvidImport = nullguid;
+ GUID MvidEmit = nullguid;
+ GUID GuidImport = GUID_NULL;
+ LPCUTF8 szModuleImport;
+ mdToken tkOuterRes = mdTokenNil;
+ HRESULT hr = S_OK;
+ BOOL bBCL = false;
+
+ _ASSERTE(pMiniMdEmit && pCommonImport && ptkType);
+ _ASSERTE(TypeFromToken(tdImport) == mdtTypeDef && tdImport != mdTypeDefNil);
+
+ // Get MVIDs for import and emit, assembly and module scopes.
+ if (pCommonAssemImport != NULL)
+ {
+ IfFailGo(pCommonAssemImport->CommonGetScopeProps(0, &MvidAssemImport));
+ }
+ IfFailGo(pCommonImport->CommonGetScopeProps(&szModuleImport, &MvidImport));
+ if (pMiniMdAssemEmit != NULL)
+ {
+ IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdAssemEmit)->CommonGetScopeProps(0, &MvidAssemEmit));
+ }
+ IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdEmit)->CommonGetScopeProps(0, &MvidEmit));
+
+ if (pCommonAssemImport == NULL && strcmp(szModuleImport, COM_RUNTIME_LIBRARY) == 0)
+ {
+ const BYTE *pBlob; // Blob with dispid.
+ ULONG cbBlob; // Length of blob.
+ WCHAR wzBlob[40]; // Wide char format of guid.
+ int ix; // Loop control.
+
+ hr = pCommonImport->CommonGetCustomAttributeByName(1, 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))
+ {
+ for (ix=1; ix<=36; ++ix)
+ wzBlob[ix] = pBlob[ix+2];
+ wzBlob[0] = '{';
+ wzBlob[37] = '}';
+ wzBlob[38] = 0;
+ // It's ok that we ignore the hr here. It's not needed, but I
+ // don't want to remove it in case a code analysis tool will complain
+ // about not capturing return codes.
+ hr = IIDFromString(wzBlob, &GuidImport);
+ }
+ }
+ bBCL = (GuidImport == LIBID_ComPlusRuntime);
+ }
+
+ // Compute the ResolutionScope for the imported type.
+ if (bBCL)
+ {
+ // This is the case that we are referring to mscorlib.dll but client does not provide the manifest for
+ // mscorlib.dll!! Do not generate ModuleRef to the mscorlib.dll. But instead we should just leave the
+ // ResolutionScope empty
+ tkOuterRes = mdTokenNil;
+ }
+ else if (MvidAssemImport == MvidAssemEmit && MvidImport == MvidEmit)
+ {
+ // The TypeDef is in the same Assembly and the Same scope.
+ if (bReturnTd)
+ {
+ *ptkType = tdImport;
+ goto ErrExit;
+ }
+ else
+ tkOuterRes = TokenFromRid(1, mdtModule);
+ }
+ else if (MvidAssemImport == MvidAssemEmit && MvidImport != MvidEmit)
+ {
+ // The TypeDef is in the same Assembly but a different module.
+
+ // Create a ModuleRef corresponding to the import scope.
+ IfFailGo(CreateModuleRefFromScope(pMiniMdEmit, pCommonImport, &tkOuterRes));
+ }
+ else if (MvidAssemImport != MvidAssemEmit)
+ {
+ if (pCommonAssemImport)
+ {
+ // The TypeDef is from a different Assembly.
+
+ // Import and Emit scopes can't be identical and be from different
+ // Assemblies at the same time.
+ _ASSERTE(MvidImport != MvidEmit &&
+ "Import scope can't be identical to the Emit scope and be from a different Assembly at the same time.");
+
+ _ASSERTE(pCommonAssemImport);
+
+ // Create an AssemblyRef corresponding to the import scope.
+ IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ pbHashValue,
+ cbHashValue,
+ &tkOuterRes));
+ }
+ else
+ {
+ // <REVISIT_TODO>@FUTURE: review this fix! We may want to return error in the future.
+ // This is to enable smc to reference mscorlib.dll while it does not have the manifest for mscorlib.dll opened.</REVISIT_TODO>
+ // Create a Nil ResolutionScope to the TypeRef.
+ tkOuterRes = mdTokenNil;
+ }
+ }
+
+ // Get the nesting hierarchy for the Type from the import scope and create
+ // the corresponding Type hierarchy in the emit scope. Note that the non-
+ // nested class case simply folds into this scheme.
+
+ IfFailGo(GetNesterHierarchy(pCommonImport,
+ tdImport,
+ cqaNesters,
+ cqaNesterNamespaces,
+ cqaNesterNames));
+
+ IfFailGo(CreateNesterHierarchy(pMiniMdEmit,
+ cqaNesterNamespaces,
+ cqaNesterNames,
+ tkOuterRes,
+ ptkType));
+ErrExit:
+ return hr;
+} // ImportHelper::ImportTypeDef
+
+//****************************************************************************
+// Given the TypeRef and the corresponding assembly and module import scopes,
+// return the corresponding token in the given emit scope.
+// <REVISIT_TODO>@FUTURE: Should we look at visibility flags on ExportedTypes and TypeDefs when
+// handling references across Assemblies?</REVISIT_TODO>
+//****************************************************************************
+HRESULT ImportHelper::ImportTypeRef(
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope.
+ CMiniMdRW *pMiniMdEmit, // [IN] Module emit scope.
+ IMetaModelCommon *pCommonAssemImport, // [IN] Assembly import scope.
+ const void *pbHashValue, // [IN] Hash value for import assembly.
+ ULONG cbHashValue, // [IN] Size in bytes of hash value.
+ IMetaModelCommon *pCommonImport, // [IN] Module import scope.
+ mdTypeRef trImport, // [IN] Imported TypeRef.
+ mdToken *ptkType) // [OUT] Output token for the imported type in the emit scope.
+{
+ CQuickArray<mdTypeDef> cqaNesters;
+ CQuickArray<LPCUTF8> cqaNesterNames;
+ CQuickArray<LPCUTF8> cqaNesterNamespaces;
+ LPCUTF8 szScopeNameEmit;
+ GUID nullguid = GUID_NULL;
+ GUID MvidAssemImport = nullguid;
+ GUID MvidAssemEmit = nullguid;
+ GUID MvidImport = nullguid;
+ GUID MvidEmit = nullguid;
+ mdToken tkOuterImportRes; // ResolutionScope for the outermost TypeRef in import scope.
+ mdToken tkOuterEmitRes = mdTokenNil; // ResolutionScope for outermost TypeRef in emit scope.
+ HRESULT hr = S_OK;
+ bool bAssemblyRefFromAssemScope = false;
+
+ _ASSERTE(pMiniMdEmit && pCommonImport && ptkType);
+ _ASSERTE(TypeFromToken(trImport) == mdtTypeRef);
+
+ // Get MVIDs for import and emit, assembly and module scopes.
+ if (pCommonAssemImport != NULL)
+ {
+ IfFailGo(pCommonAssemImport->CommonGetScopeProps(0, &MvidAssemImport));
+ }
+ IfFailGo(pCommonImport->CommonGetScopeProps(0, &MvidImport));
+ if (pMiniMdAssemEmit != NULL)
+ {
+ IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdAssemEmit)->CommonGetScopeProps(
+ 0,
+ &MvidAssemEmit));
+ }
+ IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdEmit)->CommonGetScopeProps(
+ &szScopeNameEmit,
+ &MvidEmit));
+
+ // Get the outermost resolution scope for the TypeRef being imported.
+ IfFailGo(GetNesterHierarchy(pCommonImport,
+ trImport,
+ cqaNesters,
+ cqaNesterNamespaces,
+ cqaNesterNames));
+ IfFailGo(pCommonImport->CommonGetTypeRefProps(
+ cqaNesters[cqaNesters.Size() - 1],
+ 0,
+ 0,
+ &tkOuterImportRes));
+
+ // Compute the ResolutionScope for the imported type.
+ if (MvidAssemImport == MvidAssemEmit && MvidImport == MvidEmit)
+ {
+ *ptkType = trImport;
+ goto ErrExit;
+ }
+ else if (MvidAssemImport == MvidAssemEmit && MvidImport != MvidEmit)
+ {
+ // The TypeRef is in the same Assembly but a different module.
+
+ if (IsNilToken(tkOuterImportRes))
+ {
+ tkOuterEmitRes = tkOuterImportRes;
+ }
+ else if (TypeFromToken(tkOuterImportRes) == mdtModule)
+ {
+ // TypeRef resolved to the import module in which its defined.
+
+ //
+ if (pMiniMdAssemEmit == NULL && pCommonAssemImport == NULL)
+ {
+ tkOuterEmitRes = TokenFromRid(1, mdtModule);
+ }
+ else
+ {
+ // Create a ModuleRef corresponding to the import scope.
+ IfFailGo(CreateModuleRefFromScope(pMiniMdEmit,
+ pCommonImport,
+ &tkOuterEmitRes));
+ }
+ }
+ else if (TypeFromToken(tkOuterImportRes) == mdtAssemblyRef)
+ {
+ // TypeRef is from a different Assembly.
+
+ // Create a corresponding AssemblyRef in the emit scope.
+ IfFailGo(CreateAssemblyRefFromAssemblyRef(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonImport,
+ tkOuterImportRes,
+ &tkOuterEmitRes));
+ }
+ else if (TypeFromToken(tkOuterImportRes) == mdtModuleRef)
+ {
+ // Get Name of the ModuleRef.
+ LPCUTF8 szMRName;
+ IfFailGo(pCommonImport->CommonGetModuleRefProps(tkOuterImportRes, &szMRName));
+
+ if (!strcmp(szMRName, szScopeNameEmit))
+ {
+ // ModuleRef from import scope resolves to the emit scope.
+ tkOuterEmitRes = TokenFromRid(1, mdtModule);
+ }
+ else
+ {
+ // ModuleRef does not correspond to the emit scope.
+ // Create a corresponding ModuleRef.
+ IfFailGo(CreateModuleRefFromModuleRef(pMiniMdEmit,
+ pCommonImport,
+ tkOuterImportRes,
+ &tkOuterEmitRes));
+ }
+ }
+ }
+ else if (MvidAssemImport != MvidAssemEmit)
+ {
+ // The TypeDef is from a different Assembly.
+
+ // Import and Emit scopes can't be identical and be from different
+ // Assemblies at the same time.
+ _ASSERTE(MvidImport != MvidEmit &&
+ "Import scope can't be identical to the Emit scope and be from a different Assembly at the same time.");
+
+ mdToken tkImplementation; // Implementation token for ExportedType.
+ if (IsNilToken(tkOuterImportRes))
+ {
+ // <REVISIT_TODO>BUG FIX:: URT 13626
+ // Well, before all of the clients generate AR for mscorlib.dll reference, it is not true
+ // that tkOuterImportRes == nil will imply that we have to find such an entry in the import manifest!!</REVISIT_TODO>
+
+ // Look for a ExportedType entry in the import Assembly. Its an error
+ // if we don't find a ExportedType entry.
+ mdExportedType tkExportedType;
+ hr = pCommonAssemImport->CommonFindExportedType(
+ cqaNesterNamespaces[cqaNesters.Size() - 1],
+ cqaNesterNames[cqaNesters.Size() - 1],
+ mdTokenNil,
+ &tkExportedType);
+ if (SUCCEEDED(hr))
+ {
+ IfFailGo(pCommonAssemImport->CommonGetExportedTypeProps(
+ tkExportedType,
+ NULL,
+ NULL,
+ &tkImplementation));
+ if (TypeFromToken(tkImplementation) == mdtFile)
+ {
+ // Type is from a different Assembly.
+ IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ pbHashValue,
+ cbHashValue,
+ &tkOuterEmitRes));
+ }
+ else if (TypeFromToken(tkImplementation) == mdtAssemblyRef)
+ {
+ // This folds into the case where the Type is AssemblyRef. So
+ // let it fall through to that case.
+
+ // Remember that this AssemblyRef token is actually from the Manifest scope not
+ // the module scope!!!
+ bAssemblyRefFromAssemScope = true;
+ tkOuterImportRes = tkImplementation;
+ }
+ else
+ _ASSERTE(!"Unexpected ExportedType implementation token.");
+ }
+ else
+ {
+ // In this case, we will just move over the TypeRef with Nil ResolutionScope.
+ hr = NOERROR;
+ tkOuterEmitRes = mdTokenNil;
+ }
+ }
+ else if (TypeFromToken(tkOuterImportRes) == mdtModule)
+ {
+ // Type is from a different Assembly.
+ IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ pbHashValue,
+ cbHashValue,
+ &tkOuterEmitRes));
+ }
+ // Not else if, because mdtModule case above could change
+ // tkOuterImportRes to an AssemblyRef.
+ if (TypeFromToken(tkOuterImportRes) == mdtAssemblyRef)
+ {
+ // If there is an emit assembly, see if the import assembly ref points to
+ // it. If there is no emit assembly, the import assembly, by definition,
+ // does not point to this one.
+ if (pMiniMdAssemEmit == NULL || !pMiniMdAssemEmit->getCountAssemblys())
+ hr = S_FALSE;
+ else
+ {
+ if (bAssemblyRefFromAssemScope)
+ {
+ // Check to see if the AssemblyRef resolves to the emit assembly.
+ IfFailGo(CompareAssemblyRefToAssembly(pCommonAssemImport,
+ tkOuterImportRes,
+ static_cast<IMetaModelCommon*>(pMiniMdAssemEmit)));
+
+ }
+ else
+ {
+ // Check to see if the AssemblyRef resolves to the emit assembly.
+ IfFailGo(CompareAssemblyRefToAssembly(pCommonImport,
+ tkOuterImportRes,
+ static_cast<IMetaModelCommon*>(pMiniMdAssemEmit)));
+ }
+ }
+ if (hr == S_OK)
+ {
+ // The TypeRef being imported is defined in the current Assembly.
+
+ // Find the ExportedType for the outermost TypeRef in the Emit assembly.
+ mdExportedType tkExportedType;
+
+ hr = FindExportedType(pMiniMdAssemEmit,
+ cqaNesterNamespaces[cqaNesters.Size() - 1],
+ cqaNesterNames[cqaNesters.Size() - 1],
+ mdTokenNil, // Enclosing ExportedType.
+ &tkExportedType);
+ if (hr == S_OK)
+ {
+ // Create a ModuleRef based on the File name for the ExportedType.
+ // If the ModuleRef corresponds to pMiniMdEmit, the function
+ // will return S_FALSE, in which case set tkOuterEmitRes to
+ // the Module token.
+ hr = CreateModuleRefFromExportedType(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ tkExportedType,
+ &tkOuterEmitRes);
+ if (hr == S_FALSE)
+ tkOuterEmitRes = TokenFromRid(1, mdtModule);
+ else
+ IfFailGo(hr);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ // Find the Type in the Assembly emit scope to cover the
+ // case where ExportedTypes may be implicitly defined. Its an
+ // error if we can't find the Type at this point.
+ IfFailGo(FindTypeDefByName(pMiniMdAssemEmit,
+ cqaNesterNamespaces[cqaNesters.Size() - 1],
+ cqaNesterNames[cqaNesters.Size() - 1],
+ mdTokenNil, // Enclosing Type.
+ &tkOuterEmitRes));
+ tkOuterEmitRes = TokenFromRid(1, mdtModule);
+ }
+ else
+ {
+ _ASSERTE(FAILED(hr));
+ IfFailGo(hr);
+ }
+ }
+ else if (hr == S_FALSE)
+ {
+ // The TypeRef being imported is from a different Assembly.
+
+ if (bAssemblyRefFromAssemScope)
+ {
+ // Create a corresponding AssemblyRef.
+ IfFailGo(CreateAssemblyRefFromAssemblyRef(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ tkOuterImportRes,
+ &tkOuterEmitRes));
+ }
+ else
+ {
+ // Create a corresponding AssemblyRef.
+ IfFailGo(CreateAssemblyRefFromAssemblyRef(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonImport,
+ tkOuterImportRes,
+ &tkOuterEmitRes));
+ }
+ }
+ else
+ {
+ _ASSERTE(FAILED(hr));
+ IfFailGo(hr);
+ }
+ }
+ else if (TypeFromToken(tkOuterImportRes) == mdtModuleRef)
+ {
+ // Type is from a different Assembly.
+ IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit,
+ pMiniMdEmit,
+ pCommonAssemImport,
+ pbHashValue,
+ cbHashValue,
+ &tkOuterEmitRes));
+ }
+ }
+
+ // Try to find the TypeDef in the emit scope. If we cannot find the
+ // typedef, we need to introduce a typeref.
+
+ // See if the Nested TypeDef is present in the Emit scope.
+ hr = CLDB_E_RECORD_NOTFOUND;
+ if (TypeFromToken(tkOuterEmitRes) == mdtModule && !IsNilToken(tkOuterEmitRes))
+ {
+ hr = FindNestedTypeDef(pMiniMdEmit,
+ cqaNesterNamespaces,
+ cqaNesterNames,
+ mdTokenNil,
+ ptkType);
+
+ // <REVISIT_TODO>cannot assert now!! Due to the IJW workaround!
+ // _ASSERTE(SUCCEEDED(hr));</REVISIT_TODO>
+ }
+
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailGo(CreateNesterHierarchy(pMiniMdEmit,
+ cqaNesterNamespaces,
+ cqaNesterNames,
+ tkOuterEmitRes,
+ ptkType));
+ }
+ else
+ IfFailGo(hr);
+ErrExit:
+ return hr;
+} // ImportHelper::ImportTypeRef
+
+//******************************************************************************
+// Given import scope, create a corresponding ModuleRef.
+//******************************************************************************
+HRESULT ImportHelper::CreateModuleRefFromScope( // S_OK or error.
+ CMiniMdRW *pMiniMdEmit, // [IN] Emit scope in which the ModuleRef is to be created.
+ IMetaModelCommon *pCommonImport, // [IN] Import scope.
+ mdModuleRef *ptkModuleRef) // [OUT] Output token for ModuleRef.
+{
+ HRESULT hr = S_OK;
+ LPCSTR szName;
+ ModuleRefRec *pRecordEmit;
+ RID iRecordEmit;
+
+ // Set output to nil.
+ *ptkModuleRef = mdTokenNil;
+
+ // Get name of import scope.
+ IfFailGo(pCommonImport->CommonGetScopeProps(&szName, 0));
+
+ // See if the ModuleRef exists in the Emit scope.
+ hr = FindModuleRef(pMiniMdEmit, szName, ptkModuleRef);
+
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ if (szName[0] == '\0')
+ {
+ // It the referenced Module does not have a proper name, use the nil token instead.
+ LOG((LOGMD, "WARNING!!! MD ImportHelper::CreatemoduleRefFromScope but scope does not have a proper name!!!!"));
+
+ // clear the error
+ hr = NOERROR;
+
+ // It is a bug to create an ModuleRef to an empty name!!!
+ *ptkModuleRef = mdTokenNil;
+ }
+ else
+ {
+ // Create ModuleRef record and set the output parameter.
+ IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecordEmit, &iRecordEmit));
+ *ptkModuleRef = TokenFromRid(iRecordEmit, mdtModuleRef);
+ IfFailGo(pMiniMdEmit->UpdateENCLog(*ptkModuleRef));
+
+ // It is a bug to create an ModuleRef to mscorlib.dll
+ _ASSERTE(strcmp(szName, COM_RUNTIME_LIBRARY) != 0);
+
+ // Set the name of ModuleRef.
+ IfFailGo(pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name,
+ pRecordEmit, szName));
+ }
+ }
+ else
+ IfFailGo(hr);
+ErrExit:
+ return hr;
+} // ImportHelper::CreateModuleRefFromScope
+
+
+//******************************************************************************
+// Given an import scope and a ModuleRef, create a corresponding ModuleRef in
+// the given emit scope.
+//******************************************************************************
+HRESULT ImportHelper::CreateModuleRefFromModuleRef( // S_OK or error.
+ CMiniMdRW *pMiniMdEmit, // [IN] Emit scope.
+ IMetaModelCommon *pCommon, // [IN] Import scope.
+ mdModuleRef tkModuleRef, // [IN] ModuleRef token.
+ mdModuleRef *ptkModuleRef) // [OUT] ModuleRef token in the emit scope.
+{
+ HRESULT hr = S_OK;
+ LPCSTR szName;
+ ModuleRefRec *pRecord;
+ RID iRecord;
+
+ // Set output to Nil.
+ *ptkModuleRef = mdTokenNil;
+
+ // Get name of the ModuleRef being imported.
+ IfFailGo(pCommon->CommonGetModuleRefProps(tkModuleRef, &szName));
+
+ // See if the ModuleRef exist in the Emit scope.
+ hr = FindModuleRef(pMiniMdEmit, szName, ptkModuleRef);
+
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ // Create ModuleRef record and set the output parameter.
+ IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecord, &iRecord));
+ *ptkModuleRef = TokenFromRid(iRecord, mdtModuleRef);
+ IfFailGo(pMiniMdEmit->UpdateENCLog(*ptkModuleRef));
+
+ // Set the name of ModuleRef.
+ IfFailGo(pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name,
+ pRecord, szName));
+ }
+ else
+ {
+ IfFailGo(hr);
+ }
+ErrExit:
+ return hr;
+} // ImportHelper::CreateModuleRefFromModuleRef
+
+
+//******************************************************************************
+// Given a ExportedType and the Assembly emit scope, create a corresponding ModuleRef
+// in the give emit scope. The ExportedType being passed in must belong to the
+// Assembly passed in. Function returns S_FALSE if the ExportedType is implemented
+// by the emit scope passed in.
+//******************************************************************************
+HRESULT ImportHelper::CreateModuleRefFromExportedType( // S_OK or error.
+ CMiniMdRW *pAssemEmit, // [IN] Import assembly scope.
+ CMiniMdRW *pMiniMdEmit, // [IN] Emit scope.
+ mdExportedType tkExportedType, // [IN] ExportedType token in Assembly emit scope.
+ mdModuleRef *ptkModuleRef) // [OUT] ModuleRef token in the emit scope.
+{
+ mdFile tkFile;
+ LPCUTF8 szFile;
+ LPCUTF8 szScope;
+ FileRec *pFileRec;
+ HRESULT hr = S_OK;
+
+ // Set output to nil.
+ *ptkModuleRef = mdTokenNil;
+
+ // Get the implementation token for the ExportedType. It must be a File token
+ // since the caller should call this function only on ExportedTypes that resolve
+ // to the same Assembly.
+ IfFailGo(static_cast<IMetaModelCommon*>(pAssemEmit)->CommonGetExportedTypeProps(
+ tkExportedType,
+ NULL,
+ NULL,
+ &tkFile));
+ _ASSERTE(TypeFromToken(tkFile) == mdtFile);
+
+ // Get the name of the file.
+ IfFailGo(pAssemEmit->GetFileRecord(RidFromToken(tkFile), &pFileRec));
+ IfFailGo(pAssemEmit->getNameOfFile(pFileRec, &szFile));
+
+ // Get the name of the emit scope.
+ IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdEmit)->CommonGetScopeProps(
+ &szScope,
+ 0));
+
+ // If the file corresponds to the emit scope, return S_FALSE;
+ if (!strcmp(szFile, szScope))
+ return S_FALSE;
+
+ // See if a ModuleRef exists with this name.
+ hr = FindModuleRef(pMiniMdEmit, szFile, ptkModuleRef);
+
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ // Create ModuleRef record and set the output parameter.
+
+ ModuleRefRec *pRecord;
+ RID iRecord;
+
+ IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecord, &iRecord));
+ *ptkModuleRef = TokenFromRid(iRecord, mdtModuleRef);
+ IfFailGo(pMiniMdEmit->UpdateENCLog(*ptkModuleRef));
+
+ // Set the name of ModuleRef.
+ IfFailGo(pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name,
+ pRecord, szFile));
+ }
+ else
+ IfFailGo(hr);
+ErrExit:
+ return hr;
+} // ImportHelper::CreateModuleRefFromExportedType
+
+//******************************************************************************
+// Given an AssemblyRef and the corresponding scope, create an AssemblyRef in
+// the given Module scope and Assembly scope.
+//******************************************************************************
+HRESULT ImportHelper::CreateAssemblyRefFromAssemblyRef(
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope.
+ CMiniMdRW *pMiniMdModuleEmit, // [IN] Module emit scope
+ IMetaModelCommon *pCommonImport, // [IN] Scope to import the assembly ref from.
+ mdAssemblyRef tkAssemRef, // [IN] Assembly ref to be imported.
+ mdAssemblyRef *ptkAssemblyRef) // [OUT] AssemblyRef in the emit scope.
+{
+ AssemblyRefRec *pRecordEmit;
+ CMiniMdRW *rMiniMdRW[2];
+ CMiniMdRW *pMiniMdEmit;
+ RID iRecordEmit;
+ USHORT usMajorVersion;
+ USHORT usMinorVersion;
+ USHORT usBuildNumber;
+ USHORT usRevisionNumber;
+ DWORD dwFlags;
+ const void *pbPublicKeyOrToken;
+ ULONG cbPublicKeyOrToken;
+ LPCUTF8 szName;
+ LPCUTF8 szLocale;
+ const void *pbHashValue;
+ ULONG cbHashValue;
+ HRESULT hr = S_OK;
+
+ // Set output to Nil.
+ *ptkAssemblyRef = mdTokenNil;
+
+ // Get import AssemblyRef props.
+ IfFailGo(pCommonImport->CommonGetAssemblyRefProps(
+ tkAssemRef,
+ &usMajorVersion, &usMinorVersion, &usBuildNumber, &usRevisionNumber,
+ &dwFlags, &pbPublicKeyOrToken, &cbPublicKeyOrToken,
+ &szName, &szLocale,
+ &pbHashValue, &cbHashValue));
+
+ // Create the AssemblyRef in both the Assembly and Module emit scopes.
+ rMiniMdRW[0] = pMiniMdAssemEmit;
+ rMiniMdRW[1] = pMiniMdModuleEmit;
+
+ for (ULONG i = 0; i < 2; i++)
+ {
+ pMiniMdEmit = rMiniMdRW[i];
+
+ if (!pMiniMdEmit)
+ continue;
+
+ // See if the AssemblyRef already exists in the emit scope.
+ hr = FindAssemblyRef(pMiniMdEmit, szName, szLocale, pbPublicKeyOrToken,
+ cbPublicKeyOrToken, usMajorVersion, usMinorVersion,
+ usBuildNumber, usRevisionNumber, dwFlags, &tkAssemRef);
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ // Create the AssemblyRef record and set the output parameter.
+ IfFailGo(pMiniMdEmit->AddAssemblyRefRecord(&pRecordEmit, &iRecordEmit));
+ tkAssemRef = TokenFromRid(iRecordEmit, mdtAssemblyRef);
+ IfFailGo(pMiniMdEmit->UpdateENCLog(tkAssemRef));
+
+ // Set parameters derived from the import Assembly.
+ pRecordEmit->SetMajorVersion(usMajorVersion);
+ pRecordEmit->SetMinorVersion(usMinorVersion);
+ pRecordEmit->SetBuildNumber(usBuildNumber);
+ pRecordEmit->SetRevisionNumber(usRevisionNumber);
+ pRecordEmit->SetFlags(dwFlags);
+
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken,
+ pRecordEmit, pbPublicKeyOrToken, cbPublicKeyOrToken));
+ IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Name,
+ pRecordEmit, szName));
+ IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Locale,
+ pRecordEmit, szLocale));
+
+ // Set the parameters passed in for the AssemblyRef.
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue,
+ pRecordEmit, pbHashValue, cbHashValue));
+ }
+ else
+ IfFailGo(hr);
+
+ // Set the output parameter for the AssemblyRef emitted in Module emit scope.
+ if (i)
+ *ptkAssemblyRef = tkAssemRef;
+ }
+ErrExit:
+ return hr;
+} // ImportHelper::CreateAssemblyRefFromAssemblyRef
+
+//******************************************************************************
+// Given the Assembly Import scope, hash value and execution location, create
+// a corresponding AssemblyRef in the given assembly and module emit scope.
+// Set the output parameter to the AssemblyRef token emitted in the module emit
+// scope.
+//******************************************************************************
+HRESULT
+ImportHelper::CreateAssemblyRefFromAssembly(
+ CMiniMdRW * pMiniMdAssemEmit, // [IN] Emit assembly scope.
+ CMiniMdRW * pMiniMdModuleEmit, // [IN] Emit module scope.
+ IMetaModelCommon * pCommonAssemImport, // [IN] Assembly import scope.
+ const void * pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ mdAssemblyRef * ptkAssemblyRef) // [OUT] AssemblyRef token.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ AssemblyRefRec *pRecordEmit;
+ CMiniMdRW *rMiniMdRW[2];
+ CMiniMdRW *pMiniMdEmit;
+ RID iRecordEmit;
+ USHORT usMajorVersion;
+ USHORT usMinorVersion;
+ USHORT usBuildNumber;
+ USHORT usRevisionNumber;
+ DWORD dwFlags;
+ const void *pbPublicKey;
+ ULONG cbPublicKey;
+ LPCUTF8 szName;
+ LPCUTF8 szLocale;
+ mdAssemblyRef tkAssemRef;
+ HRESULT hr = S_OK;
+ const void *pbToken = NULL;
+ ULONG cbToken = 0;
+ ULONG i;
+
+ // Set output to Nil.
+ *ptkAssemblyRef = mdTokenNil;
+
+ // Get the Assembly props.
+ IfFailGo(pCommonAssemImport->CommonGetAssemblyProps(
+ &usMajorVersion, &usMinorVersion, &usBuildNumber, &usRevisionNumber,
+ &dwFlags, &pbPublicKey, &cbPublicKey,
+ &szName, &szLocale));
+
+ // Compress the public key into a token.
+ if ((pbPublicKey != NULL) && (cbPublicKey != 0))
+ {
+ _ASSERTE(IsAfPublicKey(dwFlags));
+ dwFlags &= ~afPublicKey;
+ if (!StrongNameTokenFromPublicKey((BYTE*)pbPublicKey,
+ cbPublicKey,
+ (BYTE**)&pbToken,
+ &cbToken))
+ IfFailGo(StrongNameErrorInfo());
+ }
+ else
+ _ASSERTE(!IsAfPublicKey(dwFlags));
+
+ // Create the AssemblyRef in both the Assembly and Module emit scopes.
+ rMiniMdRW[0] = pMiniMdAssemEmit;
+ rMiniMdRW[1] = pMiniMdModuleEmit;
+
+ for (i = 0; i < 2; i++)
+ {
+ pMiniMdEmit = rMiniMdRW[i];
+
+ if (!pMiniMdEmit)
+ continue;
+
+ // See if the AssemblyRef already exists in the emit scope.
+ hr = FindAssemblyRef(pMiniMdEmit, szName, szLocale, pbToken,
+ cbToken, usMajorVersion, usMinorVersion,
+ usBuildNumber, usRevisionNumber, dwFlags,
+ &tkAssemRef);
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ // Create the AssemblyRef record and set the output parameter.
+ IfFailGo(pMiniMdEmit->AddAssemblyRefRecord(&pRecordEmit, &iRecordEmit));
+ tkAssemRef = TokenFromRid(iRecordEmit, mdtAssemblyRef);
+ IfFailGo(pMiniMdEmit->UpdateENCLog(tkAssemRef));
+
+ // Set parameters derived from the import Assembly.
+ pRecordEmit->SetMajorVersion(usMajorVersion);
+ pRecordEmit->SetMinorVersion(usMinorVersion);
+ pRecordEmit->SetBuildNumber(usBuildNumber);
+ pRecordEmit->SetRevisionNumber(usRevisionNumber);
+ pRecordEmit->SetFlags(dwFlags);
+
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken,
+ pRecordEmit, pbToken, cbToken));
+ IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Name,
+ pRecordEmit, szName));
+ IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Locale,
+ pRecordEmit, szLocale));
+
+ // Set the parameters passed in for the AssemblyRef.
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue,
+ pRecordEmit, pbHashValue, cbHashValue));
+ }
+ else
+ IfFailGo(hr);
+
+ // Set the output parameter for the AssemblyRef emitted in Module emit scope.
+ if (i)
+ *ptkAssemblyRef = tkAssemRef;
+ }
+ErrExit:
+ if (pbToken)
+ StrongNameFreeBuffer((BYTE*)pbToken);
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // ImportHelper::CreateAssemblyRefFromAssembly
+
+//******************************************************************************
+// Given an AssemblyRef and the corresponding scope, compare it to see if it
+// refers to the given Assembly.
+//******************************************************************************
+HRESULT ImportHelper::CompareAssemblyRefToAssembly( // S_OK, S_FALSE or error.
+ IMetaModelCommon *pCommonAssem1, // [IN] Scope that defines the AssemblyRef.
+ mdAssemblyRef tkAssemRef, // [IN] AssemblyRef.
+ IMetaModelCommon *pCommonAssem2) // [IN] Assembly against which the Ref is compared.
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ HRESULT hr;
+
+ USHORT usMajorVersion1;
+ USHORT usMinorVersion1;
+ USHORT usBuildNumber1;
+ USHORT usRevisionNumber1;
+ const void *pbPublicKeyOrToken1;
+ ULONG cbPublicKeyOrToken1;
+ LPCUTF8 szName1;
+ LPCUTF8 szLocale1;
+ DWORD dwFlags1;
+
+ USHORT usMajorVersion2;
+ USHORT usMinorVersion2;
+ USHORT usBuildNumber2;
+ USHORT usRevisionNumber2;
+ const void *pbPublicKey2;
+ ULONG cbPublicKey2;
+ LPCUTF8 szName2;
+ LPCUTF8 szLocale2;
+ const void *pbToken = NULL;
+ ULONG cbToken = 0;
+ bool fMatch;
+
+ // Get the AssemblyRef props.
+ IfFailRet(pCommonAssem1->CommonGetAssemblyRefProps(
+ tkAssemRef,
+ &usMajorVersion1, &usMinorVersion1, &usBuildNumber1, &usRevisionNumber1,
+ &dwFlags1, &pbPublicKeyOrToken1, &cbPublicKeyOrToken1,
+ &szName1, &szLocale1,
+ NULL, NULL));
+ // Get the Assembly props.
+ IfFailRet(pCommonAssem2->CommonGetAssemblyProps(
+ &usMajorVersion2, &usMinorVersion2, &usBuildNumber2, &usRevisionNumber2,
+ 0, &pbPublicKey2, &cbPublicKey2,
+ &szName2, &szLocale2));
+
+ // Compare.
+ if (usMajorVersion1 != usMajorVersion2 ||
+ usMinorVersion1 != usMinorVersion2 ||
+ usBuildNumber1 != usBuildNumber2 ||
+ usRevisionNumber1 != usRevisionNumber2 ||
+ strcmp(szName1, szName2) ||
+ strcmp(szLocale1, szLocale2))
+ {
+ return S_FALSE;
+ }
+
+ // Defs always contain a full public key (or no key at all). Refs may have
+ // no key, a full public key or a tokenized key.
+ if ((cbPublicKeyOrToken1 && !cbPublicKey2) ||
+ (!cbPublicKeyOrToken1 && cbPublicKey2))
+ return S_FALSE;
+
+ if (cbPublicKeyOrToken1)
+ {
+ // If ref contains a full public key we can just directly compare.
+ if (IsAfPublicKey(dwFlags1) &&
+ (cbPublicKeyOrToken1 != cbPublicKey2 ||
+ memcmp(pbPublicKeyOrToken1, pbPublicKey2, cbPublicKeyOrToken1)))
+ return S_FALSE;
+
+ // Otherwise we need to compress the def public key into a token.
+ if (!StrongNameTokenFromPublicKey((BYTE*)pbPublicKey2,
+ cbPublicKey2,
+ (BYTE**)&pbToken,
+ &cbToken))
+ return StrongNameErrorInfo();
+
+ fMatch = cbPublicKeyOrToken1 == cbToken &&
+ !memcmp(pbPublicKeyOrToken1, pbToken, cbPublicKeyOrToken1);
+
+ StrongNameFreeBuffer((BYTE*)pbToken);
+
+ if (!fMatch)
+ return S_FALSE;
+ }
+
+ return S_OK;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // ImportHelper::CompareAssemblyRefToAssembly
+
+#endif //FEATURE_METADATA_EMIT
diff --git a/src/md/compiler/importhelper.h b/src/md/compiler/importhelper.h
new file mode 100644
index 0000000000..ea152f53ef
--- /dev/null
+++ b/src/md/compiler/importhelper.h
@@ -0,0 +1,368 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// ImportHelper.h
+//
+
+//
+// contains utility code to MD directory
+//
+//*****************************************************************************
+#ifndef __IMPORTHELPER__h__
+#define __IMPORTHELPER__h__
+
+class CMiniMdRW;
+class MDTOKENMAP;
+
+//*********************************************************************
+// Class to handle merge
+//*********************************************************************
+class ImportHelper
+{
+public:
+ // Options for code:FindMemberRef.
+ enum HashSearchOption
+ {
+ DoNotCreateHash, // Do not create hash if it does not exist (faster for isolated calls)
+ CreateHash // Create hash if it does not exist (faster for multiple calls)
+ };
+
+
+ static HRESULT FindMethodSpecByMethodAndInstantiation(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ /*mdMethodDefOrRef*/ mdToken tkMethod, // [IN] MethodSpec method field
+ PCCOR_SIGNATURE pInstantiation, // [IN] MethodSpec instantiation (a signature)
+ ULONG cbInstantiation, // [IN] Size of instantiation.
+ mdMethodSpec *pMethodSpec, // [OUT] Put the MethodSpec token here.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+
+ static HRESULT FindGenericParamConstraintByOwnerAndConstraint(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdGenericParam tkOwner, // [IN] GenericParamConstraint Owner
+ mdToken tkConstraint, // [IN] GenericParamConstraint Constraint
+ mdGenericParamConstraint *pGenericParamConstraint, // [OUT] Put the GenericParamConstraint token here.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+
+ static HRESULT FindGenericParamByOwner(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkOwner, // [IN] GenericParam Owner
+ LPCUTF8 szUTF8Name, // [IN] GeneriParam Name, may be NULL if not used for search
+ ULONG *pNumber, // [IN] GeneriParam Number, may be NULL if not used for search
+ mdGenericParam *pGenericParam, // [OUT] Put the GenericParam token here.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT FindMethod(
+ CMiniMdRW * pMiniMd, // [IN] the minimd to lookup
+ mdTypeDef td, // [IN] parent.
+ LPCUTF8 szName, // [IN] MethodDef name.
+ PCCOR_SIGNATURE pSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdMethodDef * pmb, // [OUT] Put the MethodDef token here.
+ RID rid = 0, // [IN] Optional rid to be ignored.
+ PSIGCOMPARE pSignatureCompare = NULL, // [IN] Optional Routine to compare signatures
+ void * pCompareContext = NULL); // [IN] Optional context for the compare function
+
+ static HRESULT FindField(
+ CMiniMdRW * pMiniMd, // [IN] the minimd to lookup
+ mdTypeDef td, // [IN] parent.
+ LPCUTF8 szName, // [IN] FieldDef name.
+ PCCOR_SIGNATURE pSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdFieldDef * pfd, // [OUT] Put the FieldDef token here.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT FindMember(
+ CMiniMdRW * pMiniMd, // [IN] the minimd to lookup
+ mdTypeDef td, // [IN] parent.
+ LPCUTF8 szName, // [IN] Member name.
+ PCCOR_SIGNATURE pSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdToken * ptk); // [OUT] Put the token here.
+
+ static HRESULT FindMemberRef(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkParent, // [IN] the parent token
+ LPCUTF8 szName, // [IN] memberref name
+ const COR_SIGNATURE *pSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdMemberRef *pmr, // [OUT] Put the MemberRef token found
+ RID rid = 0, // [IN] Optional rid to be ignored.
+ HashSearchOption fCreateHash = DoNotCreateHash); // [IN] Should we create hash first? (Optimize for multiple calls vs. single isolated call)
+
+ static HRESULT FindStandAloneSig(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ const COR_SIGNATURE *pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdSignature *psa); // [OUT] Put the StandAloneSig token found
+
+ static HRESULT FindTypeSpec(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ const COR_SIGNATURE *pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdTypeSpec *ptypespec); // [OUT] Put the TypeSpec token found
+
+ static HRESULT FindMethodImpl(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdTypeDef tkClass, // [IN] The parent TypeDef token.
+ mdToken tkBody, // [IN] Method body token.
+ mdToken tkDecl, // [IN] Method declaration token.
+ RID *pRid); // [OUT] Put the MethodImpl rid here
+
+ static HRESULT FindCustomAttributeCtorByName(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ LPCUTF8 szAssemblyName, // [IN] Assembly Name.
+ LPCUTF8 szNamespace, // [IN] TypeRef Namespace.
+ LPCUTF8 szName, // [IN] TypeRef Name.
+ mdTypeDef *ptk, // [OUT] Put the TypeRef token here.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT FindTypeRefByName(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkResolutionScope, // [IN] ResolutionScope, mdAssemblyRef or mdModuleRef.
+ LPCUTF8 szNamespace, // [IN] TypeRef Namespace.
+ LPCUTF8 szName, // [IN] TypeRef Name.
+ mdTypeDef *ptk, // [OUT] Put the TypeRef token here.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT FindModuleRef(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ LPCUTF8 szUTF8Name, // [IN] ModuleRef name.
+ mdModuleRef *pmur, // [OUT] Put the ModuleRef token here.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT FindTypeDefByName(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ LPCUTF8 szNamespace, // [IN] Namespace of the TypeDef.
+ LPCUTF8 szName, // [IN] Name of the TypeDef.
+ mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef enclosing class.
+ mdTypeDef *ptk, // [OUT] Put the TypeDef token here.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT FindInterfaceImpl(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkClass, // [IN] TypeDef of the type
+ mdToken tkInterface, // [IN] could be typedef/typeref
+ mdInterfaceImpl *ptk, // [OUT] Put the interface token here.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT FindPermission(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkParent, // [IN] Token with the Permission
+ USHORT usAction, // [IN] The action of the permission
+ mdPermission *ppm); // [OUT] Put permission token here
+
+ static HRESULT FindProperty(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkTypeDef, // [IN] typedef token
+ LPCUTF8 szName, // [IN] name of the property
+ const COR_SIGNATURE *pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size of signature.
+ mdProperty *ppr); // [OUT] Property token
+
+ static HRESULT FindEvent(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkTypeDef, // [IN] typedef token
+ LPCUTF8 szName, // [IN] name of the event
+ mdProperty *pev); // [OUT] Event token
+
+ static HRESULT FindCustomAttributeByToken(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkParent, // [IN] the parent that custom value is associated with
+ mdToken tkType, // [IN] type of the CustomAttribute
+ const void *pCustBlob, // [IN] custom value blob
+ ULONG cbCustBlob, // [IN] size of the blob.
+ mdCustomAttribute *pcv); // [OUT] CustomAttribute token
+
+ static HRESULT GetCustomAttributeByName(// S_OK or error.
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData); // [OUT] Put size of data here.
+
+ static HRESULT GetCustomAttributeByName(// S_OK or error.
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ mdCustomAttribute pca); // [OUT] found CA token
+
+ static HRESULT MergeUpdateTokenInFieldSig(
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope.
+ CMiniMdRW *pMiniMdEmit, // [IN] The emit scope.
+ IMetaModelCommon *pCommonAssemImport, // [IN] Assembly scope where the signature is from.
+ const void *pbHashValue, // [IN] Hash value for the import assembly.
+ ULONG cbHashValue, // [IN] Size in bytes for the hash value.
+ IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope.
+ PCCOR_SIGNATURE pbSigImp, // [IN] signature from the imported scope
+ MDTOKENMAP *ptkMap, // [IN] Internal OID mapping structure.
+ CQuickBytes *pqkSigEmit, // [OUT] buffer for translated signature
+ ULONG cbStartEmit, // [IN] start point of buffer to write to
+ ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp
+ ULONG *pcbEmit); // [OUT] total number of bytes write to pqkSigEmit
+
+ static HRESULT MergeUpdateTokenInSig( // S_OK or error.
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope.
+ CMiniMdRW *pMiniMdEmit, // [IN] The emit scope.
+ IMetaModelCommon *pCommonAssemImport, // [IN] Assembly scope where the signature is from.
+ const void *pbHashValue, // [IN] Hash value for the import assembly.
+ ULONG cbHashValue, // [IN] Size in bytes for the hash value.
+ IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope.
+ PCCOR_SIGNATURE pbSigImp, // [IN] signature from the imported scope
+ MDTOKENMAP *ptkMap, // [IN] Internal OID mapping structure.
+ CQuickBytes *pqkSigEmit, // [OUT] translated signature
+ ULONG cbStartEmit, // [IN] start point of buffer to write to
+ ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp
+ ULONG *pcbEmit); // [OUT] total number of bytes write to pqkSigEmit
+
+ // This is implemented in a satellite lib because it is only used in emit and depends on
+ // strong name support in mscorwks.dll.
+ static HRESULT FindAssemblyRef(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup.
+ LPCUTF8 szName, // [IN] Name.
+ LPCUTF8 szLocale, // [IN] Locale.
+ const void *pbPublicKeyOrToken, // [IN] Public key or token (based on flags).
+ ULONG cbPublicKeyOrToken, // [IN] Byte count of public key or token.
+ USHORT usMajorVersion, // [IN] Major version.
+ USHORT usMinorVersion, // [IN] Minor version.
+ USHORT usBuildNumber, // [IN] Build number.
+ USHORT usRevisionNumber, // [IN] Revision number.
+ DWORD dwFlags, // [IN] Flags.
+ mdAssemblyRef *pmar); // [OUT] returned AssemblyRef token.
+
+ static HRESULT FindFile(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup.
+ LPCUTF8 szName, // [IN] name for the File.
+ mdFile *pmf, // [OUT] returned File token.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT FindExportedType(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup.
+ LPCUTF8 szNamespace, // [IN] namespace for the ExportedType.
+ LPCUTF8 szName, // [IN] name for the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] enclosing ExportedType token.
+ mdExportedType *pmct, // [OUT] returned ExportedType token.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT FindManifestResource(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup.
+ LPCUTF8 szName, // [IN] name for the ManifestResource.
+ mdManifestResource *pmmr, // [OUT] returned ManifestResource token.
+ RID rid = 0); // [IN] Optional rid to be ignored.
+
+ static HRESULT GetNesterHierarchy(
+ IMetaModelCommon *pCommon, // Scope in which to find the hierarchy.
+ mdTypeDef td, // TypeDef whose hierarchy is needed.
+ CQuickArray<mdTypeDef> &cqaTdNesters, // Array of Nesters.
+ CQuickArray<LPCUTF8> &cqaNamespaces, // Namespaces of the nesters.
+ CQuickArray<LPCUTF8> &cqaNames); // Names of the nesters.
+
+ static HRESULT FindNestedTypeRef(
+ CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef.
+ CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Namespaces.
+ CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Names.
+ mdToken tkResolutionScope, // [IN] Resolution scope for the outermost TypeRef.
+ mdTypeRef *ptr); // [OUT] Inner most TypeRef token.
+
+ static HRESULT FindNestedTypeDef(
+ CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef.
+ CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Namespaces.
+ CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Names.
+ mdTypeDef tdNester, // [IN] Enclosing class for the Outermost TypeDef.
+ mdTypeDef *ptd); // [OUT] Inner most TypeRef token.
+
+ static HRESULT CreateNesterHierarchy(
+ CMiniMdRW *pMiniMdEmit, // [IN] Emit scope to create the Nesters in.
+ CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Nester namespaces.
+ CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Nester names.
+ mdToken tkResolutionScope, // [IN] ResolutionScope for the innermost TypeRef.
+ mdTypeRef *ptr); // [OUT] Token for the innermost TypeRef.
+
+ static HRESULT ImportTypeDef(
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope.
+ CMiniMdRW *pMiniMdEmit, // [IN] Module emit scope.
+ IMetaModelCommon *pCommonAssemImport, // [IN] Assembly import scope.
+ const void *pbHashValue, // [IN] Hash value for import assembly.
+ ULONG cbHashValue, // [IN] Size in bytes of hash value.
+ IMetaModelCommon *pCommonImport, // [IN] Module import scope.
+ mdTypeDef tdImport, // [IN] Imported TypeDef.
+ bool bReturnTd, // [IN] If the import and emit scopes are identical, return the TypeDef.
+ mdToken *ptkType); // [OUT] Output token for the imported type in the emit scope.
+
+ static HRESULT ImportTypeRef(
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope.
+ CMiniMdRW *pMiniMdEmit, // [IN] Module emit scope.
+ IMetaModelCommon *pCommonAssemImport, // [IN] Assembly import scope.
+ const void *pbHashValue, // [IN] Hash value for import assembly.
+ ULONG cbHashValue, // [IN] Size in bytes of hash value.
+ IMetaModelCommon *pCommonImport, // [IN] Module import scope.
+ mdTypeRef trImport, // [IN] Imported TypeRef.
+ mdToken *ptkType); // [OUT] Output token for the imported type in the emit scope.
+
+private:
+ /*
+ static bool ImportHelper::CompareCustomAttribute( //
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ ULONG rid); // [IN] the rid of the custom attribute to compare to
+ */
+
+ static HRESULT GetTDNesterHierarchy(
+ IMetaModelCommon *pCommon, // Scope in which to find the hierarchy.
+ mdTypeDef td, // TypeDef whose hierarchy is needed.
+ CQuickArray<mdTypeDef> &cqaTdNesters,// Array of Nesters.
+ CQuickArray<LPCUTF8> &cqaNamespaces, // Namespaces of the nesters.
+ CQuickArray<LPCUTF8> &cqaNames); // Names of the nesters.
+
+ static HRESULT GetTRNesterHierarchy(
+ IMetaModelCommon *pCommon, // Scope in which to find the hierarchy.
+ mdTypeRef tr, // TypeRef whose hierarchy is needed.
+ CQuickArray<mdTypeRef> &cqaTrNesters,// Array of Nesters.
+ CQuickArray<LPCUTF8> &cqaNamespaces, // Namespaces of the nesters.
+ CQuickArray<LPCUTF8> &cqaNames); // Names of the nesters.
+
+ static HRESULT CreateModuleRefFromScope(
+ CMiniMdRW *pMiniMdEmit, // [IN] Emit scope in which the ModuleRef is to be created.
+ IMetaModelCommon *pCommonImport, // [IN] Import scope.
+ mdModuleRef *ptkModuleRef); // [OUT] Output token for ModuleRef.
+
+ static HRESULT CreateModuleRefFromModuleRef( // S_OK or error.
+ CMiniMdRW *pMiniMdEmit, // [IN] Emit scope.
+ IMetaModelCommon *pCommon, // [IN] Import scope.
+ mdModuleRef tkModuleRef, // [IN] ModuleRef token.
+ mdModuleRef *ptkModuleRef); // [OUT] ModuleRef token in the emit scope.
+
+ static HRESULT CreateModuleRefFromExportedType( // S_OK, S_FALSE or error.
+ CMiniMdRW *pAssemEmit, // [IN] Import assembly scope.
+ CMiniMdRW *pMiniMdEmit, // [IN] Emit scope.
+ mdExportedType tkExportedType, // [IN] ExportedType token in Assembly emit scope.
+ mdModuleRef *ptkModuleRef); // [OUT] ModuleRef token in the emit scope.
+
+ // CreateAssemblyRefFromAssembly, CompareAssemblyRefToAssembly are in satellite libs because
+ // they are only used in emit cases and need strong-name support in mscorwks.dll.
+
+ static HRESULT CreateAssemblyRefFromAssembly( // S_OK or error.
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] Emit assembly scope.
+ CMiniMdRW *pMiniMdModuleEmit, // [IN] Emit module scope.
+ IMetaModelCommon *pCommonAssemImport, // [IN] Assembly import scope.
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ mdAssemblyRef *ptkAssemblyRef); // [OUT] AssemblyRef token.
+
+ static HRESULT CompareAssemblyRefToAssembly( // S_OK, S_FALSE or error.
+ IMetaModelCommon *pCommonAssem1, // [IN] Assembly that defines the AssemblyRef.
+ mdAssemblyRef tkAssemRef, // [IN] AssemblyRef.
+ IMetaModelCommon *pCommonAssem2); // [IN] Assembly against which the Ref is compared.
+
+ static HRESULT CreateAssemblyRefFromAssemblyRef(
+ CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope.
+ CMiniMdRW *pMiniMdModuleEmit, // [IN] Module emit scope
+ IMetaModelCommon *pCommonImport, // [IN] Scope to import the assembly ref from.
+ mdAssemblyRef tkAssemRef, // [IN] Assembly ref to be imported.
+ mdAssemblyRef *ptkAssemblyRef); // [OUT] AssemblyRef in the emit scope.
+};
+
+#endif // __IMPORTHELPER__h__
diff --git a/src/md/compiler/mdperf.cpp b/src/md/compiler/mdperf.cpp
new file mode 100644
index 0000000000..4c63af84a9
--- /dev/null
+++ b/src/md/compiler/mdperf.cpp
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDperf.cpp
+//
+
+//
+// This file provides Compiler Support functionality in metadata.
+//*****************************************************************************
+
+#include "stdafx.h"
+
+#include "mdperf.h"
+
+#ifdef FEATURE_METADATA_PERF_STATS
+
+//-----------------------------------------------------------------------------
+// Global array containing the name of the APIs. This is shared across
+// all instances of MDCompilerPerf.
+//-----------------------------------------------------------------------------
+char g_szNameOfAPI[LAST_MD_API][API_NAME_STR_SIZE];
+
+//-----------------------------------------------------------------------------
+// Constructor. Initialize counters to 0. Initialize names of MD APIs.
+//-----------------------------------------------------------------------------
+MDCompilerPerf::MDCompilerPerf()
+{
+ // Initialize counters
+ for (int idx=0; idx < LAST_MD_API; idx++)
+ {
+ MDPerfStats[idx].dwCalledNumTimes = 0;
+ MDPerfStats[idx].dwQueryPerfCycles = 0;
+ }
+
+#undef MD_FUNC
+#define MD_FUNC(MDTag)\
+ strncpy(g_szNameOfAPI[MDTag ## _ENUM], #MDTag, API_NAME_STR_SIZE-1);
+
+ MD_COMPILER_PERF_TABLE; // Relies on the MD_FUNC defined above.
+}
+
+MDCompilerPerf::~MDCompilerPerf()
+ {
+ // Output the stats and cleanup.
+ MetaDataPerfReport ();
+ }
+
+//-----------------------------------------------------------------------------
+// Output stats. <TODO>TODO: grow this into stats for per fautomation</TODO>
+//-----------------------------------------------------------------------------
+void MDCompilerPerf::MetaDataPerfReport ()
+{
+ LARGE_INTEGER freqVal;
+ DWORD totalCalls=0, totalCycles=0;
+
+ if (!QueryPerformanceFrequency(&freqVal))
+ {
+ printf("Perf counters not supported\n");
+ return;
+ }
+
+ for (int idx=0; idx < LAST_MD_API; idx++)
+ {
+ totalCalls += MDPerfStats[idx].dwCalledNumTimes;
+ totalCycles += MDPerfStats[idx].dwQueryPerfCycles;
+ }
+
+ if (!(totalCalls && totalCycles && freqVal.QuadPart))
+ {
+ // if any of above is 0 then things don't look good.
+ printf("No data gathered ...\n");
+ return;
+ }
+
+ printf("\n%-32.32s %-16.16s %-16.16s %-16.16s\n", "API Name", "# Calls", "Cycles", "Time (msec)");
+ for (idx=0; idx < LAST_MD_API; idx++)
+ {
+ if(MDPerfStats[idx].dwCalledNumTimes != 0)
+ printf( "%-32.32s %-9d [%3.2d%%] %-16d %-8.2f [%3.2d%%]\n",
+ g_szNameOfAPI[idx],
+ MDPerfStats[idx].dwCalledNumTimes,
+ (MDPerfStats[idx].dwCalledNumTimes*100)/totalCalls,
+ MDPerfStats[idx].dwQueryPerfCycles,
+ ((float)MDPerfStats[idx].dwQueryPerfCycles*1000)/(float)freqVal.QuadPart,
+ (MDPerfStats[idx].dwQueryPerfCycles*100)/totalCycles);
+ }
+ printf( "%-32.32s %-9d [100%%] %-16d %-8.2f [100%%]\n\n",
+ "Total Stats",
+ totalCalls,
+ totalCycles,
+ ((float)totalCycles*1000)/(float)freqVal.QuadPart);
+
+}
+
+#endif //FEATURE_METADATA_PERF_STATS
diff --git a/src/md/compiler/mdperf.h b/src/md/compiler/mdperf.h
new file mode 100644
index 0000000000..77def32d21
--- /dev/null
+++ b/src/md/compiler/mdperf.h
@@ -0,0 +1,243 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// Mdperf.h
+//
+
+//
+//*****************************************************************************
+
+#ifndef __MDCOMPILERPERF_H__
+#define __MDCOMPILERPERF_H__
+
+//#define FEATURE_METADATA_PERF_STATS
+
+#ifdef FEATURE_METADATA_PERF_STATS
+
+// Avoid dynamic allocs to display the API names.
+#define API_NAME_STR_SIZE 80
+
+//-----------------------------------------------------------------------------
+// In order to add instrumentation for an API, two changes have to be made.
+// One, add the API name in the table below (MD_TABLE).
+// Second, add two lines of code (shown below) in the implementation
+// of the API itself. e.g.
+// RegMeta::MyNewMetataDataAPI(...)
+// {
+// LOG(...);
+// START_MD_PERF(); // <------ add this line as is.
+// ....
+// // API implementation
+// ErrExit:
+// STOP_MD_PERF(RegMeta_MyNewMetaDataAPI); // <---------- add this line with the appropriate name
+// return (hr);
+// ]
+//
+//-----------------------------------------------------------------------------
+#define MD_COMPILER_PERF_TABLE\
+ MD_FUNC(SaveToMemory)\
+ MD_FUNC(DefineMethod)\
+ MD_FUNC(DefineMethodImpl)\
+ MD_FUNC(SetRVA)\
+ MD_FUNC(DefineTypeRefByName)\
+ MD_FUNC(DefineImportType)\
+ MD_FUNC(DefineMemberRef)\
+ MD_FUNC(DefineImportMember)\
+ MD_FUNC(DefineEvent)\
+ MD_FUNC(SetClassLayout)\
+ MD_FUNC(DeleteClassLayout)\
+ MD_FUNC(SetFieldMarshal)\
+ MD_FUNC(DeleteFieldMarshal)\
+ MD_FUNC(DefinePermissionSet)\
+ MD_FUNC(SetMemberIndex)\
+ MD_FUNC(GetTokenFromSig)\
+ MD_FUNC(DefineModuleRef)\
+ MD_FUNC(SetParent)\
+ MD_FUNC(GetTokenFromTypeSpec)\
+ MD_FUNC(DefineUserString)\
+ MD_FUNC(DeleteToken)\
+ MD_FUNC(SetTypeDefProps)\
+ MD_FUNC(DefineNestedType)\
+ MD_FUNC(SetMethodProps)\
+ MD_FUNC(SetEventProps)\
+ MD_FUNC(SetPermissionSetProps)\
+ MD_FUNC(DefinePinvokeMap)\
+ MD_FUNC(SetPinvokeMap)\
+ MD_FUNC(DeletePinvokeMap)\
+ MD_FUNC(DefineField)\
+ MD_FUNC(DefineProperty)\
+ MD_FUNC(DefineParam)\
+ MD_FUNC(SetFieldProps)\
+ MD_FUNC(SetPropertyProps)\
+ MD_FUNC(SetParamProps)\
+ MD_FUNC(EnumMembers)\
+ MD_FUNC(EnumMembersWithName)\
+ MD_FUNC(EnumMethods)\
+ MD_FUNC(EnumMethodsWithName)\
+ MD_FUNC(EnumFields)\
+ MD_FUNC(EnumFieldsWithName)\
+ MD_FUNC(EnumParams)\
+ MD_FUNC(EnumMemberRefs)\
+ MD_FUNC(EnumMethodImpls)\
+ MD_FUNC(EnumPermissionSets)\
+ MD_FUNC(FindMember)\
+ MD_FUNC(FindMethod)\
+ MD_FUNC(FindField)\
+ MD_FUNC(FindMemberRef)\
+ MD_FUNC(GetMethodProps)\
+ MD_FUNC(GetMemberRefProps)\
+ MD_FUNC(EnumProperties)\
+ MD_FUNC(EnumEvents)\
+ MD_FUNC(GetEventProps)\
+ MD_FUNC(EnumMethodSemantics)\
+ MD_FUNC(GetMethodSemantics)\
+ MD_FUNC(GetClassLayout)\
+ MD_FUNC(GetFieldMarshal)\
+ MD_FUNC(GetRVA)\
+ MD_FUNC(GetPermissionSetProps)\
+ MD_FUNC(GetSigFromToken)\
+ MD_FUNC(GetModuleRefProps)\
+ MD_FUNC(EnumModuleRefs)\
+ MD_FUNC(GetTypeSpecFromToken)\
+ MD_FUNC(GetNameFromToken)\
+ MD_FUNC(EnumUnresolvedMethods)\
+ MD_FUNC(GetUserString)\
+ MD_FUNC(GetPinvokeMap)\
+ MD_FUNC(EnumSignatures)\
+ MD_FUNC(EnumTypeSpecs)\
+ MD_FUNC(EnumUserStrings)\
+ MD_FUNC(GetParamForMethodIndex)\
+ MD_FUNC(GetMemberProps)\
+ MD_FUNC(GetFieldProps)\
+ MD_FUNC(GetPropertyProps)\
+ MD_FUNC(GetParamProps)\
+ MD_FUNC(SetModuleProps)\
+ MD_FUNC(Save)\
+ MD_FUNC(SaveToStream)\
+ MD_FUNC(GetSaveSize)\
+ MD_FUNC(Merge)\
+ MD_FUNC(DefineCustomAttribute)\
+ MD_FUNC(SetCustomAttributeValue)\
+ MD_FUNC(DefineSecurityAttributeSet)\
+ MD_FUNC(UnmarkAll)\
+ MD_FUNC(MarkToken)\
+ MD_FUNC(IsTokenMarked)\
+ MD_FUNC(DefineTypeDef)\
+ MD_FUNC(SetHandler)\
+ MD_FUNC(CountEnum)\
+ MD_FUNC(ResetEnum)\
+ MD_FUNC(EnumTypeDefs)\
+ MD_FUNC(EnumInterfaceImpls)\
+ MD_FUNC(EnumTypeRefs)\
+ MD_FUNC(FindTypeDefByName)\
+ MD_FUNC(FindTypeDefByGUID)\
+ MD_FUNC(GetScopeProps)\
+ MD_FUNC(GetModuleFromScope)\
+ MD_FUNC(GetTypeDefProps)\
+ MD_FUNC(GetInterfaceImplProps)\
+ MD_FUNC(GetCustomAttributeByName)\
+ MD_FUNC(GetTypeRefProps)\
+ MD_FUNC(ResolveTypeRef)\
+ MD_FUNC(EnumCustomAttributes)\
+ MD_FUNC(GetCustomAttributeProps)\
+ MD_FUNC(FindTypeRef)\
+ MD_FUNC(RefToDefOptimization)\
+ MD_FUNC(ProcessFilter)\
+ MD_FUNC(DefineAssembly)\
+ MD_FUNC(DefineAssemblyRef)\
+ MD_FUNC(DefineFile)\
+ MD_FUNC(DefineExportedType)\
+ MD_FUNC(DefineManifestResource)\
+ MD_FUNC(DefineExecutionLocation)\
+ MD_FUNC(SetAssemblyProps)\
+ MD_FUNC(SetAssemblyRefProps)\
+ MD_FUNC(SetFileProps)\
+ MD_FUNC(SetExportedTypeProps)\
+ MD_FUNC(GetAssemblyProps)\
+ MD_FUNC(GetAssemblyRefProps)\
+ MD_FUNC(GetFileProps)\
+ MD_FUNC(GetExportedTypeProps)\
+ MD_FUNC(GetManifestResourceProps)\
+ MD_FUNC(EnumAssemblyRefs)\
+ MD_FUNC(EnumFiles)\
+ MD_FUNC(EnumExportedTypes)\
+ MD_FUNC(EnumManifestResources)\
+ MD_FUNC(EnumExecutionLocations)\
+ MD_FUNC(GetAssemblyFromScope)\
+ MD_FUNC(FindExportedTypeByName)\
+ MD_FUNC(FindManifestResourceByName)\
+ MD_FUNC(FindAssembliesByName)\
+ MD_FUNC(SetGenericPars)\
+ MD_FUNC(DefineGenericParam)\
+ MD_FUNC(SetGenericParamProps)\
+ MD_FUNC(EnumGenericParamConstraints)\
+ MD_FUNC(GetGenericParamProps)\
+ MD_FUNC(GetGenericParamConstraintProps)\
+ MD_FUNC(GetPEKind)\
+ MD_FUNC(GetVersionString)\
+ MD_FUNC(GetAssemblyUnification)
+
+//-----------------------------------------------------------------------------
+// Create an enum of all the API names. This is the index to access the APIs.
+//-----------------------------------------------------------------------------
+#undef MD_FUNC
+#define MD_FUNC(MDTag)\
+ MDTag ## _ENUM,
+
+typedef enum _MDAPIs
+{
+ MD_COMPILER_PERF_TABLE
+ LAST_MD_API
+} MDApis;
+
+//-----------------------------------------------------------------------------
+// Declare the struct which contais all the interesting stats for a particular
+// API call.
+//-----------------------------------------------------------------------------
+typedef struct _MDAPIPerfData
+{
+ DWORD dwQueryPerfCycles; // # of cycles spent in this call
+ DWORD dwCalledNumTimes; // # of times this API was called
+} MDAPIPerfData;
+
+
+//-----------------------------------------------------------------------------
+// MDCompilerPerf
+//-----------------------------------------------------------------------------
+class MDCompilerPerf
+{
+public:
+ MDCompilerPerf();
+ ~MDCompilerPerf();
+
+private:
+ MDAPIPerfData MDPerfStats[LAST_MD_API];
+
+ void MetaDataPerfReport ();
+};
+
+// Note that this macro declares a local var.
+#define START_MD_PERF()\
+ LARGE_INTEGER __startVal;\
+ QueryPerformanceCounter(&__startVal);
+
+#undef MD_FUNC
+#define MD_FUNC(MDTag)\
+ MDTag ## _ENUM
+
+// Note that this macro uses the local var startVal declared in START_MD_PERF()
+#define STOP_MD_PERF(MDTag)\
+ LARGE_INTEGER __stopVal;\
+ QueryPerformanceCounter(&__stopVal);\
+ m_MDCompilerPerf.MDPerfStats[MD_FUNC(MDTag)].dwCalledNumTimes++;\
+ m_MDCompilerPerf.MDPerfStats[MD_FUNC(MDTag)].dwQueryPerfCycles += (DWORD)(__stopVal.QuadPart - __startVal.QuadPart);
+
+#else //!FEATURE_METADATA_PERF_STATS
+
+#define START_MD_PERF()
+#define STOP_MD_PERF(MDTag)
+
+#endif //!FEATURE_METADATA_PERF_STATS
+
+#endif // __MDCOMPILERPERF_H__
diff --git a/src/md/compiler/mdsighelper.h b/src/md/compiler/mdsighelper.h
new file mode 100644
index 0000000000..0d089729bb
--- /dev/null
+++ b/src/md/compiler/mdsighelper.h
@@ -0,0 +1,128 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+
+//
+// MDSigHelp.h
+//
+// contains utility code for signature parsing and comparisons.
+//
+// This is needed for validator support, especially because it may need to compare signatures
+// across multiple metadata scopes.
+//*****************************************************************************
+
+#ifndef __mdsighelper_h_
+#define __mdsighelper_h_
+
+#include "regmeta.h"
+
+
+//****************************************************************************
+//****************************************************************************
+class MDSigParser : public SigParser
+{
+ friend class MDSigComparer;
+
+ public:
+ //------------------------------------------------------------------------
+ // Constructor.
+ //------------------------------------------------------------------------
+ FORCEINLINE MDSigParser(PCCOR_SIGNATURE ptr, DWORD len)
+ : SigParser(ptr, len)
+ { }
+
+ FORCEINLINE MDSigParser(const MDSigParser &sig)
+ : SigParser(sig.m_ptr, sig.m_dwLen)
+ { }
+};
+
+//****************************************************************************
+//****************************************************************************
+class MDSigComparer
+{
+ public:
+ //------------------------------------------------------------------------
+ // This is the base type used to provide callback comparison functionality.
+ //------------------------------------------------------------------------
+ class MDSigComparerBaseType
+ {
+ public:
+ //------------------------------------------------------------------------
+ // Returns S_OK if the tokens are equivalent, E_FAIL if they are not, or
+ // error.
+ //------------------------------------------------------------------------
+ virtual HRESULT CompareToken(const mdToken &tok1, const mdToken &tok2) = 0;
+ };
+
+ //------------------------------------------------------------------------
+ // Ctor
+ //------------------------------------------------------------------------
+ MDSigComparer(const MDSigParser &sig1,
+ const MDSigParser &sig2,
+ MDSigComparerBaseType &comparer)
+ : m_sig1(sig1), m_sig2(sig2), m_comparer(comparer)
+ { }
+
+ //------------------------------------------------------------------------
+ // Returns S_OK if the signatures are equivalent, E_FAIL if they are not,
+ // or error.
+ //------------------------------------------------------------------------
+ HRESULT CompareMethodSignature();
+
+ protected:
+ MDSigParser m_sig1;
+ MDSigParser m_sig2;
+ MDSigComparerBaseType &m_comparer;
+
+ // This will compare exactly one type in each signature to determine
+ // if they are equal
+ HRESULT _CompareMethodSignature();
+ HRESULT _CompareExactlyOne();
+ HRESULT _CompareData(ULONG *pulData);
+ HRESULT _CompareMethodSignatureHeader(ULONG &cArgs);
+};
+
+//****************************************************************************
+//****************************************************************************
+class UnifiedAssemblySigComparer : public MDSigComparer::MDSigComparerBaseType
+{
+ public:
+ //------------------------------------------------------------------------
+ // Ctor
+ //------------------------------------------------------------------------
+ UnifiedAssemblySigComparer(const RegMeta &regMeta)
+ : m_pRegMeta(const_cast<RegMeta*>(&regMeta))
+ { }
+
+ //------------------------------------------------------------------------
+ // Returns S_OK if the tokens are equivalent, E_FAIL if they are not, or
+ // error.
+ //------------------------------------------------------------------------
+ virtual HRESULT CompareToken(const mdToken &tok1, const mdToken &tok2);
+
+ protected:
+ RegMeta *m_pRegMeta;
+
+#ifdef FEATURE_FUSION
+ HRESULT _CreateIAssemblyNameFromAssemblyRef(
+ mdToken tkAsmRef,
+ IAssemblyName **ppAsmName);
+#else
+ HRESULT _CompareAssemblies(mdToken tkAsmRef1,mdToken tkAsmRef2, BOOL* pfEquivalent);
+#endif
+
+ HRESULT _CreateTypeNameFromTypeRef(
+ mdToken tkTypeRef,
+ SString &ssName,
+ mdToken &tkParent);
+
+ HRESULT _CreateFullyQualifiedTypeNameFromTypeRef(
+ mdToken tkTypeRef,
+ SString &ssFullName,
+ mdToken &tkParent);
+};
+
+
+#endif // __mdsighelper_h_
+
diff --git a/src/md/compiler/mdutil.cpp b/src/md/compiler/mdutil.cpp
new file mode 100644
index 0000000000..2e01258bea
--- /dev/null
+++ b/src/md/compiler/mdutil.cpp
@@ -0,0 +1,774 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDUtil.cpp
+//
+
+//
+// contains utility code to MD directory. This is only used for the full version.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "metadata.h"
+#include "mdutil.h"
+#include "regmeta.h"
+#include "disp.h"
+#include "mdcommon.h"
+#include "importhelper.h"
+#include "sstring.h"
+
+#include <rwutil.h>
+
+#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
+
+LOADEDMODULES * LOADEDMODULES::s_pLoadedModules = NULL;
+UTSemReadWrite * LOADEDMODULES::m_pSemReadWrite = NULL;
+RegMeta * (LOADEDMODULES::m_HashedModules[LOADEDMODULES_HASH_SIZE]) = { NULL };
+
+//*****************************************************************************
+// Hash a file name.
+//*****************************************************************************
+ULONG LOADEDMODULES::HashFileName(
+ LPCWSTR szName)
+{
+ return HashString(szName) % LOADEDMODULES_HASH_SIZE;
+} // LOADEDMODULES::HashFileName
+
+//---------------------------------------------------------------------------------------
+//
+// Initialize the static instance and lock.
+//
+HRESULT
+LOADEDMODULES::InitializeStatics()
+{
+ HRESULT hr = S_OK;
+
+ if (VolatileLoad(&s_pLoadedModules) == NULL)
+ {
+ // Initialize global read-write lock
+ {
+ NewHolder<UTSemReadWrite> pSemReadWrite = new (nothrow) UTSemReadWrite();
+ IfNullGo(pSemReadWrite);
+ IfFailGo(pSemReadWrite->Init());
+
+ if (InterlockedCompareExchangeT<UTSemReadWrite *>(&m_pSemReadWrite, pSemReadWrite, NULL) == NULL)
+ { // We won the initialization race
+ pSemReadWrite.SuppressRelease();
+ }
+ }
+
+ // Initialize the global instance
+ {
+ NewHolder<LOADEDMODULES> pLoadedModules = new (nothrow) LOADEDMODULES();
+ IfNullGo(pLoadedModules);
+
+ {
+ LOCKWRITE();
+
+ if (VolatileLoad(&s_pLoadedModules) == NULL)
+ {
+ VolatileStore(&s_pLoadedModules, pLoadedModules.Extract());
+ }
+ }
+ }
+ }
+
+ErrExit:
+ return hr;
+} // LOADEDMODULES::InitializeStatics
+
+//---------------------------------------------------------------------------------------
+//
+// Destroy the static instance and lock.
+//
+void
+LOADEDMODULES::DeleteStatics()
+{
+ HRESULT hr = S_OK;
+
+ if (s_pLoadedModules != NULL)
+ {
+ delete s_pLoadedModules;
+ s_pLoadedModules = NULL;
+ }
+ if (m_pSemReadWrite != NULL)
+ {
+ delete m_pSemReadWrite;
+ m_pSemReadWrite = NULL;
+ }
+} // LOADEDMODULES::DeleteStatics
+
+//*****************************************************************************
+// Add a RegMeta pointer to the loaded module list
+//*****************************************************************************
+HRESULT LOADEDMODULES::AddModuleToLoadedList(RegMeta * pRegMeta)
+{
+ HRESULT hr = NOERROR;
+ RegMeta ** ppRegMeta;
+
+ IfFailGo(InitializeStatics());
+
+ {
+ LOCKWRITE();
+
+ ppRegMeta = s_pLoadedModules->Append();
+ IfNullGo(ppRegMeta);
+
+ // The cache holds a copy of the pointer, but no ref-count. There is no
+ // point to the ref-count, because it just changes comparisons against 0
+ // to comparisons against 1.
+ *ppRegMeta = pRegMeta;
+
+ // If the module is read-only, hash it.
+ if (pRegMeta->IsReadOnly())
+ {
+ ULONG ixHash = HashFileName(pRegMeta->GetNameOfDBFile());
+ m_HashedModules[ixHash] = pRegMeta;
+ }
+ }
+
+ErrExit:
+ return hr;
+} // LOADEDMODULES::AddModuleToLoadedList
+
+//*****************************************************************************
+// Remove a RegMeta pointer from the loaded module list
+//*****************************************************************************
+BOOL LOADEDMODULES::RemoveModuleFromLoadedList(RegMeta * pRegMeta)
+{
+ BOOL bRemoved = FALSE; // Was this module removed from the cache?
+ int iFound = -1; // Index at which it was found.
+ ULONG cRef; // Ref count of the module.
+
+ // Lock the cache for write, so that no other thread will find what this
+ // thread is about to delete, and so that no other thread will delete
+ // what this thread is about to try to find.
+ HRESULT hr = S_OK;
+
+ IfFailGo(InitializeStatics());
+
+ {
+ LOCKWRITE();
+
+ // Search for this module in list of loaded modules.
+ int count = s_pLoadedModules->Count();
+ for (int index = 0; index < count; index++)
+ {
+ if ((*s_pLoadedModules)[index] == pRegMeta)
+ { // found a match to remove
+ iFound = index;
+ break;
+ }
+ }
+
+ // If the module is still in the cache, it hasn't been deleted yet.
+ if (iFound >= 0)
+ {
+ // See if there are any external references left.
+ cRef = pRegMeta->GetRefCount();
+
+ // If the cRef that we got from the module is zero, it will stay that way,
+ // because no other thread can discover the module while this thread holds
+ // the lock.
+
+ // OTOH, if the cRef is not zero, this thread can just return, because the
+ // other thread will eventually take the ref count to zero, and will then
+ // come through here to clean up the module. And this thread must not
+ // delete the module out from under other threads.
+
+ // It is possible that the cRef is zero, yet another thread has a pointer that
+ // it discovered before this thread took the lock. (And that thread has
+ // released the ref-counts.) In such a case, this thread can still remove the
+ // module from the cache, and tell the caller to delete it, because the
+ // other thread will wait on the lock, then discover that the module
+ // is not in the cache, and it won't try to delete the module.
+
+ if (cRef != 0)
+ { // Some other thread snuck in and found the entry in the cache.
+ return FALSE;
+ }
+
+ // No other thread owns the object. Remove from cache, and tell caller
+ // that we're done with it. (Caller will delete.)
+ s_pLoadedModules->Delete(iFound);
+ bRemoved = TRUE;
+
+ // If the module is read-only, remove from hash.
+ if (pRegMeta->IsReadOnly())
+ {
+ // There may have been multiple capitalizations pointing to the same entry.
+ // Find and remove all of them.
+ for (ULONG ixHash = 0; ixHash < LOADEDMODULES_HASH_SIZE; ++ixHash)
+ {
+ if (m_HashedModules[ixHash] == pRegMeta)
+ m_HashedModules[ixHash] = NULL;
+ }
+ }
+ }
+ }
+
+ErrExit:
+ return bRemoved;
+} // LOADEDMODULES::RemoveModuleFromLoadedList
+
+
+//*****************************************************************************
+// Search the cached RegMetas for a given scope.
+//*****************************************************************************
+HRESULT LOADEDMODULES::FindCachedReadOnlyEntry(
+ LPCWSTR szName, // Name of the desired file.
+ DWORD dwOpenFlags, // Flags the new file is opened with.
+ RegMeta ** ppMeta) // Put found RegMeta here.
+{
+ RegMeta * pRegMeta = 0;
+ BOOL bWillBeCopyMemory; // Will the opened file be copied to memory?
+ DWORD dwLowFileSize; // Low bytes of this file's size
+ DWORD dwLowFileTime; // Low butes of this file's last write time
+ HRESULT hr;
+ ULONG ixHash = 0;
+
+ IfFailGo(InitializeStatics());
+
+ {
+ LOCKREAD();
+
+ hr = S_FALSE; // We haven't found a match yet.
+
+ // Avoid confusion.
+ *ppMeta = NULL;
+
+ bWillBeCopyMemory = IsOfCopyMemory(dwOpenFlags);
+
+ // The cache is locked for read, so the list will not change.
+
+ // Figure out the size and timestamp of this file
+ WIN32_FILE_ATTRIBUTE_DATA faData;
+ if (!WszGetFileAttributesEx(szName, GetFileExInfoStandard, &faData))
+ return E_FAIL;
+ dwLowFileSize = faData.nFileSizeLow;
+ dwLowFileTime = faData.ftLastWriteTime.dwLowDateTime;
+
+ // Check the hash first.
+ ixHash = HashFileName(szName);
+ if ((pRegMeta = m_HashedModules[ixHash]) != NULL)
+ {
+ _ASSERTE(pRegMeta->IsReadOnly());
+
+ // Only match if the IsOfCopyMemory() bit is the same in both. This is because
+ // when ofCopyMemory is set, the file is not locked on disk, and may become stale
+ // in memory.
+ //
+ // Also, only match if the date and size are the same
+ if (pRegMeta->IsCopyMemory() == bWillBeCopyMemory &&
+ pRegMeta->GetLowFileTimeOfDBFile() == dwLowFileTime &&
+ pRegMeta->GetLowFileSizeOfDBFile() == dwLowFileSize)
+ {
+ // If the name matches...
+ LPCWSTR pszName = pRegMeta->GetNameOfDBFile();
+ #ifdef FEATURE_CASE_SENSITIVE_FILESYSTEM
+ if (wcscmp(szName, pszName) == 0)
+ #else
+ if (SString::_wcsicmp(szName, pszName) == 0)
+ #endif
+ {
+ ULONG cRefs;
+
+ // Found it. Add a reference, and return it.
+ *ppMeta = pRegMeta;
+ cRefs = pRegMeta->AddRef();
+
+ LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope found cached RegMeta in hash: %#8x, crefs: %d\n", pRegMeta, cRefs));
+
+ return S_OK;
+ }
+ }
+ }
+
+ // Not found in hash; loop through each loaded modules
+ int count = s_pLoadedModules->Count();
+ for (int index = 0; index < count; index++)
+ {
+ pRegMeta = (*s_pLoadedModules)[index];
+
+ // If the module is read-only, and the CopyMemory bit matches, and the date
+ // and size are the same....
+ if (pRegMeta->IsReadOnly() &&
+ pRegMeta->IsCopyMemory() == bWillBeCopyMemory &&
+ pRegMeta->GetLowFileTimeOfDBFile() == dwLowFileTime &&
+ pRegMeta->GetLowFileSizeOfDBFile() == dwLowFileSize)
+ {
+ // If the name matches...
+ LPCWSTR pszName = pRegMeta->GetNameOfDBFile();
+ #ifdef FEATURE_CASE_SENSITIVE_FILESYSTEM
+ if (wcscmp(szName, pszName) == 0)
+ #else
+ if (SString::_wcsicmp(szName, pszName) == 0)
+ #endif
+ {
+ ULONG cRefs;
+
+ // Found it. Add a reference, and return it.
+ *ppMeta = pRegMeta;
+ cRefs = pRegMeta->AddRef();
+
+ // Update the hash.
+ m_HashedModules[ixHash] = pRegMeta;
+
+ LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope found cached RegMeta by search: %#8x, crefs: %d\n", pRegMeta, cRefs));
+
+ return S_OK;
+ }
+ }
+ }
+ }
+
+ErrExit:
+ // Didn't find it.
+ LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope did not find cached RegMeta\n"));
+
+ _ASSERTE(hr != S_OK);
+ return hr;
+} // LOADEDMODULES::FindCachedReadOnlyEntry
+
+#ifdef _DEBUG
+
+//*****************************************************************************
+// Search the cached RegMetas for a given scope.
+//*****************************************************************************
+BOOL LOADEDMODULES::IsEntryInList(
+ RegMeta * pRegMeta)
+{
+ HRESULT hr = S_OK;
+
+ IfFailGo(InitializeStatics());
+
+ {
+ LOCKREAD();
+
+ // Loop through each loaded modules
+ int count = s_pLoadedModules->Count();
+ for (int index = 0; index < count; index++)
+ {
+ if ((*s_pLoadedModules)[index] == pRegMeta)
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ErrExit:
+ return FALSE;
+} // LOADEDMODULES::IsEntryInList
+
+#endif //_DEBUG
+
+#endif //FEATURE_METADATA_IN_VM || FEATURE_METADATA_STANDALONE_WINRT
+
+#ifdef FEATURE_METADATA_IN_VM
+
+//*****************************************************************************
+// Remove a RegMeta pointer from the loaded module list
+//*****************************************************************************
+// static
+HRESULT
+LOADEDMODULES::ResolveTypeRefWithLoadedModules(
+ mdTypeRef tkTypeRef, // [IN] TypeRef to be resolved.
+ RegMeta * pTypeRefRegMeta, // [IN] Scope in which the TypeRef is defined.
+ IMetaModelCommon * pTypeRefScope, // [IN] Scope in which the TypeRef is defined.
+ REFIID riid, // [IN] iid for the return interface.
+ IUnknown ** ppIScope, // [OUT] Return interface.
+ mdTypeDef * ptd) // [OUT] TypeDef corresponding the TypeRef.
+{
+ HRESULT hr = NOERROR;
+ RegMeta * pRegMeta;
+ CQuickArray<mdTypeRef> cqaNesters;
+ CQuickArray<LPCUTF8> cqaNesterNamespaces;
+ CQuickArray<LPCUTF8> cqaNesterNames;
+
+ IfFailGo(InitializeStatics());
+
+ {
+ LOCKREAD();
+
+ // Get the Nesting hierarchy.
+ IfFailGo(ImportHelper::GetNesterHierarchy(
+ pTypeRefScope,
+ tkTypeRef,
+ cqaNesters,
+ cqaNesterNamespaces,
+ cqaNesterNames));
+
+ int count = s_pLoadedModules->Count();
+ for (int index = 0; index < count; index++)
+ {
+ pRegMeta = (*s_pLoadedModules)[index];
+
+ {
+ // Do not lock the TypeRef RegMeta (again), as it is already locked for read by the caller.
+ // The code:UTSemReadWrite will block ReadLock even for thread holding already the read lock if
+ // some other thread is waiting for WriteLock on the same lock. That would cause dead-lock if we
+ // try to lock for read again here.
+ CMDSemReadWrite cSemRegMeta((pRegMeta == pTypeRefRegMeta) ? NULL : pRegMeta->GetReaderWriterLock());
+ IfFailGo(cSemRegMeta.LockRead());
+
+ hr = ImportHelper::FindNestedTypeDef(
+ pRegMeta->GetMiniMd(),
+ cqaNesterNamespaces,
+ cqaNesterNames,
+ mdTokenNil,
+ ptd);
+ }
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ { // Process next MetaData module
+ continue;
+ }
+ IfFailGo(hr);
+
+ // Found a loaded module containing the TypeDef.
+ IfFailGo(pRegMeta->QueryInterface(riid, (void **)ppIScope));
+ break;
+ }
+ }
+ if (FAILED(hr))
+ {
+ // cannot find the match!
+ hr = E_FAIL;
+ }
+ErrExit:
+ return hr;
+} // LOADEDMODULES::ResolveTypeRefWithLoadedModules
+
+#endif //FEATURE_METADATA_IN_VM
+
+#if defined(FEATURE_METADATA_IN_VM)
+
+//*****************************************************************************
+// This is a routine to try to find a class implementation given its fully
+// qualified name by using the CORPATH environment variable. CORPATH is a list
+// of directories (like PATH). Before checking CORPATH, this checks the current
+// directory, then the directory that the exe lives in. The search is
+// performed by parsing off one element at a time from the class name,
+// appending it to the directory and looking for a subdirectory or image with
+// that name. If the subdirectory exists, it drills down into that subdirectory
+// and tries the next element of the class name. When it finally bottoms out
+// but can't find the image it takes the rest of the fully qualified class name
+// and appends them with intervening '.'s trying to find a matching DLL.
+// Example:
+//
+// CORPATH=c:\bin;c:\prog
+// classname = namespace.class
+//
+// checks the following things in order:
+// c:\bin\namespace, (if <-exists) c:\bin\namespace\class.dll,
+// c:\bin\namespace.dll, c:\bin\namespace.class.dll
+// c:\prog\namespace, (if <-exists) c:\prog\namespace\class.dll,
+// c:\prog\namespace.dll, c:\prog\namespace.class.dll
+//*****************************************************************************
+HRESULT CORPATHService::GetClassFromCORPath(
+ __in __in_z LPWSTR wzClassname, // [IN] fully qualified class name
+ mdTypeRef tr, // [IN] TypeRef to be resolved.
+ IMetaModelCommon *pCommon, // [IN] Scope in which the TypeRef is defined.
+ REFIID riid, // [IN] Interface type to be returned.
+ IUnknown **ppIScope, // [OUT] Scope in which the TypeRef resolves.
+ mdTypeDef *ptd) // [OUT] typedef corresponding the typeref
+{
+ PathString rcCorPath; // The CORPATH environment variable.
+ LPWSTR szCorPath; // Used to parse CORPATH.
+ int iLen; // Length of the directory.
+ PathString rcCorDir; // Buffer for the directory.
+ WCHAR *temp; // Used as a parsing temp.
+ WCHAR *szSemiCol;
+
+ // Get the CORPATH environment variable.
+ if (WszGetEnvironmentVariable(W("CORPATH"), rcCorPath))
+ {
+ NewArrayHolder<WCHAR> szCorPathHolder = rcCorPath.GetCopyOfUnicodeString();
+ szCorPath = szCorPathHolder.GetValue();
+ // Try each directory in the path.
+ for(;*szCorPath != W('\0');)
+ {
+ // Get the next directory off the path.
+ if ((szSemiCol = wcschr(szCorPath, W(';'))))
+ {
+ temp = szCorPath;
+ *szSemiCol = W('\0');
+ szCorPath = szSemiCol + 1;
+ }
+ else
+ {
+ temp = szCorPath;
+ szCorPath += wcslen(temp);
+ }
+
+ rcCorDir.Set(temp);
+
+ // Check if we can find the class in the directory.
+ if (CORPATHService::GetClassFromDir(wzClassname, rcCorDir, tr, pCommon, riid, ppIScope, ptd) == S_OK)
+ return S_OK;
+ }
+ }
+
+ //<TODO>These should go before the path search, but it will cause test
+ // some headaches right now, so we'll give them a little time to transition.</TODO>
+
+ // Try the current directory first.
+ if ((iLen = WszGetCurrentDirectory( rcCorDir)) > 0 &&
+ CORPATHService::GetClassFromDir(wzClassname, rcCorDir, tr, pCommon, riid, ppIScope, ptd) == S_OK)
+ {
+ return S_OK;
+ }
+
+ // Try the app directory next.
+ if ((iLen = WszGetModuleFileName(NULL, rcCorDir)) > 0)
+ {
+
+ if(SUCCEEDED(CopySystemDirectory(rcCorDir, rcCorDir)) &&
+ CORPATHService::GetClassFromDir(
+ wzClassname,
+ rcCorDir,
+ tr,
+ pCommon,
+ riid,
+ ppIScope,
+ ptd) == S_OK)
+ {
+ return (S_OK);
+ }
+ }
+
+ // Couldn't find the class.
+ return S_FALSE;
+} // CORPATHService::GetClassFromCORPath
+
+//*****************************************************************************
+// This is used in conjunction with GetClassFromCORPath. See it for details
+// of the algorithm.
+//*****************************************************************************
+HRESULT CORPATHService::GetClassFromDir(
+ __in __in_z LPWSTR wzClassname, // Fully qualified class name.
+ __in SString& directory, // Directory to try. at most appended with a '\\'
+ mdTypeRef tr, // TypeRef to resolve.
+ IMetaModelCommon *pCommon, // Scope in which the TypeRef is defined.
+ REFIID riid,
+ IUnknown **ppIScope,
+ mdTypeDef *ptd) // [OUT] typedef
+{
+ WCHAR *temp; // Used as a parsing temp.
+ int iTmp;
+ bool bContinue; // Flag to check if the for loop should end.
+ LPWSTR wzSaveClassname = NULL; // Saved offset into the class name string.
+
+ // Process the class name appending each segment of the name to the
+ // directory until we find a DLL.
+ PathString dir;
+ if (!directory.EndsWith(DIRECTORY_SEPARATOR_CHAR_W))
+ {
+ directory.Append(DIRECTORY_SEPARATOR_CHAR_W);
+ }
+
+ for(;;)
+ {
+ bContinue = false;
+ dir.Set(directory);
+
+ if ((temp = wcschr(wzClassname, NAMESPACE_SEPARATOR_WCHAR)) != NULL)
+ {
+ *temp = W('\0'); //terminate with null so that it can be appended
+ dir.Append(wzClassname);
+ *temp = NAMESPACE_SEPARATOR_WCHAR; //recover the '.'
+
+ wzClassname = temp+1;
+ // Check if a directory by this name exists.
+ DWORD iAttrs = WszGetFileAttributes(dir);
+ if (iAttrs != 0xffffffff && (iAttrs & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ // Next element in the class spec.
+ bContinue = true;
+ wzSaveClassname = wzClassname;
+ }
+ }
+ else
+ {
+ dir.Append(wzClassname);
+
+ // Advance past the class name.
+ iTmp = (int)wcslen(wzClassname);
+ wzClassname += iTmp;
+ }
+
+ // Try to load the image.
+ dir.Append(W(".dll"));
+
+ // OpenScope given the dll name and make sure that the class is defined in the module.
+ if ( SUCCEEDED( CORPATHService::FindTypeDef(dir, tr, pCommon, riid, ppIScope, ptd) ) )
+ {
+ return (S_OK);
+ }
+
+ // If we didn't find the dll, try some more.
+ while (*wzClassname != W('\0'))
+ {
+ // Find the length of the next class name element.
+ if ((temp = wcschr(wzClassname, NAMESPACE_SEPARATOR_WCHAR)) == NULL)
+ {
+ temp = wzClassname + wcslen(wzClassname);
+ }
+
+ // Tack on ".element.dll"
+ SString::Iterator iter = dir.End();
+ BOOL findperiod = dir.FindBack(iter, NAMESPACE_SEPARATOR_WCHAR);
+ _ASSERTE(findperiod);
+ iter++;
+ dir.Truncate(iter);
+
+ WCHAR save = *temp;
+ *temp = W('\0');
+ dir.Append(wzClassname); //element
+ *temp = save;
+
+ // Try to load the image.
+ dir.Append(W(".dll"));
+
+ // OpenScope given the dll name and make sure that the class is defined in the module.
+ if ( SUCCEEDED( CORPATHService::FindTypeDef(dir, tr, pCommon, riid, ppIScope, ptd) ) )
+ {
+ return (S_OK);
+ }
+
+ // Advance to the next class name element.
+ wzClassname = temp;
+ if (*wzClassname != '\0')
+ ++wzClassname;
+ }
+ if (bContinue)
+ {
+
+ wzClassname = wzSaveClassname;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return S_FALSE;
+} // CORPATHService::GetClassFromDir
+
+//*************************************************************
+//
+// Open the file with anme wzModule and check to see if there is a type
+// with namespace/class of wzNamespace/wzType. If so, return the RegMeta
+// corresponding to the file and the mdTypeDef of the typedef
+//
+//*************************************************************
+HRESULT CORPATHService::FindTypeDef(
+ __in __in_z LPCWSTR wzModule, // name of the module that we are going to open
+ mdTypeRef tr, // TypeRef to resolve.
+ IMetaModelCommon * pCommon, // Scope in which the TypeRef is defined.
+ REFIID riid,
+ IUnknown ** ppIScope,
+ mdTypeDef * ptd) // [OUT] the type that we resolve to
+{
+ HRESULT hr = NOERROR;
+ NewHolder<Disp> pDisp;
+ ReleaseHolder<IMetaDataImport2> pImport = NULL;
+ CQuickArray<mdTypeRef> cqaNesters;
+ CQuickArray<LPCUTF8> cqaNesterNamespaces;
+ CQuickArray<LPCUTF8> cqaNesterNames;
+ RegMeta * pRegMeta;
+
+ _ASSERTE((ppIScope != NULL) && (ptd != NULL));
+
+ *ppIScope = NULL;
+
+ pDisp = new (nothrow) Disp;
+ IfNullGo(pDisp);
+
+ IfFailGo(pDisp->OpenScope(wzModule, 0, IID_IMetaDataImport2, (IUnknown **)&pImport));
+ pRegMeta = static_cast<RegMeta *>(pImport.GetValue());
+
+ // Get the Nesting hierarchy.
+ IfFailGo(ImportHelper::GetNesterHierarchy(pCommon, tr, cqaNesters,
+ cqaNesterNamespaces, cqaNesterNames));
+
+ hr = ImportHelper::FindNestedTypeDef(
+ pRegMeta->GetMiniMd(),
+ cqaNesterNamespaces,
+ cqaNesterNames,
+ mdTokenNil,
+ ptd);
+ if (SUCCEEDED(hr))
+ {
+ *ppIScope = pImport.Extract();
+ }
+
+ErrExit:
+ return hr;
+} // CORPATHService::FindTypeDef
+
+#endif //FEATURE_METADATA_IN_VM
+
+//*******************************************************************************
+//
+// Determine the blob size base of the ELEMENT_TYPE_* associated with the blob.
+// This cannot be a table lookup because ELEMENT_TYPE_STRING is an unicode string.
+// The size of the blob is determined by calling wcsstr of the string + 1.
+//
+//*******************************************************************************
+ULONG _GetSizeOfConstantBlob(
+ DWORD dwCPlusTypeFlag, // ELEMENT_TYPE_*
+ void * pValue, // BLOB value
+ ULONG cchString) // String length in wide chars, or -1 for auto.
+{
+ ULONG ulSize = 0;
+
+ switch (dwCPlusTypeFlag)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ ulSize = sizeof(BYTE);
+ break;
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ ulSize = sizeof(BYTE);
+ break;
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ ulSize = sizeof(SHORT);
+ break;
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ ulSize = sizeof(LONG);
+
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ ulSize = sizeof(DOUBLE);
+ break;
+
+ case ELEMENT_TYPE_STRING:
+ if (pValue == 0)
+ ulSize = 0;
+ else
+ if (cchString != (ULONG) -1)
+ ulSize = cchString * sizeof(WCHAR);
+ else
+ ulSize = (ULONG)(sizeof(WCHAR) * wcslen((LPWSTR)pValue));
+ break;
+
+ case ELEMENT_TYPE_CLASS:
+ // This was originally 'sizeof(IUnknown *)', but that varies across platforms.
+ // The only legal value is a null pointer, and on 32 bit platforms we've already
+ // stored 32 bits, so we will use just 32 bits of null. If the type is
+ // E_T_CLASS, the caller should know that the value is always NULL anyway.
+ ulSize = sizeof(ULONG);
+ break;
+ default:
+ _ASSERTE(!"Not a valid type to specify default value!");
+ break;
+ }
+ return ulSize;
+} // _GetSizeOfConstantBlob
diff --git a/src/md/compiler/mdutil.h b/src/md/compiler/mdutil.h
new file mode 100644
index 0000000000..58cdbf108a
--- /dev/null
+++ b/src/md/compiler/mdutil.h
@@ -0,0 +1,119 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDUtil.h
+//
+
+//
+// Contains utility code for MD directory
+//
+//*****************************************************************************
+#ifndef __MDUtil__h__
+#define __MDUtil__h__
+
+#include "metadata.h"
+
+
+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
+
+ULONG _GetSizeOfConstantBlob(
+ DWORD dwCPlusTypeFlag, // ELEMENT_TYPE_*
+ void *pValue, // BLOB value
+ ULONG cchString); // Size of string in wide chars, or -1 for auto.
+
+
+//*********************************************************************
+// APIs to help look up TypeRef using CORPATH environment variable
+//*********************************************************************
+class CORPATHService
+{
+public:
+
+ static HRESULT GetClassFromCORPath(
+ __in __in_z LPWSTR wzClassname, // fully qualified class name
+ mdTypeRef tr, // TypeRef to be resolved
+ IMetaModelCommon *pCommon, // Scope in which the TypeRef is defined.
+ REFIID riid,
+ IUnknown **ppIScope,
+ mdTypeDef *ptd); // [OUT] typedef corresponding the typeref
+
+ static HRESULT GetClassFromDir(
+ __in __in_z LPWSTR wzClassname, // Fully qualified class name.
+ __in SString& dir, // Directory to try.
+ mdTypeRef tr, // TypeRef to resolve.
+ IMetaModelCommon *pCommon, // Scope in which the TypeRef is defined.
+ REFIID riid,
+ IUnknown **ppIScope,
+ mdTypeDef *ptd); // [OUT] typedef
+
+ static HRESULT FindTypeDef(
+ __in __in_z LPCWSTR wzModule, // name of the module that we are going to open
+ mdTypeRef tr, // TypeRef to resolve.
+ IMetaModelCommon *pCommon, // Scope in which the TypeRef is defined.
+ REFIID riid,
+ IUnknown **ppIScope,
+ mdTypeDef *ptd ); // [OUT] the type that we resolve to
+}; // class CORPATHService
+
+
+#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
+
+class RegMeta;
+
+//*********************************************************************
+//
+// Structure to record the all loaded modules and helpers.
+// RegMeta instance is added to the global variable that is tracking
+// the opened scoped. This happens in RegMeta's constructor.
+// In RegMeta's destructor, the RegMeta pointer will be removed from
+// this list.
+//
+//*********************************************************************
+class UTSemReadWrite;
+#define LOADEDMODULES_HASH_SIZE 47
+
+class LOADEDMODULES : public CDynArray<RegMeta *>
+{
+private:
+ static HRESULT InitializeStatics();
+
+ // Global per-process list of loaded modules
+ static LOADEDMODULES * s_pLoadedModules;
+
+public:
+ static void DeleteStatics();
+
+ // Named for locking macros - see code:LOCKREAD
+ static UTSemReadWrite * m_pSemReadWrite;
+ static RegMeta *(m_HashedModules[LOADEDMODULES_HASH_SIZE]);
+
+ static ULONG HashFileName(LPCWSTR szName);
+
+ static HRESULT AddModuleToLoadedList(RegMeta *pRegMeta);
+ static BOOL RemoveModuleFromLoadedList(RegMeta *pRegMeta); // true if found and removed.
+
+ static HRESULT FindCachedReadOnlyEntry(LPCWSTR szName, DWORD dwOpenFlags, RegMeta **ppMeta);
+
+#ifdef FEATURE_METADATA_IN_VM
+ static HRESULT ResolveTypeRefWithLoadedModules(
+ mdTypeRef tkTypeRef, // [IN] TypeRef to be resolved.
+ RegMeta * pTypeRefRegMeta, // [IN] Scope in which the TypeRef is defined.
+ IMetaModelCommon * pTypeRefScope, // [IN] Scope in which the TypeRef is defined.
+ REFIID riid, // [IN] iid for the return interface.
+ IUnknown ** ppIScope, // [OUT] Return interface.
+ mdTypeDef * ptd); // [OUT] TypeDef corresponding the TypeRef.
+#endif //FEATURE_METADATA_IN_VM
+
+#ifdef _DEBUG
+ static BOOL IsEntryInList(RegMeta *pRegMeta);
+#endif
+}; // class LOADEDMODULES
+
+#endif //FEATURE_METADATA_IN_VM || FEATURE_METADATA_STANDALONE_WINRT
+
+#endif // __MDUtil__h__
diff --git a/src/md/compiler/mdvalidator.cpp b/src/md/compiler/mdvalidator.cpp
new file mode 100644
index 0000000000..adcfd51eb3
--- /dev/null
+++ b/src/md/compiler/mdvalidator.cpp
@@ -0,0 +1,7739 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDValidator.cpp
+//
+
+//
+// Implementation for the MetaData validator.
+// Only supported for full mscorwks version.
+//
+//*****************************************************************************
+#include "stdafx.h"
+
+#ifdef FEATURE_METADATA_VALIDATOR
+
+#include "regmeta.h"
+#include "importhelper.h"
+#include "pedecoder.h"
+#include "stgio.h"
+#include "corhost.h"
+#ifdef FEATURE_FUSION
+#include "fusion.h"
+#endif
+#include "sstring.h"
+#include "nsutilpriv.h"
+#include "holder.h"
+#include "vererror.h"
+
+#include "mdsighelper.h"
+
+#ifdef DACCESS_COMPILE
+#error Dac should be using standalone version of metadata, not Wks version.
+#endif
+
+//-----------------------------------------------------------------------------
+// Application specific debug macro.
+#define IfBreakGo(EXPR) \
+do {if ((EXPR) != S_OK) IfFailGo(VLDTR_E_INTERRUPTED); } while (0)
+
+//-----------------------------------------------------------------------------
+
+//#define CACHE_IMPLMAP_VALIDATION_RESULT
+#ifdef CACHE_IMPLMAP_VALIDATION_RESULT
+// To avoid multiple validation of the same thing:
+struct ValidationResult
+{
+ mdToken tok;
+ HRESULT hr;
+};
+ValidationResult* g_rValidated=NULL; // allocated in ValidateMetaData
+unsigned g_nValidated=0;
+#endif
+
+//-----------------------------------------------------------------------------
+
+#define BASE_OBJECT_CLASSNAME "Object"
+#define BASE_NAMESPACE "System"
+#define BASE_VTYPE_CLASSNAME "ValueType"
+#define BASE_ENUM_CLASSNAME "Enum"
+#define BASE_VALUE_FIELDNAME "value__"
+#define BASE_CTOR_NAME ".ctor"
+#define BASE_CCTOR_NAME ".cctor"
+#define BASE_MCDELEGATE_CLASSNAME "MulticastDelegate"
+
+#define SYSTEM_OBJECT_TOSTRING_METHODNAME "ToString"
+#define SYSTEM_OBJECT_GETHASHCODE_METHODNAME "GetHashCode"
+#define SYSTEM_OBJECT_EQUALS_METHODNAME "Equals"
+
+// string ToString()
+static const BYTE g_sigSystemObject_ToString[] =
+{
+ IMAGE_CEE_CS_CALLCONV_HASTHIS, // 0x20
+ 0, // 0x00 ... Param Count
+ ELEMENT_TYPE_STRING // 0x0e ... Return Type - string
+};
+
+// int GetHashCode()
+static const BYTE g_sigSystemObject_GetHashCode[] =
+{
+ IMAGE_CEE_CS_CALLCONV_HASTHIS, // 0x20
+ 0, // 0x00 ... Param Count
+ ELEMENT_TYPE_I4 // 0x08 ... Return Type - I4
+};
+
+// bool Equals(object)
+static const BYTE g_sigSystemObject_Equals[] =
+{
+ IMAGE_CEE_CS_CALLCONV_HASTHIS, // 0x20
+ 1, // 0x01 ... Param Count
+ ELEMENT_TYPE_BOOLEAN, // 0x02 ... Return Type - bool
+ ELEMENT_TYPE_OBJECT // 0x1c ... Param #1 - object
+};
+
+// as defined in src\vm\vars.hpp
+#define MAX_CLASSNAME_LENGTH 1024
+//-----------------------------------------------------------------------------
+// Class names used in long form signatures (namespace is always "System")
+unsigned g_NumSigLongForms = 19;
+static const LPCSTR g_SigLongFormName[] = {
+ "String",
+ "______", // "Object", <REVISIT_TODO>// uncomment when EE handles ELEMENT_TYPE_OBJECT</REVISIT_TODO>
+ "Boolean",
+ "Char",
+ "Byte",
+ "SByte",
+ "UInt16",
+ "Int16",
+ "UInt32",
+ "Int32",
+ "UInt64",
+ "Int64",
+ "Single",
+ "Double",
+ "SysInt", // Review this.
+ "SysUInt", // Review this.
+ "SingleResult",
+ "Void",
+ "IntPtr"
+};
+
+// <REVISIT_TODO>: Why are these global variables?</REVISIT_TODO>
+mdToken g_tkEntryPoint;
+bool g_fValidatingMscorlib;
+bool g_fIsDLL;
+
+//-----------------------------------------------------------------------------
+
+static HRESULT _FindClassLayout(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdTypeDef tkParent, // [IN] the parent that ClassLayout is associated with
+ RID *clRid, // [OUT] rid for the ClassLayout.
+ RID rid); // [IN] rid to be ignored.
+
+static HRESULT _FindFieldLayout(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdFieldDef tkParent, // [IN] the parent that FieldLayout is associated with
+ RID *flRid, // [OUT] rid for the FieldLayout record.
+ RID rid); // [IN] rid to be ignored.
+
+static BOOL _IsValidLocale(LPCUTF8 szLocale,
+ BOOL fIsV2Assembly);
+
+
+#define REPORT_ERROR0(_VECode) \
+ IfFailGo(_ValidateErrorHelper(_VECode, veCtxt))
+#define REPORT_ERROR1(_VECode, _Arg0) \
+ IfFailGo(_ValidateErrorHelper(_VECode, veCtxt, _Arg0))
+#define REPORT_ERROR2(_VECode, _Arg0, _Arg1) \
+ IfFailGo(_ValidateErrorHelper(_VECode, veCtxt, _Arg0, _Arg1))
+#define REPORT_ERROR3(_VECode, _Arg0, _Arg1, _Arg2) \
+ IfFailGo(_ValidateErrorHelper(_VECode, veCtxt, _Arg0, _Arg1, _Arg2))
+
+//*****************************************************************************
+// Returns true if ixPtrTbl and ixParTbl are a valid parent-child combination
+// in the pointer table scheme.
+//*****************************************************************************
+static inline bool IsTblPtr(ULONG ixPtrTbl, ULONG ixParTbl)
+{
+ if ((ixPtrTbl == TBL_Field && ixParTbl == TBL_TypeDef) ||
+ (ixPtrTbl == TBL_Method && ixParTbl == TBL_TypeDef) ||
+ (ixPtrTbl == TBL_Param && ixParTbl == TBL_Method) ||
+ (ixPtrTbl == TBL_Property && ixParTbl == TBL_PropertyMap) ||
+ (ixPtrTbl == TBL_Event && ixParTbl == TBL_EventMap))
+ {
+ return true;
+ }
+ return false;
+} // IsTblPtr()
+
+//*****************************************************************************
+// This inline function is used to set the return hr value for the Validate
+// functions to one of VLDTR_S_WRN, VLDTR_S_ERR or VLDTR_S_WRNERR based on
+// the current hr value and the new success code.
+// The general algorithm for error codes from the validation functions is:
+// if (no warnings or errors found)
+// return S_OK or S_FALSE
+// else if (warnings found)
+// return VLDTR_S_WRN
+// else if (errors found)
+// return VLDTR_S_ERR
+// else if (warnings and errors found)
+// return VLDTR_S_WRNERR
+//*****************************************************************************
+static inline void SetVldtrCode(HRESULT *phr, HRESULT successcode)
+{
+ _ASSERTE(successcode == S_OK || successcode == S_FALSE ||successcode == VLDTR_S_WRN ||
+ successcode == VLDTR_S_ERR || successcode == VLDTR_S_WRNERR);
+ _ASSERTE(*phr == S_OK || *phr == VLDTR_S_WRN || *phr == VLDTR_S_ERR ||
+ *phr == VLDTR_S_WRNERR);
+ if (successcode == S_OK || successcode == S_FALSE ||*phr == VLDTR_S_WRNERR)
+ return;
+ else if (*phr == S_OK || *phr == S_FALSE)
+ *phr = successcode;
+ else if (*phr != successcode)
+ *phr = VLDTR_S_WRNERR;
+} // SetVldtrCode()
+
+//*****************************************************************************
+// Initialize the Validator related structures in RegMeta.
+//*****************************************************************************
+HRESULT RegMeta::ValidatorInit( // S_OK or error.
+ DWORD dwModuleType, // [IN] Specifies whether the module is a PE file or an obj.
+ IUnknown *pUnk) // [IN] Validation error handler.
+{
+ HRESULT hr = S_OK; // Return value.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ int i = 0; // Index into the function pointer table.
+
+ // Initialize the array of function pointers to the validation function on
+ // each table.
+#undef MiniMdTable
+#define MiniMdTable(x) m_ValidateRecordFunctionTable[i++] = &RegMeta::Validate##x;
+ MiniMdTables()
+
+ // Verify that the ModuleType passed in is a valid one.
+ if (dwModuleType < ValidatorModuleTypeMin ||
+ dwModuleType > ValidatorModuleTypeMax)
+ {
+ IfFailGo(E_INVALIDARG);
+ }
+
+ // Verify that the interface passed in supports IID_IVEHandler.
+ IfFailGo(pUnk->QueryInterface(IID_IVEHandler, (void **)&m_pVEHandler));
+
+ // Set the ModuleType class member. Do this last, this is used in
+ // ValidateMetaData to see if the validator is correctly initialized.
+ m_ModuleType = (CorValidatorModuleType)dwModuleType;
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::ValidatorInit()
+
+
+//*****************************************************************************
+// Public implementation for code:IMetaDataValidate::ValidateMetaData
+//
+// Validate the entire MetaData. Here is the basic algorithm.
+// for each table
+// for each record
+// {
+// Do generic validation - validate that the offsets into the blob
+// pool are good, validate that all the rids are within range,
+// validate that token encodings are consistent.
+// }
+// if (problems found in generic validation)
+// return;
+// for each table
+// for each record
+// Do semantic validation.
+//******************************************************************************
+HRESULT RegMeta::ValidateMetaData()
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW * pMiniMd = &(m_pStgdb->m_MiniMd);
+ HRESULT hrSave = S_OK; // Saved hr from generic validation.
+ ULONG ulCount; // Count of records in the current table.
+ ULONG i; // Index to iterate over the tables.
+ ULONG j; // Index to iterate over the records in a given table.
+ IHostTaskManager * pHostTaskManager = NULL;
+
+#ifdef CACHE_IMPLMAP_VALIDATION_RESULT
+ ULONG rValidatedSize=0; // Size of g_rValidated array
+#endif
+
+ // Verify that the validator is initialized correctly
+ if (m_ModuleType == ValidatorModuleTypeInvalid)
+ {
+ _ASSERTE(!"Validator not initialized, initialize with ValidatorInit().");
+ IfFailGo(VLDTR_E_NOTINIT);
+ }
+
+ // First do a validation pass to do some basic structural checks based on
+ // the Meta-Meta data. This'll validate all the offsets into the pools,
+ // rid value and coded token ranges.
+ for (i = 0; i < pMiniMd->GetCountTables(); i++)
+ {
+ ulCount = pMiniMd->GetCountRecs(i);
+
+#ifdef CACHE_IMPLMAP_VALIDATION_RESULT
+ switch(i)
+ {
+ case TBL_ImplMap:
+ rValidatedSize += ulCount;
+ default:
+ ;
+ }
+#endif
+ for (j = 1; j <= ulCount; j++)
+ {
+ IfFailGo(ValidateRecord(i, j));
+ SetVldtrCode(&hrSave, hr);
+ }
+ }
+ // Validate that the size of the Ptr tables matches with the corresponding
+ // real tables.
+
+ // Do not do semantic validation if structural validation failed.
+ if (hrSave != S_OK)
+ {
+ hr = hrSave;
+ goto ErrExit;
+ }
+
+ // Verify the entry point (if any)
+ ::g_tkEntryPoint = 0;
+ ::g_fIsDLL = false;
+ if(m_pStgdb && m_pStgdb->m_pImage)
+ {
+ NewHolder<PEDecoder> pe;
+
+ EX_TRY
+ {
+ // We need to use different PEDecoder constructors 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 (m_pStgdb->m_pStgIO->GetMemoryMappedType() == MTYPE_IMAGE)
+ pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, false);
+ else
+ pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, (COUNT_T)(m_pStgdb->m_dwImageSize));
+
+ hr = S_OK;
+ }
+ EX_CATCH
+ {
+ hr = COR_E_BADIMAGEFORMAT;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ if (SUCCEEDED(hr) && pe == NULL)
+ IfFailGo(E_OUTOFMEMORY);
+
+ if(FAILED(hr) || !pe->CheckFormat())
+ {
+ VEContext veCtxt; // Context structure.
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = 0;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR0(COR_E_BADIMAGEFORMAT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if(!pe->IsILOnly())
+ {
+ VEContext veCtxt; // Context structure.
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = 0;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR0(VER_E_BAD_PE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if((pe->GetCorHeader()->Flags & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) == 0)
+ g_tkEntryPoint = pe->GetEntryPointToken();
+ g_fIsDLL = pe->IsDll() ? true : false;
+
+ if(g_tkEntryPoint)
+ {
+ RID rid = RidFromToken(g_tkEntryPoint);
+ RID maxrid = 0;
+ switch(TypeFromToken(g_tkEntryPoint))
+ {
+ case mdtMethodDef: maxrid = pMiniMd->getCountMethods(); break;
+ case mdtFile: maxrid = pMiniMd->getCountFiles(); break;
+ default: break;
+ }
+ if((rid == 0)||(rid > maxrid))
+ {
+ VEContext veCtxt; // Context structure.
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = g_tkEntryPoint;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR0(VLDTR_E_EP_BADTOKEN);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else if(!g_fIsDLL) // exe must have an entry point
+ {
+ VEContext veCtxt; // Context structure.
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = g_tkEntryPoint;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR0(VLDTR_E_EP_BADTOKEN);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ g_fValidatingMscorlib = false;
+ if(pMiniMd->GetCountRecs(TBL_Assembly))
+ {
+ AssemblyRec *pRecord;
+ IfFailGo(pMiniMd->GetAssemblyRecord(1, &pRecord));
+ LPCSTR szName;
+ IfFailGo(pMiniMd->getNameOfAssembly(pRecord, &szName));
+ g_fValidatingMscorlib = (0 == SString::_stricmp(szName,"mscorlib"));
+ }
+ // Verify there are no circular class hierarchies.
+
+ // Do per record semantic validation on the MetaData. The function
+ // pointers to the per record validation are stored in the table by the
+ // ValidatorInit() function.
+
+#ifdef CACHE_IMPLMAP_VALIDATION_RESULT
+ g_rValidated = NULL;
+ ::g_nValidated = 0;
+ if (rValidatedSize)
+ {
+ g_rValidated = new(nothrow) ValidationResult[rValidatedSize];
+ IfNullGo(g_rValidated);
+ }
+#endif
+ pHostTaskManager = CorHost2::GetHostTaskManager();
+
+#ifdef Sleep
+#undef Sleep
+#endif
+ //DWORD cBegin=0,cEnd=0;
+ for (i = 0; i < pMiniMd->GetCountTables(); i++)
+ {
+ ulCount = pMiniMd->GetCountRecs(i);
+ //cBegin = GetTickCount();
+ for (j = 1; j <= ulCount; j++)
+ {
+ IfFailGo((this->*m_ValidateRecordFunctionTable[i])(j));
+ SetVldtrCode(&hrSave, hr);
+ if(pHostTaskManager)
+ {
+ // SwitchToTask forces the current thread to give up quantum, while a host can decide what
+ // to do with Sleep if the current thread has not run out of quantum yet.
+ ClrSleepEx(0, FALSE);
+ }
+ }
+ //cEnd = GetTickCount();
+ //printf("Table %d, recs: %d, time: %d\n",i,ulCount,(cEnd-cBegin));
+ }
+ hr = hrSave;
+ErrExit:
+
+#ifdef CACHE_IMPLMAP_VALIDATION_RESULT
+ if(g_rValidated) delete [] g_rValidated;
+#endif
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateMetaData()
+
+//*****************************************************************************
+// Validate the Module record.
+//*****************************************************************************
+HRESULT RegMeta::ValidateModule(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ ModuleRec *pRecord; // Module record.
+ VEContext veCtxt; // Context structure.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ LPCSTR szName;
+ GUID GuidOfModule;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Get the Module record.
+ veCtxt.Token = TokenFromRid(rid, mdtModule);
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetModuleRecord(rid, &pRecord));
+
+ // There can only be one Module record.
+ if (rid > 1)
+ {
+ REPORT_ERROR0(VLDTR_E_MOD_MULTI);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Verify the name
+ IfFailGo(pMiniMd->getNameOfModule(pRecord, &szName));
+ if(szName && *szName)
+ {
+ ULONG L = (ULONG)strlen(szName);
+ if(L >= MAX_CLASSNAME_LENGTH)
+ {
+ // Name too long
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(strchr(szName,':') || strchr(szName,'\\'))
+ {
+ REPORT_ERROR0(VLDTR_E_MOD_NAMEFULLQLFD);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else
+ {
+ REPORT_ERROR0(VLDTR_E_MOD_NONAME);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Verify that the MVID is valid.
+ IfFailGo(pMiniMd->getMvidOfModule(pRecord, &GuidOfModule));
+ if (GuidOfModule == GUID_NULL)
+ {
+ REPORT_ERROR0(VLDTR_E_MOD_NULLMVID);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateModule()
+
+//*****************************************************************************
+// Validate the given TypeRef.
+//*****************************************************************************
+HRESULT RegMeta::ValidateTypeRef(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ TypeRefRec *pRecord; // TypeRef record.
+ mdToken tkRes; // Resolution scope.
+ LPCSTR szNamespace; // TypeRef Namespace.
+ LPCSTR szName; // TypeRef Name.
+ mdTypeRef tkTypeRef; // Duplicate TypeRef.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ // Get the TypeRef record.
+ veCtxt.Token = TokenFromRid(rid, mdtTypeRef);
+ veCtxt.uOffset = 0;
+
+ IfFailGo(pMiniMd->GetTypeRefRecord(rid, &pRecord));
+
+ // Check name is not NULL.
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pRecord, &szNamespace));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pRecord, &szName));
+ if (!*szName)
+ {
+ REPORT_ERROR0(VLDTR_E_TR_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ RID ridScope;
+ // Look for a Duplicate, this function reports only one duplicate.
+ tkRes = pMiniMd->getResolutionScopeOfTypeRef(pRecord);
+ hr = ImportHelper::FindTypeRefByName(pMiniMd, tkRes, szNamespace, szName, &tkTypeRef, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_TR_DUP, tkTypeRef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ ULONG L = (ULONG)(strlen(szName)+strlen(szNamespace));
+ if(L >= MAX_CLASSNAME_LENGTH)
+ {
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ ridScope = RidFromToken(tkRes);
+ if(ridScope)
+ {
+ bool badscope = true;
+ //check if valid scope
+ switch(TypeFromToken(tkRes))
+ {
+ case mdtAssemblyRef:
+ case mdtModuleRef:
+ case mdtModule:
+ case mdtTypeRef:
+ badscope = !IsValidToken(tkRes);
+ break;
+ default:
+ break;
+ }
+ if(badscope)
+ {
+ REPORT_ERROR1(VLDTR_E_TR_BADSCOPE, tkTypeRef);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+ else
+ {
+ // check if there is a ExportedType
+ //hr = ImportHelper::FindExportedType(pMiniMd, szNamespace, szName, tkImpl, &tkExportedType, rid);
+ }
+ // Check if there is TypeDef with the same name
+ if(!ridScope)
+ {
+ if((TypeFromToken(tkRes) != mdtTypeRef) &&
+ (S_OK == ImportHelper::FindTypeDefByName(pMiniMd, szNamespace, szName, mdTokenNil,&tkTypeRef, 0)))
+ {
+ REPORT_ERROR1(VLDTR_E_TR_HASTYPEDEF, tkTypeRef);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+ }
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateTypeRef()
+
+//*****************************************************************************
+// Validate the given TypeDef.
+//*****************************************************************************
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+HRESULT RegMeta::ValidateTypeDef(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ TypeDefRec *pRecord; // TypeDef record.
+ TypeDefRec *pExtendsRec = 0; // TypeDef record for the parent class.
+ mdTypeDef tkTypeDef; // Duplicate TypeDef token.
+ DWORD dwFlags; // TypeDef flags.
+ DWORD dwExtendsFlags; // TypeDef flags of the parent class.
+ LPCSTR szName; // TypeDef Name.
+ LPCSTR szNameSpace; // TypeDef NameSpace.
+ LPCSTR szExtName = NULL; // Parent Name.
+ LPCSTR szExtNameSpace = NULL; // Parent NameSpace.
+ CQuickBytes qb; // QuickBytes for flexible allocation.
+ mdToken tkExtends; // TypeDef of the parent class.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ mdToken tkEncloser=mdTokenNil; // Encloser, if any
+ BOOL bIsEnum,bExtendsEnum,bExtendsVType,bIsVType,bExtendsObject,bIsObject,bExtendsMCDelegate;
+ BOOL bHasMethods=FALSE, bHasFields=FALSE;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // Skip validating m_tdModule class.
+ if (rid == RidFromToken(m_tdModule))
+ goto ErrExit;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Get the TypeDef record.
+ veCtxt.Token = TokenFromRid(rid, mdtTypeDef);
+ veCtxt.uOffset = 0;
+
+ IfFailGo(pMiniMd->GetTypeDefRecord(rid, &pRecord));
+
+ // Do checks for name validity..
+ IfFailGo(pMiniMd->getNameOfTypeDef(pRecord, &szName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pRecord, &szNameSpace));
+ if (!*szName)
+ {
+ // TypeDef Name is null.
+ REPORT_ERROR0(VLDTR_E_TD_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (!IsDeletedName(szName))
+ {
+ RID iRecord;
+ IfFailGo(pMiniMd->FindNestedClassHelper(TokenFromRid(rid, mdtTypeDef), &iRecord));
+
+ if (InvalidRid(iRecord))
+ {
+ tkEncloser = mdTokenNil;
+ }
+ else
+ {
+ NestedClassRec *pNestedClassRec;
+ IfFailGo(pMiniMd->GetNestedClassRecord(iRecord, &pNestedClassRec));
+ tkEncloser = pMiniMd->getEnclosingClassOfNestedClass(pNestedClassRec);
+ }
+
+ // Check for duplicates based on Name/NameSpace. Do not do Dup checks
+ // on deleted records.
+ hr = ImportHelper::FindTypeDefByName(pMiniMd, szNameSpace, szName, tkEncloser,
+ &tkTypeDef, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_TD_DUPNAME, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ ULONG L = (ULONG)(strlen(szName)+strlen(szNameSpace));
+ if(L >= MAX_CLASSNAME_LENGTH)
+ {
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ // Get the flag value for the TypeDef.
+ dwFlags = pMiniMd->getFlagsOfTypeDef(pRecord);
+ // Do semantic checks on the flags.
+ // RTSpecialName bit must be set on Deleted records.
+ if (IsDeletedName(szName))
+ {
+ if(!IsTdRTSpecialName(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_DLTNORTSPCL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ hr = hrSave;
+ goto ErrExit;
+ }
+
+ // If RTSpecialName bit is set, the record must be a Deleted record.
+ if (IsTdRTSpecialName(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_RTSPCLNOTDLT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ if(!IsTdSpecialName(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_RTSPCLNOTSPCL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ // Check if flag value is valid
+ {
+ DWORD dwInvalidMask, dwExtraBits;
+ dwInvalidMask = (DWORD)~(tdVisibilityMask | tdLayoutMask | tdClassSemanticsMask |
+ tdAbstract | tdSealed | tdSpecialName | tdImport | tdSerializable | tdWindowsRuntime |
+ tdStringFormatMask | tdBeforeFieldInit | tdReservedMask);
+ // check for extra bits
+ dwExtraBits = dwFlags & dwInvalidMask;
+ if (dwExtraBits == 0)
+ {
+ // if no extra bits, check layout
+ dwExtraBits = dwFlags & tdLayoutMask;
+ if (dwExtraBits != tdLayoutMask)
+ {
+ // layout OK, check string format
+ dwExtraBits = dwFlags & tdStringFormatMask;
+ if (dwExtraBits != tdStringFormatMask)
+ dwExtraBits = 0;
+ }
+ }
+ if (dwExtraBits != 0)
+ {
+ REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwExtraBits);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+
+ // Generic types may be specified to have only AutoLayout or SequentialLayout (never ExplicitLayout).
+ if (IsTdExplicitLayout(dwFlags))
+ {
+ HENUMInternal hEnumTyPars;
+ ULONG ulTypeDefArity;
+ hr = pMiniMd->FindGenericParamHelper(TokenFromRid(rid, mdtTypeDef), &hEnumTyPars);
+ if (SUCCEEDED(hr))
+ {
+ IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulTypeDefArity));
+ HENUMInternal::ClearEnum(&hEnumTyPars);
+ if (ulTypeDefArity != 0)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_GENERICHASEXPLAYOUT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+
+ }
+
+ // Get the parent of the TypeDef.
+ tkExtends = pMiniMd->getExtendsOfTypeDef(pRecord);
+
+ // Check if TypeDef extends itself
+ if (tkExtends == veCtxt.Token)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_EXTENDSITSELF);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Check if TypeDef extends one of its children
+ if (RidFromToken(tkExtends)&&(TypeFromToken(tkExtends)==mdtTypeDef))
+ {
+ TypeDefRec *pRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pRec));
+ mdToken tkExtends2 = pMiniMd->getExtendsOfTypeDef(pRec);
+ if( tkExtends2 == veCtxt.Token)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_EXTENDSCHILD);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+
+
+ if (IsNilToken(tkEncloser) == IsTdNested(dwFlags))
+ {
+ REPORT_ERROR0(IsNilToken(tkEncloser) ? VLDTR_E_TD_NESTEDNOENCL : VLDTR_E_TD_ENCLNOTNESTED);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ bIsObject = bIsEnum = bIsVType = FALSE;
+ if(0 == strcmp(szNameSpace,BASE_NAMESPACE))
+ {
+ bIsObject = (0 == strcmp(szName,BASE_OBJECT_CLASSNAME));
+ if(!bIsObject)
+ {
+ bIsEnum = (0 == strcmp(szName,BASE_ENUM_CLASSNAME));
+ if(!bIsEnum)
+ {
+ bIsVType = (0 == strcmp(szName,BASE_VTYPE_CLASSNAME));
+ }
+ }
+ }
+
+ if (IsNilToken(tkExtends))
+ {
+ // If the parent token is nil, the class must be marked Interface,
+ // unless it's the System.Object class.
+ if ( !(bIsObject || IsTdInterface(dwFlags)))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_NOTIFACEOBJEXTNULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ szExtName = "";
+ szExtNameSpace = "";
+ }
+ else
+ {
+
+ // If tkExtends is a TypeSpec, extract the generic type and continue
+ if (TypeFromToken(tkExtends) == mdtTypeSpec)
+ {
+ //@GENERICSVER: TODO first validate the spec
+
+ TypeSpecRec *pRec;
+ IfFailGo(pMiniMd->GetTypeSpecRecord(RidFromToken(tkExtends), &pRec));
+ PCCOR_SIGNATURE pSig;
+ ULONG cSig;
+
+ IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRec, &pSig, &cSig));
+
+ switch(CorSigUncompressElementType(pSig))
+ {
+ default:
+ {
+ REPORT_ERROR1(VLDTR_E_TD_EXTBADTYPESPEC, tkExtends);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ szExtName = "";
+ szExtNameSpace = "";
+ break;
+ }
+ case ELEMENT_TYPE_GENERICINST:
+ {
+ switch(CorSigUncompressElementType(pSig))
+ {
+ default:
+ {
+ REPORT_ERROR1(VLDTR_E_TD_EXTBADTYPESPEC, tkExtends);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ szExtName = "";
+ szExtNameSpace = "";
+ break;
+ }
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ {
+ tkExtends = CorSigUncompressToken(pSig);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // If tkExtends is a TypeRef try to resolve it to a corresponding
+ // TypeDef. If it resolves successfully, issue a warning. It means
+ // that the Ref to Def optimization didn't happen successfully.
+ if (TypeFromToken(tkExtends) == mdtTypeRef)
+ {
+ TypeRefRec *pTypeRefRec;
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pTypeRefRec));
+
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szExtName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szExtNameSpace));
+
+ BOOL fLookForDef = TRUE;
+ mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec);
+ if (TypeFromToken(tkResScope) == mdtAssemblyRef)
+ { // We will look for the TypeDef of the same name, only if the AssemblyRef has the same name as AssemblyDef
+ fLookForDef = FALSE;
+ RID ridResScope = RidFromToken(tkResScope);
+ if ((ridResScope > 0) && (ridResScope <= pMiniMd->GetCountRecs(TBL_AssemblyRef)))
+ {
+ if (pMiniMd->GetCountRecs(TBL_Assembly) > 0)
+ {
+ AssemblyRefRec * pAsmRefRec;
+ IfFailGo(pMiniMd->GetAssemblyRefRecord(ridResScope, &pAsmRefRec));
+ AssemblyRec *pAsmRec;
+ IfFailGo(pMiniMd->GetAssemblyRecord(1, &pAsmRec));
+ if ((pAsmRec != NULL) && (pAsmRefRec != NULL))
+ {
+ LPCUTF8 szAsmName;
+ IfFailGo(pMiniMd->getNameOfAssembly(pAsmRec, &szAsmName));
+ LPCUTF8 szAsmRefName;
+ IfFailGo(pMiniMd->getNameOfAssemblyRef(pAsmRefRec, &szAsmRefName));
+ if ((szAsmName != NULL) && (szAsmRefName != NULL))
+ fLookForDef = (strcmp(szAsmName,szAsmRefName) == 0);
+ }
+ }
+ }
+ }
+
+ if (fLookForDef)
+ {
+ mdTypeDef tkResTd;
+
+ if (ImportHelper::FindTypeDefByName(pMiniMd,
+ szExtNameSpace,
+ szExtName,
+ tkResScope,
+ &tkResTd) == S_OK)
+ {
+ // Ref to Def optimization is not expected to happen for Obj files.
+ /*
+ if (m_ModuleType != ValidatorModuleTypeObj)
+ {
+ REPORT_ERROR2(VLDTR_E_TD_EXTTRRES, tkExtends, tkResTd);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ */
+
+ // Set tkExtends to the new TypeDef, so we can continue
+ // with the validation.
+ tkExtends = tkResTd;
+ }
+ }
+ }
+
+ // Continue validation, even for the case where TypeRef got resolved
+ // to a corresponding TypeDef in the same Module.
+ if (TypeFromToken(tkExtends) == mdtTypeDef)
+ {
+ // Extends must not be sealed.
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pExtendsRec));
+ dwExtendsFlags = pMiniMd->getFlagsOfTypeDef(pExtendsRec);
+ IfFailGo(pMiniMd->getNameOfTypeDef(pExtendsRec, &szExtName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtendsRec, &szExtNameSpace));
+ if (IsTdSealed(dwExtendsFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_TD_EXTENDSSEALED, tkExtends);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if (IsTdInterface(dwExtendsFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_TD_EXTENDSIFACE, tkExtends);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else if(TypeFromToken(tkExtends) == mdtTypeSpec)
+ {
+ //If we got here, the instantiated generic type is itself a type spec, which is illegal
+ REPORT_ERROR1(VLDTR_E_TD_EXTBADTYPESPEC, tkExtends);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ szExtName = "";
+ szExtNameSpace = "";
+
+ }
+ // If the parent token is non-null, the class must not be System.Object.
+ if (bIsObject)
+ {
+ REPORT_ERROR1(VLDTR_E_TD_OBJEXTENDSNONNULL, tkExtends);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ bExtendsObject = bExtendsEnum = bExtendsVType = bExtendsMCDelegate = FALSE;
+ if(0 == strcmp(szExtNameSpace,BASE_NAMESPACE))
+ {
+ bExtendsObject = (0 == strcmp(szExtName,BASE_OBJECT_CLASSNAME));
+ if(!bExtendsObject)
+ {
+ bExtendsEnum = (0 == strcmp(szExtName,BASE_ENUM_CLASSNAME));
+ if(!bExtendsEnum)
+ {
+ bExtendsVType = (0 == strcmp(szExtName,BASE_VTYPE_CLASSNAME));
+ if(!bExtendsVType)
+ {
+ bExtendsMCDelegate = (0 == strcmp(szExtName,BASE_MCDELEGATE_CLASSNAME));
+ }
+ }
+ }
+ }
+
+ // System.ValueType must extend System.Object
+ if(bIsVType && !bExtendsObject)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_SYSVTNOTEXTOBJ);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Validate rules for interface. Some of the VOS rules are verified as
+ // part of the validation for the corresponding Methods, fields etc.
+ if (IsTdInterface(dwFlags))
+ {
+ // Interface type must be marked abstract.
+ if (!IsTdAbstract(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_IFACENOTABS);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Interface must not be sealed
+ if(IsTdSealed(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_IFACESEALED);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Interface must have parent Nil token.
+ if (!IsNilToken(tkExtends))
+ {
+ REPORT_ERROR1(VLDTR_E_TD_IFACEPARNOTNIL, tkExtends);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ //Interface must have only static fields -- checked in ValidateField
+ //Interface must have only public fields -- checked in ValidateField
+ //Interface must have only abstract or static methods -- checked in ValidateMethod
+ //Interface must have only public methods -- checked in ValidateMethod
+
+ // Interface must have GUID
+ /*
+ if (*pGuid == GUID_NULL)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_IFACEGUIDNULL);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ */
+ }
+
+
+ // Class must have valid method and field lists
+ {
+ ULONG ridStart,ridEnd;
+ ridStart = pMiniMd->getMethodListOfTypeDef(pRecord);
+ ridEnd = pMiniMd->getCountMethods() + 1;
+ if(ridStart > ridEnd)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_BADMETHODLST);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ IfFailGo(pMiniMd->getEndMethodListOfTypeDef(rid, &ridEnd));
+ bHasMethods = (ridStart && (ridStart < ridEnd));
+ }
+
+ ridStart = pMiniMd->getFieldListOfTypeDef(pRecord);
+ ridEnd = pMiniMd->getCountFields() + 1;
+ if(ridStart > ridEnd)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_BADFIELDLST);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ IfFailGo(pMiniMd->getEndFieldListOfTypeDef(rid, &ridEnd));
+ bHasFields = (ridStart && (ridStart < ridEnd));
+ }
+ }
+
+ // Validate rules for System.Enum
+ if(bIsEnum)
+ {
+ if(!IsTdClass(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_SYSENUMNOTCLASS);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(!bExtendsVType)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_SYSENUMNOTEXTVTYPE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else
+ {
+ if(bExtendsVType || bExtendsEnum)
+ {
+ // ValueTypes and Enums must be sealed
+ if(!IsTdSealed(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_VTNOTSEAL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Value class must have fields or size
+ if(!bHasFields)
+ {
+ ULONG ulClassSize = 0;
+ ClassLayoutRec *pRec;
+ RID ridClassLayout;
+ IfFailGo(pMiniMd->FindClassLayoutHelper(TokenFromRid(rid, mdtTypeDef), &ridClassLayout));
+
+ if (!InvalidRid(ridClassLayout))
+ {
+ IfFailGo(pMiniMd->GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec));
+ ulClassSize = pMiniMd->getClassSizeOfClassLayout(pRec);
+ }
+ if(ulClassSize == 0)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_VTNOSIZE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ else if(bExtendsMCDelegate)
+ {
+ // Delegates must be sealed
+ if(!IsTdSealed(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_VTNOTSEAL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+
+ // Enum-related checks
+ if (bExtendsEnum)
+ {
+ {
+ PCCOR_SIGNATURE pValueSig = NULL;
+ ULONG cbValueSig = 0;
+ mdFieldDef tkValueField=0, tkField, tkValue__Field = 0;
+ ULONG ridStart,ridEnd,index;
+ FieldRec *pFieldRecord; // Field record.
+ DWORD dwRecordFlags, dwTally, dwValueFlags, dwValue__Flags = 0;
+ RID ridField,ridValue=0,ridValue__ = 0;
+
+ ridStart = pMiniMd->getFieldListOfTypeDef(pRecord);
+ IfFailGo(pMiniMd->getEndFieldListOfTypeDef(rid, &ridEnd));
+ // check the instance (value__) field(s)
+ dwTally = 0;
+ for (index = ridStart; index < ridEnd; index++ )
+ {
+ IfFailGo(pMiniMd->GetFieldRid(index, &ridField));
+ IfFailGo(pMiniMd->GetFieldRecord(ridField, &pFieldRecord));
+ dwRecordFlags = pFieldRecord->GetFlags();
+ if(!IsFdStatic(dwRecordFlags))
+ {
+ dwTally++;
+ if(ridValue == 0)
+ {
+ ridValue = ridField;
+ tkValueField = TokenFromRid(ridField, mdtFieldDef);
+ IfFailGo(pMiniMd->getSignatureOfField(pFieldRecord, &pValueSig, &cbValueSig));
+ dwValueFlags = dwRecordFlags;
+ }
+ }
+ LPCSTR szFieldName;
+ IfFailGo(pMiniMd->getNameOfField(pFieldRecord, &szFieldName));
+ if(!strcmp(szFieldName, BASE_VALUE_FIELDNAME))
+ {
+ ridValue__ = ridField;
+ dwValue__Flags = dwRecordFlags;
+ tkValue__Field = TokenFromRid(ridField, mdtFieldDef);
+ }
+ }
+ // Enum must have one (and only one) inst.field
+ if(dwTally == 0)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_ENUMNOINSTFLD);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if(dwTally > 1)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_ENUMMULINSTFLD);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // inst.field name must be "value__" (CLS)
+ if(ridValue__ == 0)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_ENUMNOVALUE);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ else
+ {
+ // if "value__" field is present ...
+ // ... it must be 1st instance field
+ if(ridValue__ != ridValue)
+ {
+ REPORT_ERROR1(VLDTR_E_TD_ENUMVALNOT1ST, tkValue__Field);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // ... it must not be static
+ if(IsFdStatic(dwValue__Flags))
+ {
+ REPORT_ERROR1(VLDTR_E_TD_ENUMVALSTATIC, tkValue__Field);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // ... it must be fdRTSpecialName
+ if(!IsFdRTSpecialName(dwValue__Flags))
+ {
+ REPORT_ERROR1(VLDTR_E_TD_ENUMVALNOTSN, tkValueField);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // ... its type must be integral
+ if(cbValueSig && pValueSig)
+ {
+ //ULONG ulCurByte = CorSigUncompressedDataSize(pValueSig);
+ //CorSigUncompressData(pValueSig);
+ //ULONG ulElemSize,ulElementType;
+ //ulCurByte += (ulElemSize = CorSigUncompressedDataSize(pValueSig));
+ //ulElementType = CorSigUncompressData(pValueSig);
+ //switch (ulElementType)
+ BYTE* pB = (BYTE*)pValueSig;
+ pB++; // skip the calling convention
+ while((*pB == ELEMENT_TYPE_CMOD_OPT)||
+ (*pB == ELEMENT_TYPE_CMOD_REQD))
+ {
+ mdToken tok;
+ pB++; // move from E_T_... to compressed token
+ pB += CorSigUncompressToken((PCOR_SIGNATURE)pB,&tok);
+ }
+ switch(*pB)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ break;
+ default:
+ REPORT_ERROR1(VLDTR_E_TD_ENUMFLDBADTYPE, tkValue__Field);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ // check all the fields
+ dwTally = 0;
+ for (index = ridStart; index < ridEnd; index++ )
+ {
+ IfFailGo(pMiniMd->GetFieldRid(index, &ridField));
+ if(ridField == ridValue) continue;
+ IfFailGo(pMiniMd->GetFieldRecord(ridField, &pFieldRecord));
+ LPCSTR szFieldName;
+ IfFailGo(pMiniMd->getNameOfField(pFieldRecord, &szFieldName));
+ if(IsFdRTSpecialName(pFieldRecord->GetFlags())
+ && IsDeletedName(szFieldName)) continue;
+ dwTally++;
+ tkField = TokenFromRid(ridField, mdtFieldDef);
+ if(!IsFdStatic(pFieldRecord->GetFlags()))
+ {
+ REPORT_ERROR1(VLDTR_E_TD_ENUMFLDNOTST, tkField);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(!IsFdLiteral(pFieldRecord->GetFlags()))
+ {
+ REPORT_ERROR1(VLDTR_E_TD_ENUMFLDNOTLIT, tkField);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ /*
+ IfFailGo(pMiniMd->getSignatureOfField(pFieldRecord, &pvSigTmp, &cbSig));
+ if(!(pvSigTmp && (cbSig==cbValueSig) &&(memcmp(pvSigTmp,pValueSig,cbSig)==0)))
+ {
+ REPORT_ERROR1(VLDTR_E_TD_ENUMFLDSIGMISMATCH, tkField);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ */
+ }
+ if(dwTally == 0)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_ENUMNOLITFLDS);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+ // Enum must have no methods
+ if (bHasMethods)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_ENUMHASMETHODS);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Enum must implement no interfaces
+ {
+ ULONG ridStart = 1;
+ ULONG ridEnd = pMiniMd->getCountInterfaceImpls() + 1;
+ ULONG index;
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ InterfaceImplRec *pInterfaceImplRecord;
+ IfFailGo(pMiniMd->GetInterfaceImplRecord(index, &pInterfaceImplRecord));
+ if (veCtxt.Token == pMiniMd->getClassOfInterfaceImpl(pInterfaceImplRecord))
+ {
+ REPORT_ERROR0(VLDTR_E_TD_ENUMIMPLIFACE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ }
+ }
+ // Enum must have no properties
+ {
+ ULONG ridStart = 1;
+ ULONG ridEnd = pMiniMd->getCountPropertys() + 1;
+ ULONG index;
+ mdToken tkClass;
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(pMiniMd->FindParentOfPropertyHelper(index | mdtProperty, &tkClass));
+ if (veCtxt.Token == tkClass)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_ENUMHASPROP);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ }
+ }
+ // Enum must have no events
+ {
+ ULONG ridStart = 1;
+ ULONG ridEnd = pMiniMd->getCountEvents() + 1;
+ ULONG index;
+ mdToken tkClass;
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(pMiniMd->FindParentOfEventHelper(index | mdtEvent, &tkClass));
+ if (veCtxt.Token == tkClass)
+ {
+ REPORT_ERROR0(VLDTR_E_TD_ENUMHASEVENT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ }
+ }
+ } // end if(bExtendsEnum)
+ // Class having security must be marked tdHasSecurity and vice versa
+ {
+ ULONG ridStart = 1;
+ ULONG ridEnd = pMiniMd->getCountDeclSecuritys() + 1;
+ ULONG index;
+ BOOL bHasSecurity = FALSE;
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ DeclSecurityRec *pDeclSecurityRecord;
+ IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pDeclSecurityRecord));
+ if (veCtxt.Token == pMiniMd->getParentOfDeclSecurity(pDeclSecurityRecord))
+ {
+ bHasSecurity = TRUE;
+ break;
+ }
+ }
+ if (!bHasSecurity) // No records, check for CA "SuppressUnmanagedCodeSecurityAttribute"
+ {
+ bHasSecurity = (S_OK == ImportHelper::GetCustomAttributeByName(pMiniMd, veCtxt.Token,
+ "System.Security.SuppressUnmanagedCodeSecurityAttribute", NULL, NULL));
+ }
+ if(bHasSecurity != (IsTdHasSecurity(pRecord->GetFlags())!=0))
+ {
+ REPORT_ERROR0(bHasSecurity ? VLDTR_E_TD_SECURNOTMARKED : VLDTR_E_TD_MARKEDNOSECUR);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateTypeDef()
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+// Validate the given FieldPtr.
+//*****************************************************************************
+HRESULT RegMeta::ValidateFieldPtr(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateFieldPtr()
+
+
+//*****************************************************************************
+// Validate the given Field.
+//*****************************************************************************
+HRESULT RegMeta::ValidateField(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ FieldRec *pRecord; // Field record.
+ mdTypeDef tkTypeDef; // Parent TypeDef token.
+ mdFieldDef tkFieldDef; // Duplicate FieldDef token.
+ LPCSTR szName; // FieldDef name.
+ PCCOR_SIGNATURE pbSig; // FieldDef signature.
+ ULONG cbSig; // Signature size in bytes.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ BOOL bIsValueField;
+ BOOL bIsGlobalField = FALSE;
+ BOOL bHasValidRVA = FALSE;
+ DWORD dwInvalidFlags;
+ DWORD dwFlags;
+ RID tempRid;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Get the FieldDef record.
+ veCtxt.Token = TokenFromRid(rid, mdtFieldDef);
+ veCtxt.uOffset = 0;
+
+ IfFailGo(pMiniMd->GetFieldRecord(rid, &pRecord));
+
+ // Do checks for name validity.
+ IfFailGo(pMiniMd->getNameOfField(pRecord, &szName));
+ if (!*szName)
+ {
+ // Field name is NULL.
+ REPORT_ERROR0(VLDTR_E_FD_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit;
+ ULONG L = (ULONG)strlen(szName);
+ if(L >= MAX_CLASSNAME_LENGTH)
+ {
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ bIsValueField = (strcmp(szName,BASE_VALUE_FIELDNAME)==0);
+ // If field is RTSpecialName, its name must be 'value__' and vice versa
+ if((IsFdRTSpecialName(pRecord->GetFlags())!=0) != bIsValueField)
+ {
+ REPORT_ERROR1(bIsValueField ? VLDTR_E_TD_ENUMVALNOTSN : VLDTR_E_FD_NOTVALUERTSN, veCtxt.Token);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate flags
+ dwFlags = pRecord->GetFlags();
+ dwInvalidFlags = ~(fdFieldAccessMask | fdStatic | fdInitOnly | fdLiteral | fdNotSerialized | fdSpecialName
+ | fdPinvokeImpl | fdReservedMask);
+ if(dwFlags & dwInvalidFlags)
+ {
+ REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwFlags & dwInvalidFlags);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate access
+ if((dwFlags & fdFieldAccessMask) == fdFieldAccessMask)
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_BADACCESSFLAG);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Literal : Static, !InitOnly
+ if(IsFdLiteral(dwFlags))
+ {
+ if(IsFdInitOnly(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FD_INITONLYANDLITERAL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(!IsFdStatic(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FD_LITERALNOTSTATIC);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(!IsFdHasDefault(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FD_LITERALNODEFAULT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ // RTSpecialName => SpecialName
+ if(IsFdRTSpecialName(dwFlags) && !IsFdSpecialName(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_RTSNNOTSN);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate Field signature.
+ IfFailGo(pMiniMd->getSignatureOfField(pRecord, &pbSig, &cbSig));
+ IfFailGo(ValidateFieldSig(TokenFromRid(rid, mdtFieldDef), pbSig, cbSig));
+ if (hr != S_OK)
+ SetVldtrCode(&hrSave, hr);
+
+ // Validate Field RVA
+ if(IsFdHasFieldRVA(dwFlags))
+ {
+ ULONG iFieldRVARid;
+ IfFailGo(pMiniMd->FindFieldRVAHelper(TokenFromRid(rid, mdtFieldDef), &iFieldRVARid));
+ if((iFieldRVARid==0) || (iFieldRVARid > pMiniMd->getCountFieldRVAs()))
+ {
+ REPORT_ERROR0(VLDTR_E_FD_RVAHASNORVA);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ /*
+ FieldRVARec *pRVARec;
+ IfFailGo(pMiniMd->GetFieldRVARecord(iFieldRVARid, &pRVARec));
+ if(pRVARec->GetRVA() == 0)
+ {
+ REPORT_ERROR0(VLDTR_E_FD_RVAHASZERORVA);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ */
+ bHasValidRVA = TRUE;
+ }
+ }
+
+ // Get the parent of the Field.
+ IfFailGo(pMiniMd->FindParentOfFieldHelper(TokenFromRid(rid, mdtFieldDef), &tkTypeDef));
+ // Validate that the parent is not nil.
+ if (IsNilToken(tkTypeDef))
+ {
+ REPORT_ERROR0(VLDTR_E_FD_PARNIL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (RidFromToken(tkTypeDef) != RidFromToken(m_tdModule))
+ {
+ if(IsValidToken(tkTypeDef) && (TypeFromToken(tkTypeDef) == mdtTypeDef))
+ {
+ TypeDefRec *pParentRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkTypeDef), &pParentRec));
+ // If the name is "value__" ...
+ if(bIsValueField)
+ {
+ // parent must be Enum
+ mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pParentRec);
+ RID ridExtends = RidFromToken(tkExtends);
+ LPCSTR szExtName="",szExtNameSpace="";
+ if(ridExtends)
+ {
+ if(TypeFromToken(tkExtends) == mdtTypeRef)
+ {
+ TypeRefRec *pExtRec;
+ IfFailGo(pMiniMd->GetTypeRefRecord(ridExtends, &pExtRec));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pExtRec, &szExtName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pExtRec, &szExtNameSpace));
+ }
+ else if(TypeFromToken(tkExtends) == mdtTypeDef)
+ {
+ TypeDefRec *pExtRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(ridExtends, &pExtRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pExtRec, &szExtName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtRec, &szExtNameSpace));
+ }
+ }
+ if(strcmp(szExtName,BASE_ENUM_CLASSNAME) || strcmp(szExtNameSpace,BASE_NAMESPACE))
+ {
+ REPORT_ERROR0(VLDTR_E_FD_VALUEPARNOTENUM);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // field must be instance - checked in ValidateTypeDef
+ // must be no other instance fields - checked in ValidateTypeDef
+ // must be first field - checked in ValidateTypeDef
+ // must be RTSpecialName -- checked in ValidateTypeDef
+ }
+ if(IsTdInterface(pMiniMd->getFlagsOfTypeDef(pParentRec)))
+ {
+ // Fields in interface are not CLS compliant
+ REPORT_ERROR0(VLDTR_E_FD_FLDINIFACE);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+
+ // If field is not static, verify parent is not interface.
+ if(!IsFdStatic(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FD_INSTINIFACE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ // If field is not public, verify parent is not interface.
+ if(!IsFdPublic(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FD_NOTPUBINIFACE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ } // end if Valid and TypeDef
+ else
+ {
+ REPORT_ERROR1(VLDTR_E_FD_BADPARENT, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else // i.e. if (RidFromToken(tkTypeDef) == RidFromToken(m_tdModule))
+ {
+ bIsGlobalField = TRUE;
+ // Globals are not CLS-compliant
+ REPORT_ERROR0(VLDTR_E_FMD_GLOBALITEM);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ // Validate global field:
+ // Must be static
+ if(!IsFdStatic(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_GLOBALNOTSTATIC);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Must have a non-zero RVA
+ /*
+ if(!bHasValidRVA)
+ {
+ REPORT_ERROR0(VLDTR_E_FD_GLOBALNORVA);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ */
+ }
+
+ // Check for duplicates, except global fields with PrivateScope.
+ if (*szName && cbSig && !IsFdPrivateScope(dwFlags))
+ {
+ hr = ImportHelper::FindField(pMiniMd, tkTypeDef, szName, pbSig, cbSig, &tkFieldDef, rid);
+ if (hr == S_OK)
+ {
+ if(!IsFdPrivateScope(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_FD_DUP, tkFieldDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else hr = S_OK;
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ hr = S_OK;
+ }
+ else
+ {
+ IfFailGo(hr);
+ }
+ }
+ // Field having security must be marked fdHasSecurity and vice versa
+ {
+ ULONG ridStart = 1;
+ ULONG ridEnd = pMiniMd->getCountDeclSecuritys() + 1;
+ ULONG index;
+ BOOL bHasSecurity = FALSE;
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ DeclSecurityRec *pDeclSecurityRecord;
+ IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pDeclSecurityRecord));
+ if ( veCtxt.Token == pMiniMd->getParentOfDeclSecurity(pDeclSecurityRecord))
+ {
+ bHasSecurity = TRUE;
+ break;
+ }
+ }
+ if(!bHasSecurity) // No records, check for CA "SuppressUnmanagedCodeSecurityAttribute"
+ {
+ bHasSecurity = (S_OK == ImportHelper::GetCustomAttributeByName(pMiniMd, veCtxt.Token,
+ "System.Security.SuppressUnmanagedCodeSecurityAttribute", NULL, NULL));
+ }
+ if(bHasSecurity)
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_SECURNOTMARKED);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ // Field having marshaling must be marked fdHasFieldMarshal and vice versa
+ IfFailGo(pMiniMd->FindFieldMarshalHelper(veCtxt.Token, &tempRid));
+ if (InvalidRid(tempRid) == (IsFdHasFieldMarshal(dwFlags) != 0))
+ {
+ REPORT_ERROR0(IsFdHasFieldMarshal(dwFlags)? VLDTR_E_FD_MARKEDNOMARSHAL : VLDTR_E_FD_MARSHALNOTMARKED);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Field having const value must be marked fdHasDefault and vice versa
+ IfFailGo(pMiniMd->FindConstantHelper(veCtxt.Token, &tempRid));
+ if(InvalidRid(tempRid) == (IsFdHasDefault(dwFlags) != 0))
+ {
+ REPORT_ERROR0(IsFdHasDefault(dwFlags)? VLDTR_E_FD_MARKEDNODEFLT : VLDTR_E_FD_DEFLTNOTMARKED);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Check the field's impl.map
+ {
+ ULONG iRecord;
+ IfFailGo(pMiniMd->FindImplMapHelper(veCtxt.Token, &iRecord));
+ if(IsFdPinvokeImpl(dwFlags))
+ {
+ // must be static
+ if(!IsFdStatic(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTSTATIC);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // must have ImplMap
+ if (InvalidRid(iRecord))
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_MARKEDNOPINVOKE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else
+ {
+ // must have no ImplMap
+ if (!InvalidRid(iRecord))
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTMARKED);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ if (!InvalidRid(iRecord))
+ {
+ hr = ValidateImplMap(iRecord);
+ if(hr != S_OK)
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_BADIMPLMAP);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateField()
+
+//*****************************************************************************
+// Validate the given MethodPtr.
+//*****************************************************************************
+HRESULT RegMeta::ValidateMethodPtr(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateMethodPtr()
+
+
+//*****************************************************************************
+// Validate the given Method.
+//*****************************************************************************
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+HRESULT RegMeta::ValidateMethod(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ MethodRec *pRecord = NULL; // Method record.
+ mdTypeDef tkTypeDef; // Parent TypeDef token.
+ mdMethodDef tkMethodDef; // Duplicate MethodDef token.
+ LPCSTR szName; // MethodDef name.
+ DWORD dwFlags = 0; // Method flags.
+ DWORD dwImplFlags = 0; // Method impl.flags.
+ PCCOR_SIGNATURE pbSig; // MethodDef signature.
+ ULONG cbSig; // Signature size in bytes.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ BOOL bIsCtor=FALSE;
+ BOOL bIsCctor=FALSE;
+ BOOL bIsGlobal=FALSE;
+ BOOL bIsParentImport = FALSE;
+ BOOL bIsGeneric = FALSE;
+ unsigned retType;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Get the MethodDef record.
+ veCtxt.Token = TokenFromRid(rid, mdtMethodDef);
+ veCtxt.uOffset = 0;
+
+ IfFailGo(pMiniMd->GetMethodRecord(rid, &pRecord));
+
+ // Do checks for name validity.
+ IfFailGo(pMiniMd->getNameOfMethod(pRecord, &szName));
+ if (!*szName)
+ {
+ // Method name is NULL.
+ REPORT_ERROR0(VLDTR_E_MD_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit;
+ bIsCtor = (0 == strcmp(szName,BASE_CTOR_NAME));
+ bIsCctor = (0 == strcmp(szName,BASE_CCTOR_NAME));
+ ULONG L = (ULONG)strlen(szName);
+ if(L >= MAX_CLASSNAME_LENGTH)
+ {
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ // Get the parent, flags and signature of the Method.
+ IfFailGo(pMiniMd->FindParentOfMethodHelper(TokenFromRid(rid, mdtMethodDef), &tkTypeDef));
+ dwFlags = pMiniMd->getFlagsOfMethod(pRecord);
+ dwImplFlags = pMiniMd->getImplFlagsOfMethod(pRecord);
+ IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig));
+
+ // Check for duplicates.
+ if (*szName && cbSig && !IsNilToken(tkTypeDef) && !IsMdPrivateScope(dwFlags))
+ {
+ hr = ImportHelper::FindMethod(pMiniMd, tkTypeDef, szName, pbSig, cbSig, &tkMethodDef, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_MD_DUP, tkMethodDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ }
+
+ // No further error checking for VtblGap methods.
+ if (IsVtblGapName(szName))
+ {
+ hr = hrSave;
+ goto ErrExit;
+ }
+
+ // Validate Method signature.
+ IfFailGo(ValidateMethodSig(TokenFromRid(rid, mdtMethodDef), pbSig, cbSig,
+ dwFlags));
+ if (hr != S_OK)
+ SetVldtrCode(&hrSave, hr);
+
+ // Validate that the parent is not nil.
+ if (IsNilToken(tkTypeDef))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_PARNIL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (RidFromToken(tkTypeDef) != RidFromToken(m_tdModule))
+ {
+ if(TypeFromToken(tkTypeDef) == mdtTypeDef)
+ {
+ TypeDefRec *pTDRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkTypeDef), &pTDRec));
+ DWORD dwTDFlags = pTDRec->GetFlags();
+ LPCSTR szTDName;
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTDRec, &szTDName));
+ LPCSTR szTDNameSpace;
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTDRec, &szTDNameSpace));
+ BOOL fIsTdValue=FALSE, fIsTdEnum=FALSE;
+ mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pTDRec);
+
+ if(0 == strcmp(szTDNameSpace,BASE_NAMESPACE))
+ {
+ fIsTdEnum = (0 == strcmp(szTDName,BASE_ENUM_CLASSNAME));
+ if(!fIsTdEnum)
+ {
+ fIsTdValue = (0 == strcmp(szTDName,BASE_VTYPE_CLASSNAME));
+ }
+ }
+ if(fIsTdEnum || fIsTdValue)
+ {
+ fIsTdEnum = fIsTdValue = FALSE; // System.Enum and System.ValueType themselves are classes
+ }
+ else if(RidFromToken(tkExtends))
+ {
+ if(TypeFromToken(tkExtends) == mdtTypeDef)
+ {
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pTDRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTDRec, &szTDName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTDRec, &szTDNameSpace));
+ }
+ else if(TypeFromToken(tkExtends) == mdtTypeSpec)
+ {
+ fIsTdEnum = fIsTdValue = FALSE; // a type extending a spec cannot be an enum or value type
+ // the assignments are redundant, but clear.
+ }
+ else
+ {
+ TypeRefRec *pTRRec;
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pTRRec));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTRRec, &szTDName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTRRec, &szTDNameSpace));
+ }
+
+ if(0 == strcmp(szTDNameSpace,BASE_NAMESPACE))
+ {
+ fIsTdEnum = (0 == strcmp(szTDName,BASE_ENUM_CLASSNAME));
+ if(!fIsTdEnum)
+ {
+ fIsTdValue = (0 == strcmp(szTDName,BASE_VTYPE_CLASSNAME));
+ }
+ else fIsTdValue = FALSE;
+ }
+ }
+
+ // If Method is abstract, verify parent is abstract.
+ if(IsMdAbstract(dwFlags) && !IsTdAbstract(dwTDFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_ABSTPARNOTABST, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // If parent is import, method must have zero RVA, otherwise it depends...
+ if(IsTdImport(dwTDFlags)) bIsParentImport = TRUE;
+ if(IsTdInterface(dwTDFlags))
+ {
+ if(!IsMdStatic(dwFlags))
+ {
+ // No non-abstract instance methods in interface.
+ if(!IsMdAbstract(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_NOTSTATABSTININTF, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // No non-public instance methods in interface.
+ if(!IsMdPublic(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_NOTPUBININTF, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ // If Method is constructor, verify parent is not interface.
+ if(bIsCtor)
+ {
+ REPORT_ERROR1(VLDTR_E_MD_CTORININTF, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }//end if(interface)
+ if((fIsTdValue || fIsTdEnum) && IsMiSynchronized(dwImplFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_SYNCMETHODINVTYPE, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(bIsCtor)
+ {
+ // .ctor must be instance
+ if(IsMdStatic(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_CTORSTATIC, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }//end if .ctor
+ else if(bIsCctor)
+ {
+ // .cctor must be static
+ if(!IsMdStatic(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_CCTORNOTSTATIC, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // ..cctor must have default callconv
+ IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig));
+ if(IMAGE_CEE_CS_CALLCONV_DEFAULT != CorSigUncompressData(pbSig))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_CCTORCALLCONV);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // .cctor must have no arguments
+ if(0 != CorSigUncompressData(pbSig))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_CCTORHASARGS);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+
+ }//end if .cctor
+ if(bIsCtor || bIsCctor)
+ {
+ // .ctor, .cctor must be SpecialName and RTSpecialName
+ if(!(IsMdSpecialName(dwFlags) && IsMdRTSpecialName(dwFlags)))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_CTORNOTSNRTSN, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+#ifdef NO_SUCH_CHECKS_NEEDED_SPEC_TO_BE_UODATED
+ // .ctor, .cctor must not be virtual
+ if(IsMdVirtual(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_CTORVIRT, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // .ctor, .cctor must not be abstract
+ if(IsMdAbstract(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_CTORABST, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // .ctor, .cctor must not be PInvoke
+ if(IsMdPinvokeImpl(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_CTORPINVOKE, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // .ctor,.cctor must have RVA!=0
+ if(pRecord->GetRVA()==0)
+ {
+ REPORT_ERROR0(VLDTR_E_MD_CTORZERORVA);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+#endif
+ }//end if .ctor or .cctor
+ }// end if(parent == TypeDef)
+ }// end if not Module
+ else // i.e. if (RidFromToken(tkTypeDef) == RidFromToken(m_tdModule))
+ {
+ bIsGlobal = TRUE;
+ // Globals are not CLS-compliant
+ REPORT_ERROR0(VLDTR_E_FMD_GLOBALITEM);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ // Validate global method:
+ // Must be static
+ if(!IsMdStatic(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_GLOBALNOTSTATIC);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Must not be abstract or virtual
+ if(IsMdAbstract(dwFlags) || IsMdVirtual(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_GLOBALABSTORVIRT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Must be not .ctor or .cctor
+ if(bIsCtor)
+ {
+ REPORT_ERROR0(VLDTR_E_MD_GLOBALCTORCCTOR);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ } //end if Module
+
+ // Signature specifics: .ctor, .cctor, entrypoint
+ if(bIsCtor || bIsCctor)
+ {
+ // .ctor, .cctor must return void
+ IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig));
+ CorSigUncompressData(pbSig); // get call conv out of the way
+ CorSigUncompressData(pbSig); // get num args out of the way
+ while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT)
+ || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig);
+ if(retType != ELEMENT_TYPE_VOID)
+ {
+ REPORT_ERROR0(VLDTR_E_MD_CTORNOTVOID);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ if(g_tkEntryPoint == veCtxt.Token)
+ {
+ ULONG ulCallConv;
+ // EP must be static
+ if(!IsMdStatic(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_EP_INSTANCE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // EP can't belong to generic class or nested in generic class
+ mdToken tkTypeDefCur;
+ for(tkTypeDefCur = tkTypeDef; tkTypeDefCur != mdTokenNil;)
+ {
+ HENUMInternal hEnumTyPars;
+ ULONG ulTypeDefArity = 0;
+ hr = pMiniMd->FindGenericParamHelper(tkTypeDefCur, &hEnumTyPars);
+ if (SUCCEEDED(hr))
+ {
+ IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulTypeDefArity));
+ HENUMInternal::ClearEnum(&hEnumTyPars);
+ if (ulTypeDefArity != 0)
+ {
+ REPORT_ERROR0(VLDTR_E_EP_GENERIC_TYPE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ if(ulTypeDefArity == 0)
+ {
+ // This class is not generic, how about the encloser?
+ RID iRecord;
+ IfFailGo(pMiniMd->FindNestedClassHelper(tkTypeDefCur, &iRecord));
+
+ if (InvalidRid(iRecord))
+ {
+ tkTypeDefCur = mdTokenNil;
+ }
+ else
+ {
+ NestedClassRec *pNestedClassRec;
+ IfFailGo(pMiniMd->GetNestedClassRecord(iRecord, &pNestedClassRec));
+ tkTypeDefCur = pMiniMd->getEnclosingClassOfNestedClass(pNestedClassRec);
+ }
+ }
+ else
+ tkTypeDefCur = mdTokenNil;
+ }
+
+ // EP must have a predetermined signature (different for DLL and EXE
+ IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig));
+ ulCallConv = CorSigUncompressData(pbSig); // get call conv out of the way
+ // EP can't be generic
+ if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ // Skip the arity
+ CorSigUncompressData(pbSig);
+ REPORT_ERROR0(VLDTR_E_EP_GENERIC_METHOD);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // EP must have 0 or 1 argument
+ unsigned nArgs = CorSigUncompressData(pbSig);
+ if(g_fIsDLL)
+ {
+ if(nArgs != 3)
+ {
+ REPORT_ERROR1(VLDTR_E_EP_TOOMANYARGS, 3);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ //EP must return I4
+ while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT)
+ || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig);
+
+ if(retType != ELEMENT_TYPE_I4)
+ {
+ REPORT_ERROR0(VLDTR_E_EP_BADRET);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Arguments must be VOID*, U4, VOID*
+ if(nArgs)
+ {
+ unsigned jj;
+ bool badarg;
+ for(jj=0; jj<nArgs;jj++)
+ {
+ while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT)
+ || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig);
+
+ switch(jj)
+ {
+ case 0:
+ case 2:
+ badarg = (retType != ELEMENT_TYPE_PTR)
+ ||(CorSigUncompressData(pbSig) != ELEMENT_TYPE_VOID);
+ break;
+
+ case 1:
+ badarg = (retType != ELEMENT_TYPE_U4);
+ break;
+
+ default:
+ badarg = true;
+ }
+ if(badarg)
+ {
+ REPORT_ERROR1(VLDTR_E_EP_BADARG, jj+1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ }
+ else
+ {
+ if(nArgs > 1)
+ {
+ REPORT_ERROR1(VLDTR_E_EP_TOOMANYARGS, 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ //EP must return VOID, I4 or U4
+ while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT)
+ || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig);
+
+ if((retType != ELEMENT_TYPE_VOID)&&(retType != ELEMENT_TYPE_I4)&&(retType != ELEMENT_TYPE_U4))
+ {
+ REPORT_ERROR0(VLDTR_E_EP_BADRET);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Argument (if any) must be vector of strings
+ if(nArgs)
+ {
+ while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT)
+ || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig);
+
+ if((retType != ELEMENT_TYPE_SZARRAY)||(CorSigUncompressData(pbSig) != ELEMENT_TYPE_STRING))
+ {
+ REPORT_ERROR1(VLDTR_E_EP_BADARG, 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ } // end if(IsDll)--else
+ } // end if (IsEntryPoint)
+
+
+ // Check method RVA
+ if(pRecord->GetRVA()==0)
+ {
+ if(!(IsMdPinvokeImpl(dwFlags) || IsMdAbstract(dwFlags)
+ || IsMiRuntime(dwImplFlags) || IsMiInternalCall(dwImplFlags)
+ || bIsParentImport))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_ZERORVA);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else
+ {
+ if(m_pStgdb && m_pStgdb->m_pImage)
+ {
+ NewHolder<PEDecoder> pe;
+
+ EX_TRY
+ {
+ // We need to use different PEDecoder constructors 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 (m_pStgdb->m_pStgIO->GetMemoryMappedType() == MTYPE_IMAGE)
+ pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, false);
+ else
+ pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, (COUNT_T)(m_pStgdb->m_dwImageSize));
+
+ }
+ EX_CATCH
+ {
+ hr = COR_E_BADIMAGEFORMAT;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ IfFailGo(hr);
+ IfNullGo(pe);
+
+ if (!pe->CheckRva(pRecord->GetRVA()))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_BADRVA, pRecord->GetRVA());
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ if(IsMiManaged(dwImplFlags) && (IsMiIL(dwImplFlags) || IsMiOPTIL(dwImplFlags)))
+ {
+ HRESULT hrTemp = S_OK;
+ // validate locals signature token
+ EX_TRY
+ {
+ COR_ILMETHOD_DECODER method((COR_ILMETHOD*) pe->GetRvaData(pRecord->GetRVA()));
+ if (method.LocalVarSigTok)
+ {
+ if((TypeFromToken(method.GetLocalVarSigTok()) != mdtSignature) ||
+ (!IsValidToken(method.GetLocalVarSigTok())) || (RidFromToken(method.GetLocalVarSigTok())==0))
+ {
+ hrTemp = _ValidateErrorHelper(VLDTR_E_MD_BADLOCALSIGTOK, veCtxt, method.GetLocalVarSigTok());
+ if (SUCCEEDED(hrTemp))
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ EX_CATCH
+ {
+ hrTemp = _ValidateErrorHelper(VLDTR_E_MD_BADHEADER, veCtxt);
+ if (SUCCEEDED(hrTemp))
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ IfFailGo(hrTemp);
+ }
+ }
+ }
+
+ if(IsMdAbstract(dwFlags) || bIsParentImport
+ || IsMiRuntime(dwImplFlags) || IsMiInternalCall(dwImplFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_ZERORVA);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ // Check the method flags
+ // Validate access
+ if((dwFlags & mdMemberAccessMask) == mdMemberAccessMask)
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_BADACCESSFLAG);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Final/NewSlot must be virtual
+ if((IsMdFinal(dwFlags)||IsMdNewSlot(dwFlags)||IsMdCheckAccessOnOverride(dwFlags))
+ && !IsMdVirtual(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_FINNOTVIRT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Static can't be final or virtual
+ if(IsMdStatic(dwFlags))
+ {
+ if(IsMdFinal(dwFlags) || IsMdVirtual(dwFlags) || IsMdNewSlot(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_STATANDFINORVIRT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else // non-static can't be an entry point
+ {
+ if(g_tkEntryPoint == veCtxt.Token)
+ {
+ REPORT_ERROR0(VLDTR_E_EP_INSTANCE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ if(IsMdAbstract(dwFlags))
+ {
+ // Can't be both abstract and final
+ if(IsMdFinal(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_ABSTANDFINAL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // If abstract, must be not miForwardRef, not Pinvoke, and must be virtual
+ if(IsMiForwardRef(dwImplFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_ABSTANDIMPL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(IsMdPinvokeImpl(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_ABSTANDPINVOKE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(!IsMdVirtual(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_ABSTNOTVIRT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ // If PrivateScope, must have RVA!=0
+ if(IsMdPrivateScope(dwFlags) && (pRecord->GetRVA() ==0))
+ {
+ REPORT_ERROR0(VLDTR_E_MD_PRIVSCOPENORVA);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // RTSpecialName => SpecialName
+ if(IsMdRTSpecialName(dwFlags) && !IsMdSpecialName(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_RTSNNOTSN);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Method having security must be marked mdHasSecurity and vice versa
+ {
+ ULONG ridStart = 1;
+ ULONG ridEnd = pMiniMd->getCountDeclSecuritys() + 1;
+ ULONG index;
+ BOOL bHasSecurity = FALSE;
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ DeclSecurityRec *pDeclSecurityRecord;
+ IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pDeclSecurityRecord));
+ if ( veCtxt.Token == pMiniMd->getParentOfDeclSecurity(pDeclSecurityRecord))
+ {
+ bHasSecurity = TRUE;
+ break;
+ }
+ }
+ if(!bHasSecurity) // No records, check for CA "SuppressUnmanagedCodeSecurityAttribute"
+ {
+ bHasSecurity = (S_OK == ImportHelper::GetCustomAttributeByName(pMiniMd, veCtxt.Token,
+ "System.Security.SuppressUnmanagedCodeSecurityAttribute", NULL, NULL));
+ }
+ if(bHasSecurity != (IsMdHasSecurity(dwFlags)!=0))
+ {
+ REPORT_ERROR0(bHasSecurity ? VLDTR_E_FMD_SECURNOTMARKED : VLDTR_E_FMD_MARKEDNOSECUR);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ // Validate method semantics
+ {
+ MethodSemanticsRec *pRec;
+ ULONG ridEnd;
+ ULONG index;
+ unsigned uTally = 0;
+ mdToken tkEventProp;
+ ULONG iCount;
+ DWORD dwSemantic;
+ // get the range of method rids given a typedef
+ ridEnd = pMiniMd->getCountMethodSemantics();
+
+ for (index = 1; index <= ridEnd; index++ )
+ {
+ IfFailGo(pMiniMd->GetMethodSemanticsRecord(index, &pRec));
+ if ( pMiniMd->getMethodOfMethodSemantics(pRec) == veCtxt.Token )
+ {
+ uTally++;
+ if(uTally > 1)
+ {
+ REPORT_ERROR0(VLDTR_E_MD_MULTIPLESEMANTICS);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ tkEventProp = pMiniMd->getAssociationOfMethodSemantics(pRec);
+ if((TypeFromToken(tkEventProp) == mdtEvent)||(TypeFromToken(tkEventProp) == mdtProperty))
+ {
+ iCount = (TypeFromToken(tkEventProp) == mdtEvent) ? pMiniMd->getCountEvents() :
+ pMiniMd->getCountPropertys();
+ if(RidFromToken(tkEventProp) > iCount)
+ {
+ REPORT_ERROR1(VLDTR_E_MD_SEMANTICSNOTEXIST, tkEventProp);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+ else
+ {
+ REPORT_ERROR1(VLDTR_E_MD_INVALIDSEMANTICS, tkEventProp);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ // One and only one semantics flag must be set
+ iCount = 0;
+ dwSemantic = pRec->GetSemantic();
+ if(IsMsSetter(dwSemantic)) iCount++;
+ if(IsMsGetter(dwSemantic)) iCount++;
+ if(IsMsOther(dwSemantic)) iCount++;
+ if(IsMsAddOn(dwSemantic)) iCount++;
+ if(IsMsRemoveOn(dwSemantic)) iCount++;
+ if(IsMsFire(dwSemantic)) iCount++;
+ if(iCount != 1)
+ {
+ REPORT_ERROR1(iCount ? VLDTR_E_MD_MULTSEMANTICFLAGS : VLDTR_E_MD_NOSEMANTICFLAGS, tkEventProp);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+ }// end for(index)
+ }
+ // Check the method's impl.map
+ {
+ RID iRecord;
+ IfFailGo(pMiniMd->FindImplMapHelper(veCtxt.Token, &iRecord));
+ if(IsMdPinvokeImpl(dwFlags))
+ {
+ // must be static
+ if(!IsMdStatic(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTSTATIC);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // must have either ImplMap or RVA == 0
+ if (InvalidRid(iRecord))
+ {
+ if(pRecord->GetRVA()==0)
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_MARKEDNOPINVOKE);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else
+ {
+ if(pRecord->GetRVA()!=0)
+ {
+ // C++ emits ImplMaps for IJW methods,
+ // with resolution=ModuleRef with name ""
+ ImplMapRec *pIMRecord;
+ mdToken tkModuleRef;
+ IfFailGo(pMiniMd->GetImplMapRecord(iRecord, &pIMRecord));
+ tkModuleRef = pMiniMd->getImportScopeOfImplMap(pIMRecord);
+ if((TypeFromToken(tkModuleRef) == mdtModuleRef) && (!IsNilToken(tkModuleRef)))
+ {
+ ModuleRefRec *pMRRecord; // ModuleRef record.
+ LPCUTF8 szMRName; // ModuleRef name.
+ // Get the ModuleRef record.
+ IfFailGo(pMiniMd->GetModuleRefRecord(RidFromToken(tkModuleRef), &pMRRecord));
+ // Check ModuleRef name is "".
+ IfFailGo(pMiniMd->getNameOfModuleRef(pMRRecord, &szMRName));
+ if (*szMRName)
+ {
+ REPORT_ERROR0(VLDTR_E_MD_RVAANDIMPLMAP);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ else
+ {
+ hr = ValidateImplMap(iRecord);
+ if(hr != S_OK)
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_BADIMPLMAP);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+ }
+
+ }
+ else
+ {
+ // must have no ImplMap
+ if (!InvalidRid(iRecord))
+ {
+ REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTMARKED);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ // Validate params
+ {
+ ULONG ridStart = pMiniMd->getParamListOfMethod(pRecord);
+ ULONG ridEnd;
+ IfFailGo(pMiniMd->getEndParamListOfMethod(rid, &ridEnd));
+ ParamRec* pRec;
+ ULONG cbSigT;
+ PCCOR_SIGNATURE typePtr;
+ IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &typePtr, &cbSigT));
+ unsigned callConv = CorSigUncompressData(typePtr); // get the calling convention out of the way
+ unsigned numTyArgs = 0;
+ if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ bIsGeneric = TRUE;
+ numTyArgs = CorSigUncompressData(typePtr);
+ }
+
+ unsigned numArgs = CorSigUncompressData(typePtr);
+ USHORT usPrevSeq = 0;
+
+ for(ULONG ridP = ridStart; ridP < ridEnd; ridP++)
+ {
+ RID tempRid;
+ IfFailGo(pMiniMd->GetParamRecord(ridP, &pRec));
+ // Sequence order must be ascending
+ if(ridP > ridStart)
+ {
+ if(pRec->GetSequence() <= usPrevSeq)
+ {
+ REPORT_ERROR2(VLDTR_E_MD_PARAMOUTOFSEQ, ridP-ridStart,pRec->GetSequence());
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+ usPrevSeq = pRec->GetSequence();
+ // Sequence value must not exceed num of arguments
+ if(usPrevSeq > numArgs)
+ {
+ REPORT_ERROR2(VLDTR_E_MD_PARASEQTOOBIG, ridP-ridStart,usPrevSeq);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Param having marshaling must be marked pdHasFieldMarshal and vice versa
+ IfFailGo(pMiniMd->FindFieldMarshalHelper(TokenFromRid(ridP,mdtParamDef), &tempRid));
+ if (InvalidRid(tempRid) == (IsPdHasFieldMarshal(pRec->GetFlags()) != 0))
+ {
+ REPORT_ERROR1(IsPdHasFieldMarshal(pRec->GetFlags()) ? VLDTR_E_MD_PARMMARKEDNOMARSHAL
+ : VLDTR_E_MD_PARMMARSHALNOTMARKED, ridP-ridStart);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Param having const value must be marked pdHasDefault and vice versa
+ IfFailGo(pMiniMd->FindConstantHelper(TokenFromRid(ridP,mdtParamDef), &tempRid));
+ if (InvalidRid(tempRid) == (IsPdHasDefault(pRec->GetFlags()) != 0))
+ {
+ REPORT_ERROR1(IsPdHasDefault(pRec->GetFlags()) ? VLDTR_E_MD_PARMMARKEDNODEFLT
+ : VLDTR_E_MD_PARMDEFLTNOTMARKED, ridP-ridStart);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+
+ // Generic Method related checks
+ if (bIsGeneric)
+ {
+ if (bIsCctor)
+ {
+ REPORT_ERROR0(VLDTR_E_MD_GENERIC_CCTOR);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ if (bIsCtor)
+ {
+ REPORT_ERROR0(VLDTR_E_MD_GENERIC_CTOR);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ if (bIsParentImport)
+ {
+ REPORT_ERROR0(VLDTR_E_MD_GENERIC_IMPORT);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateMethod()
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+// Validate the given ParamPtr.
+//*****************************************************************************
+HRESULT RegMeta::ValidateParamPtr(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateParamPtr()
+
+//*****************************************************************************
+// Validate the given Param.
+//*****************************************************************************
+HRESULT RegMeta::ValidateParam(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ ParamRec *pRecord; // Param record
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ LPCSTR szName; // Param name.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Get the InterfaceImpl record.
+ veCtxt.Token = TokenFromRid(rid, mdtParamDef);
+ veCtxt.uOffset = 0;
+
+ DWORD dwBadFlags = 0;
+ DWORD dwFlags = 0;
+ IfFailGo(pMiniMd->GetParamRecord(rid, &pRecord));
+ // Name, if any, must not exceed MAX_CLASSNAME_LENGTH
+ IfFailGo(pMiniMd->getNameOfParam(pRecord, &szName));
+ ULONG L = (ULONG)strlen(szName);
+ if(L >= MAX_CLASSNAME_LENGTH)
+ {
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Flags must be as defined in CorHdr.h
+ dwBadFlags = ~(pdIn | pdOut | pdOptional | pdHasDefault | pdHasFieldMarshal);
+ dwFlags = pRecord->GetFlags();
+ if(dwFlags & dwBadFlags)
+ {
+ REPORT_ERROR1(VLDTR_E_PD_BADFLAGS, dwFlags);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateParam()
+
+//*****************************************************************************
+// Helper function for ValidateInterfaceImpl
+//*****************************************************************************
+int IsMethodImplementedByClass(CMiniMdRW *pMiniMd,
+ mdToken tkMethod,
+ LPCUTF8 szName,
+ PCCOR_SIGNATURE pSig,
+ ULONG cbSig,
+ mdToken tkClass)
+{
+ HRESULT hr;
+ int numImpl = 0;
+ if(TypeFromToken(tkMethod) == mdtMethodDef)
+ {
+ if(TypeFromToken(tkClass) == mdtTypeSpec)
+ {
+ // We are trying to find out if an interface method is implemented in the generic class tkClass.
+ // Simple signature comparison doesn't work here, because "int Method()" in the interface might
+ // be implemented by "T Type.Method()" in the generic type.
+ // Therefore we assume it is implemented. Atlernatively we could implement better signature
+ // comparison which would match T with any other type, etc.
+ numImpl = 1;
+ }
+ else if(TypeFromToken(tkClass) == mdtTypeDef)
+ {
+ TypeDefRec *pClass;
+ IfFailRet(pMiniMd->GetTypeDefRecord(RidFromToken(tkClass), &pClass));
+ RID ridClsStart = pMiniMd->getMethodListOfTypeDef(pClass);
+ RID ridClsEnd;
+ IfFailRet(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(tkClass), &ridClsEnd));
+ mdMethodDef tkFoundMethod = 0;
+ DWORD dwFoundMethodFlags = 0;
+ // Check among methods
+ hr = ImportHelper::FindMethod(pMiniMd, tkClass, szName, pSig, cbSig, &tkFoundMethod, 0);
+ if(SUCCEEDED(hr))
+ {
+ MethodRec * pMethod;
+ IfFailRet(pMiniMd->GetMethodRecord(RidFromToken(tkFoundMethod), &pMethod));
+ if(pMethod)
+ {
+ dwFoundMethodFlags = pMiniMd->getFlagsOfMethod(pMethod);
+ if(IsMdVirtual(dwFoundMethodFlags)) //&&!IsMdNewSlot(dwFoundMethodFlags))
+ numImpl = 1;
+ }
+ }
+ if (numImpl==0) //if(hr == CLDB_E_RECORD_NOTFOUND)
+ { // Check among MethodImpls
+ RID ridImpl;
+ for(RID idxCls = ridClsStart; idxCls < ridClsEnd; idxCls++)
+ {
+ RID ridCls;
+ IfFailRet(pMiniMd->GetMethodRid(idxCls, &ridCls));
+
+ hr = ImportHelper::FindMethodImpl(pMiniMd,tkClass,TokenFromRid(ridCls,mdtMethodDef),
+ tkMethod,&ridImpl);
+ if(hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ if(SUCCEEDED(hr)) numImpl++;
+ break;
+ }
+ }
+ if(numImpl == 0)
+ {
+ // Check if parent class implements this method
+ mdToken tkParent = pMiniMd->getExtendsOfTypeDef(pClass);
+ if(RidFromToken(tkParent))
+ numImpl = IsMethodImplementedByClass(pMiniMd,tkMethod,szName,pSig,cbSig,tkParent);
+ }
+ }
+ }
+ else if (TypeFromToken(tkClass) == mdtTypeRef)
+ {
+ TypeRefRec *pRecord; // TypeRef record.
+ LPCSTR szTRNamespace; // TypeRef Namespace.
+ LPCSTR szTRName; // TypeRef Name.
+
+ // Get the TypeRef record.
+ IfFailRet(pMiniMd->GetTypeRefRecord(RidFromToken(tkClass), &pRecord));
+
+ // Check name is not NULL.
+ IfFailRet(pMiniMd->getNamespaceOfTypeRef(pRecord, &szTRNamespace));
+ IfFailRet(pMiniMd->getNameOfTypeRef(pRecord, &szTRName));
+
+ mdToken tkRefScope = pMiniMd->getResolutionScopeOfTypeRef(pRecord);
+ if (tkRefScope == TokenFromRid(1, mdtModule))
+ {
+ // if the typeref is referring to a type in this module then
+ // we should check the type definition it is referring to
+ mdTypeDef tkTypeDef;
+ hr = ImportHelper::FindTypeDefByName(pMiniMd, szTRNamespace, szTRName, tkRefScope, &tkTypeDef);
+ if (SUCCEEDED(hr))
+ numImpl = IsMethodImplementedByClass(pMiniMd, tkMethod, szName, pSig, cbSig, tkTypeDef);
+ }
+ else if ((strcmp(szTRNamespace, BASE_NAMESPACE) == 0) &&
+ ((strcmp(szTRName, BASE_OBJECT_CLASSNAME) == 0) ||
+ (strcmp(szTRName, BASE_VTYPE_CLASSNAME) == 0) ||
+ (strcmp(szTRName, BASE_ENUM_CLASSNAME) == 0)))
+ {
+ if (((strcmp(szName, SYSTEM_OBJECT_TOSTRING_METHODNAME) == 0) &&
+ (cbSig == _countof(g_sigSystemObject_ToString)) &&
+ (memcmp(pSig, g_sigSystemObject_ToString, cbSig) == 0)) ||
+ ((strcmp(szName, SYSTEM_OBJECT_GETHASHCODE_METHODNAME) == 0) &&
+ (cbSig == _countof(g_sigSystemObject_GetHashCode)) &&
+ (memcmp(pSig, g_sigSystemObject_GetHashCode, cbSig) == 0)) ||
+ ((strcmp(szName, SYSTEM_OBJECT_EQUALS_METHODNAME) == 0) &&
+ (cbSig == _countof(g_sigSystemObject_Equals)) &&
+ (memcmp(pSig, g_sigSystemObject_Equals, cbSig) == 0)))
+ {
+ numImpl = 1; // Method signature matches one of System.Object's virtual methods
+ }
+ else
+ {
+ numImpl = 0; // These classes (System.Object, System.ValueType and System.Enum) don't implement any other virtual methods
+ }
+ }
+ else
+ {
+ numImpl = -1; // The method is defined in another module, we cannot verify it (no external modules are loaded)
+ }
+ }
+ }
+ return numImpl;
+}
+
+//*****************************************************************************
+// Validate the given InterfaceImpl.
+//*****************************************************************************
+//@todo GENERICS: complete logic for type specs
+// - for now, we just allow them, but we should be checking more properties
+HRESULT RegMeta::ValidateInterfaceImpl(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ InterfaceImplRec *pRecord; // InterfaceImpl record.
+ mdTypeDef tkClass; // Class implementing the interface.
+ mdToken tkInterface; // TypeDef for the interface.
+ mdInterfaceImpl tkInterfaceImpl; // Duplicate InterfaceImpl.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ BOOL fCheckTheMethods=TRUE;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Get the InterfaceImpl record.
+ veCtxt.Token = TokenFromRid(rid, mdtInterfaceImpl);
+ veCtxt.uOffset = 0;
+
+ IfFailGo(pMiniMd->GetInterfaceImplRecord(rid, &pRecord));
+
+ // Get implementing Class and the TypeDef for the interface.
+ tkClass = pMiniMd->getClassOfInterfaceImpl(pRecord);
+
+ // No validation needs to be done on deleted records.
+ if (IsNilToken(tkClass))
+ goto ErrExit;
+
+ tkInterface = pMiniMd->getInterfaceOfInterfaceImpl(pRecord);
+
+ // Validate that the Class is TypeDef.
+ if((!IsValidToken(tkClass))||(TypeFromToken(tkClass) != mdtTypeDef)/*&&(TypeFromToken(tkClass) != mdtTypeRef)*/)
+ {
+ REPORT_ERROR1(VLDTR_E_IFACE_BADIMPL, tkClass);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ fCheckTheMethods = FALSE;
+ }
+ // Validate that the Interface is TypeDef or TypeRef or TypeSpec
+ if((!IsValidToken(tkInterface))||(TypeFromToken(tkInterface) != mdtTypeDef)&&(TypeFromToken(tkInterface) != mdtTypeRef)
+ &&(TypeFromToken(tkInterface) != mdtTypeSpec))
+ {
+ REPORT_ERROR1(VLDTR_E_IFACE_BADIFACE, tkInterface);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ fCheckTheMethods = FALSE;
+ }
+ // Validate that Interface is marked tdInterface.
+ else if(TypeFromToken(tkInterface) == mdtTypeDef)
+ {
+ TypeDefRec *pTDRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkInterface), &pTDRec));
+ if(!IsTdInterface(pTDRec->GetFlags()))
+ {
+ REPORT_ERROR1(VLDTR_E_IFACE_NOTIFACE, tkInterface);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ }
+
+ // Look for duplicates.
+ hr = ImportHelper::FindInterfaceImpl(pMiniMd, tkClass, tkInterface,
+ &tkInterfaceImpl, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_IFACE_DUP, tkInterfaceImpl);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+
+ // Validate that the Class (if not interface or abstract) implements all the methods of Interface
+ if((TypeFromToken(tkInterface) == mdtTypeDef) && fCheckTheMethods && (tkInterface != tkClass))
+ {
+ TypeDefRec *pClass;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkClass), &pClass));
+ if(!(IsTdAbstract(pClass->GetFlags())
+ ||IsTdImport(pClass->GetFlags())
+ ||IsTdInterface(pClass->GetFlags())))
+ {
+ TypeDefRec *pInterface;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkInterface), &pInterface));
+ RID ridIntStart = pMiniMd->getMethodListOfTypeDef(pInterface);
+ RID ridIntEnd;
+ IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(tkInterface), &ridIntEnd));
+ MethodRec* pIntMethod;
+ for(RID idxInt = ridIntStart; idxInt < ridIntEnd; idxInt++)
+ {
+ RID ridInt;
+ IfFailGo(pMiniMd->GetMethodRid(idxInt, &ridInt));
+ IfFailGo(pMiniMd->GetMethodRecord(ridInt, &pIntMethod));
+ const char* szName;
+ IfFailGo(pMiniMd->getNameOfMethod(pIntMethod, &szName));
+ if(!IsMdStatic(pIntMethod->GetFlags())
+ && !IsDeletedName(szName)
+ && !IsVtblGapName(szName))
+ {
+ ULONG cbSig;
+ PCCOR_SIGNATURE pSig;
+ IfFailGo(pMiniMd->getSignatureOfMethod(pIntMethod, &pSig, &cbSig));
+ if(cbSig)
+ {
+ int num = IsMethodImplementedByClass(pMiniMd,TokenFromRid(ridInt,mdtMethodDef),szName,pSig,cbSig,tkClass);
+ if(num == 0)
+ { // Error: method not implemented
+ REPORT_ERROR3(VLDTR_E_IFACE_METHNOTIMPL, tkClass, tkInterface, TokenFromRid(ridInt,mdtMethodDef));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(num == -1)
+ {
+ // Traced to a TypeRef, which might implement the method, give warning
+ REPORT_ERROR3(VLDTR_E_IFACE_METHNOTIMPLTHISMOD, tkClass, tkInterface, TokenFromRid(ridInt,mdtMethodDef));
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ if(num > 1)
+ { // Error: multiple method implementation
+ REPORT_ERROR3(VLDTR_E_IFACE_METHMULTIMPL, tkClass, tkInterface, TokenFromRid(ridInt,mdtMethodDef));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ }
+ }
+ }
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateInterfaceImpl()
+
+//*****************************************************************************
+// Validate the given GenericParam.
+//*****************************************************************************
+HRESULT RegMeta::ValidateGenericParam(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ GenericParamRec *pRecord; // GenericParam record.
+ LPCSTR szName; // GenericParam name field.
+ mdToken tkOwner; // GenericParam owner field.
+ ULONG ulNumber; // GenericParam number field.
+ DWORD dwFlags; // GenericParam flags field
+
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Get the GenericParam record.
+ veCtxt.Token = TokenFromRid(rid, mdtGenericParam);
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetGenericParamRecord(rid, &pRecord));
+
+ // 1. GenericParam may contain zero or more rows.
+ // (Nothing to check.)
+
+ tkOwner = pMiniMd->getOwnerOfGenericParam(pRecord);
+ // 2. Owner must be a valid token and a type def or method def
+ // (Already checked by ValidateRecord)
+
+ // CLR tolerates Nil owners, ECMA does not
+ if(IsNilToken(tkOwner))
+ {
+ REPORT_ERROR0(VLDTR_E_GP_OWNERNIL);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+
+ //3. Every generic type shall own one row in the Generic Param table for each of its type parameters. [ERROR]
+ // (Nothing to check, as the arity of a generic type is, by definition, the number of generic param entries).
+
+ //4. Every generic method shall own one row in the Generic Param table for each of its type parameters. [ERROR]
+ // (This is checked in ValidateMethodSig, error VLDTR_E_MD_GPMISMATCH).
+
+ ulNumber = pMiniMd->getNumberOfGenericParam(pRecord);
+
+ // 5. Flags must be valid
+ {
+ DWORD dwInvalidMask, dwExtraBits;
+
+ dwFlags = pMiniMd->getFlagsOfGenericParam(pRecord);
+
+
+ // check for extra bits
+ dwInvalidMask = (DWORD)~(gpVarianceMask|gpSpecialConstraintMask);
+ dwExtraBits = dwFlags & dwInvalidMask;
+ if(dwExtraBits)
+ {
+ //@GENERICS: we could use a custom error,
+ // but this is one is already used in more than one context.
+ REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwExtraBits);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ //Check Variance
+ {
+ DWORD dwVariance = dwFlags & gpVarianceMask;
+ switch (dwVariance)
+ {
+ case gpNonVariant:
+ // always ok
+ break;
+ case gpCovariant:
+ case gpContravariant:
+ if (TypeFromToken(tkOwner)==mdtTypeDef)
+ {
+ if (IsNilToken(tkOwner))
+ break;
+ TypeDefRec *pTypeDefRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkOwner), &pTypeDefRec));
+ // co-contra variance only legal on interfaces and delegates
+ // If owner is not an interface and does not extend MultiCastDelegate, report an error
+ if(!IsTdInterface(pTypeDefRec->GetFlags()))
+ {
+ // Get the parent of the TypeDef.
+ mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pTypeDefRec);
+ LPCSTR szExtName = NULL; // Parent Name.
+ LPCSTR szExtNameSpace = NULL; // Parent NameSpace.
+ BOOL bExtendsMCDelegate = FALSE;
+
+ // Determine if the parent is MCDelegate
+ if (TypeFromToken(tkExtends) != mdtTypeSpec)
+ {
+ if (TypeFromToken(tkExtends) == mdtTypeRef)
+ {
+ TypeRefRec *pExtTypeRefRec;
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pExtTypeRefRec));
+ mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pExtTypeRefRec);
+ if (RidFromToken(tkResScope) && (TypeFromToken(tkResScope) == mdtAssemblyRef))
+ {
+ AssemblyRefRec * pARRec;
+ IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResScope), &pARRec));
+ LPCSTR szAssemblyRefName;
+ IfFailGo(pMiniMd->getNameOfAssemblyRef(pARRec, &szAssemblyRefName));
+ if ((0 == SString::_stricmp("mscorlib", szAssemblyRefName)) || (0 == SString::_stricmp("System.Runtime", szAssemblyRefName)))
+ // otherwise don't even bother extracting the name
+ {
+ IfFailGo(pMiniMd->getNameOfTypeRef(pExtTypeRefRec, &szExtName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pExtTypeRefRec, &szExtNameSpace));
+ }
+ }
+ }
+ else if (TypeFromToken(tkExtends) == mdtTypeDef)
+ {
+ if (g_fValidatingMscorlib) // otherwise don't even bother extracting the name
+ {
+ TypeDefRec * pExtTypeRefRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pExtTypeRefRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pExtTypeRefRec, &szExtName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtTypeRefRec, &szExtNameSpace));
+ }
+ }
+
+ bExtendsMCDelegate =
+ szExtNameSpace && szExtName &&
+ (0 == strcmp(szExtNameSpace,BASE_NAMESPACE)) &&
+ (0 == strcmp(szExtName,BASE_MCDELEGATE_CLASSNAME));
+ }
+
+ // Report any error
+ if (!bExtendsMCDelegate)
+ {
+ REPORT_ERROR1(VLDTR_E_GP_UNEXPECTED_OWNER_FOR_VARIANT_VAR,tkOwner);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ else
+ {
+ // co-contra variance never legal on MVARs
+ REPORT_ERROR0(VLDTR_E_GP_ILLEGAL_VARIANT_MVAR);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ break;
+ default:
+ REPORT_ERROR1(VLDTR_E_GP_ILLEGAL_VARIANCE_FLAGS,dwFlags);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ }
+
+ // Check special constraints
+ {
+ DWORD dwSpecialConstraints = dwFlags & gpSpecialConstraintMask;
+ // It is illegal go declare both gpNotNullableValueTypeConstraint
+ // and gpReferenceTypeConstraint, but gpDefaultConstructorConstraint
+ // is legal with either (or neither).
+ if ((dwSpecialConstraints & (gpReferenceTypeConstraint | gpNotNullableValueTypeConstraint)) ==
+ (gpReferenceTypeConstraint | gpNotNullableValueTypeConstraint))
+ {
+ REPORT_ERROR1(VLDTR_E_GP_REFANDVALUETYPE,dwFlags);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+
+ // 6. Number shall have a value >=0 and < number of type parameters in owner type or method.
+ // 7. Successive rows of the GenericParam table that are owned by the same method (sic) (owner?) shall
+ // be ordered by increasing Number value; there can be no gaps in the Number sequence.
+ {
+ if(ulNumber>0)
+ { if(rid==1)
+ {
+ REPORT_ERROR0(VLDTR_E_GP_NONSEQ_BY_NUMBER);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ GenericParamRec *pPredRecord;
+ IfFailGo(pMiniMd->GetGenericParamRecord(rid-1, &pPredRecord));
+ mdToken tkPredOwner = pMiniMd->getOwnerOfGenericParam(pPredRecord);
+ ULONG ulPredNumber = pMiniMd->getNumberOfGenericParam(pPredRecord);
+ if (tkPredOwner != tkOwner)
+ {
+ REPORT_ERROR0(VLDTR_E_GP_NONSEQ_BY_OWNER);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if (ulPredNumber != ulNumber-1)
+ {
+ REPORT_ERROR0(VLDTR_E_GP_NONSEQ_BY_NUMBER);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ }
+
+ // 8. Name must be non-null and not too long
+ IfFailGo(pMiniMd->getNameOfGenericParam(pRecord, &szName));
+ if (!*szName)
+ {
+ // name is NULL.
+ REPORT_ERROR0(VLDTR_E_GP_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit; //@GENERICS: do we allow parameters to be deleted?
+ ULONG L = (ULONG)strlen(szName);
+ if(L >= MAX_CLASSNAME_LENGTH)
+ {
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+#ifdef THIS_RULE_IS_DISABLED_BECAUSE_CSHARP_EMITS_DUP_NAMES_AND_DOESNT_WANT_TO_STOP
+ // 9. There shall be no duplicates based upon Owner and Name
+ if (szName)
+ {
+ mdGenericParam tkDupGenericParam;
+ hr = ImportHelper::FindGenericParamByOwner(pMiniMd, tkOwner, szName, NULL, &tkDupGenericParam, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_GP_DUPNAME, tkDupGenericParam);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ }
+#endif
+
+ // 10. There shall be no duplicates based upon Owner and Number
+ {
+ mdGenericParam tkDupGenericParam;
+ hr = ImportHelper::FindGenericParamByOwner(pMiniMd, tkOwner, NULL, &ulNumber, &tkDupGenericParam, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_GP_DUPNUMBER, tkDupGenericParam);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ }
+
+ hr = hrSave;
+
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateGenericParam()
+
+//*****************************************************************************
+// Validate the given MemberRef.
+//*****************************************************************************
+HRESULT RegMeta::ValidateMemberRef(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ MemberRefRec *pRecord; // MemberRef record.
+ mdMemberRef tkMemberRef; // Duplicate MemberRef.
+ mdToken tkClass; // MemberRef parent.
+ LPCSTR szName; // MemberRef name.
+ PCCOR_SIGNATURE pbSig; // MemberRef signature.
+ PCCOR_SIGNATURE pbSigTmp; // Temp copy of pbSig, so that can be changed.
+ ULONG cbSig; // Size of sig in bytes.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Get the MemberRef record.
+ veCtxt.Token = TokenFromRid(rid, mdtMemberRef);
+ veCtxt.uOffset = 0;
+
+ IfFailGo(pMiniMd->GetMemberRefRecord(rid, &pRecord));
+
+ // Do checks for name validity.
+ IfFailGo(pMiniMd->getNameOfMemberRef(pRecord, &szName));
+ if (!*szName)
+ {
+ REPORT_ERROR0(VLDTR_E_MR_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ if (IsVtblGapName(szName))
+ {
+ REPORT_ERROR0(VLDTR_E_MR_VTBLNAME);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (IsDeletedName(szName))
+ {
+ REPORT_ERROR0(VLDTR_E_MR_DELNAME);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ ULONG L = (ULONG)strlen(szName);
+ if(L >= MAX_CLASSNAME_LENGTH)
+ {
+ // Name too long
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ // MemberRef parent should never be nil in a PE file.
+ tkClass = pMiniMd->getClassOfMemberRef(pRecord);
+ if (m_ModuleType == ValidatorModuleTypePE && IsNilToken(tkClass))
+ {
+ REPORT_ERROR0(VLDTR_E_MR_PARNIL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Verify that the signature is a valid signature as per signature spec.
+ IfFailGo(pMiniMd->getSignatureOfMemberRef(pRecord, &pbSig, &cbSig));
+
+ // Do some semantic checks based on the signature.
+ if (hr == S_OK)
+ {
+ ULONG ulCallingConv;
+ ULONG ulArgCount;
+ ULONG ulTyArgCount = 0;
+ ULONG ulCurByte = 0;
+
+ // Extract calling convention.
+ pbSigTmp = pbSig;
+ ulCurByte += CorSigUncompressedDataSize(pbSigTmp);
+ ulCallingConv = CorSigUncompressData(pbSigTmp);
+
+ // Get the type argument count
+ if (ulCallingConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ ulCurByte += CorSigUncompressedDataSize(pbSigTmp);
+ ulTyArgCount = CorSigUncompressData(pbSigTmp);
+ }
+
+ // Get the argument count.
+ ulCurByte += CorSigUncompressedDataSize(pbSigTmp);
+ ulArgCount = CorSigUncompressData(pbSigTmp);
+
+ // Calling convention must be one of IMAGE_CEE_CS_CALLCONV_DEFAULT,
+ // IMAGE_CEE_CS_CALLCONV_VARARG or IMAGE_CEE_CS_CALLCONV_FIELD.
+ if (!isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_DEFAULT) &&
+ !isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_VARARG) &&
+ !isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_FIELD))
+ {
+ REPORT_ERROR1(VLDTR_E_MR_BADCALLINGCONV, ulCallingConv);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // [CLS] Calling convention must not be VARARG
+ if(isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_VARARG))
+ {
+ REPORT_ERROR0(VLDTR_E_MR_VARARGCALLINGCONV);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+
+ // If the parent is a MethodDef...
+ if (TypeFromToken(tkClass) == mdtMethodDef)
+ {
+ if (RidFromToken(tkClass) != 0)
+ {
+ // The MethodDef must be the same name and the fixed part of the
+ // vararg signature must be the same.
+ MethodRec *pMethodRecord; // Method Record.
+ LPCSTR szMethodName; // Method name.
+ PCCOR_SIGNATURE pbMethodSig; // Method signature.
+ ULONG cbMethodSig; // Size in bytes of signature.
+
+ // Get Method record, name and signature.
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkClass), &pMethodRecord));
+ IfFailGo(pMiniMd->getNameOfMethod(pMethodRecord, &szMethodName));
+ IfFailGo(pMiniMd->getSignatureOfMethod(pMethodRecord, &pbMethodSig, &cbMethodSig));
+
+ // Verify that the name of the Method is the same as the MemberRef.
+ if (strcmp(szName, szMethodName))
+ {
+ REPORT_ERROR1(VLDTR_E_MR_NAMEDIFF, tkClass);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ if (isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_VARARG))
+ { // It's VARARG calling convention
+ CQuickBytes qbFixedSig; // Quick bytes to hold the fixed part of the variable signature.
+ ULONG cbFixedSig; // Size in bytes of the fixed part.
+
+ // Get the fixed part of the vararg signature of the MemberRef.
+ hr = _GetFixedSigOfVarArg(pbSig, cbSig, &qbFixedSig, &cbFixedSig);
+ if (FAILED(hr) || cbFixedSig != cbMethodSig ||
+ memcmp(pbMethodSig, qbFixedSig.Ptr(), cbFixedSig))
+ {
+ UnifiedAssemblySigComparer uasc(*this);
+ MDSigComparer sc(MDSigParser(pbMethodSig, cbMethodSig),
+ MDSigParser((PCCOR_SIGNATURE)qbFixedSig.Ptr(), cbFixedSig),
+ uasc);
+
+ hr = sc.CompareMethodSignature();
+ if (FAILED(hr))
+ {
+ hr = S_OK;
+ REPORT_ERROR1(VLDTR_E_MR_SIGDIFF, tkClass);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ else
+ { // It's not VARARG calling convention - a MemberRef is referencing MethodDef (part of
+ // NoPIA)
+ UnifiedAssemblySigComparer uasc(*this);
+ MDSigComparer sc(MDSigParser(pbMethodSig, cbMethodSig),
+ MDSigParser(pbSig, cbSig),
+ uasc);
+
+ // Compare signatures
+ hr = sc.CompareMethodSignature();
+ if (FAILED(hr))
+ {
+ hr = S_OK;
+ REPORT_ERROR1(VLDTR_E_MR_SIGDIFF, tkClass);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ }
+
+ // There should be no duplicate MemberRefs.
+ if (*szName && pbSig && cbSig)
+ {
+ hr = ImportHelper::FindMemberRef(pMiniMd, tkClass, szName, pbSig,
+ cbSig, &tkMemberRef, rid,
+ ImportHelper::CreateHash); // Optimize for multiple calls
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_MR_DUP, tkMemberRef);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ hr = S_OK;
+ }
+ else
+ {
+ IfFailGo(hr);
+ }
+ }
+
+ if (!isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_FIELD))
+ {
+ hr = ValidateMethodSig(veCtxt.Token,pbSig, cbSig,0);
+ SetVldtrCode(&hrSave,hr);
+ }
+ }
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateMemberRef()
+
+//*****************************************************************************
+// Validate the given Constant.
+//*****************************************************************************
+HRESULT RegMeta::ValidateConstant(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ ConstantRec *pRecord; // Constant record.
+ mdToken tkParent; // Constant parent.
+ const VOID* pbBlob; // Constant value blob ptr
+ DWORD cbBlob; // Constant value blob size
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Get the MemberRef record.
+ veCtxt.Token = rid;
+ veCtxt.uOffset = 0;
+
+ ULONG maxrid = 0;
+ ULONG typ = 0;
+ IfFailGo(pMiniMd->GetConstantRecord(rid, &pRecord));
+ IfFailGo(pMiniMd->getValueOfConstant(pRecord, (const BYTE **)&pbBlob, &cbBlob));
+ switch(pRecord->GetType())
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ if(pbBlob == NULL)
+ {
+ REPORT_ERROR1(VLDTR_E_CN_BLOBNULL, pRecord->GetType());
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ case ELEMENT_TYPE_STRING:
+ break;
+
+ case ELEMENT_TYPE_CLASS:
+ if(GET_UNALIGNED_32(pbBlob) != 0)
+ {
+ REPORT_ERROR1(VLDTR_E_CN_BLOBNOTNULL, pRecord->GetType());
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ break;
+
+ default:
+ REPORT_ERROR1(VLDTR_E_CN_BADTYPE, pRecord->GetType());
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ tkParent = pMiniMd->getParentOfConstant(pRecord);
+ typ = TypeFromToken(tkParent);
+ switch(typ)
+ {
+ case mdtFieldDef:
+ maxrid = pMiniMd->getCountFields();
+ break;
+ case mdtParamDef:
+ maxrid = pMiniMd->getCountParams();
+ break;
+ case mdtProperty:
+ maxrid = pMiniMd->getCountPropertys();
+ break;
+ }
+ switch(typ)
+ {
+ case mdtFieldDef:
+ case mdtParamDef:
+ case mdtProperty:
+ {
+ ULONG rid_p = RidFromToken(tkParent);
+ if((0==rid_p)||(rid_p > maxrid))
+ {
+ REPORT_ERROR1(VLDTR_E_CN_PARENTRANGE, tkParent);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ break;
+ }
+
+ default:
+ REPORT_ERROR1(VLDTR_E_CN_PARENTTYPE, tkParent);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ break;
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateConstant()
+
+//*****************************************************************************
+// Validate the given CustomAttribute.
+//*****************************************************************************
+HRESULT RegMeta::ValidateCustomAttribute(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CustomAttributeRec *pRecord;
+ IfFailGo(pMiniMd->GetCustomAttributeRecord(rid, &pRecord));
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ veCtxt.Token = TokenFromRid(rid,mdtCustomAttribute);
+ veCtxt.uOffset = 0;
+
+ if (pRecord != NULL)
+ {
+ mdToken tkOwner = pMiniMd->getParentOfCustomAttribute(pRecord);
+ if(RidFromToken(tkOwner))
+ { // if 0, it's deleted CA, don't pay attention
+ mdToken tkCAType = pMiniMd->getTypeOfCustomAttribute(pRecord);
+ DWORD cbValue=0;
+ const BYTE *pbValue;
+ IfFailGo(pMiniMd->getValueOfCustomAttribute(pRecord, &pbValue, &cbValue));
+ if((TypeFromToken(tkOwner)==mdtCustomAttribute)||(!IsValidToken(tkOwner)))
+ {
+ REPORT_ERROR1(VLDTR_E_CA_BADPARENT, tkOwner);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(((TypeFromToken(tkCAType)!=mdtMethodDef)&&(TypeFromToken(tkCAType)!=mdtMemberRef))
+ ||(!IsValidToken(tkCAType))||(RidFromToken(tkCAType)==0))
+ {
+ REPORT_ERROR1(VLDTR_E_CA_BADTYPE, tkCAType);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ { //i.e. Type is valid MethodDef or MemberRef
+ LPCUTF8 szName;
+ PCCOR_SIGNATURE pSig=NULL;
+ DWORD cbSig=0;
+ DWORD dwFlags=0;
+ if (TypeFromToken(tkCAType) == mdtMethodDef)
+ {
+ MethodRec *pTypeRec;
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkCAType), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfMethod(pTypeRec, &szName));
+ IfFailGo(pMiniMd->getSignatureOfMethod(pTypeRec, &pSig, &cbSig));
+ dwFlags = pTypeRec->GetFlags();
+ }
+ else // it can be only MemberRef, otherwise we wouldn't be here
+ {
+ MemberRefRec *pTypeRec;
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkCAType), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfMemberRef(pTypeRec, &szName));
+ IfFailGo(pMiniMd->getSignatureOfMemberRef(pTypeRec, &pSig, &cbSig));
+ }
+ if (strcmp(szName, ".ctor") != 0)
+ {
+ REPORT_ERROR1(VLDTR_E_CA_NOTCTOR, tkCAType);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if ((cbSig > 0) && (pSig != NULL))
+ {
+ if(FAILED(ValidateMethodSig(tkCAType, pSig,cbSig,dwFlags))
+ || (!((*pSig) & IMAGE_CEE_CS_CALLCONV_HASTHIS)))
+ {
+ REPORT_ERROR1(VLDTR_E_CA_BADSIG, tkCAType);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ { // sig seems to be OK
+ if ((pbValue != NULL) && (cbValue > 0))
+ {
+ // Check if prolog is OK
+ WORD pW = *((UNALIGNED WORD*)pbValue);
+ if(pW != 0x0001)
+ {
+ REPORT_ERROR1(VLDTR_E_CA_BADPROLOG, pW);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Check if blob corresponds to the signature
+ }
+ }
+
+ }
+ else
+ {
+ REPORT_ERROR1(VLDTR_E_CA_NOSIG, tkCAType);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ } // end if bad Type - else
+ } // end if RidFromToken(tkOwner)
+ } // end if pRecord
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateCustomAttribute()
+
+//*****************************************************************************
+// Validate the given FieldMarshal.
+//*****************************************************************************
+HRESULT RegMeta::ValidateFieldMarshal(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateFieldMarshal()
+
+//*****************************************************************************
+// Validate the given DeclSecurity.
+//*****************************************************************************
+HRESULT RegMeta::ValidateDeclSecurity(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ DeclSecurityRec *pRecord;
+ mdToken tkOwner; // Owner of the decl security
+ DWORD dwAction; // action flags
+ BOOL bIsValidOwner = FALSE;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ IfFailGo(pMiniMd->GetDeclSecurityRecord(rid, &pRecord));
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ veCtxt.Token = TokenFromRid(rid,mdtPermission);
+ veCtxt.uOffset = 0;
+
+ // Must have a valid owner
+ tkOwner = pMiniMd->getParentOfDeclSecurity(pRecord);
+ if(RidFromToken(tkOwner)==0) goto ErrExit; // deleted record, no need to validate
+ switch(TypeFromToken(tkOwner))
+ {
+ case mdtModule:
+ case mdtAssembly:
+ case mdtTypeDef:
+ case mdtMethodDef:
+ case mdtFieldDef:
+ case mdtInterfaceImpl:
+ bIsValidOwner = IsValidToken(tkOwner);
+ break;
+ default:
+ break;
+ }
+ if(!bIsValidOwner)
+ {
+ REPORT_ERROR1(VLDTR_E_DS_BADOWNER, tkOwner);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Must have one and only one flag set
+ dwAction = pRecord->GetAction() & dclActionMask;
+ if(dwAction > dclMaximumValue) // the flags are 0,1,2,3,...,dclMaximumValue
+ {
+ REPORT_ERROR1(VLDTR_E_DS_BADFLAGS, dwAction);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // If field has DeclSecurity, verify its parent is not an interface.-- checked in ValidateField
+ // If method has DeclSecurity, verify its parent is not an interface.-- checked in ValidateMethod
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateDeclSecurity()
+
+//*****************************************************************************
+// Validate the given ClassLayout.
+//*****************************************************************************
+HRESULT RegMeta::ValidateClassLayout(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ ClassLayoutRec *pRecord; // ClassLayout record.
+ TypeDefRec *pTypeDefRec; // Parent TypeDef record.
+ DWORD dwPackingSize; // Packing size.
+ mdTypeDef tkParent; // Parent TypeDef token.
+ DWORD dwTypeDefFlags; // Parent TypeDef flags.
+ RID clRid; // Duplicate ClassLayout rid.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // Extract the record.
+ veCtxt.Token = rid;
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetClassLayoutRecord(rid, &pRecord));
+
+ // Get the parent, if parent is nil its a deleted record. Skip validation.
+ tkParent = pMiniMd->getParentOfClassLayout(pRecord);
+ if (IsNilToken(tkParent))
+ goto ErrExit;
+
+ // Parent should not have AutoLayout set on it.
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec));
+ dwTypeDefFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec);
+ if (IsTdAutoLayout(dwTypeDefFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_CL_TDAUTO, tkParent);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Parent must not be an Interface
+ if(IsTdInterface(dwTypeDefFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_CL_TDINTF, tkParent);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate the PackingSize.
+ dwPackingSize = pMiniMd->getPackingSizeOfClassLayout(pRecord);
+ if((dwPackingSize > 128)||((dwPackingSize & (dwPackingSize-1)) !=0 ))
+ {
+ REPORT_ERROR2(VLDTR_E_CL_BADPCKSZ, tkParent, dwPackingSize);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate that there are no duplicates.
+ hr = _FindClassLayout(pMiniMd, tkParent, &clRid, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR2(VLDTR_E_CL_DUP, tkParent, clRid);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateClassLayout()
+
+//*****************************************************************************
+// Validate the given FieldLayout.
+//*****************************************************************************
+HRESULT RegMeta::ValidateFieldLayout(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ FieldLayoutRec *pRecord; // FieldLayout record.
+ mdFieldDef tkField; // Field token.
+ ULONG ulOffset; // Field offset.
+ FieldRec *pFieldRec; // Field record.
+ TypeDefRec *pTypeDefRec; // Parent TypeDef record.
+ mdTypeDef tkTypeDef; // Parent TypeDef token.
+ RID clRid; // Corresponding ClassLayout token.
+ RID flRid = 0; // Duplicate FieldLayout rid.
+ DWORD dwTypeDefFlags; // Parent TypeDef flags.
+ DWORD dwFieldFlags; // Field flags.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Extract the record.
+ veCtxt.Token = rid;
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetFieldLayoutRecord(rid, &pRecord));
+
+ // Get the field, if it's nil it's a deleted record, so just skip it.
+ tkField = pMiniMd->getFieldOfFieldLayout(pRecord);
+ if (IsNilToken(tkField))
+ goto ErrExit;
+
+ // Validate the Offset value.
+ ulOffset = pMiniMd->getOffSetOfFieldLayout(pRecord);
+ if (ulOffset == ULONG_MAX)
+ {
+ REPORT_ERROR2(VLDTR_E_FL_BADOFFSET, tkField, ulOffset);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Get the parent of the Field.
+ IfFailGo(pMiniMd->FindParentOfFieldHelper(tkField, &tkTypeDef));
+ // Validate that the parent is not nil.
+ if (IsNilToken(tkTypeDef))
+ {
+ REPORT_ERROR1(VLDTR_E_FL_TDNIL, tkField);
+ SetVldtrCode(&hr, hrSave);
+ goto ErrExit;
+ }
+
+ // Validate that there exists a ClassLayout record associated with
+ // this TypeDef.
+ IfFailGo(pMiniMd->FindClassLayoutHelper(tkTypeDef, &clRid));
+ if (InvalidRid(rid))
+ {
+ REPORT_ERROR2(VLDTR_E_FL_NOCL, tkField, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate that ExplicitLayout is set on the TypeDef flags.
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkTypeDef), &pTypeDefRec));
+ dwTypeDefFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec);
+ if (IsTdAutoLayout(dwTypeDefFlags))
+ {
+ REPORT_ERROR2(VLDTR_E_FL_TDNOTEXPLCT, tkField, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Extract Field record.
+ IfFailGo(pMiniMd->GetFieldRecord(RidFromToken(tkField), &pFieldRec));
+ // Validate that the field is non-static.
+ dwFieldFlags = pMiniMd->getFlagsOfField(pFieldRec);
+ if (IsFdStatic(dwFieldFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_FL_FLDSTATIC, tkField);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Look for duplicates.
+ hr = _FindFieldLayout(pMiniMd, tkField, &flRid, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_FL_DUP, flRid);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateFieldLayout()
+
+//*****************************************************************************
+// Validate the given StandAloneSig.
+//*****************************************************************************
+HRESULT RegMeta::ValidateStandAloneSig(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ StandAloneSigRec *pRecord; // FieldLayout record.
+ PCCOR_SIGNATURE pbSig; // Signature.
+ ULONG cbSig; // Size in bytes of the signature.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ ULONG ulCurByte = 0; // Current index into the signature.
+ ULONG ulCallConv; // Calling convention.
+ ULONG ulArgCount; // Count of arguments.
+ ULONG ulTyArgCount = 0; // Count of type arguments.
+ ULONG i; // Looping index.
+ ULONG ulNSentinels = 0; // Number of sentinels in the signature
+ BOOL bNoVoidAllowed=TRUE;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+
+ // Extract the record.
+ veCtxt.Token = TokenFromRid(rid,mdtSignature);
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetStandAloneSigRecord(rid, &pRecord));
+ IfFailGo(pMiniMd->getSignatureOfStandAloneSig(pRecord, &pbSig, &cbSig));
+
+ // Validate the signature is well-formed with respect to the compression
+ // scheme. If this fails, no further validation needs to be done.
+ if ( (hr = ValidateSigCompression(veCtxt.Token, pbSig, cbSig)) != S_OK)
+ goto ErrExit;
+
+ //_ASSERTE((rid != 0x2c2)&&(rid!=0x2c8)&&(rid!=0x2c9)&&(rid!=0x2d6)&&(rid!=0x38b));
+ // Validate the calling convention.
+ ulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulCallConv = CorSigUncompressData(pbSig);
+ i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK;
+ if(i == IMAGE_CEE_CS_CALLCONV_FIELD) // <REVISIT_TODO>it's a temporary bypass (VB bug)</REVISIT_TODO>
+ ulArgCount = 1;
+ else
+ {
+ if(i != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) // then it is function sig for calli
+ {
+ if((i >= IMAGE_CEE_CS_CALLCONV_FIELD)
+ ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
+ &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS))))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_BADCALLINGCONV, ulCallConv);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ bNoVoidAllowed = FALSE;
+ }
+ // Is there any sig left for arguments?
+ _ASSERTE(ulCurByte <= cbSig);
+ if (cbSig == ulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_MD_NOARGCNT, ulCurByte+1);
+ SetVldtrCode(&hr, hrSave);
+ goto ErrExit;
+ }
+
+ // Get the type argument count.
+ if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ ulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulTyArgCount = CorSigUncompressData(pbSig);
+ }
+
+ // Get the argument count.
+ ulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulArgCount = CorSigUncompressData(pbSig);
+ }
+ // Validate the the arguments.
+ if(ulArgCount)
+ {
+ for(i=1; ulCurByte < cbSig; i++)
+ {
+ hr = ValidateOneArg(veCtxt.Token, pbSig, cbSig, &ulCurByte,&ulNSentinels,bNoVoidAllowed);
+ if (hr != S_OK)
+ {
+ if(hr == VLDTR_E_SIG_MISSARG)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSARG, i);
+ }
+ SetVldtrCode(&hr, hrSave);
+ hrSave = hr;
+ break;
+ }
+ bNoVoidAllowed = TRUE; // whatever it was for the 1st arg, it must be TRUE for the rest
+ }
+ if((ulNSentinels != 0) && (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_VARARG )))
+ {
+ REPORT_ERROR0(VLDTR_E_SIG_SENTMUSTVARARG);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(ulNSentinels > 1)
+ {
+ REPORT_ERROR0(VLDTR_E_SIG_MULTSENTINELS);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateStandAloneSig()
+
+//*****************************************************************************
+// Validate the given EventMap.
+//*****************************************************************************
+HRESULT RegMeta::ValidateEventMap(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateEventMap()
+
+//*****************************************************************************
+// Validate the given EventPtr.
+//*****************************************************************************
+HRESULT RegMeta::ValidateEventPtr(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateEventPtr()
+
+//*****************************************************************************
+// Validate the given Event.
+//*****************************************************************************
+HRESULT RegMeta::ValidateEvent(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ mdToken tkClass; // Declaring TypeDef
+ mdToken tkEventType; // Event Type (TypeDef/TypeRef)
+ EventRec *pRecord;
+ HENUMInternal hEnum;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ IfFailGo(pMiniMd->GetEventRecord(rid, &pRecord));
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ memset(&hEnum, 0, sizeof(HENUMInternal));
+ veCtxt.Token = TokenFromRid(rid,mdtEvent);
+ veCtxt.uOffset = 0;
+
+ // The scope must be a valid TypeDef
+ if (FAILED(pMiniMd->FindParentOfEventHelper(veCtxt.Token, &tkClass)) ||
+ (TypeFromToken(tkClass) != mdtTypeDef) ||
+ !IsValidToken(tkClass))
+ {
+ REPORT_ERROR1(VLDTR_E_EV_BADSCOPE, tkClass);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ tkClass = 0;
+ }
+ // Must have name
+ {
+ LPCUTF8 szName;
+ IfFailGo(pMiniMd->getNameOfEvent(pRecord, &szName));
+
+ if (*szName == 0)
+ {
+ REPORT_ERROR0(VLDTR_E_EV_NONAME);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ if (strcmp(szName, COR_DELETED_NAME_A) == 0)
+ goto ErrExit;
+ if (tkClass != 0) // Must be no duplicates
+ {
+ RID ridEventMap;
+ EventMapRec *pEventMapRec;
+ EventRec *pRec;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+
+ IfFailGo(pMiniMd->FindEventMapFor(RidFromToken(tkClass), &ridEventMap));
+ if (!InvalidRid(ridEventMap))
+ {
+ IfFailGo(pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec));
+ ridStart = pMiniMd->getEventListOfEventMap(pEventMapRec);
+ IfFailGo(pMiniMd->getEndEventListOfEventMap(ridEventMap, &ridEnd));
+
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ if (i == rid)
+ continue;
+ IfFailGo(pMiniMd->GetEventRecord(i, &pRec));
+
+ LPCSTR szEventName;
+ IfFailGo(pMiniMd->getNameOfEvent(pRec, &szEventName));
+ if (strcmp(szName, szEventName) != 0)
+ continue;
+
+ REPORT_ERROR1(VLDTR_E_EV_DUP, TokenFromRid(i, mdtEvent));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ }
+ }// end of name block
+ // EventType must be Nil or valid TypeDef, TypeRef or TypeSpec representing an instantiated generic type
+ tkEventType = pMiniMd->getEventTypeOfEvent(pRecord);
+ if (!IsNilToken(tkEventType))
+ {
+ if(IsValidToken(tkEventType) &&
+ ((TypeFromToken(tkEventType)==mdtTypeDef)||
+ (TypeFromToken(tkEventType)==mdtTypeRef)||
+ (TypeFromToken(tkEventType)==mdtTypeSpec)))
+ {
+ // TypeSpecs can be many things, we only handle instantiated generic types currently.
+ if (TypeFromToken(tkEventType)==mdtTypeSpec)
+ {
+ TypeSpecRec *pRec;
+ IfFailGo(pMiniMd->GetTypeSpecRecord(RidFromToken(tkEventType), &pRec));
+ PCCOR_SIGNATURE pSig;
+ ULONG cSig;
+
+ IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRec, &pSig, &cSig));
+
+ if (CorSigUncompressElementType(pSig) == ELEMENT_TYPE_GENERICINST &&
+ CorSigUncompressElementType(pSig) == ELEMENT_TYPE_CLASS)
+ {
+ // Just update the event type token variable and fall through to the validation code below (it doesn't care
+ // whether the type is generic or not).
+ tkEventType = CorSigUncompressToken(pSig);
+ }
+ else
+ {
+ REPORT_ERROR1(VLDTR_E_EV_BADEVTYPE, tkEventType);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ // EventType must not be Interface or ValueType
+ if(TypeFromToken(tkEventType)==mdtTypeDef) // can't say anything about TypeRef: no flags available!
+ {
+ TypeDefRec *pTypeDefRecord;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkEventType), &pTypeDefRecord));
+ DWORD dwFlags = pTypeDefRecord->GetFlags();
+ if(!IsTdClass(dwFlags))
+ {
+ REPORT_ERROR2(VLDTR_E_EV_EVTYPENOTCLASS, tkEventType, dwFlags);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ else
+ {
+ REPORT_ERROR1(VLDTR_E_EV_BADEVTYPE, tkEventType);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ // Validate related methods
+ {
+ MethodSemanticsRec *pSemantics;
+ RID ridCur;
+ ULONG ulSemantics;
+ mdMethodDef tkMethod;
+ bool bHasAddOn = false;
+ bool bHasRemoveOn = false;
+
+ IfFailGo( pMiniMd->FindMethodSemanticsHelper(veCtxt.Token, &hEnum) );
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur))
+ {
+ IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics));
+ ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics);
+ tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef );
+ // Semantics must be Setter, Getter or Other
+ switch (ulSemantics)
+ {
+ case msAddOn:
+ bHasAddOn = true;
+ break;
+ case msRemoveOn:
+ bHasRemoveOn = true;
+ break;
+ case msFire:
+ case msOther:
+ break;
+ default:
+ REPORT_ERROR2(VLDTR_E_EV_BADSEMANTICS, tkMethod,ulSemantics);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Method must be valid
+ if(!IsValidToken(tkMethod))
+ {
+ REPORT_ERROR1(VLDTR_E_EV_BADMETHOD, tkMethod);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ // Method's parent must be the same
+ mdToken tkTypeDef;
+ IfFailGo(pMiniMd->FindParentOfMethodHelper(tkMethod, &tkTypeDef));
+ if(tkTypeDef != tkClass)
+ {
+ REPORT_ERROR2(VLDTR_E_EV_ALIENMETHOD, tkMethod,tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ } // end loop over methods
+ // AddOn and RemoveOn are a must
+ if(!bHasAddOn)
+ {
+ REPORT_ERROR0(VLDTR_E_EV_NOADDON);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(!bHasRemoveOn)
+ {
+ REPORT_ERROR0(VLDTR_E_EV_NOREMOVEON);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }// end of related method validation block
+
+ hr = hrSave;
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateEvent()
+
+
+//*****************************************************************************
+// Validate the given PropertyMap.
+//*****************************************************************************
+HRESULT RegMeta::ValidatePropertyMap(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidatePropertyMap(0
+
+//*****************************************************************************
+// Validate the given PropertyPtr.
+//*****************************************************************************
+HRESULT RegMeta::ValidatePropertyPtr(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidatePropertyPtr()
+
+//*****************************************************************************
+// Validate the given Property.
+//*****************************************************************************
+HRESULT RegMeta::ValidateProperty(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ mdToken tkClass = mdTokenNil; // Declaring TypeDef
+ PropertyRec *pRecord;
+ HENUMInternal hEnum;
+ RID tempRid;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ IfFailGo(pMiniMd->GetPropertyRecord(rid, &pRecord));
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ memset(&hEnum, 0, sizeof(HENUMInternal));
+ veCtxt.Token = TokenFromRid(rid,mdtProperty);
+ veCtxt.uOffset = 0;
+ // The scope must be a valid TypeDef
+ IfFailGo(pMiniMd->FindParentOfPropertyHelper( veCtxt.Token, &tkClass));
+ if ((TypeFromToken(tkClass) != mdtTypeDef) ||
+ !IsValidToken(tkClass) ||
+ IsNilToken(tkClass))
+ {
+ REPORT_ERROR1(VLDTR_E_PR_BADSCOPE, tkClass);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Must have name and signature
+ {
+ ULONG cbSig;
+ PCCOR_SIGNATURE pvSig;
+ IfFailGo(pMiniMd->getTypeOfProperty(pRecord, &pvSig, &cbSig));
+
+ LPCUTF8 szName;
+ IfFailGo(pMiniMd->getNameOfProperty(pRecord, &szName));
+ ULONG ulNameLen = (szName != NULL) ? (ULONG)strlen(szName) : 0;
+
+ if (ulNameLen == 0)
+ {
+ REPORT_ERROR0(VLDTR_E_PR_NONAME);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ if(strcmp(szName, COR_DELETED_NAME_A) == 0)
+ goto ErrExit;
+ }
+ if (cbSig == 0)
+ {
+ REPORT_ERROR0(VLDTR_E_PR_NOSIG);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Must be no duplicates
+ if ((ulNameLen != 0) && (cbSig != 0))
+ {
+ RID ridPropertyMap;
+ PropertyMapRec *pPropertyMapRec;
+ PropertyRec *pRec;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+ ULONG cbSig1;
+ PCCOR_SIGNATURE pvSig1;
+
+ IfFailGo(pMiniMd->FindPropertyMapFor(RidFromToken(tkClass), &ridPropertyMap));
+ if (!InvalidRid(ridPropertyMap) )
+ {
+ IfFailGo(pMiniMd->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec));
+ ridStart = pMiniMd->getPropertyListOfPropertyMap(pPropertyMapRec);
+ IfFailGo(pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd));
+
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ if (i == rid)
+ continue;
+ IfFailGo(pMiniMd->GetPropertyRecord(i, &pRec));
+ IfFailGo(pMiniMd->getTypeOfProperty(pRec, &pvSig1, &cbSig1));
+
+ if (cbSig != cbSig1)
+ continue;
+ if (memcmp(pvSig,pvSig1,cbSig) != 0)
+ continue;
+
+ LPCSTR szPropertyName;
+ IfFailGo(pMiniMd->getNameOfProperty(pRec, &szPropertyName));
+ if (strcmp(szName, szPropertyName) != 0)
+ continue;
+
+ REPORT_ERROR1(VLDTR_E_PR_DUP, TokenFromRid(i,mdtProperty));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ // Validate the signature
+ if ((pvSig != NULL) && (cbSig != 0))
+ {
+ ULONG ulCurByte = 0; // Current index into the signature.
+ ULONG ulCallConv; // Calling convention.
+ ULONG ulArgCount;
+ ULONG i;
+ ULONG ulNSentinels = 0;
+
+ // Validate the calling convention.
+ ulCurByte += CorSigUncompressedDataSize(pvSig);
+ ulCallConv = CorSigUncompressData(pvSig);
+ if (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_PROPERTY ))
+ {
+ REPORT_ERROR1(VLDTR_E_PR_BADCALLINGCONV, ulCallConv);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Get the argument count.
+ ulCurByte += CorSigUncompressedDataSize(pvSig);
+ ulArgCount = CorSigUncompressData(pvSig);
+
+ // Validate the arguments.
+ for (i = 0; i < ulArgCount; i++)
+ {
+ hr = ValidateOneArg(veCtxt.Token, pvSig, cbSig, &ulCurByte,&ulNSentinels,(i>0));
+ if (hr != S_OK)
+ {
+ if (hr == VLDTR_E_SIG_MISSARG)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSARG, i+1);
+ }
+ SetVldtrCode(&hr, hrSave);
+ break;
+ }
+ }
+ }//end if(pvSig && cbSig)
+ }// end of name/signature block
+
+ // Marked HasDefault <=> has default value
+ IfFailGo(pMiniMd->FindConstantHelper(veCtxt.Token, &tempRid));
+ if (InvalidRid(tempRid) == IsPrHasDefault(pRecord->GetPropFlags()))
+ {
+ REPORT_ERROR0(IsPrHasDefault(pRecord->GetPropFlags())? VLDTR_E_PR_MARKEDNODEFLT : VLDTR_E_PR_DEFLTNOTMARKED);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate related methods
+ {
+ MethodSemanticsRec *pSemantics;
+ RID ridCur;
+ ULONG ulSemantics;
+ mdMethodDef tkMethod;
+
+ IfFailGo( pMiniMd->FindMethodSemanticsHelper(veCtxt.Token, &hEnum) );
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &ridCur))
+ {
+ IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics));
+ ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics);
+ tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef );
+ // Semantics must be Setter, Getter or Other
+ switch (ulSemantics)
+ {
+ case msSetter:
+ case msGetter:
+ case msOther:
+ break;
+ default:
+ REPORT_ERROR2(VLDTR_E_PR_BADSEMANTICS, tkMethod, ulSemantics);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Method must be valid
+ if(!IsValidToken(tkMethod))
+ {
+ REPORT_ERROR1(VLDTR_E_PR_BADMETHOD, tkMethod);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ // Method's parent must be the same
+ mdToken tkTypeDef;
+ IfFailGo(pMiniMd->FindParentOfMethodHelper(tkMethod, &tkTypeDef));
+ if(tkTypeDef != tkClass)
+ {
+ REPORT_ERROR2(VLDTR_E_PR_ALIENMETHOD, tkMethod, tkTypeDef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ } // end loop over methods
+ }// end of related method validation block
+
+ hr = hrSave;
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateProperty()
+
+//*****************************************************************************
+// Validate the given MethodSemantics.
+//*****************************************************************************
+HRESULT RegMeta::ValidateMethodSemantics(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateMethodSemantics()
+
+//*****************************************************************************
+// Validate the given MethodImpl.
+//*****************************************************************************
+HRESULT RegMeta::ValidateMethodImpl(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ MethodImplRec* pRecord;
+ MethodImplRec* pRec;
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ mdToken tkClass; // Declaring TypeDef
+ mdToken tkBody; // Implementing method (MethodDef or MemberRef)
+ mdToken tkDecl; // Implemented method (MethodDef or MemberRef)
+ unsigned iCount;
+ unsigned index;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = TokenFromRid(rid, mdtMethodImpl);
+ veCtxt.uOffset = 0;
+
+ PCCOR_SIGNATURE pbBodySig = NULL;
+ PCCOR_SIGNATURE pbDeclSig = NULL;
+
+ IfFailGo(pMiniMd->GetMethodImplRecord(rid, &pRecord));
+ tkClass = pMiniMd->getClassOfMethodImpl(pRecord);
+ // Class must be valid
+ if(!IsValidToken(tkClass) || (TypeFromToken(tkClass) != mdtTypeDef))
+ {
+ REPORT_ERROR1(VLDTR_E_MI_BADCLASS, tkClass);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ { // ... and not an Interface
+ TypeDefRec *pTypeDefRecord;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkClass), &pTypeDefRecord));
+ if(IsTdInterface(pTypeDefRecord->GetFlags()))
+ {
+ REPORT_ERROR1(VLDTR_E_MI_CLASSISINTF, tkClass);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ // Decl must be valid MethodDef or MemberRef
+ tkDecl = pMiniMd->getMethodDeclarationOfMethodImpl(pRecord);
+ if(!(IsValidToken(tkDecl) &&
+ ((TypeFromToken(tkDecl) == mdtMethodDef) || (TypeFromToken(tkDecl) == mdtMemberRef))))
+ {
+ REPORT_ERROR1(VLDTR_E_MI_BADDECL, tkDecl);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Body must be valid MethodDef or MemberRef
+ tkBody = pMiniMd->getMethodBodyOfMethodImpl(pRecord);
+ if(!(IsValidToken(tkBody) &&
+ ((TypeFromToken(tkBody) == mdtMethodDef) || (TypeFromToken(tkBody) == mdtMemberRef))))
+ {
+ REPORT_ERROR1(VLDTR_E_MI_BADBODY, tkBody);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // No duplicates based on (tkClass,tkDecl)
+ iCount = pMiniMd->getCountMethodImpls();
+ for(index = rid+1; index <= iCount; index++)
+ {
+ IfFailGo(pMiniMd->GetMethodImplRecord(index, &pRec));
+ if((tkClass == pMiniMd->getClassOfMethodImpl(pRec)) &&
+ (tkDecl == pMiniMd->getMethodDeclarationOfMethodImpl(pRec)))
+ {
+ REPORT_ERROR1(VLDTR_E_MI_DUP, index);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ mdToken tkBodyParent;
+ ULONG cbBodySig;
+
+ if(TypeFromToken(tkBody) == mdtMethodDef)
+ {
+ MethodRec *pBodyRec;
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkBody), &pBodyRec));
+ IfFailGo(pMiniMd->getSignatureOfMethod(pBodyRec, &pbBodySig, &cbBodySig));
+ IfFailGo(pMiniMd->FindParentOfMethodHelper(tkBody, &tkBodyParent));
+ // Body must not be static
+ if(IsMdStatic(pBodyRec->GetFlags()))
+ {
+ REPORT_ERROR1(VLDTR_E_MI_BODYSTATIC, tkBody);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else if(TypeFromToken(tkBody) == mdtMemberRef)
+ {
+ MemberRefRec *pBodyRec;
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkBody), &pBodyRec));
+ tkBodyParent = pMiniMd->getClassOfMemberRef(pBodyRec);
+ IfFailGo(pMiniMd->getSignatureOfMemberRef(pBodyRec, &pbBodySig, &cbBodySig));
+ }
+ // Body must belong to the same class
+ if(tkBodyParent != tkClass)
+ {
+ REPORT_ERROR1(VLDTR_E_MI_ALIENBODY, tkBodyParent);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ mdToken tkDeclParent;
+ ULONG cbDeclSig;
+
+ if(TypeFromToken(tkDecl) == mdtMethodDef)
+ {
+ MethodRec *pDeclRec;
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkDecl), &pDeclRec));
+ IfFailGo(pMiniMd->getSignatureOfMethod(pDeclRec, &pbDeclSig, &cbDeclSig));
+ IfFailGo(pMiniMd->FindParentOfMethodHelper(tkDecl, &tkDeclParent));
+ // Decl must be virtual
+ if(!IsMdVirtual(pDeclRec->GetFlags()))
+ {
+ REPORT_ERROR1(VLDTR_E_MI_DECLNOTVIRT, tkDecl);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Decl must not be final
+ if(IsMdFinal(pDeclRec->GetFlags()))
+ {
+ REPORT_ERROR1(VLDTR_E_MI_DECLFINAL, tkDecl);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Decl must not be private
+ if(IsMdPrivate(pDeclRec->GetFlags()) && IsMdCheckAccessOnOverride(pDeclRec->GetFlags()))
+ {
+ REPORT_ERROR1(VLDTR_E_MI_DECLPRIV, tkDecl);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else if(TypeFromToken(tkDecl) == mdtMemberRef)
+ {
+ MemberRefRec *pDeclRec;
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkDecl), &pDeclRec));
+ tkDeclParent = pMiniMd->getClassOfMemberRef(pDeclRec);
+ IfFailGo(pMiniMd->getSignatureOfMemberRef(pDeclRec, &pbDeclSig, &cbDeclSig));
+ }
+
+ // Compare the signatures as best we can, delegating some comparisons to the loader.
+ if (*pbBodySig & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ // decl's callconv must be generic
+ if (*pbDeclSig & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ // and the arities must match
+ ULONG ulBodyArity = CorSigUncompressData(++pbBodySig);
+ ULONG ulDeclArity = CorSigUncompressData(++pbDeclSig);
+ if(ulBodyArity != ulDeclArity)
+ {
+ REPORT_ERROR3(VLDTR_E_MI_ARITYMISMATCH,tkDecl,ulDeclArity,ulBodyArity);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else
+ {
+ REPORT_ERROR1(VLDTR_E_MI_DECLNOTGENERIC,tkDecl);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // delegate precise signature checking to the loader,
+ // as this requires signature comparison modulo substitution
+ }
+ else if (*pbDeclSig & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ REPORT_ERROR1(VLDTR_E_MI_IMPLNOTGENERIC,tkDecl);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (TypeFromToken(tkDeclParent) == mdtTypeSpec)
+ {
+ // do nothing for now...
+ // delegate precise signature checking to the loader,
+ // as this requires signature comparison modulo substitution
+ }
+ // Signatures must match (except call conv)
+ else if((cbDeclSig != cbBodySig)||(memcmp(pbDeclSig+1,pbBodySig+1,cbDeclSig-1)))
+ {
+ //@GENERICSVER: todo:
+ /*
+ //@TODO: Fix to have peverify resolve assemblies
+ // through the runtime. At that point, use this method instead
+ // of the current compare
+
+ // @TODO: check for other bad memcmp sig comparisons in peverify
+
+ // Can't use memcmp because there may be two AssemblyRefs
+ // in this scope, pointing to the same assembly, etc.).
+ if (!MetaSig::CompareMethodSigs(pbDeclSig,
+ cbDeclSig,
+ Module* pModule1,
+ pbBodySig,
+ cbDeclSig,
+ Module* pModule2))
+ */
+ UnifiedAssemblySigComparer uasc(*this);
+ MDSigComparer sc(MDSigParser(pbDeclSig, cbDeclSig),
+ MDSigParser(pbBodySig, cbBodySig),
+ uasc);
+
+ hr = sc.CompareMethodSignature();
+
+ if (FAILED(hr))
+ {
+ REPORT_ERROR2(VLDTR_E_MI_SIGMISMATCH,tkDecl,tkBody);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateMethodImpl()
+
+//*****************************************************************************
+// Validate the given ModuleRef.
+//*****************************************************************************
+HRESULT RegMeta::ValidateModuleRef(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ ModuleRefRec *pRecord; // ModuleRef record.
+ LPCUTF8 szName; // ModuleRef name.
+ mdModuleRef tkModuleRef; // Duplicate ModuleRef.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ // Get the ModuleRef record.
+ veCtxt.Token = TokenFromRid(rid, mdtModuleRef);
+ veCtxt.uOffset = 0;
+
+ IfFailGo(pMiniMd->GetModuleRefRecord(rid, &pRecord));
+
+ // C++ emits IJW methods with ImplMaps
+ // which have resolution=ModuleRef with empty name
+ IfFailGo(pMiniMd->getNameOfModuleRef(pRecord, &szName));
+ if (*szName)
+ {
+ // Look for a Duplicate, this function reports only one duplicate.
+ hr = ImportHelper::FindModuleRef(pMiniMd, szName, &tkModuleRef, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_MODREF_DUP, tkModuleRef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ }
+ else
+ hrSave = S_FALSE;
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateModuleRef()
+
+//*****************************************************************************
+// Validate the given TypeSpec.
+//*****************************************************************************
+//@todo GENERICS: reject duplicate specs?
+HRESULT RegMeta::ValidateTypeSpec(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ TypeSpecRec *pRecord; // TypeSpec record.
+ PCCOR_SIGNATURE pbSig; // Signature.
+ ULONG cbSig; // Size in bytes of the signature.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ ULONG ulCurByte = 0; // Current index into the signature.
+ ULONG ulNSentinels = 0; // Number of sentinels in the signature
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ // Extract the record.
+ veCtxt.Token = TokenFromRid(rid,mdtTypeSpec);
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetTypeSpecRecord(rid, &pRecord));
+ IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRecord, &pbSig, &cbSig));
+
+ // Validate the signature is well-formed with respect to the compression
+ // scheme. If this fails, no further validation needs to be done.
+ if ( (hr = ValidateSigCompression(veCtxt.Token, pbSig, cbSig)) != S_OK)
+ goto ErrExit;
+
+ hr = ValidateOneArg(veCtxt.Token, pbSig, cbSig, &ulCurByte,&ulNSentinels,FALSE);
+ if (hr != S_OK)
+ {
+ if(hr == VLDTR_E_SIG_MISSARG)
+ {
+ REPORT_ERROR0(VLDTR_E_TS_EMPTY);
+ }
+ SetVldtrCode(&hr, hrSave);
+ hrSave = hr;
+ }
+ if(ulNSentinels != 0)
+ {
+ REPORT_ERROR0(VLDTR_E_TS_HASSENTINALS);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateTypeSpec()
+
+//*****************************************************************************
+// This function validates the given Field signature. This function works
+// with Field signature for both the MemberRef and FieldDef.
+//*****************************************************************************
+HRESULT RegMeta::ValidateMethodSpecSig(
+ mdMethodSpec tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size in bytes of the signature.
+ ULONG *pArity) // [Out] Arity of the instantiation
+{
+ ULONG ulCurByte = 0; // Current index into the signature.
+ ULONG ulCallConv; // Calling convention.
+ ULONG ulArity; // Arity of instantiation.
+ ULONG ulArgCnt;
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ _ASSERTE(TypeFromToken(tk) == mdtMethodSpec);
+
+ veCtxt.Token = tk;
+ veCtxt.uOffset = 0;
+
+ // Validate the calling convention.
+ ulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulCallConv = CorSigUncompressData(pbSig);
+ if (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_GENERICINST))
+ {
+ REPORT_ERROR1(VLDTR_E_MS_BADCALLINGCONV, ulCallConv);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ if (cbSig == ulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_MS_MISSARITY, ulCurByte + 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ ulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulArity = CorSigUncompressData(pbSig);
+
+ if (ulArity == 0)
+ {
+ REPORT_ERROR1(VLDTR_E_MS_ARITYZERO, ulCurByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ ulArgCnt = ulArity;
+
+ if(pArity != NULL)
+ {
+ *pArity = ulArity;
+ }
+
+ // Validate and consume the arguments.
+ while(ulArgCnt--)
+ {
+
+ PCCOR_SIGNATURE pbTypeArg = pbSig;
+ ULONG ulTypeArgByte = ulCurByte;
+
+ IfFailGo(ValidateOneArg(tk, pbSig, cbSig, &ulCurByte, NULL, TRUE));
+ if (hr != S_OK)
+ {
+ if(hr == VLDTR_E_SIG_MISSARG)
+ {
+ REPORT_ERROR1(VLDTR_E_MS_MISSARG, ulArity-ulArgCnt);
+ }
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ // reject byref-like args
+ switch (CorSigUncompressData(pbTypeArg))
+ {
+ case ELEMENT_TYPE_TYPEDBYREF:
+ case ELEMENT_TYPE_BYREF:
+ {
+ REPORT_ERROR1(VLDTR_E_MS_BYREFINST, ulTypeArgByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateMethodSpecSig()
+
+
+//*****************************************************************************
+// Validate the given MethodSpec.
+//*****************************************************************************
+HRESULT RegMeta::ValidateMethodSpec(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ MethodSpecRec *pRecord; // MethodSpec record.
+ mdToken tkMethod; // Method field (a MethodDefOrRef)
+ PCCOR_SIGNATURE pInstantiation; // MethodSpec instantiation (a signature)
+ ULONG cbInstantiation; // Size of instantiation.
+ ULONG ulInstantiationArity; // Arity of the Instantiation
+
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ // Get the GenericParamConstraint record.
+ veCtxt.Token = TokenFromRid(rid, mdtMethodSpec);
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetMethodSpecRecord(rid, &pRecord));
+
+ // 1. The MethodSpec table may contain zero or more rows.
+ // (Nothing to check.)
+
+ // Implicit (missing from spec): Method is not nil [ERROR]
+ tkMethod = pMiniMd->getMethodOfMethodSpec(pRecord);
+ if(IsNilToken(tkMethod))
+ {
+ REPORT_ERROR0(VLDTR_E_MS_METHODNIL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Implicit in ValidateRecord: Method is a valid mdMethodDefOrRef.
+
+ // 2. One or more rows may refer to the same row in the MethodDef or MethodRef table.
+ // (There may be more multiple instantions of the same generic method)
+ // (nothing to check!)
+
+ // 3. "The signature stored at Instantiation shall be a valid instantiation of the signature of the generic method stored at Method. [ERROR]
+ {
+ IfFailGo(pMiniMd->getInstantiationOfMethodSpec(pRecord, &pInstantiation, &cbInstantiation));
+ IfFailGo(ValidateMethodSpecSig(TokenFromRid(rid, mdtMethodSpec), pInstantiation, cbInstantiation,&ulInstantiationArity));
+ if (hr != S_OK)
+ SetVldtrCode(&hrSave, hr);
+ }
+
+ IfFailGo(pMiniMd->getInstantiationOfMethodSpec(pRecord, &pInstantiation, &cbInstantiation));
+ // 4. There shall be no duplicate rows based upon Method and Instantiation [ERROR]
+ {
+ mdMethodSpec tkDupMethodSpec;
+ hr = ImportHelper::FindMethodSpecByMethodAndInstantiation(pMiniMd, tkMethod, pInstantiation, cbInstantiation, &tkDupMethodSpec, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_MS_DUP, tkDupMethodSpec);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ }
+
+ // check the method is generic and that the arity of the instantiation is correct
+ {
+ PCCOR_SIGNATURE pbGenericMethodSig;
+ ULONG cbGenericMethodSig;
+
+ if(TypeFromToken(tkMethod) == mdtMethodDef)
+ {
+ MethodRec *pMethodRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkMethod), &pMethodRec));
+ IfFailGo(pMiniMd->getSignatureOfMethod(pMethodRec, &pbGenericMethodSig, &cbGenericMethodSig));
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(tkMethod) == mdtMemberRef);
+ MemberRefRec *pMethodRefRec;
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkMethod), &pMethodRefRec));
+ IfFailGo(pMiniMd->getSignatureOfMemberRef(pMethodRefRec, &pbGenericMethodSig, &cbGenericMethodSig));
+ }
+
+ if (*pbGenericMethodSig & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ ULONG ulGenericArity = CorSigUncompressData(++pbGenericMethodSig);
+ if(ulGenericArity != ulInstantiationArity)
+ {
+ REPORT_ERROR2(VLDTR_E_MS_ARITYMISMATCH,ulGenericArity,ulInstantiationArity);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else
+ {
+ REPORT_ERROR1(VLDTR_E_MS_METHODNOTGENERIC, tkMethod);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ hr = hrSave;
+
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateMethodSpec()
+
+
+//*****************************************************************************
+// Validate the given GenericParamConstraint.
+//*****************************************************************************
+HRESULT RegMeta::ValidateGenericParamConstraint(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope.
+ GenericParamConstraintRec *pRecord; // GenericParamConstraint record.
+ mdGenericParam tkOwner; // GenericParamConstraint owner field.
+ mdToken tkConstraint; // GenericParamConstraint constraint field.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ // Get the GenericParamConstraint record.
+ veCtxt.Token = TokenFromRid(rid, mdtGenericParamConstraint);
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetGenericParamConstraintRecord(rid, &pRecord));
+
+ // 1. GenericParamConstraint may contain zero or more rows.
+ // (Nothing to check.)
+
+ // 2. Each row shall have one, and only one, owner row in the GenericParamTable [ERROR]
+ // (Nothing to check except owner not nil)
+ tkOwner = pMiniMd->getOwnerOfGenericParamConstraint(pRecord);
+ if(IsNilToken(tkOwner))
+ {
+ REPORT_ERROR0(VLDTR_E_GPC_OWNERNIL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // 3. Each row in the GenericParam table shall own a separate row in the GenericParamConstraint table for each constraint that type parameter has [ERROR]
+ // (Nothing to check)
+
+ // 4.All of the rows in the GenericParamConstraint table that are owned by a given row in the GenericParamTable
+ // shall form a contiguous range of rows [ERROR]
+ //@NOTE: this check is (iterated over all rows) is quadratic in the (typically small) number of constraints
+ {
+ RID curRid = rid;
+ GenericParamConstraintRec *pCurRecord;
+ mdGenericParam tkCurOwner = tkOwner;
+ // find the first preceding row with a distinct owner
+ while (curRid > 1 && tkCurOwner == tkOwner)
+ {
+ curRid--;
+ IfFailGo(pMiniMd->GetGenericParamConstraintRecord(curRid, &pCurRecord));
+ tkCurOwner = pMiniMd->getOwnerOfGenericParamConstraint(pCurRecord);
+ };
+ // reject this row if there is some row preceding the current row with this owner
+ while (curRid > 1)
+ {
+ curRid--;
+ IfFailGo(pMiniMd->GetGenericParamConstraintRecord(curRid, &pCurRecord));
+ tkCurOwner = pMiniMd->getOwnerOfGenericParamConstraint(pCurRecord);
+ if (tkCurOwner == tkOwner)
+ {
+ REPORT_ERROR1(VLDTR_E_GPC_NONCONTIGUOUS,tkOwner);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ };
+ }
+
+ // 5. "At most one class constraint per GenericParam" --- no longer required.
+ // 6. "Zero or more interface constraints per GenericParam" --- no longer required.
+
+ tkConstraint = pMiniMd->getConstraintOfGenericParamConstraint(pRecord);
+ // 7. There shall be no duplicates based upon Owner and Constraint
+ {
+ mdGenericParamConstraint tkDupGenericParamConstraint;
+ hr = ImportHelper::FindGenericParamConstraintByOwnerAndConstraint(pMiniMd, tkOwner, tkConstraint, &tkDupGenericParamConstraint, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_GPC_DUP, tkDupGenericParamConstraint);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ }
+
+ hr = hrSave;
+
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateGenericParamConstraint()
+
+//*****************************************************************************
+// Validate the given ImplMap.
+//*****************************************************************************
+HRESULT RegMeta::ValidateImplMap(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ ImplMapRec *pRecord;
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ HRESULT hrModuleRef=S_OK;
+ mdToken tkModuleRef;
+ mdToken tkMember;
+ USHORT usFlags;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+#ifdef CACHE_IMPLMAP_VALIDATION_RESULT
+ for(unsigned jjj=0; jjj<g_nValidated; jjj++)
+ {
+ if(g_rValidated[jjj].tok == (rid | 0x51000000)) return g_rValidated[jjj].hr;
+ }
+#endif
+ veCtxt.Token = rid;
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetImplMapRecord(rid, &pRecord));
+ if(pRecord == NULL) IfFailGo(E_FAIL);
+ // ImplMap must have ModuleRef
+ tkModuleRef = pMiniMd->getImportScopeOfImplMap(pRecord);
+ if((TypeFromToken(tkModuleRef) != mdtModuleRef) || IsNilToken(tkModuleRef)
+ || FAILED(hrModuleRef= ValidateModuleRef(RidFromToken(tkModuleRef))))
+ {
+ REPORT_ERROR1(VLDTR_E_IMAP_BADMODREF, tkModuleRef);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // ImplMap must belong to FieldDef or MethodDef
+ tkMember = pMiniMd->getMemberForwardedOfImplMap(pRecord);
+ if((TypeFromToken(tkMember) != mdtFieldDef) && (TypeFromToken(tkMember) != mdtMethodDef))
+ {
+ REPORT_ERROR1(VLDTR_E_IMAP_BADMEMBER, tkMember);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // ImplMap must have import name, unless ModuleRef has no name
+ // (special case for C++ IJW methods)
+ if(hrModuleRef != S_FALSE)
+ {
+ LPCSTR szName; // Import name.
+ IfFailGo(pMiniMd->getImportNameOfImplMap(pRecord, &szName));
+ if((szName==NULL)||(*szName == 0))
+ {
+ REPORT_ERROR0(VLDTR_E_IMAP_BADIMPORTNAME);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ // ImplMap must have valid flags:
+ // one value of pmCharSetMask - always so, no check needed (values: 0,2,4,6, mask=6)
+ // one value of pmCallConvMask...
+ // ...and it's not pmCallConvThiscall
+ usFlags = pRecord->GetMappingFlags() & pmCallConvMask;
+ if((usFlags < pmCallConvWinapi)||(usFlags > pmCallConvFastcall))
+ {
+ REPORT_ERROR1(VLDTR_E_IMAP_BADCALLCONV, usFlags);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ErrExit:
+
+#ifdef CACHE_IMPLMAP_VALIDATION_RESULT
+ g_rValidated[g_nValidated].tok = rid | 0x51000000;
+ g_rValidated[g_nValidated].hr = hrSave;
+ g_nValidated++;
+#endif
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateImplMap()
+
+//*****************************************************************************
+// Validate the given FieldRVA.
+//*****************************************************************************
+HRESULT RegMeta::ValidateFieldRVA(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ FieldRVARec *pRecord;
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ mdToken tkField;
+ ULONG ulRVA;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = rid;
+ veCtxt.uOffset = 0;
+ IfFailGo(pMiniMd->GetFieldRVARecord(rid, &pRecord));
+ ulRVA = pRecord->GetRVA();
+ tkField = pMiniMd->getFieldOfFieldRVA(pRecord);
+ /*
+ if(ulRVA == 0)
+ {
+ REPORT_ERROR1(VLDTR_E_FRVA_ZERORVA, tkField);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ */
+ if((0==RidFromToken(tkField))||(TypeFromToken(tkField) != mdtFieldDef)||(!IsValidToken(tkField)))
+ {
+ REPORT_ERROR2(VLDTR_E_FRVA_BADFIELD, tkField, ulRVA);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ {
+ RID N = pMiniMd->getCountFieldRVAs();
+ RID tmp;
+ FieldRVARec* pRecTmp;
+ for(tmp = rid+1; tmp <= N; tmp++)
+ {
+ IfFailGo(pMiniMd->GetFieldRVARecord(tmp, &pRecTmp));
+ if(tkField == pMiniMd->getFieldOfFieldRVA(pRecTmp))
+ {
+ REPORT_ERROR2(VLDTR_E_FRVA_DUPFIELD, tkField, tmp);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateFieldRVA()
+
+//*****************************************************************************
+// Validate the given ENCLog.
+//*****************************************************************************
+HRESULT RegMeta::ValidateENCLog(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateENCLog()
+
+//*****************************************************************************
+// Validate the given ENCMap.
+//*****************************************************************************
+HRESULT RegMeta::ValidateENCMap(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateENCMap()
+
+//*****************************************************************************
+// Validate the given Assembly.
+//*****************************************************************************
+HRESULT RegMeta::ValidateAssembly(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ AssemblyRec *pRecord; // Assembly record.
+ CorAssemblyFlags dwFlags; // Assembly flags.
+ LPCSTR szName; // Assembly Name.
+ VEContext veCtxt; // Context structure.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ BOOL invalidAssemblyFlags; // Whether the CorAssemblyFlags are valid.
+ BOOL fIsV2Assembly = FALSE;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ // Get the Assembly record.
+ veCtxt.Token = TokenFromRid(rid, mdtAssembly);
+ veCtxt.uOffset = 0;
+
+ IfFailGo(pMiniMd->GetAssemblyRecord(rid, &pRecord));
+
+ // There can only be one Assembly record.
+ if (rid > 1)
+ {
+ REPORT_ERROR0(VLDTR_E_AS_MULTI);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Do checks for name validity..
+ IfFailGo(pMiniMd->getNameOfAssembly(pRecord, &szName));
+ if (!*szName)
+ {
+ // Assembly Name is null.
+ REPORT_ERROR0(VLDTR_E_AS_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ unsigned L = (unsigned)strlen(szName);
+ if((*szName==' ')||strchr(szName,':') || strchr(szName,'\\') || strchr(szName, '/')
+ || strchr(szName, ',') || strchr(szName, '\n') || strchr(szName, '\r')
+ || ((L > 4)&&((!SString::_stricmp(&szName[L-4],".exe"))||(!SString::_stricmp(&szName[L-4],".dll")))))
+ {
+ //Assembly name has path and/or extension
+ REPORT_ERROR0(VLDTR_E_AS_BADNAME);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ // Get the flags value for the Assembly.
+ dwFlags = (CorAssemblyFlags) pMiniMd->getFlagsOfAssembly(pRecord);
+
+ // Validate the flags
+ invalidAssemblyFlags = dwFlags & (~(afPublicKey | afRetargetable | afPA_FullMask | afEnableJITcompileTracking | afDisableJITcompileOptimizer | afContentType_Mask));
+
+ // Validate we only set a legal processor architecture flags
+ // The processor architecture flags were introduced in CLR v2.0.
+ // Note that METAMODEL_MINOR_VER_V2_0 is 0. GCC points out the comparison
+ // is useless, so that part is commented out.
+ fIsV2Assembly = (m_pStgdb->m_MiniMd.m_Schema.m_major >= METAMODEL_MAJOR_VER_V2_0
+ /* && m_pStgdb->m_MiniMd.m_Schema.m_minor >= METAMODEL_MINOR_VER_V2_0*/);
+ if (fIsV2Assembly)
+ {
+ if ((dwFlags & afPA_Mask) > afPA_AMD64 && !IsAfPA_NoPlatform(dwFlags))
+ invalidAssemblyFlags = true;
+ }
+ else {
+ if ((dwFlags & afPA_Mask) != 0)
+ invalidAssemblyFlags = true;
+ }
+
+ if (!IsAfContentType_Default(dwFlags) && !IsAfContentType_WindowsRuntime(dwFlags))
+ { // Unknown ContentType value
+ invalidAssemblyFlags = true;
+ }
+
+ if (invalidAssemblyFlags)
+ {
+ REPORT_ERROR1(VLDTR_E_AS_BADFLAGS, dwFlags);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate hash algorithm ID
+ switch(pRecord->GetHashAlgId())
+ {
+ case CALG_MD2:
+ case CALG_MD4:
+ case CALG_MD5:
+ case CALG_SHA:
+ //case CALG_SHA1: // same as CALG_SHA
+ case CALG_MAC:
+ case CALG_SSL3_SHAMD5:
+ case CALG_HMAC:
+ case 0:
+ break;
+ default:
+ REPORT_ERROR1(VLDTR_E_AS_HASHALGID, pRecord->GetHashAlgId());
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ // Validate locale
+ {
+ LPCSTR szLocale;
+ IfFailGo(pMiniMd->getLocaleOfAssembly(pRecord, &szLocale));
+ if(!_IsValidLocale(szLocale, fIsV2Assembly))
+ {
+ REPORT_ERROR0(VLDTR_E_AS_BADLOCALE);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateAssembly()
+
+//*****************************************************************************
+// Validate the given AssemblyProcessor.
+//*****************************************************************************
+HRESULT RegMeta::ValidateAssemblyProcessor(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateAssemblyProcessor()
+
+//*****************************************************************************
+// Validate the given AssemblyOS.
+//*****************************************************************************
+HRESULT RegMeta::ValidateAssemblyOS(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateAssemblyOS()
+
+//*****************************************************************************
+// Validate the given AssemblyRef.
+//*****************************************************************************
+HRESULT RegMeta::ValidateAssemblyRef(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ AssemblyRefRec *pRecord; // Assembly record.
+ LPCSTR szName; // AssemblyRef Name.
+ VEContext veCtxt; // Context structure.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = TokenFromRid(rid, mdtAssemblyRef);
+ veCtxt.uOffset = 0;
+
+ // Get the AssemblyRef record.
+ IfFailGo(pMiniMd->GetAssemblyRefRecord(rid, &pRecord));
+
+ // Do checks for name and alias validity.
+ IfFailGo(pMiniMd->getNameOfAssemblyRef(pRecord, &szName));
+ if (!*szName)
+ {
+ // AssemblyRef Name is null.
+ REPORT_ERROR0(VLDTR_E_AR_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ unsigned L = (unsigned)strlen(szName);
+ if((*szName==' ')||strchr(szName,':') || strchr(szName,'\\') || strchr(szName, '/')
+ || strchr(szName, ',') || strchr(szName, '\n') || strchr(szName, '\r')
+ || ((L > 4)&&((!SString::_stricmp(&szName[L-4],".exe"))||(!SString::_stricmp(&szName[L-4],".dll")))))
+ {
+ //Assembly name has path and/or extension
+ REPORT_ERROR0(VLDTR_E_AS_BADNAME);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ // Validate locale
+ {
+ LPCSTR szLocale;
+ IfFailGo(pMiniMd->getLocaleOfAssemblyRef(pRecord, &szLocale));
+ BOOL fIsV2Assembly = (m_pStgdb->m_MiniMd.m_Schema.m_major >= METAMODEL_MAJOR_VER_V2_0
+ /* && m_pStgdb->m_MiniMd.m_Schema.m_minor >= METAMODEL_MINOR_VER_V2_0*/);
+ if(!_IsValidLocale(szLocale, fIsV2Assembly))
+ {
+ REPORT_ERROR0(VLDTR_E_AS_BADLOCALE);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateAssemblyRef()
+
+//*****************************************************************************
+// Validate the given AssemblyRefProcessor.
+//*****************************************************************************
+HRESULT RegMeta::ValidateAssemblyRefProcessor(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateAssemblyRefProcessor()
+
+//*****************************************************************************
+// Validate the given AssemblyRefOS.
+//*****************************************************************************
+HRESULT RegMeta::ValidateAssemblyRefOS(RID rid)
+{
+ return S_OK;
+} // RegMeta::ValidateAssemblyRefOS()
+
+//*****************************************************************************
+// Validate the given File.
+//*****************************************************************************
+HRESULT RegMeta::ValidateFile(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ FileRec *pRecord; // File record.
+ mdFile tkFile; // Duplicate File token.
+ LPCSTR szName; // File Name.
+ VEContext veCtxt; // Context structure.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = TokenFromRid(rid, mdtFile);
+ veCtxt.uOffset = 0;
+
+ // Get the File record.
+ IfFailGo(pMiniMd->GetFileRecord(rid, &pRecord));
+
+ // Do checks for name validity.
+ IfFailGo(pMiniMd->getNameOfFile(pRecord, &szName));
+ if (!*szName)
+ {
+ // File Name is null.
+ REPORT_ERROR0(VLDTR_E_FILE_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ ULONG L = (ULONG)strlen(szName);
+ if(L >= MAX_PATH_FNAME)
+ {
+ // Name too long
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_PATH_FNAME-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Check for duplicates based on Name.
+ hr = ImportHelper::FindFile(pMiniMd, szName, &tkFile, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_FILE_DUP, tkFile);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+
+ // File name must not be fully qualified.
+ if(strchr(szName,':') || strchr(szName,'\\') || strchr(szName,'/'))
+ {
+ REPORT_ERROR0(VLDTR_E_FILE_NAMEFULLQLFD);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // File name must not be one of system names.
+ char *sysname[6]={"con","aux","lpt","prn","null","com"};
+ char *syssymbol = "0123456789$:";
+ for(unsigned i=0; i<6; i++)
+ {
+ L = (ULONG)strlen(sysname[i]);
+ if(!SString::_strnicmp(szName,sysname[i],L))
+ {
+ if((szName[L]==0)|| strchr(syssymbol,szName[L]))
+ {
+ REPORT_ERROR0(VLDTR_E_FILE_SYSNAME);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ }
+ }
+ }
+
+ if (pRecord->GetFlags() & (~0x00000003))
+ {
+ REPORT_ERROR1(VLDTR_E_FILE_BADFLAGS, pRecord->GetFlags());
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate hash value
+ {
+ const BYTE *pbHashValue = NULL;
+ ULONG cbHashValue;
+ IfFailGo(m_pStgdb->m_MiniMd.getHashValueOfFile(pRecord, &pbHashValue, &cbHashValue));
+ if ((pbHashValue == NULL) || (cbHashValue == 0))
+ {
+ REPORT_ERROR0(VLDTR_E_FILE_NULLHASH);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ // Validate that the name is not the same as the file containing
+ // the manifest.
+
+ // File name must be a valid file name.
+
+ // Each ModuleRef in the assembly must have a corresponding File table entry.
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateFile()
+
+//*****************************************************************************
+// Validate the given ExportedType.
+//*****************************************************************************
+HRESULT RegMeta::ValidateExportedType(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ ExportedTypeRec *pRecord; // ExportedType record.
+ mdExportedType tkExportedType; // Duplicate ExportedType.
+ mdToken tkImpl; // Implementation token
+ mdToken tkTypeDef; // TypeDef token
+
+ LPCSTR szName; // ExportedType Name.
+ LPCSTR szNamespace; // ExportedType Namespace.
+ VEContext veCtxt; // Context structure.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = TokenFromRid(rid, mdtExportedType);
+ veCtxt.uOffset = 0;
+
+ // Get the ExportedType record.
+ IfFailGo(pMiniMd->GetExportedTypeRecord(rid, &pRecord));
+
+ tkImpl = pMiniMd->getImplementationOfExportedType(pRecord);
+
+ tkTypeDef = pRecord->GetTypeDefId();
+ if ((TypeFromToken(tkImpl) == mdtFile) && IsNilToken(tkTypeDef))
+ { // Report 'No TypeDefId' warning only for types exported from other modules (do not report it for
+ // type forwarders)
+ REPORT_ERROR0(VLDTR_E_CT_NOTYPEDEFID);
+ SetVldtrCode(&hrSave, VLDTR_S_WRN);
+ }
+
+ // Do checks for name validity.
+ IfFailGo(pMiniMd->getTypeNameOfExportedType(pRecord, &szName));
+ IfFailGo(pMiniMd->getTypeNamespaceOfExportedType(pRecord, &szNamespace));
+ if (!*szName)
+ {
+ // ExportedType Name is null.
+ REPORT_ERROR0(VLDTR_E_CT_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit;
+ ULONG L = (ULONG)(strlen(szName)+strlen(szNamespace));
+ if(L >= MAX_CLASSNAME_LENGTH)
+ {
+ // Name too long
+ REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Check for duplicates based on Name and Enclosing ExportedType.
+ hr = ImportHelper::FindExportedType(pMiniMd, szNamespace, szName, tkImpl, &tkExportedType, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_CT_DUP, tkExportedType);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ // Check for duplicate TypeDef based on Name/NameSpace - only for top-level ExportedTypes.
+ if(TypeFromToken(tkImpl)==mdtFile)
+ {
+ mdToken tkTypeDef2;
+ hr = ImportHelper::FindTypeDefByName(pMiniMd, szNamespace, szName, mdTypeDefNil,
+ &tkTypeDef2, 0);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_CT_DUPTDNAME, tkTypeDef2);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ }
+ }
+ // Check if flag value is valid
+ {
+ DWORD dwFlags = pRecord->GetFlags();
+ DWORD dwInvalidMask, dwExtraBits;
+ dwInvalidMask = (DWORD)~(tdVisibilityMask | tdLayoutMask | tdClassSemanticsMask |
+ tdAbstract | tdSealed | tdSpecialName | tdImport | tdSerializable | tdForwarder |
+ tdStringFormatMask | tdBeforeFieldInit | tdReservedMask);
+ // check for extra bits
+ dwExtraBits = dwFlags & dwInvalidMask;
+ if(!dwExtraBits)
+ {
+ // if no extra bits, check layout
+ dwExtraBits = dwFlags & tdLayoutMask;
+ if(dwExtraBits != tdLayoutMask)
+ {
+ // layout OK, check string format
+ dwExtraBits = dwFlags & tdStringFormatMask;
+ if(dwExtraBits != tdStringFormatMask) dwExtraBits = 0;
+ }
+ }
+ if(dwExtraBits)
+ {
+ REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwExtraBits);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ if(IsNilToken(tkImpl)
+ || ((TypeFromToken(tkImpl) != mdtFile)&&(TypeFromToken(tkImpl) != mdtExportedType)&&(TypeFromToken(tkImpl) != mdtAssemblyRef))
+ || (!IsValidToken(tkImpl)))
+ {
+ REPORT_ERROR1(VLDTR_E_CT_BADIMPL, tkImpl);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateExportedType()
+
+//*****************************************************************************
+// Validate the given ManifestResource.
+//*****************************************************************************
+HRESULT RegMeta::ValidateManifestResource(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ ManifestResourceRec *pRecord; // ManifestResource record.
+ LPCSTR szName; // ManifestResource Name.
+ DWORD dwFlags; // ManifestResource flags.
+ mdManifestResource tkmar; // Duplicate ManifestResource.
+ VEContext veCtxt; // Context structure.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ mdToken tkImplementation;
+ BOOL bIsValidImplementation = TRUE;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = TokenFromRid(rid, mdtManifestResource);
+ veCtxt.uOffset = 0;
+
+ // Get the ManifestResource record.
+ IfFailGo(pMiniMd->GetManifestResourceRecord(rid, &pRecord));
+
+ // Do checks for name validity.
+ IfFailGo(pMiniMd->getNameOfManifestResource(pRecord, &szName));
+ if (!*szName)
+ {
+ // ManifestResource Name is null.
+ REPORT_ERROR0(VLDTR_E_MAR_NAMENULL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ // Check for duplicates based on Name.
+ hr = ImportHelper::FindManifestResource(pMiniMd, szName, &tkmar, rid);
+ if (hr == S_OK)
+ {
+ REPORT_ERROR1(VLDTR_E_MAR_DUP, tkmar);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ hr = S_OK;
+ else
+ IfFailGo(hr);
+ }
+
+ // Get the flags of the ManifestResource.
+ dwFlags = pMiniMd->getFlagsOfManifestResource(pRecord);
+ if(dwFlags &(~0x00000003))
+ {
+ REPORT_ERROR1(VLDTR_E_MAR_BADFLAGS, dwFlags);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Visibility of ManifestResource flags must either be public or private.
+ if (!IsMrPublic(dwFlags) && !IsMrPrivate(dwFlags))
+ {
+ REPORT_ERROR0(VLDTR_E_MAR_NOTPUBPRIV);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Implementation must be Nil or valid AssemblyRef or File
+ tkImplementation = pMiniMd->getImplementationOfManifestResource(pRecord);
+ if(!IsNilToken(tkImplementation))
+ {
+ switch(TypeFromToken(tkImplementation))
+ {
+ case mdtAssemblyRef:
+ bIsValidImplementation = IsValidToken(tkImplementation);
+ break;
+ case mdtFile:
+ if((bIsValidImplementation = IsValidToken(tkImplementation)))
+ { // if file not PE, offset must be 0
+ FileRec *pFR;
+ IfFailGo(pMiniMd->GetFileRecord(RidFromToken(tkImplementation), &pFR));
+ if(IsFfContainsNoMetaData(pFR->GetFlags())
+ && pRecord->GetOffset())
+ {
+ REPORT_ERROR1(VLDTR_E_MAR_BADOFFSET, tkImplementation);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ break;
+ default:
+ bIsValidImplementation = FALSE;
+ }
+ }
+ if(!bIsValidImplementation)
+ {
+ REPORT_ERROR1(VLDTR_E_MAR_BADIMPL, tkImplementation);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate the Offset into the PE file.
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateManifestResource()
+
+//*****************************************************************************
+// Validate the given NestedClass.
+//*****************************************************************************
+HRESULT RegMeta::ValidateNestedClass(RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope.
+ NestedClassRec *pRecord; // NestedClass record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save the current state.
+ VEContext veCtxt; // Context structure.
+ mdToken tkNested;
+ mdToken tkEncloser;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = rid;
+ veCtxt.uOffset = 0;
+
+ // Get the NestedClass record.
+ IfFailGo(pMiniMd->GetNestedClassRecord(rid, &pRecord));
+ tkNested = pMiniMd->getNestedClassOfNestedClass(pRecord);
+ tkEncloser = pMiniMd->getEnclosingClassOfNestedClass(pRecord);
+
+ // Nested must be valid TypeDef
+ if((TypeFromToken(tkNested) != mdtTypeDef) || !IsValidToken(tkNested))
+ {
+ REPORT_ERROR1(VLDTR_E_NC_BADNESTED, tkNested);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Encloser must be valid TypeDef
+ if((TypeFromToken(tkEncloser) != mdtTypeDef) || !IsValidToken(tkEncloser))
+ {
+ REPORT_ERROR1(VLDTR_E_NC_BADENCLOSER, tkEncloser);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ // Check for duplicates
+ {
+ RID N = pMiniMd->getCountNestedClasss();
+ RID tmp;
+ NestedClassRec* pRecTmp;
+ mdToken tkEncloserTmp;
+ for(tmp = rid+1; tmp <= N; tmp++)
+ {
+ IfFailGo(pMiniMd->GetNestedClassRecord(tmp, &pRecTmp));
+ if(tkNested == pMiniMd->getNestedClassOfNestedClass(pRecTmp))
+ {
+ if(tkEncloser == (tkEncloserTmp = pMiniMd->getEnclosingClassOfNestedClass(pRecTmp)))
+ {
+ REPORT_ERROR1(VLDTR_E_NC_DUP, tmp);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ else
+ {
+ REPORT_ERROR3(VLDTR_E_NC_DUPENCLOSER, tkNested, tkEncloser, tkEncloserTmp);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateLocalVariable()
+
+//*****************************************************************************
+// Given a Table ID and a Row ID, validate all the columns contain meaningful
+// values given the column definitions. Validate that the offsets into the
+// different pools are valid, the rids are within range and the coded tokens
+// are valid. Every failure here is considered an error.
+//*****************************************************************************
+HRESULT RegMeta::ValidateRecord(ULONG ixTbl, RID rid)
+{
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save the current state.
+ ULONG ulCount; // Count of records in the table.
+ ULONG ulRawColVal; // Raw value of the column.
+ void *pRow; // Row with the data.
+ CMiniTableDef *pTbl; // Table definition.
+ CMiniColDef *pCol; // Column definition.
+ const CCodedTokenDef *pCdTkn; // Coded token definition.
+ ULONG ix; // Index into the array of coded tokens.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // Get the table definition.
+ pTbl = &pMiniMd->m_TableDefs[ixTbl];
+
+ // Get the row. We may assume that the Row pointer we get back from
+ // this call is correct since we do the verification on the Record
+ // pools for each table during the open sequence. The only place
+ // this is not valid is for Dynamic IL and we don't do this
+ // verification in that case since we go through IMetaData* APIs
+ // in that case and it should all be consistent.
+ IfFailGo(m_pStgdb->m_MiniMd.getRow(ixTbl, rid, &pRow));
+
+ for (ULONG ixCol = 0; ixCol < pTbl->m_cCols; ixCol++)
+ {
+ // Get the column definition.
+ pCol = &pTbl->m_pColDefs[ixCol];
+
+ // Get the raw value stored in the column. getIX currently doesn't
+ // handle byte sized fields, but there are some BYTE fields in the
+ // MetaData. So using the conditional to access BYTE fields.
+ if (pCol->m_cbColumn == 1)
+ ulRawColVal = pMiniMd->getI1(pRow, *pCol);
+ else
+ ulRawColVal = pMiniMd->getIX(pRow, *pCol);
+
+ // Do some basic checks on the non-absurdity of the value stored in the
+ // column.
+ if (IsRidType(pCol->m_Type))
+ {
+ // Verify that the RID is within range.
+ _ASSERTE(pCol->m_Type < pMiniMd->GetCountTables());
+ ulCount = pMiniMd->GetCountRecs(pCol->m_Type);
+ // For records storing rids to pointer tables, the stored value may
+ // be one beyond the last record.
+ if (IsTblPtr(pCol->m_Type, ixTbl))
+ ulCount++;
+ if (ulRawColVal > ulCount)
+ {
+ VEContext veCtxt;
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = 0;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR3(VLDTR_E_RID_OUTOFRANGE, ixTbl, ixCol, rid);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else if (IsCodedTokenType(pCol->m_Type))
+ {
+ // Verify that the Coded token and rid are valid.
+ pCdTkn = &g_CodedTokens[pCol->m_Type - iCodedToken];
+ ix = ulRawColVal & ~(-1 << CMiniMdRW::m_cb[pCdTkn->m_cTokens]);
+ if (ix >= pCdTkn->m_cTokens)
+ {
+ VEContext veCtxt;
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = 0;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR3(VLDTR_E_CDTKN_OUTOFRANGE, ixTbl, ixCol, rid);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ ulCount = pMiniMd->GetCountRecs(TypeFromToken(pCdTkn->m_pTokens[ix]) >> 24);
+ if ( (ulRawColVal >> CMiniMdRW::m_cb[pCdTkn->m_cTokens]) > ulCount)
+ {
+ VEContext veCtxt;
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = 0;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR3(VLDTR_E_CDRID_OUTOFRANGE, ixTbl, ixCol, rid);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ else if (IsHeapType(pCol->m_Type))
+ {
+ // Verify that the offsets for the Heap type fields are valid offsets
+ // into the heaps.
+ switch (pCol->m_Type)
+ {
+ case iSTRING:
+ if (!pMiniMd->m_StringHeap.IsValidIndex(ulRawColVal))
+ {
+ VEContext veCtxt;
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = 0;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR3(VLDTR_E_STRING_INVALID, ixTbl, ixCol, rid);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ break;
+ case iGUID:
+ if (ulRawColVal == 0)
+ { // GUID value 0 is valid value, though it's invalid GUID heap index
+ break;
+ }
+ if (!pMiniMd->m_GuidHeap.IsValidIndex(ulRawColVal))
+ {
+ VEContext veCtxt;
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = 0;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR3(VLDTR_E_GUID_INVALID, ixTbl, ixCol, rid);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ break;
+ case iBLOB:
+ if (! pMiniMd->m_BlobHeap.IsValidIndex(ulRawColVal))
+ {
+ VEContext veCtxt;
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = 0;
+ veCtxt.uOffset = 0;
+ REPORT_ERROR3(VLDTR_E_BLOB_INVALID, ixTbl, ixCol, rid);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ break;
+ default:
+ _ASSERTE(!"Invalid heap type encountered!");
+ }
+ }
+ else
+ {
+ // Not much checking that can be done on the fixed type in a generic sense.
+ _ASSERTE (IsFixedType(pCol->m_Type));
+ }
+ hr = hrSave;
+ }
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateRecord()
+
+//*****************************************************************************
+// This function validates that the given Method signature is consistent as per
+// the compression scheme.
+//*****************************************************************************
+HRESULT RegMeta::ValidateSigCompression(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig) // [IN] Size in bytes of the signature.
+{
+ VEContext veCtxt; // Context record.
+ ULONG ulCurByte = 0; // Current index into the signature.
+ ULONG ulSize; // Size of uncompressed data at each point.
+ HRESULT hr = S_OK; // Value returned.
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = tk;
+ veCtxt.uOffset = 0;
+
+ // Check for NULL signature.
+ if (!cbSig)
+ {
+ REPORT_ERROR0(VLDTR_E_SIGNULL);
+ SetVldtrCode(&hr, VLDTR_S_ERR);
+ goto ErrExit;
+ }
+
+ // Walk through the signature. At each point make sure there is enough
+ // room left in the signature based on the encoding in the current byte.
+ while (cbSig - ulCurByte)
+ {
+ _ASSERTE(ulCurByte <= cbSig);
+ // Get next chunk of uncompressed data size.
+ if ((ulSize = CorSigUncompressedDataSize(pbSig)) > (cbSig - ulCurByte))
+ {
+ REPORT_ERROR1(VLDTR_E_SIGNODATA, ulCurByte+1);
+ SetVldtrCode(&hr, VLDTR_S_ERR);
+ goto ErrExit;
+ }
+ // Go past this chunk.
+ ulCurByte += ulSize;
+ CorSigUncompressData(pbSig);
+ }
+ErrExit:
+
+ return hr;
+} // RegMeta::ValidateSigCompression()
+
+//*****************************************************************************
+// This function validates one argument given an offset into the signature
+// where the argument begins. This function assumes that the signature is well
+// formed as far as the compression scheme is concerned.
+//*****************************************************************************
+//@GENERICS: todo: reject uninstantiated generic types used as types.
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+HRESULT RegMeta::ValidateOneArg(
+ mdToken tk, // [IN] Token whose signature is being processed.
+ PCCOR_SIGNATURE &pbSig, // [IN] Pointer to the beginning of argument.
+ ULONG cbSig, // [IN] Size in bytes of the full signature.
+ ULONG *pulCurByte, // [IN/OUT] Current offset into the signature..
+ ULONG *pulNSentinels, // [IN/OUT] Number of sentinels
+ BOOL bNoVoidAllowed) // [IN] Flag indicating whether "void" is disallowed for this arg
+{
+ ULONG ulElementType; // Current element type being processed.
+ ULONG ulElemSize; // Size of the element type.
+ mdToken token; // Embedded token.
+ ULONG ulArgCnt; // Argument count for function pointer.
+ ULONG ulRank; // Rank of the array.
+ ULONG ulSizes; // Count of sized dimensions of the array.
+ ULONG ulLbnds; // Count of lower bounds of the array.
+ ULONG ulTkSize; // Token size.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ BOOL bRepeat = TRUE; // MODOPT and MODREQ belong to the arg after them
+ BOOL bByRefForbidden = FALSE;// ByRef is not allowed for fields
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ switch(TypeFromToken(tk))
+ {
+ case mdtFieldDef:
+ bByRefForbidden = TRUE;
+ break;
+ case mdtName:
+ tk = TokenFromRid(RidFromToken(tk),mdtFieldDef);
+ // Field type can be a FNPTR with a sig containing ByRefs.
+ // So we change the token type not to be mdtFieldDef and thus allow ByRefs,
+ // but the token needs to be restored to its original type for reporting
+ break;
+ }
+
+ _ASSERTE (pulCurByte);
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = tk;
+ veCtxt.uOffset = 0;
+
+ while(bRepeat)
+ {
+ bRepeat = FALSE;
+ // Validate that the argument is not missing.
+ _ASSERTE(*pulCurByte <= cbSig);
+ if (cbSig == *pulCurByte)
+ {
+ hr = VLDTR_E_SIG_MISSARG;
+ goto ErrExit;
+ }
+
+ // Get the element type.
+ *pulCurByte += (ulElemSize = CorSigUncompressedDataSize(pbSig));
+ ulElementType = CorSigUncompressData(pbSig);
+
+ // Walk past all the modifier types.
+ while (ulElementType & ELEMENT_TYPE_MODIFIER)
+ {
+ _ASSERTE(*pulCurByte <= cbSig);
+ if(ulElementType == ELEMENT_TYPE_SENTINEL)
+ {
+ if(pulNSentinels) *pulNSentinels+=1;
+ if(TypeFromToken(tk) == mdtMethodDef)
+ {
+ REPORT_ERROR0(VLDTR_E_SIG_SENTINMETHODDEF);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR0(VLDTR_E_SIG_LASTSENTINEL);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ goto ErrExit;
+ }
+ }
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR2(VLDTR_E_SIG_MISSELTYPE, ulElementType, *pulCurByte + 1);
+ SetVldtrCode(&hr, hrSave);
+ goto ErrExit;
+ }
+ *pulCurByte += (ulElemSize = CorSigUncompressedDataSize(pbSig));
+ ulElementType = CorSigUncompressData(pbSig);
+ }
+
+ switch (ulElementType)
+ {
+ case ELEMENT_TYPE_VOID:
+ if(bNoVoidAllowed)
+ {
+ IfBreakGo(m_pVEHandler->VEHandler(VLDTR_E_SIG_BADVOID, veCtxt, 0));
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_TYPEDBYREF:
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_I:
+ break;
+ case ELEMENT_TYPE_BYREF: //fallthru
+ if(bByRefForbidden)
+ {
+ IfBreakGo(m_pVEHandler->VEHandler(VLDTR_E_SIG_BYREFINFIELD, veCtxt, 0));
+ SetVldtrCode(&hr, hrSave);
+ }
+ case ELEMENT_TYPE_PTR:
+ // Validate the referenced type.
+ IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,pulNSentinels,FALSE));
+ if (hr != S_OK)
+ SetVldtrCode(&hrSave, hr);
+ break;
+ case ELEMENT_TYPE_PINNED:
+ case ELEMENT_TYPE_SZARRAY:
+ // Validate the referenced type.
+ IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,pulNSentinels,TRUE));
+ if (hr != S_OK)
+ SetVldtrCode(&hrSave, hr);
+ break;
+ case ELEMENT_TYPE_VALUETYPE: //fallthru
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_CMOD_OPT:
+ case ELEMENT_TYPE_CMOD_REQD:
+ // See if the token is missing.
+ _ASSERTE(*pulCurByte <= cbSig);
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSTKN, ulElementType);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ // See if the token is a valid token.
+ ulTkSize = CorSigUncompressedDataSize(pbSig);
+ token = CorSigUncompressToken(pbSig);
+ if (!IsValidToken(token))
+ {
+ REPORT_ERROR2(VLDTR_E_SIG_TKNBAD, token, *pulCurByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ *pulCurByte += ulTkSize;
+ break;
+ }
+ *pulCurByte += ulTkSize;
+ if ((ulElementType == ELEMENT_TYPE_CLASS) || (ulElementType == ELEMENT_TYPE_VALUETYPE))
+ {
+ // Check for long-form encoding
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ LPCSTR szName = ""; // token's Name.
+ LPCSTR szNameSpace = ""; // token's NameSpace.
+
+
+ // Check for TypeDef or TypeRef
+ // To prevent cycles in metadata, token must not be a TypeSpec.
+ if ((TypeFromToken(token) != mdtTypeRef) && (TypeFromToken(token) != mdtTypeDef))
+ {
+ REPORT_ERROR2(VLDTR_E_SIG_BADTOKTYPE, token, *pulCurByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ if (TypeFromToken(token) == mdtTypeRef)
+ {
+ TypeRefRec *pTokenRec;
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(token), &pTokenRec));
+ mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pTokenRec);
+ if (RidFromToken(tkResScope) && (TypeFromToken(tkResScope) == mdtAssemblyRef))
+ {
+ AssemblyRefRec * pARRec;
+ IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResScope), &pARRec));
+ LPCSTR szAssemblyRefName;
+ IfFailGo(pMiniMd->getNameOfAssemblyRef(pARRec, &szAssemblyRefName));
+ if((0 == SString::_stricmp("mscorlib", szAssemblyRefName)) || (0 == SString::_stricmp("System.Runtime", szAssemblyRefName)))
+ {
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTokenRec, &szNameSpace));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTokenRec, &szName));
+ }
+ }
+ }
+ else if (TypeFromToken(token) == mdtTypeDef)
+ {
+ TypeDefRec *pTokenRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(token), &pTokenRec));
+ if(g_fValidatingMscorlib) // otherwise don't even bother checking the name
+ {
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTokenRec, &szName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTokenRec, &szNameSpace));
+ }
+ // while at it, check if token is indeed a class (valuetype)
+ BOOL bValueType = FALSE;
+ if(!IsTdInterface(pTokenRec->GetFlags()))
+ {
+ mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pTokenRec);
+ if(RidFromToken(tkExtends))
+ {
+ LPCSTR szExtName = ""; // parent's Name.
+ LPCSTR szExtNameSpace = ""; // parent's NameSpace.
+ if(TypeFromToken(tkExtends)==mdtTypeRef)
+ {
+ TypeRefRec *pExtRec;
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pExtRec));
+ mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pExtRec);
+ if(RidFromToken(tkResScope) && (TypeFromToken(tkResScope)==mdtAssemblyRef))
+ {
+ AssemblyRefRec *pARRec;
+ IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResScope), &pARRec));
+ LPCSTR szAssemblyRefName;
+ IfFailGo(pMiniMd->getNameOfAssemblyRef(pARRec, &szAssemblyRefName));
+ if((0 == SString::_stricmp("mscorlib", szAssemblyRefName)) || (0 == SString::_stricmp("System.Runtime", szAssemblyRefName)))
+ {
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pExtRec, &szExtNameSpace));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pExtRec, &szExtName));
+ }
+ }
+ }
+ else if(TypeFromToken(tkExtends)==mdtTypeDef)
+ {
+ if(g_fValidatingMscorlib) // otherwise don't even bother checking the name
+ {
+ TypeDefRec *pExtRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pExtRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pExtRec, &szExtName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtRec, &szExtNameSpace));
+ }
+ }
+ if(0 == strcmp(szExtNameSpace,BASE_NAMESPACE))
+ {
+ if(0==strcmp(szExtName,BASE_ENUM_CLASSNAME)) bValueType = TRUE;
+ else if(0==strcmp(szExtName,BASE_VTYPE_CLASSNAME))
+ {
+ bValueType = (strcmp(szNameSpace,BASE_NAMESPACE) ||
+ strcmp(szName,BASE_ENUM_CLASSNAME));
+ }
+ }
+ }
+ }
+ if(bValueType != (ulElementType == ELEMENT_TYPE_VALUETYPE))
+ {
+ REPORT_ERROR2(VLDTR_E_SIG_TOKTYPEMISMATCH, token, *pulCurByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ }
+ if(0 == strcmp(szNameSpace,BASE_NAMESPACE))
+ {
+ for(unsigned jjj = 0; jjj < g_NumSigLongForms; jjj++)
+ {
+ if(0 == strcmp(szName,g_SigLongFormName[jjj]))
+ {
+ REPORT_ERROR2(VLDTR_E_SIG_LONGFORM, token, *pulCurByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ }
+ }
+ }
+ else // i.e. if(ELEMENT_TYPE_CMOD_OPT || ELEMENT_TYPE_CMOD_REQD)
+ bRepeat = TRUE; // go on validating, we're not done with this arg
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ // Validate that calling convention is present.
+ _ASSERTE(*pulCurByte <= cbSig);
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSFPTR, *pulCurByte + 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ // Consume calling convention.
+ *pulCurByte += CorSigUncompressedDataSize(pbSig);
+ CorSigUncompressData(pbSig);
+
+ // Validate that argument count is present.
+ _ASSERTE(*pulCurByte <= cbSig);
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSFPTRARGCNT, *pulCurByte + 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ // Consume argument count.
+ *pulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulArgCnt = CorSigUncompressData(pbSig);
+
+ // Checking the signature, ByRefs OK
+ if(bByRefForbidden)
+ tk = TokenFromRid(RidFromToken(tk),mdtName);
+
+ // Validate and consume return type.
+ IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,NULL,FALSE));
+ if (hr != S_OK)
+ {
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+
+ // Validate and consume the arguments.
+ while(ulArgCnt--)
+ {
+ IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,NULL,TRUE));
+ if (hr != S_OK)
+ {
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ // Validate and consume the base type.
+ IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,pulNSentinels,TRUE));
+
+ // Validate that the rank is present.
+ _ASSERTE(*pulCurByte <= cbSig);
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSRANK, *pulCurByte + 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ // Consume the rank.
+ *pulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulRank = CorSigUncompressData(pbSig);
+
+ // Process the sizes.
+ if (ulRank)
+ {
+ // Validate that the count of sized-dimensions is specified.
+ _ASSERTE(*pulCurByte <= cbSig);
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSNSIZE, *pulCurByte + 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ // Consume the count of sized dimensions.
+ *pulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulSizes = CorSigUncompressData(pbSig);
+
+ // Loop over the sizes.
+ while (ulSizes--)
+ {
+ // Validate the current size.
+ _ASSERTE(*pulCurByte <= cbSig);
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSSIZE, *pulCurByte + 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ // Consume the current size.
+ *pulCurByte += CorSigUncompressedDataSize(pbSig);
+ CorSigUncompressData(pbSig);
+ }
+
+ // Validate that the count of lower bounds is specified.
+ _ASSERTE(*pulCurByte <= cbSig);
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSNLBND, *pulCurByte + 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ // Consume the count of lower bound.
+ *pulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulLbnds = CorSigUncompressData(pbSig);
+
+ // Loop over the lower bounds.
+ while (ulLbnds--)
+ {
+ // Validate the current lower bound.
+ _ASSERTE(*pulCurByte <= cbSig);
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSLBND, *pulCurByte + 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ // Consume the current size.
+ *pulCurByte += CorSigUncompressedDataSize(pbSig);
+ CorSigUncompressData(pbSig);
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ // Consume index.
+ *pulCurByte += CorSigUncompressedDataSize(pbSig);
+ CorSigUncompressData(pbSig);
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ {
+ PCCOR_SIGNATURE pbGenericTypeSig = pbSig;
+ BOOL fCheckArity = FALSE;
+ ULONG ulGenericArity = 0;
+
+ // Validate and consume the type constructor
+ IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte, NULL, TRUE));
+
+ // Extract its arity
+ {
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ switch(CorSigUncompressElementType(pbGenericTypeSig))
+ {
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ {
+ mdToken tkGenericType = CorSigUncompressToken(pbGenericTypeSig);
+ if (TypeFromToken(tkGenericType) == mdtTypeDef)
+ {
+ HENUMInternal hEnumTyPars;
+ hr = pMiniMd->FindGenericParamHelper(tkGenericType, &hEnumTyPars);
+ if (SUCCEEDED(hr))
+ {
+ IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulGenericArity));
+ HENUMInternal::ClearEnum(&hEnumTyPars);
+ fCheckArity = TRUE;
+ }
+ ;
+ }
+ // for a mdtTypeRef, don't check anything until load time
+ break;
+ }
+ default:
+ break;
+ }
+
+ }
+
+ // Consume argument count.
+ if (cbSig == *pulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSARITY, *pulCurByte + 1);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+
+ *pulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulArgCnt = CorSigUncompressData(pbSig);
+
+ if (ulArgCnt == 0)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_ARITYZERO,*pulCurByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ if (fCheckArity && ulArgCnt != ulGenericArity)
+ {
+ REPORT_ERROR3(VLDTR_E_SIG_ARITYMISMATCH,ulGenericArity,ulArgCnt,*pulCurByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate and consume the arguments.
+ while(ulArgCnt--)
+ {
+ PCCOR_SIGNATURE pbTypeArg = pbSig;
+ ULONG ulTypeArgByte = *pulCurByte;
+ IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte, NULL, TRUE));
+ if (hr != S_OK)
+ {
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+
+ // reject byref-like args
+ switch (CorSigUncompressData(pbTypeArg))
+ {
+ case ELEMENT_TYPE_TYPEDBYREF:
+ case ELEMENT_TYPE_BYREF:
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_BYREFINST, ulTypeArgByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ break;
+ }
+
+
+ case ELEMENT_TYPE_SENTINEL: // this case never works because all modifiers are skipped before switch
+ if(TypeFromToken(tk) == mdtMethodDef)
+ {
+ REPORT_ERROR0(VLDTR_E_SIG_SENTINMETHODDEF);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ break;
+ default:
+ REPORT_ERROR2(VLDTR_E_SIG_BADELTYPE, ulElementType, *pulCurByte - ulElemSize);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ break;
+ } // switch (ulElementType)
+ } // end while(bRepeat)
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateOneArg()
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+// This function validates the given Method signature. This function works
+// with Method signature for both the MemberRef and MethodDef.
+//*****************************************************************************
+HRESULT RegMeta::ValidateMethodSig(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size in bytes of the signature.
+ DWORD dwFlags) // [IN] Method flags.
+{
+ ULONG ulCurByte = 0; // Current index into the signature.
+ ULONG ulCallConv; // Calling convention.
+ ULONG ulArgCount; // Count of arguments.
+ ULONG ulTyArgCount; // Count of type arguments.
+ ULONG i; // Looping index.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+ ULONG ulNSentinels = 0;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(TypeFromToken(tk) == mdtMethodDef ||
+ TypeFromToken(tk) == mdtMemberRef);
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = tk;
+ veCtxt.uOffset = 0;
+
+ // Validate the signature is well-formed with respect to the compression
+ // scheme. If this fails, no further validation needs to be done.
+ if ((hr = ValidateSigCompression(tk, pbSig, cbSig)) != S_OK)
+ goto ErrExit;
+
+ // Validate the calling convention.
+ ulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulCallConv = CorSigUncompressData(pbSig);
+
+ i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK;
+ if ((i != IMAGE_CEE_CS_CALLCONV_DEFAULT)&&( i != IMAGE_CEE_CS_CALLCONV_VARARG)
+ || (ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_BADCALLINGCONV, ulCallConv);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ if (TypeFromToken(tk) == mdtMethodDef) // MemberRefs have no flags available
+ {
+ // If HASTHIS is set on the calling convention, the method should not be static.
+ if ((ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
+ IsMdStatic(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_THISSTATIC, ulCallConv);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // If HASTHIS is not set on the calling convention, the method should be static.
+ if (!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
+ !IsMdStatic(dwFlags))
+ {
+ REPORT_ERROR1(VLDTR_E_MD_NOTTHISNOTSTATIC, ulCallConv);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+
+ // Get the type argument count.
+ if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ if (i != IMAGE_CEE_CS_CALLCONV_DEFAULT)
+ {
+ REPORT_ERROR1(VLDTR_E_MD_GENERIC_BADCALLCONV, ulCallConv);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ if (cbSig == ulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_MD_MISSARITY, ulCurByte+1);
+ SetVldtrCode(&hr, hrSave);
+ goto ErrExit;
+ }
+
+ ulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulTyArgCount = CorSigUncompressData(pbSig);
+
+ if (ulTyArgCount == 0)
+ {
+ REPORT_ERROR1(VLDTR_E_MD_ARITYZERO, ulCurByte);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // If this is a def, check the arity against the number of generic params
+ if (TypeFromToken(tk) == mdtMethodDef)
+ {
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ ULONG ulGenericParamCount;
+ HENUMInternal hEnumTyPars;
+ hr = pMiniMd->FindGenericParamHelper(tk, &hEnumTyPars);
+ if (SUCCEEDED(hr))
+ {
+ IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulGenericParamCount));
+ HENUMInternal::ClearEnum(&hEnumTyPars);
+ if (ulTyArgCount != ulGenericParamCount)
+ {
+ REPORT_ERROR2(VLDTR_E_MD_GPMISMATCH,ulTyArgCount,ulGenericParamCount);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ }
+ }
+ }
+
+
+ // Is there any sig left for arguments?
+ _ASSERTE(ulCurByte <= cbSig);
+ if (cbSig == ulCurByte)
+ {
+ REPORT_ERROR1(VLDTR_E_MD_NOARGCNT, ulCurByte+1);
+ SetVldtrCode(&hr, hrSave);
+ goto ErrExit;
+ }
+
+ // Get the argument count.
+ ulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulArgCount = CorSigUncompressData(pbSig);
+
+ // Validate the return type and the arguments.
+// for (i = 0; i < (ulArgCount + 1); i++)
+ for(i=1; ulCurByte < cbSig; i++)
+ {
+ hr = ValidateOneArg(tk, pbSig, cbSig, &ulCurByte,&ulNSentinels,(i > 1));
+ if (hr != S_OK)
+ {
+ if(hr == VLDTR_E_SIG_MISSARG)
+ {
+ REPORT_ERROR1(VLDTR_E_SIG_MISSARG, i);
+ }
+ SetVldtrCode(&hr, hrSave);
+ hrSave = hr;
+ break;
+ }
+ }
+ if((ulNSentinels != 0) && (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_VARARG )))
+ {
+ REPORT_ERROR0(VLDTR_E_SIG_SENTMUSTVARARG);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+ if(ulNSentinels > 1)
+ {
+ REPORT_ERROR0(VLDTR_E_SIG_MULTSENTINELS);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateMethodSig()
+
+//*****************************************************************************
+// This function validates the given Field signature. This function works
+// with Field signature for both the MemberRef and FieldDef.
+//*****************************************************************************
+HRESULT RegMeta::ValidateFieldSig(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig) // [IN] Size in bytes of the signature.
+{
+ ULONG ulCurByte = 0; // Current index into the signature.
+ ULONG ulCallConv; // Calling convention.
+ VEContext veCtxt; // Context record.
+ HRESULT hr = S_OK; // Value returned.
+ HRESULT hrSave = S_OK; // Save state.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(TypeFromToken(tk) == mdtFieldDef ||
+ TypeFromToken(tk) == mdtMemberRef);
+
+ memset(&veCtxt, 0, sizeof(VEContext));
+ veCtxt.Token = tk;
+ veCtxt.uOffset = 0;
+
+ // Validate the calling convention.
+ ulCurByte += CorSigUncompressedDataSize(pbSig);
+ ulCallConv = CorSigUncompressData(pbSig);
+ if (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_FIELD ))
+ {
+ REPORT_ERROR1(VLDTR_E_FD_BADCALLINGCONV, ulCallConv);
+ SetVldtrCode(&hrSave, VLDTR_S_ERR);
+ }
+
+ // Validate the field.
+ IfFailGo(ValidateOneArg(tk, pbSig, cbSig, &ulCurByte,NULL,TRUE));
+ SetVldtrCode(&hrSave, hr);
+
+ hr = hrSave;
+ErrExit:
+ ;
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::ValidateFieldSig()
+
+//*****************************************************************************
+// This is a utility function to allocate a one-dimensional zero-based safe
+// array of variants.
+//*****************************************************************************
+static HRESULT _AllocSafeVariantArrayVector( // Return status.
+ VARIANT *rVar, // [IN] Variant array.
+ int cElem, // [IN] Size of the array.
+ SAFEARRAY **ppArray) // [OUT] Double pointer to SAFEARRAY.
+{
+ HRESULT hr = S_OK;
+ LONG i;
+
+ _ASSERTE(rVar && cElem && ppArray);
+
+ IfNullGo(*ppArray = SafeArrayCreateVector(VT_VARIANT, 0, cElem));
+ for (i = 0; i < cElem; i++)
+ IfFailGo(SafeArrayPutElement(*ppArray, &i, &rVar[i]));
+ErrExit:
+ return hr;
+} // _AllocSafeVariantArrayVector()
+
+//*****************************************************************************
+// Helper function for reporting error with no arguments
+//*****************************************************************************
+HRESULT RegMeta::_ValidateErrorHelper(
+ HRESULT VECode,
+ VEContext Context)
+{
+ HRESULT hr = S_OK;
+
+ //
+ // MDValidator does not zero out the Context. Fix it here. This fix relies
+ // on the fact that MDValidator just uses the token and offset field of the
+ // context.
+ //
+
+ if (Context.Token != 0) {
+ Context.flags = VER_ERR_TOKEN;
+ }
+
+ IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, NULL));
+ErrExit:
+
+ return hr;
+} // _ValidateErrorHelper()
+
+//*****************************************************************************
+// Helper function for reporting error with 1 argument
+//*****************************************************************************
+HRESULT RegMeta::_ValidateErrorHelper(
+ HRESULT VECode,
+ VEContext Context,
+ ULONG ulVal1)
+{
+ HRESULT hr = S_OK;
+ SAFEARRAY *psa = 0; // The SAFEARRAY.
+ VARIANT rVar[1]; // The VARIANT array
+
+ if (Context.Token != 0) {
+ Context.flags = VER_ERR_TOKEN;
+ }
+
+ V_VT(&rVar[0]) = VT_UI4;
+ V_UI4(&rVar[0]) = ulVal1;
+ IfFailGo(_AllocSafeVariantArrayVector(rVar, 1, &psa));
+ IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, psa));
+
+ErrExit:
+ if (psa)
+ {
+ HRESULT hrSave = SafeArrayDestroy(psa);
+ if (FAILED(hrSave))
+ hr = hrSave;
+ }
+ return hr;
+} // _ValidateErrorHelper()
+
+//*****************************************************************************
+// Helper function for reporting error with 2 arguments
+//*****************************************************************************
+HRESULT RegMeta::_ValidateErrorHelper(
+ HRESULT VECode,
+ VEContext Context,
+ ULONG ulVal1,
+ ULONG ulVal2)
+{
+ HRESULT hr = S_OK;
+ SAFEARRAY *psa = 0; // The SAFEARRAY.
+ VARIANT rVar[2]; // The VARIANT array
+
+ if (Context.Token != 0) {
+ Context.flags = VER_ERR_TOKEN;
+ }
+
+ V_VT(&rVar[0]) = VT_UI4;
+ V_UI4(&rVar[0]) = ulVal1;
+ V_VT(&rVar[1]) = VT_UI4;
+ V_UI4(&rVar[1]) = ulVal2;
+
+ IfFailGo(_AllocSafeVariantArrayVector(rVar, 2, &psa));
+ IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, psa));
+
+ErrExit:
+ if (psa)
+ {
+ HRESULT hrSave = SafeArrayDestroy(psa);
+ if (FAILED(hrSave))
+ hr = hrSave;
+ }
+ return hr;
+} // _ValidateErrorHelper()
+
+//*****************************************************************************
+// Helper function for reporting error with 3 arguments
+//*****************************************************************************
+HRESULT RegMeta::_ValidateErrorHelper(
+ HRESULT VECode,
+ VEContext Context,
+ ULONG ulVal1,
+ ULONG ulVal2,
+ ULONG ulVal3)
+{
+ HRESULT hr = S_OK;
+ SAFEARRAY *psa = 0; // The SAFEARRAY.
+ VARIANT rVar[3]; // The VARIANT array
+
+ if (Context.Token != 0) {
+ Context.flags = VER_ERR_TOKEN;
+ }
+
+ V_VT(&rVar[0]) = VT_UI4;
+ V_UI4(&rVar[0]) = ulVal1;
+ V_VT(&rVar[1]) = VT_UI4;
+ V_UI4(&rVar[1]) = ulVal2;
+ V_VT(&rVar[2]) = VT_UI4;
+ V_UI4(&rVar[2]) = ulVal3;
+
+ IfFailGo(_AllocSafeVariantArrayVector(rVar, 3, &psa));
+ IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, psa));
+
+ErrExit:
+ if (psa)
+ {
+ HRESULT hrSave = SafeArrayDestroy(psa);
+ if (FAILED(hrSave))
+ hr = hrSave;
+ }
+ return hr;
+}
+
+//*****************************************************************************
+// Helper function to see if there is a duplicate record for ClassLayout.
+//*****************************************************************************
+static HRESULT _FindClassLayout(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdTypeDef tkParent, // [IN] the parent that ClassLayout is associated with
+ RID *pclRid, // [OUT] rid for the ClassLayout.
+ RID rid) // [IN] rid to be ignored.
+{
+ HRESULT hr;
+ ULONG cClassLayoutRecs;
+ ClassLayoutRec *pRecord;
+ mdTypeDef tkParTmp;
+ ULONG i;
+
+ _ASSERTE(pMiniMd && pclRid && rid);
+ _ASSERTE(TypeFromToken(tkParent) == mdtTypeDef && RidFromToken(tkParent));
+
+ cClassLayoutRecs = pMiniMd->getCountClassLayouts();
+
+ for (i = 1; i <= cClassLayoutRecs; i++)
+ {
+ // Ignore the rid to be ignored!
+ if (rid == i)
+ continue;
+
+ IfFailRet(pMiniMd->GetClassLayoutRecord(i, &pRecord));
+ tkParTmp = pMiniMd->getParentOfClassLayout(pRecord);
+ if (tkParTmp == tkParent)
+ {
+ *pclRid = i;
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // _FindClassLayout()
+
+//*****************************************************************************
+// Helper function to see if there is a duplicate for FieldLayout.
+//*****************************************************************************
+static HRESULT _FindFieldLayout(
+ CMiniMdRW *pMiniMd, // [IN] the minimd to lookup
+ mdFieldDef tkParent, // [IN] the parent that FieldLayout is associated with
+ RID *pflRid, // [OUT] rid for the FieldLayout record.
+ RID rid) // [IN] rid to be ignored.
+{
+ HRESULT hr;
+ ULONG cFieldLayoutRecs;
+ FieldLayoutRec *pRecord;
+ mdFieldDef tkField;
+ ULONG i;
+
+ _ASSERTE(pMiniMd && pflRid && rid);
+ _ASSERTE(TypeFromToken(tkParent) == mdtFieldDef && RidFromToken(tkParent));
+
+ cFieldLayoutRecs = pMiniMd->getCountFieldLayouts();
+
+ for (i = 1; i <= cFieldLayoutRecs; i++)
+ {
+ // Ignore the rid to be ignored!
+ if (rid == i)
+ continue;
+
+ IfFailRet(pMiniMd->GetFieldLayoutRecord(i, &pRecord));
+ tkField = pMiniMd->getFieldOfFieldLayout(pRecord);
+ if (tkField == tkParent)
+ {
+ *pflRid = i;
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // _FindFieldLayout()
+
+//*****************************************************************************
+//*****************************************************************************
+HRESULT
+MDSigComparer::CompareMethodSignature()
+{
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ hr = _CompareMethodSignature();
+ }
+ EX_CATCH
+ {
+ hr = E_FAIL;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ return hr;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+HRESULT
+MDSigComparer::_CompareMethodSignature()
+{
+ HRESULT hr;
+
+ // Test equivalency of method signature header
+ ULONG cArgs;
+ IfFailRet(_CompareMethodSignatureHeader(cArgs));
+
+ // Iterate for cArgs + 1 to include the return type
+ for (ULONG i = 0; i < cArgs + 1; i++)
+ {
+ IfFailRet(_CompareExactlyOne());
+ }
+
+ return S_OK;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+HRESULT
+MDSigComparer::_CompareExactlyOne()
+{
+ HRESULT hr;
+
+ CorElementType typ1, typ2;
+ IfFailRet(m_sig1.GetElemType(&typ1));
+ IfFailRet(m_sig2.GetElemType(&typ2));
+
+ if (typ1 != typ2)
+ {
+ return E_FAIL;
+ }
+
+ CorElementType typ = typ1;
+ if (!CorIsPrimitiveType((CorElementType)typ))
+ {
+ switch (typ)
+ {
+ default:
+ {
+ // _ASSERT(!"Illegal or unimplement type in COM+ sig.");
+ return META_E_BAD_SIGNATURE;
+ break;
+ }
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ {
+ IfFailRet(_CompareData(NULL)); // Skip variable number
+ break;
+ }
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_TYPEDBYREF:
+ {
+ break;
+ }
+
+ case ELEMENT_TYPE_BYREF: // fallthru
+ case ELEMENT_TYPE_PTR:
+ case ELEMENT_TYPE_PINNED:
+ case ELEMENT_TYPE_SZARRAY:
+ {
+ IfFailRet(_CompareExactlyOne()); // Compare referenced type
+ break;
+ }
+
+ case ELEMENT_TYPE_VALUETYPE: // fallthru
+ case ELEMENT_TYPE_CLASS:
+ {
+ mdToken tok1, tok2;
+ IfFailRet(m_sig1.GetToken(&tok1));
+ IfFailRet(m_sig2.GetToken(&tok2));
+ IfFailRet(m_comparer.CompareToken(tok1, tok2));
+ break;
+ }
+
+ case ELEMENT_TYPE_FNPTR:
+ {
+ IfFailRet(_CompareMethodSignature());
+ break;
+ }
+
+ case ELEMENT_TYPE_ARRAY:
+ {
+ IfFailRet(_CompareExactlyOne()); // Compare element type
+
+ ULONG rank;
+ IfFailRet(_CompareData(&rank)); // Compare & get rank
+
+ if (rank)
+ {
+ ULONG nsizes;
+ IfFailRet(_CompareData(&nsizes)); // Compare & get # of sizes
+ while (nsizes--)
+ {
+ IfFailRet(_CompareData(NULL)); // Compare size
+ }
+
+ ULONG nlbounds;
+ IfFailRet(_CompareData(&nlbounds)); // Compare & get # of lower bounds
+ while (nlbounds--)
+ {
+ IfFailRet(_CompareData(NULL)); // Compare lower bounds
+ }
+ }
+
+ break;
+ }
+
+ case ELEMENT_TYPE_SENTINEL:
+ {
+ // Should be unreachable since GetElem strips it
+ break;
+ }
+
+ case ELEMENT_TYPE_INTERNAL:
+ {
+ // Shouldn't ever get this since it is internal to the runtime,
+ // but just in case we know how to compare and skip these.
+ PVOID val1 = *((PVOID *)m_sig1.m_ptr);
+ PVOID val2 = *((PVOID *)m_sig2.m_ptr);
+
+ if (val1 != val2)
+ {
+ return E_FAIL;
+ }
+
+ m_sig1.SkipBytes(sizeof(void*));
+ m_sig2.SkipBytes(sizeof(void*));
+ break;
+ }
+
+ case ELEMENT_TYPE_GENERICINST:
+ {
+ IfFailRet(_CompareExactlyOne()); // Compare generic type
+ ULONG argCnt;
+ IfFailRet(_CompareData(&argCnt)); // Compare & get number of parameters
+ _ASSERTE(argCnt > 0);
+ while (argCnt--)
+ {
+ IfFailRet(_CompareExactlyOne()); // Compare the parameters
+ }
+ break;
+ }
+ }
+ }
+
+ return S_OK;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+HRESULT
+MDSigComparer::_CompareData(
+ ULONG *pulData)
+{
+ ULONG cbCompressedData1, cbCompressedData2;
+ ULONG ulData1, ulData2;
+
+ cbCompressedData1 = CorSigUncompressData(m_sig1.m_ptr, &ulData1);
+ cbCompressedData2 = CorSigUncompressData(m_sig2.m_ptr, &ulData2);
+
+ if ((cbCompressedData1 == ((ULONG)(-1))) ||
+ (cbCompressedData2 == ((ULONG)(-1))) ||
+ (cbCompressedData1 != cbCompressedData2) ||
+ (ulData1 != ulData2))
+ {
+ return E_FAIL;
+ }
+
+ m_sig1.SkipBytes(cbCompressedData1);
+ m_sig2.SkipBytes(cbCompressedData2);
+
+ // Out data
+ if (pulData)
+ *pulData = ulData1;
+
+ return S_OK;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+HRESULT
+MDSigComparer::_CompareMethodSignatureHeader(
+ ULONG &cArgs)
+{
+ HRESULT hr;
+
+ // Get calling convention information, but only use it to get type param information.
+ ULONG uCallConv1, uCallConv2;
+ IfFailRet(m_sig1.GetData(&uCallConv1));
+ IfFailRet(m_sig2.GetData(&uCallConv2));
+
+ // Check type parameter information
+ ULONG uTypeParamCount1 = 0;
+ ULONG uTypeParamCount2 = 0;
+
+ if (uCallConv1 & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ IfFailRet(m_sig1.GetData(&uTypeParamCount1));
+
+ if (uCallConv2 & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ IfFailRet(m_sig2.GetData(&uTypeParamCount2));
+
+ if (uTypeParamCount1 != uTypeParamCount2)
+ {
+ return E_FAIL;
+ }
+
+ // Get arg count
+ ULONG cArgs1, cArgs2;
+ IfFailRet(m_sig1.GetData(&cArgs1));
+ IfFailRet(m_sig2.GetData(&cArgs2));
+
+ if (cArgs1 != cArgs2)
+ {
+ return E_FAIL;
+ }
+
+ // Out parameter
+ cArgs = cArgs1;
+
+ return S_OK;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+
+#ifdef FEATURE_FUSION
+HRESULT
+UnifiedAssemblySigComparer::_CreateIAssemblyNameFromAssemblyRef(
+ mdToken tkAsmRef,
+ IAssemblyName **ppAsmName)
+{
+ HRESULT hr;
+
+ void const * pvPublicKey;
+ ULONG cbPublicKey;
+ ULONG cchName;
+ ASSEMBLYMETADATA amd;
+ void const * pvHashValue;
+ ULONG cbHashValue;
+ DWORD dwFlags;
+
+ ZeroMemory(&amd, sizeof(amd));
+
+ IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ &cchName,
+ &amd,
+ NULL,
+ NULL,
+ NULL));
+
+ StackSString ssName;
+ StackSString ssLocale;
+ amd.szLocale = ssLocale.OpenUnicodeBuffer(amd.cbLocale);
+
+ IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef,
+ &pvPublicKey,
+ &cbPublicKey,
+ ssName.OpenUnicodeBuffer(cchName),
+ cchName,
+ &cchName,
+ &amd,
+ &pvHashValue,
+ &cbHashValue,
+ &dwFlags));
+
+ ssName.CloseBuffer();
+ ssLocale.CloseBuffer();
+
+ IAssemblyName *pAsmName = NULL;
+
+ IfFailRet(CreateAssemblyNameObject(&pAsmName,
+ ssName.GetUnicode(),
+ CANOF_SET_DEFAULT_VALUES,
+ NULL));
+
+ // Set the public key token
+ IfFailRet(pAsmName->SetProperty(ASM_NAME_PUBLIC_KEY_TOKEN,
+ (LPVOID)pvPublicKey,
+ cbPublicKey));
+
+ // Set the culture
+ if (amd.cbLocale == 0 || amd.szLocale == NULL)
+ {
+ IfFailRet(pAsmName->SetProperty(ASM_NAME_CULTURE,
+ W("Neutral"),
+ sizeof(W("Neutral"))));
+ }
+ else
+ {
+ IfFailRet(pAsmName->SetProperty(ASM_NAME_CULTURE,
+ amd.szLocale,
+ amd.cbLocale));
+ }
+
+ // Set the major version
+ IfFailRet(pAsmName->SetProperty(ASM_NAME_MAJOR_VERSION,
+ &amd.usMajorVersion,
+ sizeof(amd.usMajorVersion)));
+
+ // Set the minor version
+ IfFailRet(pAsmName->SetProperty(ASM_NAME_MINOR_VERSION,
+ &amd.usMinorVersion,
+ sizeof(amd.usMinorVersion)));
+
+ // Set the build number
+ IfFailRet(pAsmName->SetProperty(ASM_NAME_BUILD_NUMBER,
+ &amd.usBuildNumber,
+ sizeof(amd.usBuildNumber)));
+
+ // Set the revision number
+ IfFailRet(pAsmName->SetProperty(ASM_NAME_REVISION_NUMBER,
+ &amd.usRevisionNumber,
+ sizeof(amd.usRevisionNumber)));
+
+ *ppAsmName = pAsmName;
+
+ return S_OK;
+}
+
+//*****************************************************************************
+// Define holder to release IAssemblyName on exception.
+//*****************************************************************************
+void UnifiedAssemblySigComparer_IAssemblyNameRelease(IAssemblyName *value)
+{
+ if (value != NULL)
+ {
+ value->Release();
+ }
+}
+
+typedef Holder<IAssemblyName*,
+ DoNothing<IAssemblyName*>,
+ &UnifiedAssemblySigComparer_IAssemblyNameRelease,
+ NULL> UnifiedAssemblySigComparer_IAssemblyNameHolder;
+
+#endif // FEATURE_FUSION
+
+#ifndef FEATURE_FUSION
+HRESULT UnifiedAssemblySigComparer::_CompareAssemblies(mdToken tkAsmRef1,mdToken tkAsmRef2, BOOL* pfEquivalent)
+{
+
+ HRESULT hr;
+ void const * pvPublicKey1;
+ ULONG cbPublicKey1;
+ ULONG cchName1;
+ ASSEMBLYMETADATA amd1;
+ void const * pvHashValue;
+ ULONG cbHashValue;
+ DWORD dwFlags1;
+
+ void const * pvPublicKey2;
+ ULONG cbPublicKey2;
+ ULONG cchName2;
+ ASSEMBLYMETADATA amd2;
+ DWORD dwFlags2;
+
+
+ ZeroMemory(&amd1, sizeof(amd1));
+ ZeroMemory(&amd2, sizeof(amd2));
+
+ IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef1,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ &cchName1,
+ &amd1,
+ NULL,
+ NULL,
+ NULL));
+
+ StackSString ssName1;
+ StackSString ssLocale1;
+ amd1.szLocale = ssLocale1.OpenUnicodeBuffer(amd1.cbLocale);
+
+ IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef1,
+ &pvPublicKey1,
+ &cbPublicKey1,
+ ssName1.OpenUnicodeBuffer(cchName1),
+ cchName1,
+ &cchName1,
+ &amd1,
+ &pvHashValue,
+ &cbHashValue,
+ &dwFlags1));
+
+ ssName1.CloseBuffer();
+ ssLocale1.CloseBuffer();
+
+ IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef2,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ &cchName2,
+ &amd2,
+ NULL,
+ NULL,
+ NULL));
+
+ StackSString ssName2;
+ StackSString ssLocale2;
+ amd2.szLocale = ssLocale2.OpenUnicodeBuffer(amd2.cbLocale);
+
+ IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef2,
+ &pvPublicKey2,
+ &cbPublicKey2,
+ ssName2.OpenUnicodeBuffer(cchName2),
+ cchName2,
+ &cchName2,
+ &amd2,
+ &pvHashValue,
+ &cbHashValue,
+ &dwFlags2));
+
+ ssName2.CloseBuffer();
+ ssLocale2.CloseBuffer();
+
+ StackSString sMscorlib(W("mscorlib"));
+
+
+ if(ssName1.CompareCaseInsensitive(sMscorlib)==0 &&
+ ssName2.CompareCaseInsensitive(sMscorlib)==0 )
+ {
+ *pfEquivalent=TRUE;
+ return S_OK;
+ }
+
+ *pfEquivalent=FALSE;
+
+ if (ssName1.CompareCaseInsensitive(ssName2)!=0)
+ return S_OK;
+ if (ssLocale1.CompareCaseInsensitive(ssLocale2)!=0)
+ return S_OK;
+ if(cbPublicKey1!=cbPublicKey2)
+ return S_OK;
+ if(memcmp(pvPublicKey1,pvPublicKey2,cbPublicKey1)!=0)
+ return S_OK;
+ if(dwFlags1!=dwFlags2)
+ return S_OK;
+ if(amd1.usMajorVersion!=amd2.usMajorVersion)
+ return S_OK;
+ if(amd1.usMinorVersion!=amd2.usMinorVersion)
+ return S_OK;
+ if(amd1.usBuildNumber!=amd2.usBuildNumber)
+ return S_OK;
+ if(amd1.usRevisionNumber!=amd2.usRevisionNumber)
+ return S_OK;
+
+ *pfEquivalent=TRUE;
+ return S_OK;
+
+};
+#endif // FEATURE_FUSION
+
+//*****************************************************************************
+//*****************************************************************************
+HRESULT
+UnifiedAssemblySigComparer::_CreateTypeNameFromTypeRef(
+ mdToken tkTypeRef,
+ SString &ssName,
+ mdToken &tkParent)
+{
+ HRESULT hr;
+
+ // Get the parent token as well as the name, and return.
+ ULONG cchTypeRef;
+ IfFailRet(m_pRegMeta->GetTypeRefProps(tkTypeRef, NULL, NULL, 0, &cchTypeRef));
+ IfFailRet(m_pRegMeta->GetTypeRefProps(tkTypeRef, &tkParent, ssName.OpenUnicodeBuffer(cchTypeRef), cchTypeRef, NULL));
+ ssName.CloseBuffer();
+
+ return S_OK;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+HRESULT
+UnifiedAssemblySigComparer::_CreateFullyQualifiedTypeNameFromTypeRef(
+ mdToken tkTypeRef,
+ SString &ssFullName,
+ mdToken &tkParent)
+{
+ HRESULT hr;
+
+ StackSString ssBuf;
+ StackSString ssName;
+ mdToken tok = tkTypeRef;
+ BOOL fFirstLoop = TRUE;
+
+ // Loop stops at first non-typeref parent token.
+ do
+ {
+ // Get the name for this token, as well as the parent token value.
+ IfFailRet(_CreateTypeNameFromTypeRef(tok, ssName, tok));
+
+ // If this is the first time through the loop, just assign values.
+ if (fFirstLoop)
+ {
+ ssFullName = ssName;
+ fFirstLoop = FALSE;
+ }
+ // If this isn't the first time through, make nested type name
+ else
+ {
+ ns::MakeNestedTypeName(ssBuf, ssName, ssFullName);
+ ssFullName = ssBuf;
+ }
+ } while (TypeFromToken(tok) == mdtTypeRef);
+
+ // Assign non-typeref token parent
+ tkParent = tok;
+
+ return S_OK;
+}
+
+
+
+//*****************************************************************************
+//*****************************************************************************
+HRESULT
+UnifiedAssemblySigComparer::CompareToken(
+ const mdToken &tok1,
+ const mdToken &tok2)
+{
+ HRESULT hr;
+
+ // Check binary equality
+ if (tok1 == tok2)
+ {
+ return S_OK;
+ }
+
+ // Currently only want to do extra checking on TypeRefs
+ if (TypeFromToken(tok1) != mdtTypeRef || TypeFromToken(tok2) != mdtTypeRef)
+ {
+ return E_FAIL;
+ }
+
+ // Get the fully qualified type names as well as the non-typeref parents.
+ mdToken tkParent1, tkParent2;
+ StackSString ssName1, ssName2;
+
+ IfFailRet(_CreateFullyQualifiedTypeNameFromTypeRef(tok1, ssName1, tkParent1));
+ IfFailRet(_CreateFullyQualifiedTypeNameFromTypeRef(tok2, ssName2, tkParent2));
+
+ // Currently only want to do extra checking if the parent tokens are AssemblyRefs
+ if (TypeFromToken(tkParent1) != mdtAssemblyRef || TypeFromToken(tkParent2) != mdtAssemblyRef)
+ {
+ return E_FAIL;
+ }
+
+ // If the type names are not equal, no need to check the assembly refs for unification since
+ // we know the types couldn't possibly match.
+ if (!ssName1.Equals(ssName2))
+ {
+ return E_FAIL;
+ }
+ BOOL fEquivalent;
+
+#ifdef FEATURE_FUSION //move into _CompareAssemblies
+ IAssemblyName *pAsmName1 = NULL;
+ IfFailRet(_CreateIAssemblyNameFromAssemblyRef(tkParent1, &pAsmName1));
+ UnifiedAssemblySigComparer_IAssemblyNameHolder anh1(pAsmName1);
+
+ IAssemblyName *pAsmName2 = NULL;
+ IfFailRet(_CreateIAssemblyNameFromAssemblyRef(tkParent2, &pAsmName2));
+ UnifiedAssemblySigComparer_IAssemblyNameHolder anh2(pAsmName2);
+
+ DWORD cchDisplayName = 0;
+
+ StackSString ssDisplayName1;
+ pAsmName1->GetDisplayName(NULL, &cchDisplayName, NULL);
+ IfFailRet(pAsmName1->GetDisplayName(ssDisplayName1.OpenUnicodeBuffer(cchDisplayName), &cchDisplayName, NULL));
+ ssDisplayName1.CloseBuffer();
+
+ StackSString ssDisplayName2;
+ pAsmName2->GetDisplayName(NULL, &cchDisplayName, NULL);
+ IfFailRet(pAsmName2->GetDisplayName(ssDisplayName2.OpenUnicodeBuffer(cchDisplayName), &cchDisplayName, NULL));
+ ssDisplayName2.CloseBuffer();
+
+ AssemblyComparisonResult res;
+ IfFailRet(CompareAssemblyIdentity(ssDisplayName1.GetUnicode(),
+ TRUE,
+ ssDisplayName2.GetUnicode(),
+ TRUE,
+ &fEquivalent,
+ &res));
+#else
+ // no redirects supported
+ IfFailRet(_CompareAssemblies(tkParent1,tkParent2,&fEquivalent));
+#endif
+
+ if (!fEquivalent)
+ {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+
+//*****************************************************************************
+// Helper function to validate a locale.
+//*****************************************************************************
+static const char* const g_szValidLocale_V1[] = {
+"ar","ar-SA","ar-IQ","ar-EG","ar-LY","ar-DZ","ar-MA","ar-TN","ar-OM","ar-YE","ar-SY","ar-JO","ar-LB","ar-KW","ar-AE","ar-BH","ar-QA",
+"bg","bg-BG",
+"ca","ca-ES",
+"zh-CHS","zh-TW","zh-CN","zh-HK","zh-SG","zh-MO","zh-CHT",
+"cs","cs-CZ",
+"da","da-DK",
+"de","de-DE","de-CH","de-AT","de-LU","de-LI",
+"el","el-GR",
+"en","en-US","en-GB","en-AU","en-CA","en-NZ","en-IE","en-ZA","en-JM","en-CB","en-BZ","en-TT","en-ZW","en-PH",
+"es","es-ES-Ts","es-MX","es-ES","es-GT","es-CR","es-PA","es-DO","es-VE","es-CO","es-PE","es-AR","es-EC","es-CL",
+"es-UY","es-PY","es-BO","es-SV","es-HN","es-NI","es-PR",
+"fi","fi-FI",
+"fr","fr-FR","fr-BE","fr-CA","fr-CH","fr-LU","fr-MC",
+"he","he-IL",
+"hu","hu-HU",
+"is","is-IS",
+"it","it-IT","it-CH",
+"ja","ja-JP",
+"ko","ko-KR",
+"nl","nl-NL","nl-BE",
+"no",
+"nb-NO",
+"nn-NO",
+"pl","pl-PL",
+"pt","pt-BR","pt-PT",
+"ro","ro-RO",
+"ru","ru-RU",
+"hr","hr-HR",
+"sk","sk-SK",
+"sq","sq-AL",
+"sv","sv-SE","sv-FI",
+"th","th-TH",
+"tr","tr-TR",
+"ur","ur-PK",
+"id","id-ID",
+"uk","uk-UA",
+"be","be-BY",
+"sl","sl-SI",
+"et","et-EE",
+"lv","lv-LV",
+"lt","lt-LT",
+"fa","fa-IR",
+"vi","vi-VN",
+"hy","hy-AM",
+"az",
+"eu","eu-ES",
+"mk","mk-MK",
+"af","af-ZA",
+"ka","ka-GE",
+"fo","fo-FO",
+"hi","hi-IN",
+"ms","ms-MY","ms-BN",
+"kk","kk-KZ",
+"ky","ky-KZ",
+"sw","sw-KE",
+"uz",
+"tt","tt-RU",
+"pa","pa-IN",
+"gu","gu-IN",
+"ta","ta-IN",
+"te","te-IN",
+"kn","kn-IN",
+"mr","mr-IN",
+"sa","sa-IN",
+"mn","mn-MN",
+"gl","gl-ES",
+"kok","kok-IN",
+"syr","syr-SY",
+"div"
+};
+
+static const char* const g_szValidLocale_V2[] = {
+ "bn", "bn-IN",
+ "bs-Latn-BA", "bs-Cyrl-BA",
+ "hr-BA",
+ "fil", "fil-PH",
+ "fy", "fy-NL",
+ "iu-Latn-CA",
+ "ga", "ga-IE",
+ "ky-KG",
+ "lb", "lb-LU",
+ "ml", "ml-IN",
+ "mt", "mt-MT",
+ "mi", "mi-NZ",
+ "arn", "arn-CL",
+ "moh", "moh-CA",
+ "ne", "ne-NP",
+ "ps", "ps-AF",
+ "quz", "quz-BO", "quz-EC", "quz-PE",
+ "rm", "rm-CH",
+ "smn", "smn-FI",
+ "smj" , "smj-SE", "smj-NO",
+ "se", "se-NO", "se-SE", "se-FI",
+ "sms", "sms-FI",
+ "sma", "sma-NO", "sma-SE",
+ "sr-Latn-BA", "sr-Cyrl-BA",
+ "nso", "nso-ZA"
+};
+
+// Pre-vista specific cultures (renamed on Vista)
+static const char* const g_szValidLocale_PreVista[] = {
+ "div-MV",
+ "sr-SP-Latn", "sr-SP-Cyrl",
+ "az-AZ-Latn", "az-AZ-Cyrl",
+ "uz-UZ-Latn", "uz-UZ-Cyrl",
+};
+
+// Vista only specific cultures (renamed and freshly introduced)
+static const char * const g_szValidLocale_Vista[] = {
+ "dv-MV",
+ "sr-Latn-CS", "sr-Cyrl-CS",
+ "az-Latn-AZ", "az-Cyrl-AZ",
+ "uz-Latn-UZ", "uz-Cyrl-UZ",
+ "zh-Hant", "zh-Hans",
+ "gsw", "gsw-FR",
+ "am", "am-ET",
+ "as", "as-IN",
+ "ba", "ba-RU",
+ "br", "br-FR",
+ "en-IN",
+ "kl", "kl-GL",
+ "iu-Cans-CA",
+ "km", "km-KH",
+ "lo", "lo-LA",
+ "dsb", "dsb-DE",
+ "mn-Mong-CN",
+ "oc", "oc-FR",
+ "or", "or-IN"
+};
+
+static BOOL FindInArray(LPCUTF8 szLocale, const char * const *cultureArr, const int nCultures)
+{
+ for (int i = 0; i < nCultures; i++)
+ {
+ if(!SString::_stricmp(szLocale, cultureArr[i]))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#define LENGTH_OF(x) (sizeof(x) / sizeof(x[0]))
+
+// For Everett assemblies, only the preVista cultures are valid even if running on Vista.
+static BOOL _IsValidLocale(LPCUTF8 szLocale,
+ BOOL fIsV2Assembly)
+{
+ if (szLocale && *szLocale)
+ {
+ // Locales valid for Everett and Whidbey
+ if (FindInArray(szLocale, g_szValidLocale_V1, LENGTH_OF(g_szValidLocale_V1)))
+ return TRUE;
+
+ // Locales valid for Whidbey assemblies only
+ if (fIsV2Assembly &&
+ FindInArray(szLocale, g_szValidLocale_V2, LENGTH_OF(g_szValidLocale_V2)))
+ return TRUE;
+
+ // Finally search OS specific cultures
+ if (fIsV2Assembly)
+ return FindInArray(szLocale, g_szValidLocale_Vista, LENGTH_OF(g_szValidLocale_Vista));
+ else
+ return FindInArray(szLocale, g_szValidLocale_PreVista, LENGTH_OF(g_szValidLocale_PreVista));
+ }
+
+ return TRUE;
+}
+
+#endif //FEATURE_METADATA_VALIDATOR
diff --git a/src/md/compiler/newmerger.cpp b/src/md/compiler/newmerger.cpp
new file mode 100644
index 0000000000..d5199bb570
--- /dev/null
+++ b/src/md/compiler/newmerger.cpp
@@ -0,0 +1,6303 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// NewMerger.cpp
+//
+
+//
+// contains utility code to MD directory
+//
+// This file provides Compiler Support functionality in metadata.
+//*****************************************************************************
+#include "stdafx.h"
+
+#include "newmerger.h"
+#include "regmeta.h"
+
+
+#include "importhelper.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include <posterror.h>
+#include <sstring.h>
+#include "ndpversion.h"
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+
+#define MODULEDEFTOKEN TokenFromRid(1, mdtModule)
+
+#define COR_MSCORLIB_NAME "mscorlib"
+#define COR_MSCORLIB_TYPEREF {0xb7, 0x7a, 0x5c, 0x56,0x19,0x34,0xe0,0x89}
+
+#define COR_CONSTRUCTOR_METADATA_IDENTIFIER W(".ctor")
+
+#define COR_COMPILERSERVICE_NAMESPACE "System.Runtime.CompilerServices"
+#define COR_EXCEPTIONSERVICE_NAMESPACE "System.Runtime.ExceptionServices"
+#define COR_SUPPRESS_MERGE_CHECK_ATTRIBUTE "SuppressMergeCheckAttribute"
+#define COR_HANDLE_PROCESS_CORRUPTED_STATE_EXCEPTION_ATTRIBUTE "HandleProcessCorruptedStateExceptionsAttribute"
+#define COR_MISCBITS_NAMESPACE "Microsoft.VisualC"
+#define COR_MISCBITS_ATTRIBUTE "Microsoft.VisualC.MiscellaneousBitsAttribute"
+#define COR_NATIVECPPCLASS_ATTRIBUTE "System.Runtime.CompilerServices.NativeCppClassAttribute"
+
+// MODULE_CA_LOCATION W("System.Runtime.CompilerServices.AssemblyAttributesGoHere")
+#define MODULE_CA_TYPENAME "AssemblyAttributesGoHere" // fake assembly type-ref for hanging Assembly-level CAs off of
+
+//*****************************************************************************
+// BEGIN: Security Critical Attributes and Enumeration
+//*****************************************************************************
+#define COR_SECURITYCRITICALSCOPE_ENUM_W W("System.Security.SecurityCriticalScope")
+
+#define COR_SECURITYCRITICAL_ATTRIBUTE_FULL "System.Security.SecurityCriticalAttribute"
+#define COR_SECURITYTRANSPARENT_ATTRIBUTE_FULL "System.Security.SecurityTransparentAttribute"
+#define COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL "System.Security.SecurityTreatAsSafeAttribute"
+
+#define COR_SECURITYCRITICAL_ATTRIBUTE_FULL_W W("System.Security.SecurityCriticalAttribute")
+#define COR_SECURITYTRANSPARENT_ATTRIBUTE_FULL_W W("System.Security.SecurityTransparentAttribute")
+#define COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL_W W("System.Security.SecurityTreatAsSafeAttribute")
+#define COR_SECURITYSAFECRITICAL_ATTRIBUTE_FULL_W W("System.Security.SecuritySafeCriticalAttribute")
+
+ // definitions of enumeration for System.Security.SecurityCriticalScope (Explicit or Everything)
+#define COR_SECURITYCRITICAL_CTOR_ARGCOUNT_NO_SCOPE 0
+#define COR_SECURITYCRITICAL_CTOR_ARGCOUNT_SCOPE_EVERYTHING 1
+#define COR_SECURITYCRITICAL_CTOR_NO_SCOPE_SIG_MAX_SIZE (3)
+#define COR_SECURITYCRITICAL_CTOR_SCOPE_SIG_MAX_SIZE (5 + sizeof(mdTypeRef) * 1)
+
+#define COR_SECURITYCRITICAL_ATTRIBUTE_NAMESPACE "System.Security"
+#define COR_SECURITYCRITICAL_ATTRIBUTE "SecurityCriticalAttribute"
+#define COR_SECURITYTRANSPARENT_ATTRIBUTE_NAMESPACE "System.Security"
+#define COR_SECURITYTRANSPARENT_ATTRIBUTE "SecurityTransparentAttribute"
+#define COR_SECURITYTREATASSAFE_ATTRIBUTE_NAMESPACE "System.Security"
+#define COR_SECURITYTREATASSAFE_ATTRIBUTE "SecurityTreatAsSafeAttribute"
+#define COR_SECURITYSAFECRITICAL_ATTRIBUTE "SecuritySafeCriticalAttribute"
+
+
+#define COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EVERYTHING { 0x01, 0x00 ,0x01, 0x00, 0x00, 0x00 ,0x00, 0x00 }
+#define COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EXPLICIT {0x01, 0x00, 0x00 ,0x00}
+#define COR_SECURITYTREATASSAFE_ATTRIBUTE_VALUE {0x01, 0x00, 0x00 ,0x00}
+
+
+ // if true, then registry has been read for enabling or disabling SecurityCritical support
+static BOOL g_fRefShouldMergeCriticalChecked = FALSE;
+
+// by default, security critical attributes will be merged (e.g. unmarked CRT marked Critical/TAS)
+// - unless registry config explicitly disables merging
+static BOOL g_fRefShouldMergeCritical = TRUE;
+//*****************************************************************************
+// END: Security Critical Attributes and Enumeration
+//*****************************************************************************
+
+//*****************************************************************************
+// Checks to see if the given type is managed or native. We'll key off of the
+// Custom Attribute "Microsoft.VisualC.MiscellaneousBitsAttribute". If the third
+// byte has the 01000000 bit set then it is an unmanaged type.
+// If we can't find the attribute, we will also check for the presence of the
+// "System.Runtime.CompilerServices.NativeCppClassAttribute" Custom Attribute
+// since the CPP compiler stopped emitting MiscellaneousBitsAttribute in Dev11.
+//*****************************************************************************
+HRESULT IsManagedType(CMiniMdRW* pMiniMd,
+ mdTypeDef td,
+ BOOL *fIsManagedType)
+{
+ // First look for the custom attribute
+ HENUMInternal hEnum;
+ HRESULT hr = S_OK;
+
+ IfFailRet(pMiniMd->CommonEnumCustomAttributeByName(td, COR_MISCBITS_ATTRIBUTE, false, &hEnum));
+
+ // If there aren't any custom attributes here, then this must be a managed type
+ if (hEnum.m_ulCount > 0)
+ {
+ // Let's loop through these, and see if any of them have that magical bit set.
+ mdCustomAttribute ca;
+ CustomAttributeRec *pRec;
+ ULONG cbData = 0;
+
+ while(HENUMInternal::EnumNext(&hEnum, &ca))
+ {
+ const BYTE* pData = NULL;
+
+ IfFailGo(pMiniMd->GetCustomAttributeRecord(RidFromToken(ca), &pRec));
+ IfFailGo(pMiniMd->getValueOfCustomAttribute(pRec, &pData, &cbData));
+
+ if (pData != NULL && cbData >=3)
+ {
+ // See if the magical bit is set to make this an unmanaged type
+ if ((*(pData+2)&0x40) > 0)
+ {
+ // Yes, this is an unmanaged type
+ HENUMInternal::ClearEnum(&hEnum);
+ *fIsManagedType = FALSE;
+ return S_OK;
+ }
+ }
+ }
+
+ }
+
+ HENUMInternal::ClearEnum(&hEnum);
+
+ // If this was emitted by a Dev11+ CPP compiler, we only have NativeCppClassAttribute
+ // so let's check for that before calling this a managed class.
+ IfFailRet(pMiniMd->CommonEnumCustomAttributeByName(td, COR_NATIVECPPCLASS_ATTRIBUTE, false, &hEnum));
+ if (hEnum.m_ulCount > 0)
+ {
+ // Yes, this is an unmanaged type
+ HENUMInternal::ClearEnum(&hEnum);
+ *fIsManagedType = FALSE;
+ return S_OK;
+ }
+
+ // Nope, this isn't an unmanaged type.... must be managed
+ HENUMInternal::ClearEnum(&hEnum);
+ *fIsManagedType = TRUE;
+ hr = S_OK;
+ErrExit:
+ return hr;
+}// IsManagedType
+
+
+//*****************************************************************************
+// "Is CustomAttribute from certain namespace and assembly" check helper
+// Returns S_OK and fills **ppTypeRefRec.
+// Returns error code or S_FALSE otherwise as not found and fills **ppTypeRefRec with NULL.
+//*****************************************************************************
+HRESULT IsAttributeFromNamespace(
+ CMiniMdRW *pMiniMd,
+ mdToken tk,
+ LPCSTR szNamespace,
+ LPCSTR szAssembly,
+ TypeRefRec **ppTypeRefRec)
+{
+ HRESULT hr = S_OK;
+ if(TypeFromToken(tk) == mdtMemberRef)
+ {
+ MemberRefRec *pMemRefRec;
+ IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tk), &pMemRefRec));
+ tk = pMiniMd->getClassOfMemberRef(pMemRefRec);
+ }
+ if(TypeFromToken(tk) == mdtTypeRef)
+ {
+ TypeRefRec *pTypeRefRec;
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tk), &pTypeRefRec));
+ LPCSTR szTypeRefNamespace;
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szTypeRefNamespace));
+ if (strcmp(szTypeRefNamespace, szNamespace) == 0)
+ {
+ mdToken tkResTmp = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec);
+ if (TypeFromToken(tkResTmp) == mdtAssemblyRef)
+ {
+ AssemblyRefRec *pAsmRefRec;
+ IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResTmp), &pAsmRefRec));
+ LPCSTR szAssemblyRefName;
+ IfFailGo(pMiniMd->getNameOfAssemblyRef(pAsmRefRec, &szAssemblyRefName));
+ if(SString::_stricmp(szAssemblyRefName, szAssembly) == 0)
+ {
+ *ppTypeRefRec = pTypeRefRec;
+ return S_OK;
+ }
+ }
+ }
+ }
+ // Record not found
+ hr = S_FALSE;
+ErrExit:
+ *ppTypeRefRec = NULL;
+ return hr;
+}
+
+//*****************************************************************************
+// constructor
+//*****************************************************************************
+NEWMERGER::NEWMERGER()
+ : m_pRegMetaEmit(0),
+ m_pImportDataList(NULL),
+ m_optimizeRefToDef(MDRefToDefDefault),
+ m_isscsSecurityCritical(ISSCS_Unknown),
+ m_isscsSecurityCriticalAllScopes(~ISSCS_Unknown)
+{
+ m_pImportDataTail = &(m_pImportDataList);
+#if _DEBUG
+ m_iImport = 0;
+#endif // _DEBUG
+} // NEWMERGER::NEWMERGER()
+
+//*****************************************************************************
+// initializer
+//*****************************************************************************
+HRESULT NEWMERGER::Init(RegMeta *pRegMeta)
+{
+ HRESULT hr = NOERROR;
+ MergeTypeData * pMTD;
+
+ m_pRegMetaEmit = pRegMeta;
+
+ // burn an entry so that the RID matches the array index
+ IfNullGo(pMTD = m_rMTDs.Append());
+
+ pMTD->m_bSuppressMergeCheck = false;
+ pMTD->m_cMethods = 0;
+ pMTD->m_cFields = 0;
+ pMTD->m_cEvents = 0;
+ pMTD->m_cProperties = 0;
+
+ErrExit:
+ return hr;
+} // NEWMERGER::Init
+
+//*****************************************************************************
+// destructor
+//*****************************************************************************
+NEWMERGER::~NEWMERGER()
+{
+ if (m_pImportDataList)
+ {
+ // delete this list and release all AddRef'ed interfaces!
+ MergeImportData *pNext;
+ for (pNext = m_pImportDataList; pNext != NULL; )
+ {
+ pNext = m_pImportDataList->m_pNextImportData;
+ if (m_pImportDataList->m_pHandler)
+ m_pImportDataList->m_pHandler->Release();
+ if (m_pImportDataList->m_pHostMapToken)
+ m_pImportDataList->m_pHostMapToken->Release();
+ if (m_pImportDataList->m_pMDTokenMap)
+ delete m_pImportDataList->m_pMDTokenMap;
+ m_pImportDataList->m_pRegMetaImport->Release();
+ delete m_pImportDataList;
+ m_pImportDataList = pNext;
+ }
+ }
+} // NEWMERGER::~NEWMERGER
+
+//*****************************************************************************
+CMiniMdRW *NEWMERGER::GetMiniMdEmit()
+{
+ return &(m_pRegMetaEmit->m_pStgdb->m_MiniMd);
+} // CMiniMdRW *NEWMERGER::GetMiniMdEmit()
+
+//*****************************************************************************
+// Adding a new import
+//*****************************************************************************
+HRESULT NEWMERGER::AddImport(
+ IMetaDataImport2 *pImport, // [IN] The scope to be merged.
+ IMapToken *pHostMapToken, // [IN] Host IMapToken interface to receive token remap notification
+ IUnknown *pHandler) // [IN] An object to receive error notification.
+{
+ HRESULT hr = NOERROR;
+ MergeImportData *pData;
+
+ RegMeta *pRM = static_cast<RegMeta*>(pImport);
+
+ // Add a MergeImportData to track the information for this import scope
+ pData = new (nothrow) MergeImportData;
+ IfNullGo( pData );
+ pData->m_pRegMetaImport = pRM;
+ pData->m_pRegMetaImport->AddRef();
+ pData->m_pHostMapToken = pHostMapToken;
+ if (pData->m_pHostMapToken)
+ pData->m_pHostMapToken->AddRef();
+ if (pHandler)
+ {
+ pData->m_pHandler = pHandler;
+ pData->m_pHandler->AddRef();
+ }
+ else
+ {
+ pData->m_pHandler = NULL;
+ }
+
+ pData->m_pMDTokenMap = NULL;
+ pData->m_pNextImportData = NULL;
+#if _DEBUG
+ pData->m_iImport = ++m_iImport;
+#endif // _DEBUG
+
+ pData->m_tkHandleProcessCorruptedStateCtor = mdTokenNil;
+ // add the newly create node to the tail of the list
+ *m_pImportDataTail = pData;
+ m_pImportDataTail = &(pData->m_pNextImportData);
+
+ErrExit:
+
+ return hr;
+} // HRESULT NEWMERGER::AddImport()
+
+HRESULT NEWMERGER::InitMergeTypeData()
+{
+ CMiniMdRW *pMiniMdEmit;
+ ULONG cTypeDefRecs;
+ ULONG i, j;
+ bool bSuppressMergeCheck;
+
+ ULONG ridStart, ridEnd;
+ RID ridMap;
+
+ mdToken tkSuppressMergeCheckCtor = mdTokenNil;
+ mdToken tkCA;
+ mdMethodDef mdEmit;
+ mdFieldDef fdEmit;
+ mdEvent evEmit;
+ mdProperty prEmit;
+
+ TypeDefRec *pTypeDefRec;
+ EventMapRec *pEventMapRec;
+ PropertyMapRec *pPropertyMapRec;
+
+ MergeTypeData *pMTD;
+
+ HRESULT hr = NOERROR;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ // cache the SuppressMergeCheckAttribute.ctor token
+ ImportHelper::FindCustomAttributeCtorByName(
+ pMiniMdEmit, COR_MSCORLIB_NAME,
+ COR_COMPILERSERVICE_NAMESPACE, COR_SUPPRESS_MERGE_CHECK_ATTRIBUTE,
+ &tkSuppressMergeCheckCtor);
+
+ cTypeDefRecs = pMiniMdEmit->getCountTypeDefs();
+ _ASSERTE(m_rMTDs.Count() > 0);
+
+ for (i = m_rMTDs.Count(); i <= cTypeDefRecs; i++)
+ {
+ IfNullGo(pMTD = m_rMTDs.Append());
+
+ pMTD->m_cMethods = 0;
+ pMTD->m_cFields = 0;
+ pMTD->m_cEvents = 0;
+ pMTD->m_cProperties = 0;
+ pMTD->m_bSuppressMergeCheck = (tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit,
+ TokenFromRid(i, mdtTypeDef), tkSuppressMergeCheckCtor,
+ NULL, 0, &tkCA));
+
+ IfFailGo(pMiniMdEmit->GetTypeDefRecord(i, &pTypeDefRec));
+
+ // Count the number methods
+ ridStart = pMiniMdEmit->getMethodListOfTypeDef(pTypeDefRec);
+ IfFailGo(pMiniMdEmit->getEndMethodListOfTypeDef(i, &ridEnd));
+
+ for (j = ridStart; j < ridEnd; j++)
+ {
+ IfFailGo(pMiniMdEmit->GetMethodRid(j, (ULONG *)&mdEmit));
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit,
+ mdEmit, tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (!bSuppressMergeCheck)
+ {
+ pMTD->m_cMethods++;
+ }
+ }
+
+ // Count the number fields
+ ridStart = pMiniMdEmit->getFieldListOfTypeDef(pTypeDefRec);
+ IfFailGo(pMiniMdEmit->getEndFieldListOfTypeDef(i, &ridEnd));
+
+ for (j = ridStart; j < ridEnd; j++)
+ {
+ IfFailGo(pMiniMdEmit->GetFieldRid(j, (ULONG *)&fdEmit));
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit,
+ fdEmit, tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (!bSuppressMergeCheck)
+ {
+ pMTD->m_cFields++;
+ }
+ }
+
+ // Count the number of events
+ IfFailGo(pMiniMdEmit->FindEventMapFor(i, &ridMap));
+ if (!InvalidRid(ridMap))
+ {
+ IfFailGo(pMiniMdEmit->GetEventMapRecord(ridMap, &pEventMapRec));
+ ridStart = pMiniMdEmit->getEventListOfEventMap(pEventMapRec);
+ IfFailGo(pMiniMdEmit->getEndEventListOfEventMap(ridMap, &ridEnd));
+
+ for (j = ridStart; j < ridEnd; j++)
+ {
+ IfFailGo(pMiniMdEmit->GetEventRid(j, (ULONG *)&evEmit));
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit,
+ evEmit, tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (!bSuppressMergeCheck)
+ {
+ pMTD->m_cEvents++;
+ }
+ }
+ }
+
+ // Count the number of properties
+ IfFailGo(pMiniMdEmit->FindPropertyMapFor(i, &ridMap));
+ if (!InvalidRid(ridMap))
+ {
+ IfFailGo(pMiniMdEmit->GetPropertyMapRecord(ridMap, &pPropertyMapRec));
+ ridStart = pMiniMdEmit->getPropertyListOfPropertyMap(pPropertyMapRec);
+ IfFailGo(pMiniMdEmit->getEndPropertyListOfPropertyMap(ridMap, &ridEnd));
+
+ for (j = ridStart; j < ridEnd; j++)
+ {
+ IfFailGo(pMiniMdEmit->GetPropertyRid(j, (ULONG *)&prEmit));
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit,
+ prEmit, tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (!bSuppressMergeCheck)
+ {
+ pMTD->m_cProperties++;
+ }
+ }
+ }
+ }
+
+ErrExit:
+ return hr;
+}
+
+//*****************************************************************************
+// Merge now
+//*****************************************************************************
+HRESULT NEWMERGER::Merge(MergeFlags dwMergeFlags, CorRefToDefCheck optimizeRefToDef)
+{
+ MergeImportData *pImportData = m_pImportDataList;
+ MDTOKENMAP **pPrevMap = NULL;
+ MDTOKENMAP *pMDTokenMap;
+ HRESULT hr = NOERROR;
+ MDTOKENMAP *pCurTKMap;
+ int i;
+
+#if _DEBUG
+ {
+ LOG((LOGMD, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"));
+ LOG((LOGMD, "Merge scope list\n"));
+ i = 0;
+ for (MergeImportData *pID = m_pImportDataList; pID != NULL; pID = pID->m_pNextImportData)
+ {
+ WCHAR szScope[1024], szGuid[40];
+ GUID mvid;
+ ULONG cchScope;
+ pID->m_pRegMetaImport->GetScopeProps(szScope, 1024, &cchScope, &mvid);
+ szScope[1023] = 0;
+ GuidToLPWSTR(mvid, szGuid, 40);
+ ++i; // Counter is 1-based.
+ LOG((LOGMD, "%3d: %ls : %ls\n", i, szGuid, szScope));
+ }
+ LOG((LOGMD, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"));
+ }
+#endif // _DEBUG
+
+ m_dwMergeFlags = dwMergeFlags;
+ m_optimizeRefToDef = optimizeRefToDef;
+
+ // check to see if we need to do dup check
+ m_fDupCheck = ((m_dwMergeFlags & NoDupCheck) != NoDupCheck);
+
+ while (pImportData)
+ {
+ // Verify that we have a filter for each import scope.
+ IfNullGo( pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd.GetFilterTable() );
+
+ // cache the SuppressMergeCheckAttribute.ctor token for each import scope
+ ImportHelper::FindCustomAttributeCtorByName(
+ &pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd, COR_MSCORLIB_NAME,
+ COR_COMPILERSERVICE_NAMESPACE, COR_SUPPRESS_MERGE_CHECK_ATTRIBUTE,
+ &pImportData->m_tkSuppressMergeCheckCtor);
+
+ // cache the HandleProcessCorruptedStateExceptionsAttribute.ctor token for each import scope
+ ImportHelper::FindCustomAttributeCtorByName(
+ &pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd, COR_MSCORLIB_NAME,
+ COR_EXCEPTIONSERVICE_NAMESPACE, COR_HANDLE_PROCESS_CORRUPTED_STATE_EXCEPTION_ATTRIBUTE,
+ &pImportData->m_tkHandleProcessCorruptedStateCtor);
+
+ // check for security critical attribute in the assembly (i.e. explicit annotations)
+ InputScopeSecurityCriticalStatus isscsTemp = CheckInputScopeIsCritical(pImportData, hr);
+ IfFailGo(hr);
+ // clear the unset flag bits (e.g. if critical, clear transparent bit)
+ // whatever bits remain are bits that have been set in all scopes
+ if (ISSCS_Unknown == (isscsTemp & ISSCS_SECURITYCRITICAL_FLAGS))
+ m_isscsSecurityCriticalAllScopes &= ISSCS_SECURITYCRITICAL_LEGACY;
+ else
+ m_isscsSecurityCriticalAllScopes &= isscsTemp;
+ // set the flag bits (essentially, this allows us to see if _any_ scopes requested a bit)
+ m_isscsSecurityCritical |= isscsTemp;
+
+ // create the tokenmap class to track metadata token remap for each import scope
+ pMDTokenMap = new (nothrow) MDTOKENMAP;
+ IfNullGo(pMDTokenMap);
+ IfFailGo(pMDTokenMap->Init((IMetaDataImport2*)pImportData->m_pRegMetaImport));
+ pImportData->m_pMDTokenMap = pMDTokenMap;
+ pImportData->m_pMDTokenMap->m_pMap = pImportData->m_pHostMapToken;
+ if (pImportData->m_pHostMapToken)
+ pImportData->m_pHostMapToken->AddRef();
+ pImportData->m_pMDTokenMap->m_pNextMap = NULL;
+ if (pPrevMap)
+ *pPrevMap = pImportData->m_pMDTokenMap;
+ pPrevMap = &(pImportData->m_pMDTokenMap->m_pNextMap);
+ pImportData = pImportData->m_pNextImportData;
+ }
+
+ // Populate the m_rMTDs with the type info already defined in the emit scope
+ IfFailGo( InitMergeTypeData() );
+
+ // 1. Merge Module
+ IfFailGo( MergeModule( ) );
+
+ // 2. Merge TypeDef partially (i.e. only name)
+ IfFailGo( MergeTypeDefNamesOnly() );
+
+ // 3. Merge ModuleRef property and do ModuleRef to ModuleDef optimization
+ IfFailGo( MergeModuleRefs() );
+
+ // 4. Merge AssemblyRef.
+ IfFailGo( MergeAssemblyRefs() );
+
+ // 5. Merge TypeRef with TypeRef to TypeDef optimization
+ IfFailGo( MergeTypeRefs() );
+
+ // 6. Merge TypeSpec & MethodSpec
+ IfFailGo( MergeTypeSpecs() );
+
+ // 7. Now Merge the remaining of TypeDef records
+ IfFailGo( CompleteMergeTypeDefs() );
+
+ // 8. Merge Methods and Fields. Such that Signature translation is respecting the TypeRef to TypeDef optimization.
+ IfFailGo( MergeTypeDefChildren() );
+
+ // 9. Merge MemberRef with MemberRef to MethodDef/FieldDef optimization
+ IfFailGo( MergeMemberRefs( ) );
+
+ // 10. Merge InterfaceImpl
+ IfFailGo( MergeInterfaceImpls( ) );
+
+ // merge all of the remaining in metadata ....
+
+ // 11. constant has dependency on property, field, param
+ IfFailGo( MergeConstants() );
+
+ // 12. field marshal has dependency on param and field
+ IfFailGo( MergeFieldMarshals() );
+
+ // 13. in ClassLayout, move over the FieldLayout and deal with FieldLayout as well
+ IfFailGo( MergeClassLayouts() );
+
+ // 14. FieldLayout has dependency on FieldDef.
+ IfFailGo( MergeFieldLayouts() );
+
+ // 15. FieldRVA has dependency on FieldDef.
+ IfFailGo( MergeFieldRVAs() );
+
+ // 16. MethodImpl has dependency on MemberRef, MethodDef, TypeRef and TypeDef.
+ IfFailGo( MergeMethodImpls() );
+
+ // 17. pinvoke depends on MethodDef and ModuleRef
+ IfFailGo( MergePinvoke() );
+
+ IfFailGo( MergeStandAloneSigs() );
+
+ IfFailGo( MergeMethodSpecs() );
+
+ IfFailGo( MergeStrings() );
+
+ if (m_dwMergeFlags & MergeManifest)
+ {
+ // keep the manifest!!
+ IfFailGo( MergeAssembly() );
+ IfFailGo( MergeFiles() );
+ IfFailGo( MergeExportedTypes() );
+ IfFailGo( MergeManifestResources() );
+ }
+ else if (m_dwMergeFlags & ::MergeExportedTypes)
+ {
+ IfFailGo( MergeFiles() );
+ IfFailGo( MergeExportedTypes() );
+ }
+
+ IfFailGo( MergeCustomAttributes() );
+ IfFailGo( MergeDeclSecuritys() );
+
+
+ // Please don't add any MergeXxx() below here. CustomAttributess must be
+ // very late, because custom values are various other types.
+
+ // Fixup list cannot be merged. Linker will need to re-emit them.
+
+ // Now call back to host for the result of token remap
+ //
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // Send token remap information for each import scope
+ pCurTKMap = pImportData->m_pMDTokenMap;
+ TOKENREC *pRec;
+ if (pImportData->m_pHostMapToken)
+ {
+ for (i = 0; i < pCurTKMap->Count(); i++)
+ {
+ pRec = pCurTKMap->Get(i);
+ if (!pRec->IsEmpty())
+ pImportData->m_pHostMapToken->Map(pRec->m_tkFrom, pRec->m_tkTo);
+ }
+ }
+ }
+
+ // And last, but not least, let's do Security critical module-level attribute consolidation
+ // and metadata fixups.
+ IfFailGo( MergeSecurityCriticalAttributes() );
+
+
+
+
+
+#if _DEBUG
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // dump the mapping
+ LOG((LOGMD, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"));
+ LOG((LOGMD, "Dumping token remap for one import scope!\n"));
+ LOG((LOGMD, "This is the %d import scope for merge!\n", pImportData->m_iImport));
+
+ pCurTKMap = pImportData->m_pMDTokenMap;
+ TOKENREC *pRec;
+ for (i = 0; i < pCurTKMap->Count(); i++)
+ {
+ pRec = pCurTKMap->Get(i);
+ if (!pRec->IsEmpty())
+ {
+ LOG((LOGMD, " Token 0x%08x ====>>>> Token 0x%08x\n", pRec->m_tkFrom, pRec->m_tkTo));
+ }
+ }
+ LOG((LOGMD, "End dumping token remap!\n"));
+ LOG((LOGMD, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"));
+ }
+#endif // _DEBUG
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::Merge()
+
+
+//*****************************************************************************
+// Merge ModuleDef
+//*****************************************************************************
+HRESULT NEWMERGER::MergeModule()
+{
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+ HRESULT hr = NOERROR;
+ TOKENREC *pTokenRec;
+
+ // we don't really merge Module information but we create a one to one mapping for each module token into the TokenMap
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ // set the current MDTokenMap
+
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ IfFailGo( pCurTkMap->InsertNotFound(MODULEDEFTOKEN, true, MODULEDEFTOKEN, &pTokenRec) );
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeModule()
+
+
+//*****************************************************************************
+// Merge TypeDef but only Names. This is a partial merge to support TypeRef to TypeDef optimization
+//*****************************************************************************
+HRESULT NEWMERGER::MergeTypeDefNamesOnly()
+{
+ HRESULT hr = NOERROR;
+ TypeDefRec *pRecImport = NULL;
+ TypeDefRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdTypeDef tdEmit;
+ mdTypeDef tdImport;
+ bool bDuplicate;
+ DWORD dwFlags;
+ DWORD dwExportFlags;
+ NestedClassRec *pNestedRec;
+ RID iNestedRec;
+ mdTypeDef tdNester;
+ TOKENREC *pTokenRec;
+
+ LPCUTF8 szNameImp;
+ LPCUTF8 szNamespaceImp;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ MergeTypeData *pMTD;
+ BOOL bSuppressMergeCheck;
+ mdCustomAttribute tkCA;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ iCount = pMiniMdImport->getCountTypeDefs();
+
+ // Merge the typedefs
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those TypeDefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsTypeDefMarked(TokenFromRid(i, mdtTypeDef)) == false)
+ continue;
+
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetTypeDefRecord(i, &pRecImport));
+ IfFailGo(pMiniMdImport->getNameOfTypeDef(pRecImport, &szNameImp));
+ IfFailGo(pMiniMdImport->getNamespaceOfTypeDef(pRecImport, &szNamespaceImp));
+
+ // If the class is a Nested class, get the parent token.
+ dwFlags = pMiniMdImport->getFlagsOfTypeDef(pRecImport);
+ if (IsTdNested(dwFlags))
+ {
+ IfFailGo(pMiniMdImport->FindNestedClassHelper(TokenFromRid(i, mdtTypeDef), &iNestedRec));
+ if (InvalidRid(iNestedRec))
+ {
+ _ASSERTE(!"Bad state!");
+ IfFailGo(META_E_BADMETADATA);
+ }
+ else
+ {
+ IfFailGo(pMiniMdImport->GetNestedClassRecord(iNestedRec, &pNestedRec));
+ tdNester = pMiniMdImport->getEnclosingClassOfNestedClass(pNestedRec);
+ _ASSERTE(!IsNilToken(tdNester));
+ IfFailGo(pCurTkMap->Remap(tdNester, &tdNester));
+ }
+ }
+ else
+ tdNester = mdTokenNil;
+
+ bSuppressMergeCheck = (pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport,
+ TokenFromRid(i, mdtTypeDef), pImportData->m_tkSuppressMergeCheckCtor,
+ NULL, 0, &tkCA));
+
+ // does this TypeDef already exist in the emit scope?
+ if ( ImportHelper::FindTypeDefByName(
+ pMiniMdEmit,
+ szNamespaceImp,
+ szNameImp,
+ tdNester,
+ &tdEmit) == S_OK )
+ {
+ // Yes, it does
+ bDuplicate = true;
+
+ // Let's look at their accessiblities.
+ IfFailGo(pMiniMdEmit->GetTypeDefRecord(RidFromToken(tdEmit), &pRecEmit));
+ dwExportFlags = pMiniMdEmit->getFlagsOfTypeDef(pRecEmit);
+
+ // Managed types need to have the same accessiblity
+ BOOL fManagedType = FALSE;
+ IfFailGo(IsManagedType(pMiniMdImport, TokenFromRid(i, mdtTypeDef), &fManagedType));
+ if (fManagedType)
+ {
+ if ((dwFlags&tdVisibilityMask) != (dwExportFlags&tdVisibilityMask))
+ {
+ CheckContinuableErrorEx(META_E_MISMATCHED_VISIBLITY, pImportData, TokenFromRid(i, mdtTypeDef));
+ }
+
+ }
+ pMTD = m_rMTDs.Get(RidFromToken(tdEmit));
+ if (pMTD->m_bSuppressMergeCheck != bSuppressMergeCheck)
+ {
+ CheckContinuableErrorEx(META_E_MD_INCONSISTENCY, pImportData, TokenFromRid(i, mdtTypeDef));
+ }
+ }
+ else
+ {
+ // No, it doesn't. Copy it over.
+ bDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddTypeDefRecord(&pRecEmit, (RID *)&tdEmit));
+
+ // make sure the index matches
+ _ASSERTE(((mdTypeDef)m_rMTDs.Count()) == tdEmit);
+
+ IfNullGo(pMTD = m_rMTDs.Append());
+
+ pMTD->m_cMethods = 0;
+ pMTD->m_cFields = 0;
+ pMTD->m_cEvents = 0;
+ pMTD->m_cProperties = 0;
+ pMTD->m_bSuppressMergeCheck = bSuppressMergeCheck;
+
+ tdEmit = TokenFromRid( tdEmit, mdtTypeDef );
+
+ // Set Full Qualified Name.
+ IfFailGo( CopyTypeDefPartially( pRecEmit, pMiniMdImport, pRecImport) );
+
+ // Create a NestedClass record if the class is a Nested class.
+ if (! IsNilToken(tdNester))
+ {
+ IfFailGo(pMiniMdEmit->AddNestedClassRecord(&pNestedRec, &iNestedRec));
+
+ // copy over the information
+ IfFailGo( pMiniMdEmit->PutToken(TBL_NestedClass, NestedClassRec::COL_NestedClass,
+ pNestedRec, tdEmit));
+
+ // tdNester has already been remapped above to the Emit scope.
+ IfFailGo( pMiniMdEmit->PutToken(TBL_NestedClass, NestedClassRec::COL_EnclosingClass,
+ pNestedRec, tdNester));
+ IfFailGo( pMiniMdEmit->AddNestedClassToHash(iNestedRec) );
+
+ }
+ }
+
+ // record the token movement
+ tdImport = TokenFromRid(i, mdtTypeDef);
+ IfFailGo( pCurTkMap->InsertNotFound(tdImport, bDuplicate, tdEmit, &pTokenRec) );
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeTypeDefNamesOnly()
+
+
+//*****************************************************************************
+// Merge EnclosingType tables
+//*****************************************************************************
+HRESULT NEWMERGER::CopyTypeDefPartially(
+ TypeDefRec *pRecEmit, // [IN] the emit record to fill
+ CMiniMdRW *pMiniMdImport, // [IN] the importing scope
+ TypeDefRec *pRecImp) // [IN] the record to import
+
+{
+ HRESULT hr;
+ LPCUTF8 szNameImp;
+ LPCUTF8 szNamespaceImp;
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+
+ IfFailGo(pMiniMdImport->getNameOfTypeDef(pRecImp, &szNameImp));
+ IfFailGo(pMiniMdImport->getNamespaceOfTypeDef(pRecImp, &szNamespaceImp));
+
+ IfFailGo( pMiniMdEmit->PutString( TBL_TypeDef, TypeDefRec::COL_Name, pRecEmit, szNameImp) );
+ IfFailGo( pMiniMdEmit->PutString( TBL_TypeDef, TypeDefRec::COL_Namespace, pRecEmit, szNamespaceImp) );
+
+ pRecEmit->SetFlags(pRecImp->GetFlags());
+
+ // Don't copy over the extends until TypeRef's remap is calculated
+
+ErrExit:
+ return hr;
+
+} // HRESULT NEWMERGER::CopyTypeDefPartially()
+
+
+//*****************************************************************************
+// Merge ModuleRef tables including ModuleRef to ModuleDef optimization
+//*****************************************************************************
+HRESULT NEWMERGER::MergeModuleRefs()
+{
+ HRESULT hr = NOERROR;
+ ModuleRefRec *pRecImport = NULL;
+ ModuleRefRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdModuleRef mrEmit;
+ bool bDuplicate = false;
+ TOKENREC *pTokenRec;
+ LPCUTF8 szNameImp;
+ bool isModuleDef;
+
+ MergeImportData *pImportData;
+ MergeImportData *pData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountModuleRefs();
+
+ // loop through all ModuleRef
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those ModuleRefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsModuleRefMarked(TokenFromRid(i, mdtModuleRef)) == false)
+ continue;
+
+ isModuleDef = false;
+
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetModuleRefRecord(i, &pRecImport));
+ IfFailGo(pMiniMdImport->getNameOfModuleRef(pRecImport, &szNameImp));
+
+ // Only do the ModuleRef to ModuleDef optimization if ModuleRef's name is meaningful!
+ if ( szNameImp && szNameImp[0] != '\0')
+ {
+
+ // Check to see if this ModuleRef has become the ModuleDef token
+ for (pData = m_pImportDataList; pData != NULL; pData = pData->m_pNextImportData)
+ {
+ CMiniMdRW *pMiniMd = &(pData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ ModuleRec *pRec;
+ LPCUTF8 szName;
+
+ IfFailGo(pMiniMd->GetModuleRecord(MODULEDEFTOKEN, &pRec));
+ IfFailGo(pMiniMd->getNameOfModule(pRec, &szName));
+ if (szName && szName[0] != '\0' && strcmp(szNameImp, szName) == 0)
+ {
+ // We found an import Module for merging that has the same name as the ModuleRef
+ isModuleDef = true;
+ bDuplicate = true;
+ mrEmit = MODULEDEFTOKEN; // set the resulting token to ModuleDef Token
+ break;
+ }
+ }
+ }
+
+ if (isModuleDef == false)
+ {
+ // does this ModuleRef already exist in the emit scope?
+ hr = ImportHelper::FindModuleRef(pMiniMdEmit,
+ szNameImp,
+ &mrEmit);
+ if (hr == S_OK)
+ {
+ // Yes, it does
+ bDuplicate = true;
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ // No, it doesn't. Copy it over.
+ bDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecEmit, (RID*)&mrEmit));
+ mrEmit = TokenFromRid(mrEmit, mdtModuleRef);
+
+ // Set ModuleRef Name.
+ IfFailGo( pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, pRecEmit, szNameImp) );
+ }
+ else
+ IfFailGo(hr);
+ }
+
+ // record the token movement
+ IfFailGo( pCurTkMap->InsertNotFound(
+ TokenFromRid(i, mdtModuleRef),
+ bDuplicate,
+ mrEmit,
+ &pTokenRec) );
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeModuleRefs()
+
+
+//*****************************************************************************
+// Merge AssemblyRef tables
+//*****************************************************************************
+HRESULT NEWMERGER::MergeAssemblyRefs()
+{
+ HRESULT hr = NOERROR;
+ AssemblyRefRec *pRecImport = NULL;
+ AssemblyRefRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ mdAssemblyRef arEmit;
+ bool bDuplicate = false;
+ LPCUTF8 szTmp;
+ const void *pbTmp;
+ ULONG cbTmp;
+ ULONG iCount;
+ ULONG i;
+ ULONG iRecord;
+ TOKENREC *pTokenRec;
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountAssemblyRefs();
+
+ // loope through all the AssemblyRefs.
+ for (i = 1; i <= iCount; i++)
+ {
+ // Compare with the emit scope.
+ IfFailGo(pMiniMdImport->GetAssemblyRefRecord(i, &pRecImport));
+ IfFailGo(pMiniMdImport->getPublicKeyOrTokenOfAssemblyRef(pRecImport, (const BYTE **)&pbTmp, &cbTmp));
+ hr = CLDB_E_RECORD_NOTFOUND;
+ if (m_fDupCheck)
+ {
+ LPCSTR szAssemblyRefName;
+ LPCSTR szAssemblyRefLocale;
+ IfFailGo(pMiniMdImport->getNameOfAssemblyRef(pRecImport, &szAssemblyRefName));
+ IfFailGo(pMiniMdImport->getLocaleOfAssemblyRef(pRecImport, &szAssemblyRefLocale));
+ hr = ImportHelper::FindAssemblyRef(
+ pMiniMdEmit,
+ szAssemblyRefName,
+ szAssemblyRefLocale,
+ pbTmp,
+ cbTmp,
+ pRecImport->GetMajorVersion(),
+ pRecImport->GetMinorVersion(),
+ pRecImport->GetBuildNumber(),
+ pRecImport->GetRevisionNumber(),
+ pRecImport->GetFlags(),
+ &arEmit);
+ }
+ if (hr == S_OK)
+ {
+ // Yes, it does
+ bDuplicate = true;
+
+ // <TODO>@FUTURE: more verification?</TODO>
+ }
+ else if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ // No, it doesn't. Copy it over.
+ bDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddAssemblyRefRecord(&pRecEmit, &iRecord));
+ arEmit = TokenFromRid(iRecord, mdtAssemblyRef);
+
+ pRecEmit->Copy(pRecImport);
+
+ IfFailGo(pMiniMdImport->getPublicKeyOrTokenOfAssemblyRef(pRecImport, (const BYTE **)&pbTmp, &cbTmp));
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken,
+ pRecEmit, pbTmp, cbTmp));
+
+ IfFailGo(pMiniMdImport->getNameOfAssemblyRef(pRecImport, &szTmp));
+ IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Name,
+ pRecEmit, szTmp));
+
+ IfFailGo(pMiniMdImport->getLocaleOfAssemblyRef(pRecImport, &szTmp));
+ IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Locale,
+ pRecEmit, szTmp));
+
+ IfFailGo(pMiniMdImport->getHashValueOfAssemblyRef(pRecImport, (const BYTE **)&pbTmp, &cbTmp));
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue,
+ pRecEmit, pbTmp, cbTmp));
+
+ }
+ else
+ IfFailGo(hr);
+
+ // record the token movement.
+ IfFailGo(pCurTkMap->InsertNotFound(
+ TokenFromRid(i, mdtAssemblyRef),
+ bDuplicate,
+ arEmit,
+ &pTokenRec));
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeAssemblyRefs()
+
+
+//*****************************************************************************
+// Merge TypeRef tables also performing TypeRef to TypeDef opitimization. ie.
+// we will not introduce a TypeRef record if we can optimize it to a TypeDef.
+//*****************************************************************************
+HRESULT NEWMERGER::MergeTypeRefs()
+{
+ HRESULT hr = NOERROR;
+ TypeRefRec *pRecImport = NULL;
+ TypeRefRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdTypeRef trEmit;
+ bool bDuplicate = false;
+ TOKENREC *pTokenRec;
+ bool isTypeDef;
+
+ mdToken tkResImp;
+ mdToken tkResEmit;
+ LPCUTF8 szNameImp;
+ LPCUTF8 szNamespaceImp;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountTypeRefs();
+
+ // loop through all TypeRef
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those TypeRefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsTypeRefMarked(TokenFromRid(i, mdtTypeRef)) == false)
+ continue;
+
+ isTypeDef = false;
+
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetTypeRefRecord(i, &pRecImport));
+ tkResImp = pMiniMdImport->getResolutionScopeOfTypeRef(pRecImport);
+ IfFailGo(pMiniMdImport->getNamespaceOfTypeRef(pRecImport, &szNamespaceImp));
+ IfFailGo(pMiniMdImport->getNameOfTypeRef(pRecImport, &szNameImp));
+ if (!IsNilToken(tkResImp))
+ {
+ IfFailGo(pCurTkMap->Remap(tkResImp, &tkResEmit));
+ }
+ else
+ {
+ tkResEmit = tkResImp;
+ }
+
+ // There are some interesting cases to consider here.
+ // 1) If the TypeRef's ResolutionScope is a nil token, or is the MODULEDEFTOKEN (current module),
+ // then the TypeRef refers to a type in the current scope, so we should find a corresponding
+ // TypeDef in the output scope. If we find the TypeDef, we'll remap this TypeRef token
+ // to that TypeDef token.
+ // If we don't find that TypeDef, or if "TypeRef to TypeDef" optimization is turned off, we'll
+ // create the TypeRef in the output scope.
+ // 2) If the TypeRef's ResolutionScope has been resolved to a TypeDef, then this TypeRef was part
+ // of a nested type definition. In that case, we'd better find a corresponding TypeDef
+ // or we have an error.
+ if (IsNilToken(tkResEmit) || tkResEmit == MODULEDEFTOKEN || TypeFromToken(tkResEmit) == mdtTypeDef)
+ {
+ hr = ImportHelper::FindTypeDefByName(
+ pMiniMdEmit,
+ szNamespaceImp,
+ szNameImp,
+ (TypeFromToken(tkResEmit) == mdtTypeDef) ? tkResEmit : mdTokenNil,
+ &trEmit);
+ if (hr == S_OK)
+ {
+ isTypeDef = true;
+
+ // it really does not matter if we set the duplicate to true or false.
+ bDuplicate = true;
+ }
+ }
+
+ // If the ResolutionScope was merged as a TypeDef, and this token wasn't found as TypeDef, send the error.
+ if (TypeFromToken(tkResEmit) == mdtTypeDef && !isTypeDef)
+ {
+ // Send the error notification. Use the "continuable error" callback, but even if linker says it is
+ // ok, don't continue.
+ CheckContinuableErrorEx(META_E_TYPEDEF_MISSING, pImportData, TokenFromRid(i, mdtTypeRef));
+ IfFailGo(META_E_TYPEDEF_MISSING);
+ }
+
+ // If this TypeRef cannot be optmized to a TypeDef or the Ref to Def optimization is turned off, do the following.
+ if (!isTypeDef || !((m_optimizeRefToDef & MDTypeRefToDef) == MDTypeRefToDef))
+ {
+ // does this TypeRef already exist in the emit scope?
+ if ( m_fDupCheck && ImportHelper::FindTypeRefByName(
+ pMiniMdEmit,
+ tkResEmit,
+ szNamespaceImp,
+ szNameImp,
+ &trEmit) == S_OK )
+ {
+ // Yes, it does
+ bDuplicate = true;
+ }
+ else
+ {
+ // No, it doesn't. Copy it over.
+ bDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddTypeRefRecord(&pRecEmit, (RID*)&trEmit));
+ trEmit = TokenFromRid(trEmit, mdtTypeRef);
+
+ // Set ResolutionScope. tkResEmit has already been re-mapped.
+ IfFailGo(pMiniMdEmit->PutToken(TBL_TypeRef, TypeRefRec::COL_ResolutionScope,
+ pRecEmit, tkResEmit));
+
+ // Set Name.
+ IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Name,
+ pRecEmit, szNameImp));
+ IfFailGo(pMiniMdEmit->AddNamedItemToHash(TBL_TypeRef, trEmit, szNameImp, 0));
+
+ // Set Namespace.
+ IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Namespace,
+ pRecEmit, szNamespaceImp));
+ }
+ }
+
+ // record the token movement
+ IfFailGo( pCurTkMap->InsertNotFound(
+ TokenFromRid(i, mdtTypeRef),
+ bDuplicate,
+ trEmit,
+ &pTokenRec) );
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeTypeRefs()
+
+
+//*****************************************************************************
+// copy over the remaining information of partially merged TypeDef records. Right now only
+// extends field is delayed to here. The reason that we delay extends field is because we want
+// to optimize TypeRef to TypeDef if possible.
+//*****************************************************************************
+HRESULT NEWMERGER::CompleteMergeTypeDefs()
+{
+ HRESULT hr = NOERROR;
+ TypeDefRec *pRecImport = NULL;
+ TypeDefRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ TOKENREC *pTokenRec;
+ mdToken tkExtendsImp;
+ mdToken tkExtendsEmit;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ iCount = pMiniMdImport->getCountTypeDefs();
+
+ // Merge the typedefs
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those TypeDefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsTypeDefMarked(TokenFromRid(i, mdtTypeDef)) == false)
+ continue;
+
+ if ( !pCurTkMap->Find(TokenFromRid(i, mdtTypeDef), &pTokenRec) )
+ {
+ _ASSERTE( !"bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+
+ if (pTokenRec->m_isDuplicate == false)
+ {
+ // get the extends token from the import
+ IfFailGo(pMiniMdImport->GetTypeDefRecord(i, &pRecImport));
+ tkExtendsImp = pMiniMdImport->getExtendsOfTypeDef(pRecImport);
+
+ // map the extends token to an merged token
+ IfFailGo( pCurTkMap->Remap(tkExtendsImp, &tkExtendsEmit) );
+
+ // set the extends to the merged TypeDef records.
+ IfFailGo(pMiniMdEmit->GetTypeDefRecord(RidFromToken(pTokenRec->m_tkTo), &pRecEmit));
+ IfFailGo(pMiniMdEmit->PutToken(TBL_TypeDef, TypeDefRec::COL_Extends, pRecEmit, tkExtendsEmit));
+ }
+ else
+ {
+ // <TODO>@FUTURE: we can check to make sure the import extends maps to the one that is set to the emit scope.
+ // Otherwise, it is a error to report to linker.</TODO>
+ }
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CompleteMergeTypeDefs()
+
+
+//*****************************************************************************
+// merging TypeSpecs
+//*****************************************************************************
+HRESULT NEWMERGER::MergeTypeSpecs()
+{
+ HRESULT hr = NOERROR;
+ TypeSpecRec *pRecImport = NULL;
+ TypeSpecRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ TOKENREC *pTokenRec;
+ mdTypeSpec tsImp;
+ mdTypeSpec tsEmit;
+ bool fDuplicate;
+ PCCOR_SIGNATURE pbSig;
+ ULONG cbSig;
+ ULONG cbEmit;
+ CQuickBytes qbSig;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ iCount = pMiniMdImport->getCountTypeSpecs();
+
+ // loop through all TypeSpec
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those TypeSpecs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsTypeSpecMarked(TokenFromRid(i, mdtTypeSpec)) == false)
+ continue;
+
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetTypeSpecRecord(i, &pRecImport));
+ IfFailGo(pMiniMdImport->getSignatureOfTypeSpec(pRecImport, &pbSig, &cbSig));
+
+ // convert tokens contained in signature to new scope
+ IfFailGo(ImportHelper::MergeUpdateTokenInFieldSig(
+ NULL, // Assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Import assembly information.
+ pMiniMdImport, // The scope to merge into the emit scope.
+ pbSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit)); // number of bytes write to cbEmit
+
+ hr = CLDB_E_RECORD_NOTFOUND;
+ if (m_fDupCheck)
+ hr = ImportHelper::FindTypeSpec(
+ pMiniMdEmit,
+ (PCOR_SIGNATURE) qbSig.Ptr(),
+ cbEmit,
+ &tsEmit );
+
+ if ( hr == S_OK )
+ {
+ // find a duplicate
+ fDuplicate = true;
+ }
+ else
+ {
+ // copy over
+ fDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddTypeSpecRecord(&pRecEmit, (ULONG *)&tsEmit));
+ tsEmit = TokenFromRid(tsEmit, mdtTypeSpec);
+ IfFailGo( pMiniMdEmit->PutBlob(
+ TBL_TypeSpec,
+ TypeSpecRec::COL_Signature,
+ pRecEmit,
+ (PCOR_SIGNATURE)qbSig.Ptr(),
+ cbEmit));
+ }
+ tsImp = TokenFromRid(i, mdtTypeSpec);
+
+ // Record the token movement
+ IfFailGo( pCurTkMap->InsertNotFound(tsImp, fDuplicate, tsEmit, &pTokenRec) );
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeTypeSpecs()
+
+
+//*****************************************************************************
+// merging Children of TypeDefs. This includes field, method, parameter, property, event
+//*****************************************************************************
+HRESULT NEWMERGER::MergeTypeDefChildren()
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdTypeDef tdEmit;
+ mdTypeDef tdImport;
+ TOKENREC *pTokenRec;
+
+#if _DEBUG
+ TypeDefRec *pRecImport = NULL;
+ LPCUTF8 szNameImp;
+ LPCUTF8 szNamespaceImp;
+#endif // _DEBUG
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountTypeDefs();
+
+ // loop through all TypeDef again to merge/copy Methods, fields, events, and properties
+ //
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those TypeDefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsTypeDefMarked(TokenFromRid(i, mdtTypeDef)) == false)
+ continue;
+
+#if _DEBUG
+ IfFailGo(pMiniMdImport->GetTypeDefRecord(i, &pRecImport));
+ IfFailGo(pMiniMdImport->getNameOfTypeDef(pRecImport, &szNameImp));
+ IfFailGo(pMiniMdImport->getNamespaceOfTypeDef(pRecImport, &szNamespaceImp));
+#endif // _DEBUG
+
+ // check to see if the typedef is duplicate or not
+ tdImport = TokenFromRid(i, mdtTypeDef);
+ if ( pCurTkMap->Find( tdImport, &pTokenRec) == false)
+ {
+ _ASSERTE( !"bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ tdEmit = pTokenRec->m_tkTo;
+ if (pTokenRec->m_isDuplicate == false)
+ {
+ // now move all of the children records over
+ IfFailGo( CopyMethods(pImportData, tdImport, tdEmit) );
+ IfFailGo( CopyFields(pImportData, tdImport, tdEmit) );
+
+ IfFailGo( CopyEvents(pImportData, tdImport, tdEmit) );
+
+ // Property has dependency on events
+ IfFailGo( CopyProperties(pImportData, tdImport, tdEmit) );
+
+ // Generic Params.
+ IfFailGo( CopyGenericParams(pImportData, tdImport, tdEmit) );
+ }
+ else
+ {
+ // verify the children records
+ IfFailGo( VerifyMethods(pImportData, tdImport, tdEmit) );
+ IfFailGo( VerifyFields(pImportData, tdImport, tdEmit) );
+ IfFailGo( VerifyEvents(pImportData, tdImport, tdEmit) );
+
+ // property has dependency on events
+ IfFailGo( VerifyProperties(pImportData, tdImport, tdEmit) );
+
+ IfFailGo( VerifyGenericParams(pImportData, tdImport, tdEmit) );
+ }
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeTypeDefChildren()
+
+
+//*******************************************************************************
+// Helper to copy an Method record
+//*******************************************************************************
+HRESULT NEWMERGER::CopyMethod(
+ MergeImportData *pImportData, // [IN] import scope
+ MethodRec *pRecImp, // [IN] the record to import
+ MethodRec *pRecEmit) // [IN] the emit record to fill
+{
+ HRESULT hr;
+ CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+ LPCUTF8 szName;
+ PCCOR_SIGNATURE pbSig;
+ ULONG cbSig;
+ ULONG cbEmit;
+ CQuickBytes qbSig;
+ MDTOKENMAP *pCurTkMap;
+
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // copy over the fix part of the record
+ pRecEmit->Copy(pRecImp);
+
+ // copy over the name
+ IfFailGo(pMiniMdImp->getNameOfMethod(pRecImp, &szName));
+ IfFailGo(pMiniMdEmit->PutString(TBL_Method, MethodRec::COL_Name, pRecEmit, szName));
+
+ // copy over the signature
+ IfFailGo(pMiniMdImp->getSignatureOfMethod(pRecImp, &pbSig, &cbSig));
+
+ // convert rid contained in signature to new scope
+ IfFailGo(ImportHelper::MergeUpdateTokenInSig(
+ NULL, // Assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Import assembly scope information.
+ pMiniMdImp, // The scope to merge into the emit scope.
+ pbSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit)); // number of bytes write to cbEmit
+
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_Method, MethodRec::COL_Signature, pRecEmit, qbSig.Ptr(), cbEmit));
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyMethod()
+
+
+//*******************************************************************************
+// Helper to copy an field record
+//*******************************************************************************
+HRESULT NEWMERGER::CopyField(
+ MergeImportData *pImportData, // [IN] import scope
+ FieldRec *pRecImp, // [IN] the record to import
+ FieldRec *pRecEmit) // [IN] the emit record to fill
+{
+ HRESULT hr;
+ CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+ LPCUTF8 szName;
+ PCCOR_SIGNATURE pbSig;
+ ULONG cbSig;
+ ULONG cbEmit;
+ CQuickBytes qbSig;
+ MDTOKENMAP *pCurTkMap;
+
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // copy over the fix part of the record
+ pRecEmit->SetFlags(pRecImp->GetFlags());
+
+ // copy over the name
+ IfFailGo(pMiniMdImp->getNameOfField(pRecImp, &szName));
+ IfFailGo(pMiniMdEmit->PutString(TBL_Field, FieldRec::COL_Name, pRecEmit, szName));
+
+ // copy over the signature
+ IfFailGo(pMiniMdImp->getSignatureOfField(pRecImp, &pbSig, &cbSig));
+
+ // convert rid contained in signature to new scope
+ IfFailGo(ImportHelper::MergeUpdateTokenInSig(
+ NULL, // Emit assembly scope.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Import assembly scope information.
+ pMiniMdImp, // The scope to merge into the emit scope.
+ pbSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit)); // number of bytes write to cbEmit
+
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_Field, FieldRec::COL_Signature, pRecEmit, qbSig.Ptr(), cbEmit));
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyField()
+
+//*******************************************************************************
+// Helper to copy an field record
+//*******************************************************************************
+HRESULT NEWMERGER::CopyParam(
+ MergeImportData *pImportData, // [IN] import scope
+ ParamRec *pRecImp, // [IN] the record to import
+ ParamRec *pRecEmit) // [IN] the emit record to fill
+{
+ HRESULT hr;
+ CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+ LPCUTF8 szName;
+ MDTOKENMAP *pCurTkMap;
+
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // copy over the fix part of the record
+ pRecEmit->Copy(pRecImp);
+
+ // copy over the name
+ IfFailGo(pMiniMdImp->getNameOfParam(pRecImp, &szName));
+ IfFailGo(pMiniMdEmit->PutString(TBL_Param, ParamRec::COL_Name, pRecEmit, szName));
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyParam()
+
+//*******************************************************************************
+// Helper to copy an Event record
+//*******************************************************************************
+HRESULT NEWMERGER::CopyEvent(
+ MergeImportData *pImportData, // [IN] import scope
+ EventRec *pRecImp, // [IN] the record to import
+ EventRec *pRecEmit) // [IN] the emit record to fill
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+ mdToken tkEventTypeImp;
+ mdToken tkEventTypeEmit; // could be TypeDef or TypeRef
+ LPCUTF8 szName;
+ MDTOKENMAP *pCurTkMap;
+
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ pRecEmit->SetEventFlags(pRecImp->GetEventFlags());
+
+ //move over the event name
+ IfFailGo(pMiniMdImp->getNameOfEvent(pRecImp, &szName));
+ IfFailGo( pMiniMdEmit->PutString(TBL_Event, EventRec::COL_Name, pRecEmit, szName) );
+
+ // move over the EventType
+ tkEventTypeImp = pMiniMdImp->getEventTypeOfEvent(pRecImp);
+ if ( !IsNilToken(tkEventTypeImp) )
+ {
+ IfFailGo( pCurTkMap->Remap(tkEventTypeImp, &tkEventTypeEmit) );
+ IfFailGo(pMiniMdEmit->PutToken(TBL_Event, EventRec::COL_EventType, pRecEmit, tkEventTypeEmit));
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyEvent()
+
+
+//*******************************************************************************
+// Helper to copy a property record
+//*******************************************************************************
+HRESULT NEWMERGER::CopyProperty(
+ MergeImportData *pImportData, // [IN] import scope
+ PropertyRec *pRecImp, // [IN] the record to import
+ PropertyRec *pRecEmit) // [IN] the emit record to fill
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+ LPCUTF8 szName;
+ PCCOR_SIGNATURE pbSig;
+ ULONG cbSig;
+ ULONG cbEmit;
+ CQuickBytes qbSig;
+ MDTOKENMAP *pCurTkMap;
+
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // move over the flag value
+ pRecEmit->SetPropFlags(pRecImp->GetPropFlags());
+
+ //move over the property name
+ IfFailGo(pMiniMdImp->getNameOfProperty(pRecImp, &szName));
+ IfFailGo( pMiniMdEmit->PutString(TBL_Property, PropertyRec::COL_Name, pRecEmit, szName) );
+
+ // move over the type of the property
+ IfFailGo(pMiniMdImp->getTypeOfProperty(pRecImp, &pbSig, &cbSig));
+
+ // convert rid contained in signature to new scope
+ IfFailGo( ImportHelper::MergeUpdateTokenInSig(
+ NULL, // Assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Import assembly scope information.
+ pMiniMdImp, // The scope to merge into the emit scope.
+ pbSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit) ); // number of bytes write to cbEmit
+
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_Property, PropertyRec::COL_Type, pRecEmit, qbSig.Ptr(), cbEmit));
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyProperty()
+
+
+//*****************************************************************************
+// Copy MethodSemantics for an event or a property
+//*****************************************************************************
+HRESULT NEWMERGER::CopyMethodSemantics(
+ MergeImportData *pImportData,
+ mdToken tkImport, // Event or property in the import scope
+ mdToken tkEmit) // corresponding event or property in the emitting scope
+{
+ HRESULT hr = NOERROR;
+ MethodSemanticsRec *pRecImport = NULL;
+ MethodSemanticsRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+ ULONG i;
+ ULONG msEmit; // MethodSemantics are just index not tokens
+ mdToken tkMethodImp;
+ mdToken tkMethodEmit;
+ MDTOKENMAP *pCurTkMap;
+ HENUMInternal hEnum;
+
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // copy over the associates
+ IfFailGo( pMiniMdImport->FindMethodSemanticsHelper(tkImport, &hEnum) );
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &i))
+ {
+ IfFailGo(pMiniMdImport->GetMethodSemanticsRecord(i, &pRecImport));
+ IfFailGo(pMiniMdEmit->AddMethodSemanticsRecord(&pRecEmit, &msEmit));
+ pRecEmit->SetSemantic(pRecImport->GetSemantic());
+
+ // set the MethodSemantics
+ tkMethodImp = pMiniMdImport->getMethodOfMethodSemantics(pRecImport);
+ IfFailGo( pCurTkMap->Remap(tkMethodImp, &tkMethodEmit) );
+ IfFailGo( pMiniMdEmit->PutToken(TBL_MethodSemantics, MethodSemanticsRec::COL_Method, pRecEmit, tkMethodEmit));
+
+ // set the associate
+ _ASSERTE( pMiniMdImport->getAssociationOfMethodSemantics(pRecImport) == tkImport );
+ IfFailGo( pMiniMdEmit->PutToken(TBL_MethodSemantics, MethodSemanticsRec::COL_Association, pRecEmit, tkEmit));
+
+ // no need to record the movement since it is not a token
+ IfFailGo( pMiniMdEmit->AddMethodSemanticsToHash(msEmit) );
+ }
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+ return hr;
+} // HRESULT NEWMERGER::CopyMethodSemantics()
+
+
+//*****************************************************************************
+// Copy Methods given a TypeDef
+//*****************************************************************************
+HRESULT NEWMERGER::CopyMethods(
+ MergeImportData *pImportData,
+ mdTypeDef tdImport,
+ mdTypeDef tdEmit)
+{
+ HRESULT hr = NOERROR;
+ MethodRec *pRecImport = NULL;
+ MethodRec *pRecEmit = NULL;
+ TypeDefRec *pTypeDefRec;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG ridStart, ridEnd;
+ ULONG i;
+ mdMethodDef mdEmit;
+ mdMethodDef mdImp;
+ TOKENREC *pTokenRec;
+ MDTOKENMAP *pCurTkMap;
+
+ MergeTypeData *pMTD;
+ BOOL bSuppressMergeCheck;
+ mdCustomAttribute tkCA;
+
+ pMiniMdEmit = GetMiniMdEmit();
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ IfFailGo(pMiniMdImport->GetTypeDefRecord(RidFromToken(tdImport), &pTypeDefRec));
+ ridStart = pMiniMdImport->getMethodListOfTypeDef(pTypeDefRec);
+ IfFailGo(pMiniMdImport->getEndMethodListOfTypeDef(RidFromToken(tdImport), &ridEnd));
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ pMTD = m_rMTDs.Get(RidFromToken(tdEmit));
+
+ // make sure we didn't count the methods yet
+ _ASSERTE(pMTD->m_cMethods == 0);
+
+ // loop through all Methods
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetMethodRid(i, (ULONG *)&mdImp));
+
+ // only merge those MethodDefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsMethodMarked(TokenFromRid(mdImp, mdtMethodDef)) == false)
+ continue;
+
+ IfFailGo(pMiniMdImport->GetMethodRecord(mdImp, &pRecImport));
+ IfFailGo(pMiniMdEmit->AddMethodRecord(&pRecEmit, (RID *)&mdEmit));
+
+ // copy the method content over
+ IfFailGo( CopyMethod(pImportData, pRecImport, pRecEmit) );
+
+ IfFailGo( pMiniMdEmit->AddMethodToTypeDef(RidFromToken(tdEmit), mdEmit));
+
+ // record the token movement
+ mdImp = TokenFromRid(mdImp, mdtMethodDef);
+ mdEmit = TokenFromRid(mdEmit, mdtMethodDef);
+ IfFailGo( pMiniMdEmit->AddMemberDefToHash(
+ mdEmit,
+ tdEmit) );
+
+ IfFailGo( pCurTkMap->InsertNotFound(mdImp, false, mdEmit, &pTokenRec) );
+
+ // copy over the children
+ IfFailGo( CopyParams(pImportData, mdImp, mdEmit) );
+ IfFailGo( CopyGenericParams(pImportData, mdImp, mdEmit) );
+
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport,
+ mdImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+
+ if (!bSuppressMergeCheck) {
+ pMTD->m_cMethods++;
+ }
+ }
+
+ // make sure we don't count any methods if merge check is suppressed on the type
+ _ASSERTE(pMTD->m_cMethods == 0 || !pMTD->m_bSuppressMergeCheck);
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyMethods()
+
+
+//*****************************************************************************
+// Copy Fields given a TypeDef
+//*****************************************************************************
+HRESULT NEWMERGER::CopyFields(
+ MergeImportData *pImportData,
+ mdTypeDef tdImport,
+ mdTypeDef tdEmit)
+{
+ HRESULT hr = NOERROR;
+ FieldRec *pRecImport = NULL;
+ FieldRec *pRecEmit = NULL;
+ TypeDefRec *pTypeDefRec;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG ridStart, ridEnd;
+ ULONG i;
+ mdFieldDef fdEmit;
+ mdFieldDef fdImp;
+ bool bDuplicate;
+ TOKENREC *pTokenRec;
+ PCCOR_SIGNATURE pvSigBlob;
+ ULONG cbSigBlob;
+ MDTOKENMAP *pCurTkMap;
+
+ MergeTypeData *pMTD;
+ BOOL bSuppressMergeCheck;
+ mdCustomAttribute tkCA;
+
+ pMiniMdEmit = GetMiniMdEmit();
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ IfFailGo(pMiniMdImport->GetTypeDefRecord(RidFromToken(tdImport), &pTypeDefRec));
+ ridStart = pMiniMdImport->getFieldListOfTypeDef(pTypeDefRec);
+ IfFailGo(pMiniMdImport->getEndFieldListOfTypeDef(RidFromToken(tdImport), &ridEnd));
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ pMTD = m_rMTDs.Get(RidFromToken(tdEmit));
+
+ // make sure we didn't count the methods yet
+ _ASSERTE(pMTD->m_cFields == 0);
+
+ // loop through all FieldDef of a TypeDef
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetFieldRid(i, (ULONG *)&fdImp));
+
+ // only merge those FieldDefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(TokenFromRid(fdImp, mdtFieldDef)) == false)
+ continue;
+
+
+ IfFailGo(pMiniMdImport->GetFieldRecord(fdImp, &pRecImport));
+ bDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddFieldRecord(&pRecEmit, (RID *)&fdEmit));
+
+ // copy the field content over
+ IfFailGo( CopyField(pImportData, pRecImport, pRecEmit) );
+
+ IfFailGo( pMiniMdEmit->AddFieldToTypeDef(RidFromToken(tdEmit), fdEmit));
+
+ // record the token movement
+ fdImp = TokenFromRid(fdImp, mdtFieldDef);
+ fdEmit = TokenFromRid(fdEmit, mdtFieldDef);
+ IfFailGo(pMiniMdEmit->getSignatureOfField(pRecEmit, &pvSigBlob, &cbSigBlob));
+ IfFailGo( pMiniMdEmit->AddMemberDefToHash(
+ fdEmit,
+ tdEmit) );
+
+ IfFailGo( pCurTkMap->InsertNotFound(fdImp, false, fdEmit, &pTokenRec) );
+
+ // count the number of fields that didn't suppress merge check
+ // non-static fields doesn't inherite the suppress merge check attribute from the type
+ bSuppressMergeCheck =
+ (IsFdStatic(pRecEmit->GetFlags()) && pMTD->m_bSuppressMergeCheck) ||
+ ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport,
+ fdImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (!bSuppressMergeCheck) {
+ pMTD->m_cFields++;
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyFields()
+
+
+//*****************************************************************************
+// Copy Events given a TypeDef
+//*****************************************************************************
+HRESULT NEWMERGER::CopyEvents(
+ MergeImportData *pImportData,
+ mdTypeDef tdImport,
+ mdTypeDef tdEmit)
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+ RID ridEventMap;
+ EventMapRec *pEventMapRec;
+ EventRec *pRecImport;
+ EventRec *pRecEmit;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+ mdEvent evImp;
+ mdEvent evEmit;
+ TOKENREC *pTokenRec;
+ ULONG iEventMap;
+ EventMapRec *pEventMap;
+ MDTOKENMAP *pCurTkMap;
+
+ MergeTypeData *pMTD;
+ BOOL bSuppressMergeCheck;
+ mdCustomAttribute tkCA;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ pMTD = m_rMTDs.Get(RidFromToken(tdEmit));
+
+ // make sure we didn't count the events yet
+ _ASSERTE(pMTD->m_cEvents == 0);
+
+ IfFailGo(pMiniMdImport->FindEventMapFor(RidFromToken(tdImport), &ridEventMap));
+ if (!InvalidRid(ridEventMap))
+ {
+ IfFailGo(pMiniMdImport->GetEventMapRecord(ridEventMap, &pEventMapRec));
+ ridStart = pMiniMdImport->getEventListOfEventMap(pEventMapRec);
+ IfFailGo(pMiniMdImport->getEndEventListOfEventMap(ridEventMap, &ridEnd));
+
+ if (ridEnd > ridStart)
+ {
+ // If there is any event, create the eventmap record in the emit scope
+ // Create new record.
+ IfFailGo(pMiniMdEmit->AddEventMapRecord(&pEventMap, &iEventMap));
+
+ // Set parent.
+ IfFailGo(pMiniMdEmit->PutToken(TBL_EventMap, EventMapRec::COL_Parent, pEventMap, tdEmit));
+ }
+
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // get the real event rid
+ IfFailGo(pMiniMdImport->GetEventRid(i, (ULONG *)&evImp));
+
+ // only merge those Events that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsEventMarked(TokenFromRid(evImp, mdtEvent)) == false)
+ continue;
+
+ IfFailGo(pMiniMdImport->GetEventRecord(evImp, &pRecImport));
+ IfFailGo(pMiniMdEmit->AddEventRecord(&pRecEmit, (RID *)&evEmit));
+
+ // copy the event record over
+ IfFailGo( CopyEvent(pImportData, pRecImport, pRecEmit) );
+
+ // Add Event to the EventMap.
+ IfFailGo( pMiniMdEmit->AddEventToEventMap(iEventMap, evEmit) );
+
+ // record the token movement
+ evImp = TokenFromRid(evImp, mdtEvent);
+ evEmit = TokenFromRid(evEmit, mdtEvent);
+
+ IfFailGo( pCurTkMap->InsertNotFound(evImp, false, evEmit, &pTokenRec) );
+
+ // copy over the method semantics
+ IfFailGo( CopyMethodSemantics(pImportData, evImp, evEmit) );
+
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport,
+ evImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (!bSuppressMergeCheck) {
+ pMTD->m_cEvents++;
+ }
+ }
+ }
+
+ // make sure we don't count any events if merge check is suppressed on the type
+ _ASSERTE(pMTD->m_cEvents == 0 || !pMTD->m_bSuppressMergeCheck);
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyEvents()
+
+
+//*****************************************************************************
+// Copy Properties given a TypeDef
+//*****************************************************************************
+HRESULT NEWMERGER::CopyProperties(
+ MergeImportData *pImportData,
+ mdTypeDef tdImport,
+ mdTypeDef tdEmit)
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+ RID ridPropertyMap;
+ PropertyMapRec *pPropertyMapRec;
+ PropertyRec *pRecImport;
+ PropertyRec *pRecEmit;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+ mdProperty prImp;
+ mdProperty prEmit;
+ TOKENREC *pTokenRec;
+ ULONG iPropertyMap;
+ PropertyMapRec *pPropertyMap;
+ MDTOKENMAP *pCurTkMap;
+
+ MergeTypeData *pMTD;
+ BOOL bSuppressMergeCheck;
+ mdCustomAttribute tkCA;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ pMTD = m_rMTDs.Get(RidFromToken(tdEmit));
+
+ // make sure we didn't count the properties yet
+ _ASSERTE(pMTD->m_cProperties == 0);
+
+ IfFailGo(pMiniMdImport->FindPropertyMapFor(RidFromToken(tdImport), &ridPropertyMap));
+ if (!InvalidRid(ridPropertyMap))
+ {
+ IfFailGo(pMiniMdImport->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec));
+ ridStart = pMiniMdImport->getPropertyListOfPropertyMap(pPropertyMapRec);
+ IfFailGo(pMiniMdImport->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd));
+
+ if (ridEnd > ridStart)
+ {
+ // If there is any event, create the PropertyMap record in the emit scope
+ // Create new record.
+ IfFailGo(pMiniMdEmit->AddPropertyMapRecord(&pPropertyMap, &iPropertyMap));
+
+ // Set parent.
+ IfFailGo(pMiniMdEmit->PutToken(TBL_PropertyMap, PropertyMapRec::COL_Parent, pPropertyMap, tdEmit));
+ }
+
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // get the property rid
+ IfFailGo(pMiniMdImport->GetPropertyRid(i, (ULONG *)&prImp));
+
+ // only merge those Properties that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsPropertyMarked(TokenFromRid(prImp, mdtProperty)) == false)
+ continue;
+
+
+ IfFailGo(pMiniMdImport->GetPropertyRecord(prImp, &pRecImport));
+ IfFailGo(pMiniMdEmit->AddPropertyRecord(&pRecEmit, (RID *)&prEmit));
+
+ // copy the property record over
+ IfFailGo( CopyProperty(pImportData, pRecImport, pRecEmit) );
+
+ // Add Property to the PropertyMap.
+ IfFailGo( pMiniMdEmit->AddPropertyToPropertyMap(iPropertyMap, prEmit) );
+
+ // record the token movement
+ prImp = TokenFromRid(prImp, mdtProperty);
+ prEmit = TokenFromRid(prEmit, mdtProperty);
+
+ IfFailGo( pCurTkMap->InsertNotFound(prImp, false, prEmit, &pTokenRec) );
+
+ // copy over the method semantics
+ IfFailGo( CopyMethodSemantics(pImportData, prImp, prEmit) );
+
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport,
+ prImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (!bSuppressMergeCheck) {
+ pMTD->m_cProperties++;
+ }
+ }
+ }
+
+ // make sure we don't count any properties if merge check is suppressed on the type
+ _ASSERTE(pMTD->m_cProperties == 0 || !pMTD->m_bSuppressMergeCheck);
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyProperties()
+
+
+//*****************************************************************************
+// Copy Parameters given a TypeDef
+//*****************************************************************************
+HRESULT NEWMERGER::CopyParams(
+ MergeImportData *pImportData,
+ mdMethodDef mdImport,
+ mdMethodDef mdEmit)
+{
+ HRESULT hr = NOERROR;
+ ParamRec *pRecImport = NULL;
+ ParamRec *pRecEmit = NULL;
+ MethodRec *pMethodRec;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG ridStart, ridEnd;
+ ULONG i;
+ mdParamDef pdEmit;
+ mdParamDef pdImp;
+ TOKENREC *pTokenRec;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+
+ IfFailGo(pMiniMdImport->GetMethodRecord(RidFromToken(mdImport), &pMethodRec));
+ ridStart = pMiniMdImport->getParamListOfMethod(pMethodRec);
+ IfFailGo(pMiniMdImport->getEndParamListOfMethod(RidFromToken(mdImport), &ridEnd));
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // loop through all InterfaceImpl
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // Get the param rid
+ IfFailGo(pMiniMdImport->GetParamRid(i, (ULONG *)&pdImp));
+
+ // only merge those Params that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsParamMarked(TokenFromRid(pdImp, mdtParamDef)) == false)
+ continue;
+
+
+ IfFailGo(pMiniMdImport->GetParamRecord(pdImp, &pRecImport));
+ IfFailGo(pMiniMdEmit->AddParamRecord(&pRecEmit, (RID *)&pdEmit));
+
+ // copy the Parameter record over
+ IfFailGo( CopyParam(pImportData, pRecImport, pRecEmit) );
+
+ // warning!! warning!!
+ // We cannot add paramRec to method list until it is fully set.
+ // AddParamToMethod will use the ulSequence in the record
+ IfFailGo( pMiniMdEmit->AddParamToMethod(RidFromToken(mdEmit), pdEmit));
+
+ // record the token movement
+ pdImp = TokenFromRid(pdImp, mdtParamDef);
+ pdEmit = TokenFromRid(pdEmit, mdtParamDef);
+
+ IfFailGo( pCurTkMap->InsertNotFound(pdImp, false, pdEmit, &pTokenRec) );
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyParams()
+
+
+//*****************************************************************************
+// Copy GenericParams given a TypeDef
+//*****************************************************************************
+HRESULT NEWMERGER::CopyGenericParams(
+ MergeImportData *pImportData,
+ mdToken tkImport,
+ mdToken tkEmit)
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ TOKENREC *pTokenRec;
+ GenericParamRec *pRecImport = NULL;
+ GenericParamRec *pRecEmit = NULL;
+ MDTOKENMAP *pCurTkMap;
+ HENUMInternal hEnum;
+ mdGenericParam gpImport;
+ mdGenericParam gpEmit;
+ LPCSTR szGenericParamName;
+
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pMiniMdEmit = GetMiniMdEmit();
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ IfFailGo( pMiniMdImport->FindGenericParamHelper(tkImport, &hEnum) );
+
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &gpImport))
+ {
+ // Get the import GenericParam record
+ _ASSERTE(TypeFromToken(gpImport) == mdtGenericParam);
+ IfFailGo(pMiniMdImport->GetGenericParamRecord(RidFromToken(gpImport), &pRecImport));
+
+ // Create new emit record.
+ IfFailGo(pMiniMdEmit->AddGenericParamRecord(&pRecEmit, (RID *)&gpEmit));
+
+ // copy the GenericParam content
+ pRecEmit->SetNumber( pRecImport->GetNumber());
+ pRecEmit->SetFlags( pRecImport->GetFlags());
+
+ IfFailGo( pMiniMdEmit->PutToken(TBL_GenericParam, GenericParamRec::COL_Owner, pRecEmit, tkEmit));
+
+ IfFailGo(pMiniMdImport->getNameOfGenericParam(pRecImport, &szGenericParamName));
+ IfFailGo( pMiniMdEmit->PutString(TBL_GenericParam, GenericParamRec::COL_Name, pRecEmit, szGenericParamName));
+
+ // record the token movement
+ gpImport = TokenFromRid(gpImport, mdtGenericParam);
+ gpEmit = TokenFromRid(gpEmit, mdtGenericParam);
+
+ IfFailGo( pCurTkMap->InsertNotFound(gpImport, false, gpEmit, &pTokenRec) );
+
+ // copy over any constraints
+ IfFailGo( CopyGenericParamConstraints(pImportData, gpImport, gpEmit) );
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyGenericParams()
+
+
+//*****************************************************************************
+// Copy GenericParamConstraints given a GenericParam
+//*****************************************************************************
+HRESULT NEWMERGER::CopyGenericParamConstraints(
+ MergeImportData *pImportData,
+ mdGenericParamConstraint tkImport,
+ mdGenericParamConstraint tkEmit)
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ TOKENREC *pTokenRec;
+ GenericParamConstraintRec *pRecImport = NULL;
+ GenericParamConstraintRec *pRecEmit = NULL;
+ MDTOKENMAP *pCurTkMap;
+ HENUMInternal hEnum;
+ mdGenericParamConstraint gpImport;
+ mdGenericParamConstraint gpEmit;
+ mdToken tkConstraint;
+
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pMiniMdEmit = GetMiniMdEmit();
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ IfFailGo( pMiniMdImport->FindGenericParamConstraintHelper(tkImport, &hEnum) );
+
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &gpImport))
+ {
+ // Get the import GenericParam record
+ _ASSERTE(TypeFromToken(gpImport) == mdtGenericParamConstraint);
+ IfFailGo(pMiniMdImport->GetGenericParamConstraintRecord(RidFromToken(gpImport), &pRecImport));
+
+ // Translate the constraint before creating new record.
+ tkConstraint = pMiniMdImport->getConstraintOfGenericParamConstraint(pRecImport);
+ if (pCurTkMap->Find(tkConstraint, &pTokenRec) == false)
+ {
+ // This should never fire unless the TypeDefs/Refs weren't merged
+ // before this code runs.
+ _ASSERTE(!"GenericParamConstraint Constraint not found in MERGER::CopyGenericParamConstraints. Bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ tkConstraint = pTokenRec->m_tkTo;
+
+ // Create new emit record.
+ IfFailGo(pMiniMdEmit->AddGenericParamConstraintRecord(&pRecEmit, (RID *)&gpEmit));
+
+ // copy the GenericParamConstraint content
+ IfFailGo( pMiniMdEmit->PutToken(TBL_GenericParamConstraint, GenericParamConstraintRec::COL_Owner, pRecEmit, tkEmit));
+
+ IfFailGo( pMiniMdEmit->PutToken(TBL_GenericParamConstraint, GenericParamConstraintRec::COL_Constraint, pRecEmit, tkConstraint));
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyGenericParamConstraints()
+
+
+//*****************************************************************************
+// Verify GenericParams given a TypeDef
+//*****************************************************************************
+HRESULT NEWMERGER::VerifyGenericParams(
+ MergeImportData *pImportData,
+ mdToken tkImport,
+ mdToken tkEmit)
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ TOKENREC *pTokenRec;
+ MDTOKENMAP *pCurTkMap;
+ HENUMInternal hEnumImport; // Enumerator for import scope.
+ HENUMInternal hEnumEmit; // Enumerator for emit scope.
+ ULONG cImport, cEmit; // Count of import & emit records.
+ ULONG i; // Enumerating records in import scope.
+ ULONG iEmit; // Tracking records in emit scope.
+ mdGenericParam gpImport; // Import scope GenericParam token.
+ mdGenericParam gpEmit; // Emit scope GenericParam token.
+ GenericParamRec *pRecImport = NULL;
+ GenericParamRec *pRecEmit = NULL;
+ LPCSTR szNameImport; // Name of param in import scope.
+ LPCSTR szNameEmit; // Name of param in emit scope.
+
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pMiniMdEmit = GetMiniMdEmit();
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // Get enumerators for the input and output scopes.
+ IfFailGo(pMiniMdImport->FindGenericParamHelper(tkImport, &hEnumImport));
+ IfFailGo(pMiniMdEmit->FindGenericParamHelper(tkEmit, &hEnumEmit));
+
+ // The counts should be the same.
+ IfFailGo(HENUMInternal::GetCount(&hEnumImport, &cImport));
+ IfFailGo(HENUMInternal::GetCount(&hEnumEmit, &cEmit));
+
+ if (cImport != cEmit)
+ {
+ CheckContinuableErrorEx(META_E_GENERICPARAM_INCONSISTENT, pImportData, tkImport);
+ // If we are here, the linker says this error is OK.
+ }
+
+ for (i=iEmit=0; i<cImport; ++i)
+ {
+ // Get the import GenericParam record
+ IfFailGo(HENUMInternal::GetElement(&hEnumImport, i, &gpImport));
+ _ASSERTE(TypeFromToken(gpImport) == mdtGenericParam);
+ IfFailGo(pMiniMdImport->GetGenericParamRecord(RidFromToken(gpImport), &pRecImport));
+
+ // Find the emit record. If the import and emit scopes are ordered the same
+ // this is easy; otherwise go looking for it.
+ // Get the "next" emit record.
+ if (iEmit < cEmit)
+ {
+ IfFailGo(HENUMInternal::GetElement(&hEnumEmit, iEmit, &gpEmit));
+ _ASSERTE(TypeFromToken(gpEmit) == mdtGenericParam);
+ IfFailGo(pMiniMdEmit->GetGenericParamRecord(RidFromToken(gpEmit), &pRecEmit));
+ }
+
+ // If the import and emit sequence numbers don't match, go looking.
+ // Also, if we would have walked off end of array, go looking.
+ if (iEmit >= cEmit || pRecImport->GetNumber() != pRecEmit->GetNumber())
+ {
+ for (iEmit=0; iEmit<cEmit; ++iEmit)
+ {
+ IfFailGo( HENUMInternal::GetElement(&hEnumEmit, iEmit, &gpEmit));
+ _ASSERTE(TypeFromToken(gpEmit) == mdtGenericParam);
+ IfFailGo(pMiniMdEmit->GetGenericParamRecord(RidFromToken(gpEmit), &pRecEmit));
+
+ // The one we want?
+ if (pRecImport->GetNumber() == pRecEmit->GetNumber())
+ break;
+ }
+ if (iEmit >= cEmit)
+ goto Error; // Didn't find it
+ }
+
+ // Check that these "n'th" GenericParam records match.
+
+ // Flags.
+ if (pRecImport->GetFlags() != pRecEmit->GetFlags())
+ goto Error;
+
+ // Name.
+ IfFailGo(pMiniMdImport->getNameOfGenericParam(pRecImport, &szNameImport));
+ IfFailGo(pMiniMdEmit->getNameOfGenericParam(pRecEmit, &szNameEmit));
+ if (strcmp(szNameImport, szNameEmit) != 0)
+ goto Error;
+
+ // Verify any constraints.
+ gpImport = TokenFromRid(gpImport, mdtGenericParam);
+ gpEmit = TokenFromRid(gpEmit, mdtGenericParam);
+ hr = VerifyGenericParamConstraints(pImportData, gpImport, gpEmit);
+
+ if (SUCCEEDED(hr))
+ {
+ // record the token movement
+ IfFailGo( pCurTkMap->InsertNotFound(gpImport, true, gpEmit, &pTokenRec) );
+ }
+ else
+ {
+Error:
+ // inconsistent in GenericParams
+ hr = S_OK; // discard old error; new error will be returned from CheckContinuableError
+ CheckContinuableErrorEx(META_E_GENERICPARAM_INCONSISTENT, pImportData, tkImport);
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::VerifyGenericParams()
+
+
+//*****************************************************************************
+// Verify GenericParamConstraints given a GenericParam
+//*****************************************************************************
+HRESULT NEWMERGER::VerifyGenericParamConstraints(
+ MergeImportData *pImportData, // The import scope.
+ mdGenericParam gpImport, // Import GenericParam.
+ mdGenericParam gpEmit) // Emit GenericParam.
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ TOKENREC *pTokenRec;
+ HENUMInternal hEnumImport; // Enumerator for import scope.
+ HENUMInternal hEnumEmit; // Enumerator for emit scope.
+ ULONG cImport, cEmit; // Count of import & emit records.
+ ULONG i; // Enumerating records in import scope.
+ ULONG iEmit; // Tracking records in emit scope.
+ GenericParamConstraintRec *pRecImport = NULL;
+ GenericParamConstraintRec *pRecEmit = NULL;
+ MDTOKENMAP *pCurTkMap;
+ mdToken tkConstraintImport = mdTokenNil;
+ mdToken tkConstraintEmit = mdTokenNil;
+
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pMiniMdEmit = GetMiniMdEmit();
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // Get enumerators for the input and output scopes.
+ IfFailGo(pMiniMdImport->FindGenericParamConstraintHelper(gpImport, &hEnumImport));
+ IfFailGo(pMiniMdEmit->FindGenericParamConstraintHelper(gpEmit, &hEnumEmit));
+
+ // The counts should be the same.
+ IfFailGo(HENUMInternal::GetCount(&hEnumImport, &cImport));
+ IfFailGo(HENUMInternal::GetCount(&hEnumEmit, &cEmit));
+
+ if (cImport != cEmit)
+ IfFailGo(META_E_GENERICPARAM_INCONSISTENT); // Different numbers of constraints.
+
+ for (i=iEmit=0; i<cImport; ++i)
+ {
+ // Get the import GenericParam record
+ IfFailGo( HENUMInternal::GetElement(&hEnumImport, i, &gpImport));
+ _ASSERTE(TypeFromToken(gpImport) == mdtGenericParamConstraint);
+ IfFailGo(pMiniMdImport->GetGenericParamConstraintRecord(RidFromToken(gpImport), &pRecImport));
+
+ // Get the constraint.
+ tkConstraintImport = pMiniMdImport->getConstraintOfGenericParamConstraint(pRecImport);
+ if (pCurTkMap->Find(tkConstraintImport, &pTokenRec) == false)
+ {
+ // This should never fire unless the TypeDefs/Refs weren't merged
+ // before this code runs.
+ _ASSERTE(!"GenericParamConstraint Constraint not found in MERGER::VerifyGenericParamConstraints. Bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ tkConstraintImport = pTokenRec->m_tkTo;
+
+ // Find the emit record. If the import and emit scopes are ordered the same
+ // this is easy; otherwise go looking for it.
+ // Get the "next" emit record.
+ if (iEmit < cEmit)
+ {
+ IfFailGo( HENUMInternal::GetElement(&hEnumEmit, iEmit, &gpEmit));
+ _ASSERTE(TypeFromToken(gpEmit) == mdtGenericParamConstraint);
+ IfFailGo(pMiniMdEmit->GetGenericParamConstraintRecord(RidFromToken(gpEmit), &pRecEmit));
+ tkConstraintEmit = pMiniMdEmit->getConstraintOfGenericParamConstraint(pRecEmit);
+ }
+
+ // If the import and emit constraints don't match, go looking.
+ if (iEmit >= cEmit || tkConstraintEmit != tkConstraintImport)
+ {
+ for (iEmit=0; iEmit<cEmit; ++iEmit)
+ {
+ IfFailGo( HENUMInternal::GetElement(&hEnumEmit, iEmit, &gpEmit));
+ _ASSERTE(TypeFromToken(gpEmit) == mdtGenericParamConstraint);
+ IfFailGo(pMiniMdEmit->GetGenericParamConstraintRecord(RidFromToken(gpEmit), &pRecEmit));
+ tkConstraintEmit = pMiniMdEmit->getConstraintOfGenericParamConstraint(pRecEmit);
+
+ // The one we want?
+ if (tkConstraintEmit == tkConstraintImport)
+ break;
+ }
+ if (iEmit >= cEmit)
+ {
+ IfFailGo(META_E_GENERICPARAM_INCONSISTENT); // Didn't find the constraint
+ }
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::VerifyGenericParamConstraints()
+
+
+//*****************************************************************************
+// Verify Methods
+//*****************************************************************************
+HRESULT NEWMERGER::VerifyMethods(
+ MergeImportData *pImportData,
+ mdTypeDef tdImport,
+ mdTypeDef tdEmit)
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ MethodRec *pRecImp;
+ MethodRec *pRecEmit;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+
+ TypeDefRec *pTypeDefRec;
+ LPCUTF8 szName;
+ PCCOR_SIGNATURE pbSig;
+ ULONG cbSig;
+ ULONG cbEmit;
+ CQuickBytes qbSig;
+ TOKENREC *pTokenRec;
+ mdMethodDef mdImp;
+ mdMethodDef mdEmit;
+ MDTOKENMAP *pCurTkMap;
+
+ MergeTypeData *pMTD;
+ BOOL bSuppressMergeCheck;
+ ULONG cImport = 0; // count of non-merge check suppressed methods
+ mdCustomAttribute tkCA;
+
+ pMiniMdEmit = GetMiniMdEmit();
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // Get a count of records in the import scope; prepare to enumerate them.
+ IfFailGo(pMiniMdImport->GetTypeDefRecord(RidFromToken(tdImport), &pTypeDefRec));
+ ridStart = pMiniMdImport->getMethodListOfTypeDef(pTypeDefRec);
+ IfFailGo(pMiniMdImport->getEndMethodListOfTypeDef(RidFromToken(tdImport), &ridEnd));
+
+ pMTD = m_rMTDs.Get(RidFromToken(tdEmit));
+
+ // loop through all Methods of the TypeDef
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ IfFailGo(pMiniMdImport->GetMethodRid(i, (ULONG *)&mdImp));
+
+ // only verify those Methods that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsMethodMarked(TokenFromRid(mdImp, mdtMethodDef)) == false)
+ continue;
+
+ IfFailGo(pMiniMdImport->GetMethodRecord(mdImp, &pRecImp));
+
+ if (m_fDupCheck == FALSE && tdImport == pImportData->m_pRegMetaImport->m_tdModule) // TokenFromRid(1, mdtTypeDef))
+ {
+ // No dup check. This is the scenario that we only have one import scope. Just copy over the
+ // globals.
+ goto CopyMethodLabel;
+ }
+
+ IfFailGo(pMiniMdImport->getNameOfMethod(pRecImp, &szName));
+ IfFailGo(pMiniMdImport->getSignatureOfMethod(pRecImp, &pbSig, &cbSig));
+
+ mdImp = TokenFromRid(mdImp, mdtMethodDef);
+
+ if ( IsMdPrivateScope( pRecImp->GetFlags() ) )
+ {
+ // Trigger additive merge
+ goto CopyMethodLabel;
+ }
+
+ // convert rid contained in signature to new scope
+ IfFailGo(ImportHelper::MergeUpdateTokenInSig(
+ NULL, // Assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Import assembly scope information.
+ pMiniMdImport, // The scope to merge into the emit scope.
+ pbSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit)); // number of bytes write to cbEmit
+
+ hr = ImportHelper::FindMethod(
+ pMiniMdEmit,
+ tdEmit,
+ szName,
+ (const COR_SIGNATURE *)qbSig.Ptr(),
+ cbEmit,
+ &mdEmit);
+
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport,
+ mdImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (bSuppressMergeCheck || (tdImport == pImportData->m_pRegMetaImport->m_tdModule))
+ {
+ // global functions! Make sure that we move over the non-duplicate global function
+ // declaration
+ //
+ if (hr == S_OK)
+ {
+ // found the duplicate
+ IfFailGo( VerifyMethod(pImportData, mdImp, mdEmit) );
+ }
+ else
+ {
+CopyMethodLabel:
+ // not a duplicate! Copy over the
+ IfFailGo(pMiniMdEmit->AddMethodRecord(&pRecEmit, (RID *)&mdEmit));
+
+ // copy the method content over
+ IfFailGo( CopyMethod(pImportData, pRecImp, pRecEmit) );
+
+ IfFailGo( pMiniMdEmit->AddMethodToTypeDef(RidFromToken(tdEmit), mdEmit));
+
+ // record the token movement
+ mdEmit = TokenFromRid(mdEmit, mdtMethodDef);
+ IfFailGo( pMiniMdEmit->AddMemberDefToHash(
+ mdEmit,
+ tdEmit) );
+
+ mdImp = TokenFromRid(mdImp, mdtMethodDef);
+ IfFailGo( pCurTkMap->InsertNotFound(mdImp, false, mdEmit, &pTokenRec) );
+
+ // copy over the children
+ IfFailGo( CopyParams(pImportData, mdImp, mdEmit) );
+ IfFailGo( CopyGenericParams(pImportData, mdImp, mdEmit) );
+
+ }
+ }
+ else
+ {
+ if (hr == S_OK)
+ {
+ // Good! We are supposed to find a duplicate
+ IfFailGo( VerifyMethod(pImportData, mdImp, mdEmit) );
+ }
+ else
+ {
+ // Oops! The typedef is duplicated but the method is not!!
+ hr = S_OK; // discard old error; new error will be returned from CheckContinuableError
+ CheckContinuableErrorEx(META_E_METHD_NOT_FOUND, pImportData, mdImp);
+ }
+
+ cImport++;
+ }
+ }
+
+ // The counts should be the same, unless this is <module>
+ if (cImport != pMTD->m_cMethods && tdImport != pImportData->m_pRegMetaImport->m_tdModule)
+ {
+ CheckContinuableErrorEx(META_E_METHOD_COUNTS, pImportData, tdImport);
+ // If we are here, the linker says this error is OK.
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::VerifyMethods()
+
+
+//*****************************************************************************
+// verify a duplicated method
+//*****************************************************************************
+HRESULT NEWMERGER::VerifyMethod(
+ MergeImportData *pImportData,
+ mdMethodDef mdImp, // [IN] the emit record to fill
+ mdMethodDef mdEmit) // [IN] the record to import
+{
+ HRESULT hr;
+ MethodRec *pRecImp;
+ MethodRec *pRecEmit;
+ TOKENREC *pTokenRec;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ IfFailGo( pCurTkMap->InsertNotFound(mdImp, true, mdEmit, &pTokenRec) );
+
+ IfFailGo(pMiniMdImport->GetMethodRecord(RidFromToken(mdImp), &pRecImp));
+
+ // We need to make sure that the impl flags are propagated .
+ // Rules are: if the first method has miForwardRef flag set but the new method does not,
+ // we want to disable the miForwardRef flag. If the one found in the emit scope does not have
+ // miForwardRef set and the second one doesn't either, we want to make sure that the rest of
+ // impl flags are the same.
+ //
+ if ( !IsMiForwardRef( pRecImp->GetImplFlags() ) )
+ {
+ IfFailGo(pMiniMdEmit->GetMethodRecord(RidFromToken(mdEmit), &pRecEmit));
+ if (!IsMiForwardRef(pRecEmit->GetImplFlags()))
+ {
+ // make sure the rest of ImplFlags are the same
+ if (pRecEmit->GetImplFlags() != pRecImp->GetImplFlags())
+ {
+ // inconsistent in implflags
+ CheckContinuableErrorEx(META_E_METHDIMPL_INCONSISTENT, pImportData, mdImp);
+ }
+ }
+ else
+ {
+ // propagate the importing ImplFlags
+ pRecEmit->SetImplFlags(pRecImp->GetImplFlags());
+ }
+ }
+
+ // verify the children
+ IfFailGo( VerifyParams(pImportData, mdImp, mdEmit) );
+ IfFailGo( VerifyGenericParams(pImportData, mdImp, mdEmit) );
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::VerifyMethod()
+
+
+//*****************************************************************************
+// Verify Fields
+//*****************************************************************************
+HRESULT NEWMERGER::VerifyFields(
+ MergeImportData *pImportData,
+ mdTypeDef tdImport,
+ mdTypeDef tdEmit)
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ FieldRec *pRecImp;
+ FieldRec *pRecEmit;
+ mdFieldDef fdImp;
+ mdFieldDef fdEmit;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+
+ TypeDefRec *pTypeDefRec;
+ LPCUTF8 szName;
+ PCCOR_SIGNATURE pbSig;
+ ULONG cbSig;
+ ULONG cbEmit;
+ CQuickBytes qbSig;
+ TOKENREC *pTokenRec;
+ MDTOKENMAP *pCurTkMap;
+
+ MergeTypeData *pMTD;
+ BOOL bSuppressMergeCheck;
+ ULONG cImport = 0; // count of non-merge check suppressed fields
+ mdCustomAttribute tkCA;
+
+ pMiniMdEmit = GetMiniMdEmit();
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // Get a count of records in the import scope; prepare to enumerate them.
+ IfFailGo(pMiniMdImport->GetTypeDefRecord(RidFromToken(tdImport), &pTypeDefRec));
+ ridStart = pMiniMdImport->getFieldListOfTypeDef(pTypeDefRec);
+ IfFailGo(pMiniMdImport->getEndFieldListOfTypeDef(RidFromToken(tdImport), &ridEnd));
+
+ pMTD = m_rMTDs.Get(RidFromToken(tdEmit));
+
+ // loop through all fields of the TypeDef
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ IfFailGo(pMiniMdImport->GetFieldRid(i, (ULONG *)&fdImp));
+
+ // only verify those fields that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(TokenFromRid(fdImp, mdtFieldDef)) == false)
+ continue;
+
+ IfFailGo(pMiniMdImport->GetFieldRecord(fdImp, &pRecImp));
+
+ if (m_fDupCheck == FALSE && tdImport == pImportData->m_pRegMetaImport->m_tdModule)
+ {
+ // No dup check. This is the scenario that we only have one import scope. Just copy over the
+ // globals.
+ goto CopyFieldLabel;
+ }
+
+ IfFailGo(pMiniMdImport->getNameOfField(pRecImp, &szName));
+ IfFailGo(pMiniMdImport->getSignatureOfField(pRecImp, &pbSig, &cbSig));
+
+ if ( IsFdPrivateScope(pRecImp->GetFlags()))
+ {
+ // Trigger additive merge
+ fdImp = TokenFromRid(fdImp, mdtFieldDef);
+ goto CopyFieldLabel;
+ }
+
+ // convert rid contained in signature to new scope
+ IfFailGo(ImportHelper::MergeUpdateTokenInSig(
+ NULL, // Assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Import assembly scope information.
+ pMiniMdImport, // The scope to merge into the emit scope.
+ pbSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit)); // number of bytes write to cbEmit
+
+ hr = ImportHelper::FindField(
+ pMiniMdEmit,
+ tdEmit,
+ szName,
+ (const COR_SIGNATURE *)qbSig.Ptr(),
+ cbEmit,
+ &fdEmit);
+
+ fdImp = TokenFromRid(fdImp, mdtFieldDef);
+
+ bSuppressMergeCheck =
+ (IsFdStatic(pRecImp->GetFlags()) && pMTD->m_bSuppressMergeCheck) ||
+ ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport,
+ fdImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (bSuppressMergeCheck || (tdImport == pImportData->m_pRegMetaImport->m_tdModule))
+ {
+ // global data! Make sure that we move over the non-duplicate global function
+ // declaration
+ //
+ if (hr == S_OK)
+ {
+ // found the duplicate
+ IfFailGo( pCurTkMap->InsertNotFound(fdImp, true, fdEmit, &pTokenRec) );
+ }
+ else
+ {
+CopyFieldLabel:
+ // not a duplicate! Copy over the
+ IfFailGo(pMiniMdEmit->AddFieldRecord(&pRecEmit, (RID *)&fdEmit));
+
+ // copy the field record over
+ IfFailGo( CopyField(pImportData, pRecImp, pRecEmit) );
+
+ IfFailGo( pMiniMdEmit->AddFieldToTypeDef(RidFromToken(tdEmit), fdEmit));
+
+ // record the token movement
+ fdEmit = TokenFromRid(fdEmit, mdtFieldDef);
+ IfFailGo( pMiniMdEmit->AddMemberDefToHash(
+ fdEmit,
+ tdEmit) );
+
+ fdImp = TokenFromRid(fdImp, mdtFieldDef);
+ IfFailGo( pCurTkMap->InsertNotFound(fdImp, false, fdEmit, &pTokenRec) );
+ }
+ }
+ else
+ {
+ if (hr == S_OK)
+ {
+ // Good! We are supposed to find a duplicate
+ IfFailGo( pCurTkMap->InsertNotFound(fdImp, true, fdEmit, &pTokenRec) );
+ }
+ else
+ {
+ // Oops! The typedef is duplicated but the field is not!!
+ hr = S_OK; // discard old error; new error will be returned from CheckContinuableError
+ CheckContinuableErrorEx(META_E_FIELD_NOT_FOUND, pImportData, fdImp);
+ }
+
+ cImport++;
+ }
+ }
+
+ // The counts should be the same, unless this is <module>
+ if (cImport != pMTD->m_cFields && tdImport != pImportData->m_pRegMetaImport->m_tdModule)
+ {
+ CheckContinuableErrorEx(META_E_FIELD_COUNTS, pImportData, tdImport);
+ // If we are here, the linker says this error is OK.
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::VerifyFields()
+
+
+//*****************************************************************************
+// Verify Events
+//*****************************************************************************
+HRESULT NEWMERGER::VerifyEvents(
+ MergeImportData *pImportData,
+ mdTypeDef tdImport,
+ mdTypeDef tdEmit)
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ RID ridEventMap, ridEventMapEmit;
+ EventMapRec *pEventMapRec;
+ EventRec *pRecImport;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+ mdEvent evImport;
+ mdEvent evEmit;
+ TOKENREC *pTokenRec;
+ LPCUTF8 szName;
+ mdToken tkType;
+ MDTOKENMAP *pCurTkMap;
+
+ EventMapRec *pEventMapEmit;
+ EventRec *pRecEmit;
+ MergeTypeData *pMTD;
+ BOOL bSuppressMergeCheck;
+ ULONG cImport = 0; // count of non-merge check suppressed events
+ mdCustomAttribute tkCA;
+
+ pMiniMdEmit = GetMiniMdEmit();
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ IfFailGo(pMiniMdImport->FindEventMapFor(RidFromToken(tdImport), &ridEventMap));
+ if (!InvalidRid(ridEventMap))
+ {
+ // Get a count of records already in emit scope.
+ IfFailGo(pMiniMdEmit->FindEventMapFor(RidFromToken(tdEmit), &ridEventMapEmit));
+
+ if (InvalidRid(ridEventMapEmit)) {
+ // If there is any event, create the eventmap record in the emit scope
+ // Create new record.
+ IfFailGo(pMiniMdEmit->AddEventMapRecord(&pEventMapEmit, &ridEventMapEmit));
+
+ // Set parent.
+ IfFailGo(pMiniMdEmit->PutToken(TBL_EventMap, EventMapRec::COL_Parent, pEventMapEmit, tdEmit));
+ }
+
+ // Get a count of records in the import scope; prepare to enumerate them.
+ IfFailGo(pMiniMdImport->GetEventMapRecord(ridEventMap, &pEventMapRec));
+ ridStart = pMiniMdImport->getEventListOfEventMap(pEventMapRec);
+ IfFailGo(pMiniMdImport->getEndEventListOfEventMap(ridEventMap, &ridEnd));
+
+ pMTD = m_rMTDs.Get(RidFromToken(tdEmit));
+
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // get the property rid
+ IfFailGo(pMiniMdImport->GetEventRid(i, (ULONG *)&evImport));
+
+ // only verify those Events that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsEventMarked(TokenFromRid(evImport, mdtEvent)) == false)
+ continue;
+
+ IfFailGo(pMiniMdImport->GetEventRecord(evImport, &pRecImport));
+ IfFailGo(pMiniMdImport->getNameOfEvent(pRecImport, &szName));
+ tkType = pMiniMdImport->getEventTypeOfEvent( pRecImport );
+ IfFailGo( pCurTkMap->Remap(tkType, &tkType) );
+ evImport = TokenFromRid( evImport, mdtEvent);
+
+ hr = ImportHelper::FindEvent(
+ pMiniMdEmit,
+ tdEmit,
+ szName,
+ &evEmit);
+
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport,
+ evImport, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (bSuppressMergeCheck)
+ {
+
+ if (hr == S_OK )
+ {
+ // Good. We found the matching event when we have a duplicate typedef
+ IfFailGo( pCurTkMap->InsertNotFound(evImport, true, evEmit, &pTokenRec) );
+ }
+ else
+ {
+ // not a duplicate! Copy over the
+ IfFailGo(pMiniMdEmit->AddEventRecord(&pRecEmit, (RID *)&evEmit));
+
+ // copy the event record over
+ IfFailGo( CopyEvent(pImportData, pRecImport, pRecEmit) );
+
+ // Add Event to the EventMap.
+ IfFailGo( pMiniMdEmit->AddEventToEventMap(ridEventMapEmit, evEmit) );
+
+ // record the token movement
+ evEmit = TokenFromRid(evEmit, mdtEvent);
+
+ IfFailGo( pCurTkMap->InsertNotFound(evImport, false, evEmit, &pTokenRec) );
+
+ // copy over the method semantics
+ IfFailGo( CopyMethodSemantics(pImportData, evImport, evEmit) );
+ }
+ }
+ else
+ {
+ if (hr == S_OK )
+ {
+ // Good. We found the matching event when we have a duplicate typedef
+ IfFailGo( pCurTkMap->InsertNotFound(evImport, true, evEmit, &pTokenRec) );
+ }
+ else
+ {
+ // Oops! The typedef is duplicated but the event is not!!
+ hr = S_OK; // discard old error; new error will be returned from CheckContinuableError
+ CheckContinuableErrorEx(META_E_EVENT_NOT_FOUND, pImportData, evImport);
+
+ }
+
+ cImport++;
+ }
+ }
+
+ // The counts should be the same, unless this is <module>
+ if (cImport != pMTD->m_cEvents && tdImport != pImportData->m_pRegMetaImport->m_tdModule)
+ {
+ CheckContinuableErrorEx(META_E_EVENT_COUNTS, pImportData, tdImport);
+ // If we are here, the linker says this error is OK.
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::VerifyEvents()
+
+
+//*****************************************************************************
+// Verify Properties
+//*****************************************************************************
+HRESULT NEWMERGER::VerifyProperties(
+ MergeImportData *pImportData,
+ mdTypeDef tdImport,
+ mdTypeDef tdEmit)
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ RID ridPropertyMap, ridPropertyMapEmit;
+ PropertyMapRec *pPropertyMapRec;
+ PropertyRec *pRecImport;
+ ULONG ridStart;
+ ULONG ridEnd;
+ ULONG i;
+ mdProperty prImp;
+ mdProperty prEmit;
+ TOKENREC *pTokenRec;
+ LPCUTF8 szName;
+ PCCOR_SIGNATURE pbSig;
+ ULONG cbSig;
+ ULONG cbEmit;
+ CQuickBytes qbSig;
+ MDTOKENMAP *pCurTkMap;
+
+ PropertyMapRec *pPropertyMapEmit;
+ PropertyRec *pRecEmit;
+ MergeTypeData *pMTD;
+ BOOL bSuppressMergeCheck;
+ ULONG cImport = 0; // count of non-merge check suppressed properties
+ mdCustomAttribute tkCA;
+
+ pMiniMdEmit = GetMiniMdEmit();
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ IfFailGo(pMiniMdImport->FindPropertyMapFor(RidFromToken(tdImport), &ridPropertyMap));
+ if (!InvalidRid(ridPropertyMap))
+ {
+ // Get a count of records already in emit scope.
+ IfFailGo(pMiniMdEmit->FindPropertyMapFor(RidFromToken(tdEmit), &ridPropertyMapEmit));
+
+ if (InvalidRid(ridPropertyMapEmit))
+ {
+ // If there is any event, create the PropertyMap record in the emit scope
+ // Create new record.
+ IfFailGo(pMiniMdEmit->AddPropertyMapRecord(&pPropertyMapEmit, &ridPropertyMapEmit));
+
+ // Set parent.
+ IfFailGo(pMiniMdEmit->PutToken(TBL_PropertyMap, PropertyMapRec::COL_Parent, pPropertyMapEmit, tdEmit));
+ }
+
+ // Get a count of records in the import scope; prepare to enumerate them.
+ IfFailGo(pMiniMdImport->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec));
+ ridStart = pMiniMdImport->getPropertyListOfPropertyMap(pPropertyMapRec);
+ IfFailGo(pMiniMdImport->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd));
+
+ pMTD = m_rMTDs.Get(RidFromToken(tdEmit));
+
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // get the property rid
+ IfFailGo(pMiniMdImport->GetPropertyRid(i, (ULONG *)&prImp));
+
+ // only verify those Properties that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsPropertyMarked(TokenFromRid(prImp, mdtProperty)) == false)
+ continue;
+
+ IfFailGo(pMiniMdImport->GetPropertyRecord(prImp, &pRecImport));
+ IfFailGo(pMiniMdImport->getNameOfProperty(pRecImport, &szName));
+ IfFailGo(pMiniMdImport->getTypeOfProperty(pRecImport, &pbSig, &cbSig));
+ prImp = TokenFromRid( prImp, mdtProperty);
+
+ // convert rid contained in signature to new scope
+ IfFailGo( ImportHelper::MergeUpdateTokenInSig(
+ NULL, // Emit assembly.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Import assembly scope information.
+ pMiniMdImport, // The scope to merge into the emit scope.
+ pbSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit) ); // number of bytes write to cbEmit
+
+ hr = ImportHelper::FindProperty(
+ pMiniMdEmit,
+ tdEmit,
+ szName,
+ (PCCOR_SIGNATURE) qbSig.Ptr(),
+ cbEmit,
+ &prEmit);
+
+ bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck ||
+ ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) &&
+ (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport,
+ prImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA)));
+
+ if (bSuppressMergeCheck)
+ {
+ if (hr == S_OK)
+ {
+ // Good. We found the matching property when we have a duplicate typedef
+ IfFailGo( pCurTkMap->InsertNotFound(prImp, true, prEmit, &pTokenRec) );
+ }
+ else
+ {
+ IfFailGo(pMiniMdEmit->AddPropertyRecord(&pRecEmit, (RID *)&prEmit));
+
+ // copy the property record over
+ IfFailGo( CopyProperty(pImportData, pRecImport, pRecEmit) );
+
+ // Add Property to the PropertyMap.
+ IfFailGo( pMiniMdEmit->AddPropertyToPropertyMap(ridPropertyMapEmit, prEmit) );
+
+ // record the token movement
+ prEmit = TokenFromRid(prEmit, mdtProperty);
+
+ IfFailGo( pCurTkMap->InsertNotFound(prImp, false, prEmit, &pTokenRec) );
+
+ // copy over the method semantics
+ IfFailGo( CopyMethodSemantics(pImportData, prImp, prEmit) );
+ }
+ }
+ else
+ {
+ if (hr == S_OK)
+ {
+ // Good. We found the matching property when we have a duplicate typedef
+ IfFailGo( pCurTkMap->InsertNotFound(prImp, true, prEmit, &pTokenRec) );
+ }
+ else
+ {
+ hr = S_OK; // discard old error; new error will be returned from CheckContinuableError
+ CheckContinuableErrorEx(META_E_PROP_NOT_FOUND, pImportData, prImp);
+ }
+
+ cImport++;
+ }
+ }
+
+ // The counts should be the same, unless this is <module>
+ if (cImport != pMTD->m_cProperties && tdImport != pImportData->m_pRegMetaImport->m_tdModule)
+ {
+ CheckContinuableErrorEx(META_E_PROPERTY_COUNTS, pImportData, tdImport);
+ // If we are here, the linker says this error is OK.
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::VerifyProperties()
+
+
+//*****************************************************************************
+// Verify Parameters given a Method
+//*****************************************************************************
+HRESULT NEWMERGER::VerifyParams(
+ MergeImportData *pImportData,
+ mdMethodDef mdImport,
+ mdMethodDef mdEmit)
+{
+ HRESULT hr = NOERROR;
+ ParamRec *pRecImport = NULL;
+ ParamRec *pRecEmit = NULL;
+ MethodRec *pMethodRec;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG ridStart, ridEnd;
+ ULONG ridStartEmit, ridEndEmit;
+ ULONG cImport, cEmit;
+ ULONG i, j;
+ mdParamDef pdEmit = 0;
+ mdParamDef pdImp;
+ TOKENREC *pTokenRec;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // Get count of params in import scope; prepare to enumerate.
+ IfFailGo(pMiniMdImport->GetMethodRecord(RidFromToken(mdImport), &pMethodRec));
+ ridStart = pMiniMdImport->getParamListOfMethod(pMethodRec);
+ IfFailGo(pMiniMdImport->getEndParamListOfMethod(RidFromToken(mdImport), &ridEnd));
+ cImport = ridEnd - ridStart;
+
+ // Get count of params in emit scope; prepare to enumerate.
+ IfFailGo(pMiniMdEmit->GetMethodRecord(RidFromToken(mdEmit), &pMethodRec));
+ ridStartEmit = pMiniMdEmit->getParamListOfMethod(pMethodRec);
+ IfFailGo(pMiniMdEmit->getEndParamListOfMethod(RidFromToken(mdEmit), &ridEndEmit));
+ cEmit = ridEndEmit - ridStartEmit;
+
+ // The counts should be the same.
+ if (cImport != cEmit)
+ {
+ // That is, unless this is <module>, so get the method's parent.
+ mdTypeDef tdImport;
+ IfFailGo(pMiniMdImport->FindParentOfMethodHelper(mdImport, &tdImport));
+ if (tdImport != pImportData->m_pRegMetaImport->m_tdModule)
+ CheckContinuableErrorEx(META_E_PARAM_COUNTS, pImportData, mdImport);
+ // If we are here, the linker says this error is OK.
+ }
+
+ // loop through all Parameters
+ for (i = ridStart; i < ridEnd; i++)
+ {
+ // Get the importing param row
+ IfFailGo(pMiniMdImport->GetParamRid(i, (ULONG *)&pdImp));
+
+ // only verify those Params that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsParamMarked(TokenFromRid(pdImp, mdtParamDef)) == false)
+ continue;
+
+
+ IfFailGo(pMiniMdImport->GetParamRecord(pdImp, &pRecImport));
+ pdImp = TokenFromRid(pdImp, mdtParamDef);
+
+ // It turns out when we merge a typelib with itself, the emit and import scope
+ // has different sequence of parameter
+ //
+ // find the corresponding emit param row
+ for (j = ridStartEmit; j < ridEndEmit; j++)
+ {
+ IfFailGo(pMiniMdEmit->GetParamRid(j, (ULONG *)&pdEmit));
+ IfFailGo(pMiniMdEmit->GetParamRecord(pdEmit, &pRecEmit));
+ if (pRecEmit->GetSequence() == pRecImport->GetSequence())
+ break;
+ }
+
+ if (j == ridEndEmit)
+ {
+ // did not find the corresponding parameter in the emiting scope
+ hr = S_OK; // discard old error; new error will be returned from CheckContinuableError
+ CheckContinuableErrorEx(META_S_PARAM_MISMATCH, pImportData, pdImp);
+ }
+
+ else
+ {
+ _ASSERTE( pRecEmit->GetSequence() == pRecImport->GetSequence() );
+
+ pdEmit = TokenFromRid(pdEmit, mdtParamDef);
+
+ // record the token movement
+#ifdef WE_DONT_NEED_TO_CHECK_NAMES__THEY_DONT_AFFECT_ANYTHING
+ LPCUTF8 szNameImp;
+ LPCUTF8 szNameEmit;
+ IfFailGo(pMiniMdImport->getNameOfParam(pRecImport, &szNameImp));
+ IfFailGo(pMiniMdEmit->getNameOfParam(pRecEmit, &szNameEmit));
+ if (szNameImp && szNameEmit && strcmp(szNameImp, szNameEmit) != 0)
+ {
+ // parameter name doesn't match
+ CheckContinuableErrorEx(META_S_PARAM_MISMATCH, pImportData, pdImp);
+ }
+#endif
+ if (pRecEmit->GetFlags() != pRecImport->GetFlags())
+ {
+ // flags doesn't match
+ CheckContinuableErrorEx(META_S_PARAM_MISMATCH, pImportData, pdImp);
+ }
+
+ // record token movement. This is a duplicate.
+ IfFailGo( pCurTkMap->InsertNotFound(pdImp, true, pdEmit, &pTokenRec) );
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::VerifyParams()
+
+
+//*****************************************************************************
+// merging MemberRef
+//*****************************************************************************
+HRESULT NEWMERGER::MergeMemberRefs( )
+{
+ HRESULT hr = NOERROR;
+ MemberRefRec *pRecImport = NULL;
+ MemberRefRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdMemberRef mrEmit;
+ mdMemberRef mrImp;
+ bool bDuplicate = false;
+ TOKENREC *pTokenRec;
+ mdToken tkParentImp;
+ mdToken tkParentEmit;
+
+ LPCUTF8 szNameImp;
+ PCCOR_SIGNATURE pbSig;
+ ULONG cbSig;
+ ULONG cbEmit;
+ CQuickBytes qbSig;
+
+ bool isRefOptimizedToDef;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ iCount = pMiniMdImport->getCountMemberRefs();
+
+ // loop through all MemberRef
+ for (i = 1; i <= iCount; i++)
+ {
+
+ // only merge those MemberRefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsMemberRefMarked(TokenFromRid(i, mdtMemberRef)) == false)
+ continue;
+
+ isRefOptimizedToDef = false;
+
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetMemberRefRecord(i, &pRecImport));
+ IfFailGo(pMiniMdImport->getNameOfMemberRef(pRecImport, &szNameImp));
+ IfFailGo(pMiniMdImport->getSignatureOfMemberRef(pRecImport, &pbSig, &cbSig));
+ tkParentImp = pMiniMdImport->getClassOfMemberRef(pRecImport);
+
+ IfFailGo( pCurTkMap->Remap(tkParentImp, &tkParentEmit) );
+
+ // convert rid contained in signature to new scope
+ IfFailGo(ImportHelper::MergeUpdateTokenInSig(
+ NULL, // Assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Import assembly information.
+ pMiniMdImport, // The scope to merge into the emit scope.
+ pbSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit)); // number of bytes write to cbEmit
+
+ // We want to know if we can optimize this MemberRef to a FieldDef or MethodDef
+ if (TypeFromToken(tkParentEmit) == mdtTypeDef && RidFromToken(tkParentEmit) != 0)
+ {
+ // The parent of this MemberRef has been successfully optimized to a TypeDef. Then this MemberRef should be
+ // be able to optimized to a MethodDef or FieldDef unless one of the parent in the inheritance hierachy
+ // is through TypeRef. Then this MemberRef stay as MemberRef. If This is a VarArg calling convention, then
+ // we will remap the MemberRef's parent to a MethodDef or stay as TypeRef.
+ //
+ mdToken tkParent = tkParentEmit;
+ mdToken tkMethDefOrFieldDef;
+ PCCOR_SIGNATURE pbSigTmp = (const COR_SIGNATURE *) qbSig.Ptr();
+
+ while (TypeFromToken(tkParent) == mdtTypeDef && RidFromToken(tkParent) != 0)
+ {
+ TypeDefRec *pRec;
+ hr = ImportHelper::FindMember(pMiniMdEmit, tkParent, szNameImp, pbSigTmp, cbEmit, &tkMethDefOrFieldDef);
+ if (hr == S_OK)
+ {
+ // We have found a match!!
+ if (isCallConv(CorSigUncompressCallingConv(pbSigTmp), IMAGE_CEE_CS_CALLCONV_VARARG))
+ {
+ // The found MethodDef token will replace this MemberRef's parent token
+ _ASSERTE(TypeFromToken(tkMethDefOrFieldDef) == mdtMethodDef);
+ tkParentEmit = tkMethDefOrFieldDef;
+ break;
+ }
+ else
+ {
+ // The found MethodDef/FieldDef token will replace this MemberRef token and we won't introduce a MemberRef
+ // record.
+ //
+ mrEmit = tkMethDefOrFieldDef;
+ isRefOptimizedToDef = true;
+ bDuplicate = true;
+ break;
+ }
+ }
+
+ // now walk up to the parent class of tkParent and try to resolve this MemberRef
+ IfFailGo(pMiniMdEmit->GetTypeDefRecord(RidFromToken(tkParent), &pRec));
+ tkParent = pMiniMdEmit->getExtendsOfTypeDef(pRec);
+ }
+
+ // When we exit the loop, there are several possibilities:
+ // 1. We found a MethodDef/FieldDef to replace the MemberRef
+ // 2. We found a MethodDef matches the MemberRef but the MemberRef is VarArg, thus we want to use the MethodDef in the
+ // parent column but not replacing it.
+ // 3. We exit because we run out the TypeDef on the parent chain. If it is because we encounter a TypeRef, this TypeRef will
+ // replace the parent column of the MemberRef. Or we encounter nil token! (This can be unresolved global MemberRef or
+ // compiler error to put an undefined MemberRef. In this case, we should just use the old tkParentEmit
+ // on the parent column for the MemberRef.
+
+ if (TypeFromToken(tkParent) == mdtTypeRef && RidFromToken(tkParent) != 0)
+ {
+ // we had walked up the parent's chain to resolve it but we have not been successful and got stopped by a TypeRef.
+ // Then we will use this TypeRef as the parent of the emit MemberRef record
+ //
+ tkParentEmit = tkParent;
+ }
+ }
+ else if ((TypeFromToken(tkParentEmit) == mdtMethodDef &&
+ !isCallConv(CorSigUncompressCallingConv(pbSig), IMAGE_CEE_CS_CALLCONV_VARARG)) ||
+ (TypeFromToken(tkParentEmit) == mdtFieldDef))
+ {
+ // If the MemberRef's parent is already a non-vararg MethodDef or FieldDef, we can also
+ // safely drop the MemberRef
+ mrEmit = tkParentEmit;
+ isRefOptimizedToDef = true;
+ bDuplicate = true;
+ }
+
+ // If the Ref cannot be optimized to a Def or MemberRef to Def optmization is turned off, do the following.
+ if (isRefOptimizedToDef == false || !((m_optimizeRefToDef & MDMemberRefToDef) == MDMemberRefToDef))
+ {
+ // does this MemberRef already exist in the emit scope?
+ if ( m_fDupCheck && ImportHelper::FindMemberRef(
+ pMiniMdEmit,
+ tkParentEmit,
+ szNameImp,
+ (const COR_SIGNATURE *) qbSig.Ptr(),
+ cbEmit,
+ &mrEmit) == S_OK )
+ {
+ // Yes, it does
+ bDuplicate = true;
+ }
+ else
+ {
+ // No, it doesn't. Copy it over.
+ bDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddMemberRefRecord(&pRecEmit, (RID *)&mrEmit));
+ mrEmit = TokenFromRid( mrEmit, mdtMemberRef );
+
+ // Copy over the MemberRef context
+ IfFailGo(pMiniMdEmit->PutString(TBL_MemberRef, MemberRefRec::COL_Name, pRecEmit, szNameImp));
+ IfFailGo(pMiniMdEmit->PutToken(TBL_MemberRef, MemberRefRec::COL_Class, pRecEmit, tkParentEmit));
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_MemberRef, MemberRefRec::COL_Signature, pRecEmit,
+ qbSig.Ptr(), cbEmit));
+ IfFailGo(pMiniMdEmit->AddMemberRefToHash(mrEmit) );
+ }
+ }
+ // record the token movement
+ mrImp = TokenFromRid(i, mdtMemberRef);
+ IfFailGo( pCurTkMap->InsertNotFound(mrImp, bDuplicate, mrEmit, &pTokenRec) );
+ }
+ }
+
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeMemberRefs()
+
+
+//*****************************************************************************
+// merge interface impl
+//*****************************************************************************
+HRESULT NEWMERGER::MergeInterfaceImpls( )
+{
+ HRESULT hr = NOERROR;
+ InterfaceImplRec *pRecImport = NULL;
+ InterfaceImplRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdTypeDef tkParent;
+ mdInterfaceImpl iiEmit;
+ bool bDuplicate;
+ TOKENREC *pTokenRec;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountInterfaceImpls();
+
+ // loop through all InterfaceImpl
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those InterfaceImpls that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsInterfaceImplMarked(TokenFromRid(i, mdtInterfaceImpl)) == false)
+ continue;
+
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetInterfaceImplRecord(i, &pRecImport));
+ tkParent = pMiniMdImport->getClassOfInterfaceImpl(pRecImport);
+
+ // does this TypeRef already exist in the emit scope?
+ if ( pCurTkMap->Find(tkParent, &pTokenRec) )
+ {
+ if ( pTokenRec->m_isDuplicate )
+ {
+ // parent in the emit scope
+ mdToken tkParentEmit;
+ mdToken tkInterface;
+
+ // remap the typedef token
+ tkParentEmit = pTokenRec->m_tkTo;
+
+ // remap the implemented interface token
+ tkInterface = pMiniMdImport->getInterfaceOfInterfaceImpl(pRecImport);
+ IfFailGo( pCurTkMap->Remap( tkInterface, &tkInterface) );
+
+ // Set duplicate flag
+ bDuplicate = true;
+
+ // find the corresponding interfaceimpl in the emit scope
+ if ( ImportHelper::FindInterfaceImpl(pMiniMdEmit, tkParentEmit, tkInterface, &iiEmit) != S_OK )
+ {
+ // bad state!! We have a duplicate typedef but the interface impl is not the same!!
+
+ // continuable error
+ CheckContinuableErrorEx(
+ META_E_INTFCEIMPL_NOT_FOUND,
+ pImportData,
+ TokenFromRid(i, mdtInterfaceImpl));
+
+ iiEmit = mdTokenNil;
+ }
+ }
+ else
+ {
+ // No, it doesn't. Copy it over.
+ bDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddInterfaceImplRecord(&pRecEmit, (RID *)&iiEmit));
+
+ // copy the interfaceimp record over
+ IfFailGo( CopyInterfaceImpl( pRecEmit, pImportData, pRecImport) );
+ }
+ }
+ else
+ {
+ _ASSERTE( !"bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+
+ // record the token movement
+ IfFailGo( pCurTkMap->InsertNotFound(
+ TokenFromRid(i, mdtInterfaceImpl),
+ bDuplicate,
+ TokenFromRid( iiEmit, mdtInterfaceImpl ),
+ &pTokenRec) );
+ }
+ }
+
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeInterfaceImpls()
+
+
+//*****************************************************************************
+// merge all of the constant for field, property, and parameter
+//*****************************************************************************
+HRESULT NEWMERGER::MergeConstants()
+{
+ HRESULT hr = NOERROR;
+ ConstantRec *pRecImport = NULL;
+ ConstantRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ ULONG csEmit; // constant value is not a token
+ mdToken tkParentImp;
+ TOKENREC *pTokenRec;
+ void const *pValue;
+ ULONG cbBlob;
+#if _DEBUG
+ ULONG typeParent;
+#endif // _DEBUG
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountConstants();
+
+ // loop through all Constants
+ for (i = 1; i <= iCount; i++)
+ {
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetConstantRecord(i, &pRecImport));
+ tkParentImp = pMiniMdImport->getParentOfConstant(pRecImport);
+
+ // only move those constant over if their parents are marked
+ // If MDTOKENMAP::Find returns false, we don't need to copy the constant value over
+ if ( pCurTkMap->Find(tkParentImp, &pTokenRec) )
+ {
+ // If the parent is duplicated, no need to move over the constant value
+ if ( !pTokenRec->m_isDuplicate )
+ {
+ IfFailGo(pMiniMdEmit->AddConstantRecord(&pRecEmit, &csEmit));
+ pRecEmit->SetType(pRecImport->GetType());
+
+ // set the parent
+ IfFailGo( pMiniMdEmit->PutToken(TBL_Constant, ConstantRec::COL_Parent, pRecEmit, pTokenRec->m_tkTo) );
+
+ // move over the constant blob value
+ IfFailGo(pMiniMdImport->getValueOfConstant(pRecImport, (const BYTE **)&pValue, &cbBlob));
+ IfFailGo( pMiniMdEmit->PutBlob(TBL_Constant, ConstantRec::COL_Value, pRecEmit, pValue, cbBlob) );
+ IfFailGo( pMiniMdEmit->AddConstantToHash(csEmit) );
+ }
+ else
+ {
+ // <TODO>@FUTURE: more verification on the duplicate??</TODO>
+ }
+ }
+#if _DEBUG
+ // Include this block only under Debug build. The reason is that
+ // the linker chooses all the errors that we report (such as unmatched MethodDef or FieldDef)
+ // as a continuable error. It is likely to hit this else while the tkparentImp is marked if there
+ // is any error reported earlier!!
+ else
+ {
+ typeParent = TypeFromToken(tkParentImp);
+ if (typeParent == mdtFieldDef)
+ {
+ // FieldDef should not be marked.
+ if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(tkParentImp) == false)
+ continue;
+ }
+ else if (typeParent == mdtParamDef)
+ {
+ // ParamDef should not be marked.
+ if ( pMiniMdImport->GetFilterTable()->IsParamMarked(tkParentImp) == false)
+ continue;
+ }
+ else
+ {
+ _ASSERTE(typeParent == mdtProperty);
+ // Property should not be marked.
+ if ( pMiniMdImport->GetFilterTable()->IsPropertyMarked(tkParentImp) == false)
+ continue;
+ }
+
+ // If we come to here, we have a constant whose parent is marked but we could not
+ // find it in the map!! Bad state.
+
+ _ASSERTE(!"Ignore this error if you have seen error reported earlier! Otherwise bad token map or bad metadata!");
+ }
+#endif // _DEBUG
+ // Note that we don't need to record the token movement since constant is not a valid token kind.
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeConstants()
+
+
+//*****************************************************************************
+// Merge field marshal information
+//*****************************************************************************
+HRESULT NEWMERGER::MergeFieldMarshals()
+{
+ HRESULT hr = NOERROR;
+ FieldMarshalRec *pRecImport = NULL;
+ FieldMarshalRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ ULONG fmEmit; // FieldMarhsal is not a token
+ mdToken tkParentImp;
+ TOKENREC *pTokenRec;
+ void const *pValue;
+ ULONG cbBlob;
+#if _DEBUG
+ ULONG typeParent;
+#endif // _DEBUG
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountFieldMarshals();
+
+ // loop through all TypeRef
+ for (i = 1; i <= iCount; i++)
+ {
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetFieldMarshalRecord(i, &pRecImport));
+ tkParentImp = pMiniMdImport->getParentOfFieldMarshal(pRecImport);
+
+ // We want to merge only those field marshals that parents are marked.
+ // Find will return false if the parent is not marked
+ //
+ if ( pCurTkMap->Find(tkParentImp, &pTokenRec) )
+ {
+ // If the parent is duplicated, no need to move over the constant value
+ if ( !pTokenRec->m_isDuplicate )
+ {
+ IfFailGo(pMiniMdEmit->AddFieldMarshalRecord(&pRecEmit, &fmEmit));
+
+ // set the parent
+ IfFailGo( pMiniMdEmit->PutToken(
+ TBL_FieldMarshal,
+ FieldMarshalRec::COL_Parent,
+ pRecEmit,
+ pTokenRec->m_tkTo) );
+
+ // move over the constant blob value
+ IfFailGo(pMiniMdImport->getNativeTypeOfFieldMarshal(pRecImport, (const BYTE **)&pValue, &cbBlob));
+ IfFailGo( pMiniMdEmit->PutBlob(TBL_FieldMarshal, FieldMarshalRec::COL_NativeType, pRecEmit, pValue, cbBlob) );
+ IfFailGo( pMiniMdEmit->AddFieldMarshalToHash(fmEmit) );
+
+ }
+ else
+ {
+ // <TODO>@FUTURE: more verification on the duplicate??</TODO>
+ }
+ }
+#if _DEBUG
+ else
+ {
+ typeParent = TypeFromToken(tkParentImp);
+
+ if (typeParent == mdtFieldDef)
+ {
+ // FieldDefs should not be marked
+ if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(tkParentImp) == false)
+ continue;
+ }
+ else
+ {
+ _ASSERTE(typeParent == mdtParamDef);
+ // ParamDefs should not be marked
+ if ( pMiniMdImport->GetFilterTable()->IsParamMarked(tkParentImp) == false)
+ continue;
+ }
+
+ // If we come to here, that is we have a FieldMarshal whose parent is marked and we don't find it
+ // in the map!!!
+
+ // either bad lookup map or bad metadata
+ _ASSERTE(!"Ignore this assert if you have seen error reported earlier. Otherwise, it is bad state!");
+ }
+#endif // _DEBUG
+ }
+ // Note that we don't need to record the token movement since FieldMarshal is not a valid token kind.
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeFieldMarshals()
+
+
+//*****************************************************************************
+// Merge class layout information
+//*****************************************************************************
+HRESULT NEWMERGER::MergeClassLayouts()
+{
+ HRESULT hr = NOERROR;
+ ClassLayoutRec *pRecImport = NULL;
+ ClassLayoutRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ ULONG iRecord; // class layout is not a token
+ mdToken tkParentImp;
+ TOKENREC *pTokenRec;
+ RID ridClassLayout;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountClassLayouts();
+
+ // loop through all TypeRef
+ for (i = 1; i <= iCount; i++)
+ {
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetClassLayoutRecord(i, &pRecImport));
+ tkParentImp = pMiniMdImport->getParentOfClassLayout(pRecImport);
+
+ // only merge those TypeDefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsTypeDefMarked(tkParentImp) == false)
+ continue;
+
+ if ( pCurTkMap->Find(tkParentImp, &pTokenRec) )
+ {
+ if ( !pTokenRec->m_isDuplicate )
+ {
+ // If the parent is not duplicated, just copy over the classlayout information
+ IfFailGo(pMiniMdEmit->AddClassLayoutRecord(&pRecEmit, &iRecord));
+
+ // copy over the fix part information
+ pRecEmit->Copy(pRecImport);
+ IfFailGo( pMiniMdEmit->PutToken(TBL_ClassLayout, ClassLayoutRec::COL_Parent, pRecEmit, pTokenRec->m_tkTo));
+ IfFailGo( pMiniMdEmit->AddClassLayoutToHash(iRecord) );
+ }
+ else
+ {
+
+ IfFailGo(pMiniMdEmit->FindClassLayoutHelper(pTokenRec->m_tkTo, &ridClassLayout));
+
+ if (InvalidRid(ridClassLayout))
+ {
+ // class is duplicated but not class layout info
+ CheckContinuableErrorEx(META_E_CLASS_LAYOUT_INCONSISTENT, pImportData, tkParentImp);
+ }
+ else
+ {
+ IfFailGo(pMiniMdEmit->GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRecEmit));
+ if (pMiniMdImport->getPackingSizeOfClassLayout(pRecImport) != pMiniMdEmit->getPackingSizeOfClassLayout(pRecEmit) ||
+ pMiniMdImport->getClassSizeOfClassLayout(pRecImport) != pMiniMdEmit->getClassSizeOfClassLayout(pRecEmit) )
+ {
+ CheckContinuableErrorEx(META_E_CLASS_LAYOUT_INCONSISTENT, pImportData, tkParentImp);
+ }
+ }
+ }
+ }
+ else
+ {
+ // bad lookup map
+ _ASSERTE( !"bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ // no need to record the index movement. Classlayout is not a token.
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeClassLayouts()
+
+//*****************************************************************************
+// Merge field layout information
+//*****************************************************************************
+HRESULT NEWMERGER::MergeFieldLayouts()
+{
+ HRESULT hr = NOERROR;
+ FieldLayoutRec *pRecImport = NULL;
+ FieldLayoutRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ ULONG iRecord; // field layout2 is not a token.
+ mdToken tkFieldImp;
+ TOKENREC *pTokenRec;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountFieldLayouts();
+
+ // loop through all FieldLayout records.
+ for (i = 1; i <= iCount; i++)
+ {
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetFieldLayoutRecord(i, &pRecImport));
+ tkFieldImp = pMiniMdImport->getFieldOfFieldLayout(pRecImport);
+
+ // only merge those FieldDefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(tkFieldImp) == false)
+ continue;
+
+ if ( pCurTkMap->Find(tkFieldImp, &pTokenRec) )
+ {
+ if ( !pTokenRec->m_isDuplicate )
+ {
+ // If the Field is not duplicated, just copy over the FieldLayout information
+ IfFailGo(pMiniMdEmit->AddFieldLayoutRecord(&pRecEmit, &iRecord));
+
+ // copy over the fix part information
+ pRecEmit->Copy(pRecImport);
+ IfFailGo( pMiniMdEmit->PutToken(TBL_FieldLayout, FieldLayoutRec::COL_Field, pRecEmit, pTokenRec->m_tkTo));
+ IfFailGo( pMiniMdEmit->AddFieldLayoutToHash(iRecord) );
+ }
+ else
+ {
+ // <TODO>@FUTURE: more verification??</TODO>
+ }
+ }
+ else
+ {
+ // bad lookup map
+ _ASSERTE( !"bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ // no need to record the index movement. fieldlayout2 is not a token.
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeFieldLayouts()
+
+
+//*****************************************************************************
+// Merge field RVAs
+//*****************************************************************************
+HRESULT NEWMERGER::MergeFieldRVAs()
+{
+ HRESULT hr = NOERROR;
+ FieldRVARec *pRecImport = NULL;
+ FieldRVARec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ ULONG iRecord; // FieldRVA is not a token.
+ mdToken tkFieldImp;
+ TOKENREC *pTokenRec;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountFieldRVAs();
+
+ // loop through all FieldRVA records.
+ for (i = 1; i <= iCount; i++)
+ {
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetFieldRVARecord(i, &pRecImport));
+ tkFieldImp = pMiniMdImport->getFieldOfFieldRVA(pRecImport);
+
+ // only merge those FieldDefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(TokenFromRid(tkFieldImp, mdtFieldDef)) == false)
+ continue;
+
+ if ( pCurTkMap->Find(tkFieldImp, &pTokenRec) )
+ {
+ if ( !pTokenRec->m_isDuplicate )
+ {
+ // If the Field is not duplicated, just copy over the FieldRVA information
+ IfFailGo(pMiniMdEmit->AddFieldRVARecord(&pRecEmit, &iRecord));
+
+ // copy over the fix part information
+ pRecEmit->Copy(pRecImport);
+ IfFailGo( pMiniMdEmit->PutToken(TBL_FieldRVA, FieldRVARec::COL_Field, pRecEmit, pTokenRec->m_tkTo));
+ IfFailGo( pMiniMdEmit->AddFieldRVAToHash(iRecord) );
+ }
+ else
+ {
+ // <TODO>@FUTURE: more verification??</TODO>
+ }
+ }
+ else
+ {
+ // bad lookup map
+ _ASSERTE( !"bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ // no need to record the index movement. FieldRVA is not a token.
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeFieldRVAs()
+
+
+//*****************************************************************************
+// Merge MethodImpl information
+//*****************************************************************************
+HRESULT NEWMERGER::MergeMethodImpls()
+{
+ HRESULT hr = NOERROR;
+ MethodImplRec *pRecImport = NULL;
+ MethodImplRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ RID iRecord;
+ mdTypeDef tkClassImp;
+ mdToken tkBodyImp;
+ mdToken tkDeclImp;
+ TOKENREC *pTokenRecClass;
+ mdToken tkBodyEmit;
+ mdToken tkDeclEmit;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountMethodImpls();
+
+ // loop through all the MethodImpls.
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those MethodImpls that are marked.
+ if ( pMiniMdImport->GetFilterTable()->IsMethodImplMarked(i) == false)
+ continue;
+
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetMethodImplRecord(i, &pRecImport));
+ tkClassImp = pMiniMdImport->getClassOfMethodImpl(pRecImport);
+ tkBodyImp = pMiniMdImport->getMethodBodyOfMethodImpl(pRecImport);
+ tkDeclImp = pMiniMdImport->getMethodDeclarationOfMethodImpl(pRecImport);
+
+ if ( pCurTkMap->Find(tkClassImp, &pTokenRecClass))
+ {
+ // If the TypeDef is duplicated, no need to move over the MethodImpl record.
+ if ( !pTokenRecClass->m_isDuplicate )
+ {
+ // Create a new record and set the data.
+
+ // <TODO>@FUTURE: We might want to consider changing the error for the remap into a continuable error.
+ // Because we probably can continue merging for more data...</TODO>
+
+ IfFailGo( pCurTkMap->Remap(tkBodyImp, &tkBodyEmit) );
+ IfFailGo( pCurTkMap->Remap(tkDeclImp, &tkDeclEmit) );
+ IfFailGo(pMiniMdEmit->AddMethodImplRecord(&pRecEmit, &iRecord));
+ IfFailGo( pMiniMdEmit->PutToken(TBL_MethodImpl, MethodImplRec::COL_Class, pRecEmit, pTokenRecClass->m_tkTo) );
+ IfFailGo( pMiniMdEmit->PutToken(TBL_MethodImpl, MethodImplRec::COL_MethodBody, pRecEmit, tkBodyEmit) );
+ IfFailGo( pMiniMdEmit->PutToken(TBL_MethodImpl, MethodImplRec::COL_MethodDeclaration, pRecEmit, tkDeclEmit) );
+ IfFailGo( pMiniMdEmit->AddMethodImplToHash(iRecord) );
+ }
+ else
+ {
+ // <TODO>@FUTURE: more verification on the duplicate??</TODO>
+ }
+ // No need to record the token movement, MethodImpl is not a token.
+ }
+ else
+ {
+ // either bad lookup map or bad metadata
+ _ASSERTE(!"bad state");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeMethodImpls()
+
+
+//*****************************************************************************
+// Merge PInvoke
+//*****************************************************************************
+HRESULT NEWMERGER::MergePinvoke()
+{
+ HRESULT hr = NOERROR;
+ ImplMapRec *pRecImport = NULL;
+ ImplMapRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdModuleRef mrImp;
+ mdModuleRef mrEmit;
+ mdMethodDef mdImp;
+ RID mdImplMap;
+ TOKENREC *pTokenRecMR;
+ TOKENREC *pTokenRecMD;
+
+ USHORT usMappingFlags;
+ LPCUTF8 szImportName;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountImplMaps();
+
+ // loop through all ImplMaps
+ for (i = 1; i <= iCount; i++)
+ {
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetImplMapRecord(i, &pRecImport));
+
+ // Get the MethodDef token in the new space.
+ mdImp = pMiniMdImport->getMemberForwardedOfImplMap(pRecImport);
+
+ // only merge those MethodDefs that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsMethodMarked(mdImp) == false)
+ continue;
+
+ // Get the ModuleRef token in the new space.
+ mrImp = pMiniMdImport->getImportScopeOfImplMap(pRecImport);
+
+ // map the token to the new scope
+ if (pCurTkMap->Find(mrImp, &pTokenRecMR) == false)
+ {
+ // This should never fire unless the module refs weren't merged
+ // before this code ran.
+ _ASSERTE(!"Parent ModuleRef not found in MERGER::MergePinvoke. Bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+
+ // If the ModuleRef has been remapped to the "module token", we need to undo that
+ // for the pinvokeimpl. A pinvoke can only have a ModuleRef for the ImportScope.
+ mrEmit = pTokenRecMR->m_tkTo;
+ if (mrEmit == MODULEDEFTOKEN)
+ { // Yes, the ModuleRef has been remapped to the module token. So,
+ // find the ModuleRef in the output scope; if it is not found, add
+ // it.
+ ModuleRefRec *pModRefImport;
+ LPCUTF8 szNameImp;
+ IfFailGo(pMiniMdImport->GetModuleRefRecord(RidFromToken(mrImp), &pModRefImport));
+ IfFailGo(pMiniMdImport->getNameOfModuleRef(pModRefImport, &szNameImp));
+
+ // does this ModuleRef already exist in the emit scope?
+ hr = ImportHelper::FindModuleRef(pMiniMdEmit,
+ szNameImp,
+ &mrEmit);
+
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ { // No, it doesn't. Copy it over.
+ ModuleRefRec *pModRefEmit;
+ IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pModRefEmit, (RID*)&mrEmit));
+ mrEmit = TokenFromRid(mrEmit, mdtModuleRef);
+
+ // Set ModuleRef Name.
+ IfFailGo( pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, pModRefEmit, szNameImp) );
+ }
+ else
+ IfFailGo(hr);
+ }
+
+
+ if (pCurTkMap->Find(mdImp, &pTokenRecMD) == false)
+ {
+ // This should never fire unless the method defs weren't merged
+ // before this code ran.
+ _ASSERTE(!"Parent MethodDef not found in MERGER::MergePinvoke. Bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+
+
+ // Get copy of rest of data.
+ usMappingFlags = pMiniMdImport->getMappingFlagsOfImplMap(pRecImport);
+ IfFailGo(pMiniMdImport->getImportNameOfImplMap(pRecImport, &szImportName));
+
+ // If the method associated with PInvokeMap is not duplicated, then don't bother to look up the
+ // duplicated PInvokeMap information.
+ if (pTokenRecMD->m_isDuplicate == true)
+ {
+ // Does the correct ImplMap entry exist in the emit scope?
+ IfFailGo(pMiniMdEmit->FindImplMapHelper(pTokenRecMD->m_tkTo, &mdImplMap));
+ }
+ else
+ {
+ mdImplMap = mdTokenNil;
+ }
+ if (!InvalidRid(mdImplMap))
+ {
+ // Verify that the rest of the data is identical, else it's an error.
+ IfFailGo(pMiniMdEmit->GetImplMapRecord(mdImplMap, &pRecEmit));
+ _ASSERTE(pMiniMdEmit->getMemberForwardedOfImplMap(pRecEmit) == pTokenRecMD->m_tkTo);
+ LPCSTR szImplMapImportName;
+ IfFailGo(pMiniMdEmit->getImportNameOfImplMap(pRecEmit, &szImplMapImportName));
+ if (pMiniMdEmit->getImportScopeOfImplMap(pRecEmit) != mrEmit ||
+ pMiniMdEmit->getMappingFlagsOfImplMap(pRecEmit) != usMappingFlags ||
+ strcmp(szImplMapImportName, szImportName))
+ {
+ // Mismatched p-invoke entries are found.
+ _ASSERTE(!"Mismatched P-invoke entries during merge. Bad State!");
+ IfFailGo(E_FAIL);
+ }
+ }
+ else
+ {
+ IfFailGo(pMiniMdEmit->AddImplMapRecord(&pRecEmit, &mdImplMap));
+
+ // Copy rest of data.
+ IfFailGo( pMiniMdEmit->PutToken(TBL_ImplMap, ImplMapRec::COL_MemberForwarded, pRecEmit, pTokenRecMD->m_tkTo) );
+ IfFailGo( pMiniMdEmit->PutToken(TBL_ImplMap, ImplMapRec::COL_ImportScope, pRecEmit, mrEmit) );
+ IfFailGo( pMiniMdEmit->PutString(TBL_ImplMap, ImplMapRec::COL_ImportName, pRecEmit, szImportName) );
+ pRecEmit->SetMappingFlags(usMappingFlags);
+ IfFailGo( pMiniMdEmit->AddImplMapToHash(mdImplMap) );
+ }
+ }
+ }
+
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergePinvoke()
+
+
+//*****************************************************************************
+// Merge StandAloneSigs
+//*****************************************************************************
+HRESULT NEWMERGER::MergeStandAloneSigs()
+{
+ HRESULT hr = NOERROR;
+ StandAloneSigRec *pRecImport = NULL;
+ StandAloneSigRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ TOKENREC *pTokenRec;
+ mdSignature saImp;
+ mdSignature saEmit;
+ bool fDuplicate;
+ PCCOR_SIGNATURE pbSig;
+ ULONG cbSig;
+ ULONG cbEmit;
+ CQuickBytes qbSig;
+ PCOR_SIGNATURE rgSig;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountStandAloneSigs();
+
+ // loop through all Signatures
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those Signatures that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsSignatureMarked(TokenFromRid(i, mdtSignature)) == false)
+ continue;
+
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetStandAloneSigRecord(i, &pRecImport));
+ IfFailGo(pMiniMdImport->getSignatureOfStandAloneSig(pRecImport, &pbSig, &cbSig));
+
+ // This is a signature containing the return type after count of args
+ // convert rid contained in signature to new scope
+ IfFailGo(ImportHelper::MergeUpdateTokenInSig(
+ NULL, // Assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Assembly import scope info.
+ pMiniMdImport, // The scope to merge into the emit scope.
+ pbSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit)); // number of bytes write to cbEmit
+ rgSig = ( PCOR_SIGNATURE ) qbSig.Ptr();
+
+ hr = ImportHelper::FindStandAloneSig(
+ pMiniMdEmit,
+ rgSig,
+ cbEmit,
+ &saEmit );
+ if ( hr == S_OK )
+ {
+ // find a duplicate
+ fDuplicate = true;
+ }
+ else
+ {
+ // copy over
+ fDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddStandAloneSigRecord(&pRecEmit, (ULONG *)&saEmit));
+ saEmit = TokenFromRid(saEmit, mdtSignature);
+ IfFailGo( pMiniMdEmit->PutBlob(TBL_StandAloneSig, StandAloneSigRec::COL_Signature, pRecEmit, rgSig, cbEmit));
+ }
+ saImp = TokenFromRid(i, mdtSignature);
+
+ // Record the token movement
+ IfFailGo( pCurTkMap->InsertNotFound(saImp, fDuplicate, saEmit, &pTokenRec) );
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeStandAloneSigs()
+
+//*****************************************************************************
+// Merge MethodSpecs
+//*****************************************************************************
+HRESULT NEWMERGER::MergeMethodSpecs()
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdToken tk;
+ ULONG iRecord;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ // Loop through all MethodSpec
+ iCount = pMiniMdImport->getCountMethodSpecs();
+ for (i=1; i<=iCount; ++i)
+ {
+ MethodSpecRec *pRecImport;
+ MethodSpecRec *pRecEmit;
+ TOKENREC *pTokenRecMethod;
+ TOKENREC *pTokenRecMethodNew;
+ PCCOR_SIGNATURE pvSig;
+ ULONG cbSig;
+ CQuickBytes qbSig;
+ ULONG cbEmit;
+
+ // Only copy marked records.
+ if (!pMiniMdImport->GetFilterTable()->IsMethodSpecMarked(i))
+ continue;
+
+ IfFailGo(pMiniMdImport->GetMethodSpecRecord(i, &pRecImport));
+ tk = pMiniMdImport->getMethodOfMethodSpec(pRecImport);
+
+ // Map the token to the new scope.
+ if (pCurTkMap->Find(tk, &pTokenRecMethod) == false)
+ {
+ // This should never fire unless the TypeDefs/Refs weren't merged
+ // before this code runs.
+ _ASSERTE(!"MethodSpec method not found in MERGER::MergeGenericsInfo. Bad state!");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ // Copy to output scope.
+ IfFailGo(pMiniMdEmit->AddMethodSpecRecord(&pRecEmit, &iRecord));
+ IfFailGo( pMiniMdEmit->PutToken(TBL_MethodSpec, MethodSpecRec::COL_Method, pRecEmit, pTokenRecMethod->m_tkTo));
+
+ // Copy the signature, translating any embedded tokens.
+ IfFailGo(pMiniMdImport->getInstantiationOfMethodSpec(pRecImport, &pvSig, &cbSig));
+
+ // ...convert rid contained in signature to new scope
+ IfFailGo(ImportHelper::MergeUpdateTokenInSig(
+ NULL, // Assembly emit scope.
+ pMiniMdEmit, // The emit scope.
+ NULL, NULL, 0, // Import assembly scope information.
+ pMiniMdImport, // The scope to merge into the emit scope.
+ pvSig, // signature from the imported scope
+ pCurTkMap, // Internal token mapping structure.
+ &qbSig, // [OUT] translated signature
+ 0, // start from first byte of the signature
+ 0, // don't care how many bytes consumed
+ &cbEmit)); // number of bytes write to cbEmit
+
+ // ...persist the converted signature
+ IfFailGo( pMiniMdEmit->PutBlob(TBL_MethodSpec, MethodSpecRec::COL_Instantiation, pRecEmit, qbSig.Ptr(), cbEmit) );
+
+ IfFailGo( pCurTkMap->InsertNotFound(TokenFromRid(i, mdtMethodSpec), false,
+ TokenFromRid(iRecord, mdtMethodSpec), &pTokenRecMethodNew) );
+ }
+ }
+
+ ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeMethodSpecs()
+
+//*****************************************************************************
+// Merge DeclSecuritys
+//*****************************************************************************
+HRESULT NEWMERGER::MergeDeclSecuritys()
+{
+ HRESULT hr = NOERROR;
+ DeclSecurityRec *pRecImport = NULL;
+ DeclSecurityRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdToken tkParentImp;
+ TOKENREC *pTokenRec;
+ void const *pValue;
+ ULONG cbBlob;
+ mdPermission pmImp;
+ mdPermission pmEmit;
+ bool fDuplicate;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountDeclSecuritys();
+
+ // loop through all DeclSecurity
+ for (i = 1; i <= iCount; i++)
+ {
+ // only merge those DeclSecurities that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsDeclSecurityMarked(TokenFromRid(i, mdtPermission)) == false)
+ continue;
+
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetDeclSecurityRecord(i, &pRecImport));
+ tkParentImp = pMiniMdImport->getParentOfDeclSecurity(pRecImport);
+ if ( pCurTkMap->Find(tkParentImp, &pTokenRec) )
+ {
+ if ( !pTokenRec->m_isDuplicate )
+ {
+ // If the parent is not duplicated, just copy over the custom value
+ goto CopyPermission;
+ }
+ else
+ {
+ // Try to see if the Permission is there in the emit scope or not.
+ // If not, move it over still
+ if ( ImportHelper::FindPermission(
+ pMiniMdEmit,
+ pTokenRec->m_tkTo,
+ pRecImport->GetAction(),
+ &pmEmit) == S_OK )
+ {
+ // found a match
+ // <TODO>@FUTURE: more verification??</TODO>
+ fDuplicate = true;
+ }
+ else
+ {
+ // Parent is duplicated but the Permission is not. Still copy over the
+ // Permission.
+CopyPermission:
+ fDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddDeclSecurityRecord(&pRecEmit, (ULONG *)&pmEmit));
+ pmEmit = TokenFromRid(pmEmit, mdtPermission);
+
+ pRecEmit->Copy(pRecImport);
+
+ // set the parent
+ IfFailGo( pMiniMdEmit->PutToken(
+ TBL_DeclSecurity,
+ DeclSecurityRec::COL_Parent,
+ pRecEmit,
+ pTokenRec->m_tkTo) );
+
+ // move over the CustomAttribute blob value
+ IfFailGo(pMiniMdImport->getPermissionSetOfDeclSecurity(pRecImport, (const BYTE **)&pValue, &cbBlob));
+ IfFailGo(pMiniMdEmit->PutBlob(
+ TBL_DeclSecurity,
+ DeclSecurityRec::COL_PermissionSet,
+ pRecEmit,
+ pValue,
+ cbBlob));
+ }
+ }
+ pmEmit = TokenFromRid(pmEmit, mdtPermission);
+ pmImp = TokenFromRid(i, mdtPermission);
+
+ // Record the token movement
+ IfFailGo( pCurTkMap->InsertNotFound(pmImp, fDuplicate, pmEmit, &pTokenRec) );
+ }
+ else
+ {
+ // bad lookup map
+ _ASSERTE(!"bad state");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeDeclSecuritys()
+
+
+//*****************************************************************************
+// Merge Strings
+//*****************************************************************************
+HRESULT NEWMERGER::MergeStrings()
+{
+ HRESULT hr = NOERROR;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ TOKENREC *pTokenRec;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ for (UINT32 nIndex = 0; ;)
+ {
+ MetaData::DataBlob userString;
+ UINT32 nNextIndex;
+ UINT32 nEmitIndex;
+
+ hr = pMiniMdImport->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;
+ }
+
+ if (pMiniMdImport->GetFilterTable()->IsUserStringMarked(TokenFromRid(nIndex, mdtString)) == false)
+ {
+ // Process next user string in the heap
+ nIndex = nNextIndex;
+ continue;
+ }
+
+ IfFailGo(pMiniMdEmit->PutUserString(
+ userString,
+ &nEmitIndex));
+
+ IfFailGo(pCurTkMap->InsertNotFound(
+ TokenFromRid(nIndex, mdtString),
+ false,
+ TokenFromRid(nEmitIndex, mdtString),
+ &pTokenRec));
+
+ // Process next user string in the heap
+ nIndex = nNextIndex;
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeStrings()
+
+// Helper method to merge the module-level security critical attributes
+// Strips all module-level security critical attribute [that won't be ultimately needed]
+// Returns:
+// FAILED(hr): Failure occurred retrieving metadata or parsing scopes
+// S_OK: Attribute should be merged into final output scope
+// S_FALSE: Attribute should be ignored/dropped from output scope
+HRESULT NEWMERGER::MergeSecurityCriticalModuleLevelAttributes(
+ MergeImportData* pImportData, // import scope
+ mdToken tkParentImp, // parent token with attribute
+ TOKENREC* pTypeRec, // token record of attribute ctor
+ mdToken mrSecurityTreatAsSafeAttributeCtor, // 'generic' TAS attribute token
+ mdToken mrSecurityTransparentAttributeCtor, // 'generic' Transparent attribute token
+ mdToken mrSecurityCriticalExplicitAttributeCtor, // 'generic' Critical attribute token
+ mdToken mrSecurityCriticalEverythingAttributeCtor)
+{
+ HRESULT hr = S_OK;
+
+ // if ANY assembly-level critical attributes were specified, then we'll output
+ // one assembly-level Critical(Explicit) attribute only
+ // AND if this scope has tags
+ if (ISSCS_Unknown != pImportData->m_isscsSecurityCriticalStatus)
+ {
+ _ASSERTE(ISSCS_Unknown != m_isscsSecurityCritical);
+ // drop only assembly-level attributes
+ TypeRefRec* pTypeRefRec;
+ // metadata emitter
+ CMiniMdRW* pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // if compiler is generating a module - then this will be a module token
+ LPCSTR szTypeRefName;
+ if (tkParentImp == MODULEDEFTOKEN ||
+ // otherwise, if merging assemblies, we have a fake type ref called MODULE_CA_LOCATION
+ (TypeFromToken(tkParentImp) == mdtTypeRef &&
+ (IsAttributeFromNamespace(pMiniMdImport, tkParentImp,
+ COR_COMPILERSERVICE_NAMESPACE, COR_MSCORLIB_NAME,
+ &pTypeRefRec) == S_OK) &&
+ (pMiniMdImport->getNameOfTypeRef(pTypeRefRec, &szTypeRefName) == S_OK) &&
+ (strcmp(MODULE_CA_TYPENAME, szTypeRefName) == 0)))
+ {
+ // drop the TAS attribute (unless all scopes have TAS)
+ if ( pTypeRec->m_tkTo == mrSecurityTreatAsSafeAttributeCtor )
+ {
+ if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityTreatAsSafe) ==
+ ISSCS_SecurityTreatAsSafe)
+ {
+ _ASSERTE((pImportData->m_isscsSecurityCriticalStatus & ISSCS_SecurityTreatAsSafe) ==
+ ISSCS_SecurityTreatAsSafe);
+ return S_OK;
+ }
+ return S_FALSE;
+ }
+ // drop the Transparent attribute (unless all scopes have Transparent)
+ else if (pTypeRec->m_tkTo == mrSecurityTransparentAttributeCtor)
+ {
+ if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityTransparent) ==
+ ISSCS_SecurityTransparent)
+ {
+ _ASSERTE((pImportData->m_isscsSecurityCriticalStatus & ISSCS_SecurityTransparent) ==
+ ISSCS_SecurityTransparent);
+ return S_OK;
+ }
+ return S_FALSE;
+ }
+ else if (pTypeRec->m_tkTo == mrSecurityCriticalExplicitAttributeCtor)
+ {
+ // if NOT Critical Everything, then leave the Critical.Explicit attribute
+ // the Critical.Explicit attribute will be used as the final global attribute
+ if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityCriticalEverything) !=
+ ISSCS_SecurityCriticalEverything)
+ {
+ _ASSERTE((pImportData->m_isscsSecurityCriticalStatus & ISSCS_SecurityCriticalExplicit) ==
+ ISSCS_SecurityCriticalExplicit);
+ return S_OK;
+ }
+ else
+ {
+ // drop this attribute
+ return S_FALSE;
+ }
+ }
+ else if (pTypeRec->m_tkTo == mrSecurityCriticalEverythingAttributeCtor)
+ {
+ // OPTIMIZATION: if all attributes are Critical.Everything,
+ // then leave the global Critical attribute
+ if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityCriticalEverything) ==
+ ISSCS_SecurityCriticalEverything)
+ {
+ _ASSERTE((pImportData->m_isscsSecurityCriticalStatus & ISSCS_SecurityCriticalEverything) ==
+ ISSCS_SecurityCriticalEverything);
+ return S_OK;
+ }
+ else
+ {
+ // drop this attribute
+ return S_FALSE;
+ }
+ }
+ }
+ }
+
+ return hr;
+} // NEWMERGER::MergeSecurityCriticalModuleLevelAttributes
+
+// HELPER: Retrieve the meta-data info related to SecurityCritical
+HRESULT NEWMERGER::RetrieveStandardSecurityCriticalMetaData(
+ mdAssemblyRef& tkMscorlib,
+ mdTypeRef& securityEnum,
+ BYTE*& rgSigBytesSecurityCriticalEverythingCtor,
+ DWORD& dwSigEverythingSize,
+ BYTE*& rgSigBytesSecurityCriticalExplicitCtor,
+ DWORD& dwSigExplicitSize)
+{
+ HRESULT hr = S_OK;
+
+ CMiniMdRW* emit = GetMiniMdEmit();
+
+ // get typeref for mscorlib
+ BYTE pbMscorlibToken[] = COR_MSCORLIB_TYPEREF;
+ BYTE* pCurr = rgSigBytesSecurityCriticalEverythingCtor;
+
+ IfFailGo(ImportHelper::FindAssemblyRef(emit,
+ COR_MSCORLIB_NAME,
+ NULL,
+ pbMscorlibToken,
+ sizeof(pbMscorlibToken),
+ asm_rmj,
+ asm_rmm,
+ asm_rup,
+ asm_rpt,
+ 0,
+ &tkMscorlib));
+
+ IfFailGo(m_pRegMetaEmit->DefineTypeRefByName(tkMscorlib,
+ COR_SECURITYCRITICALSCOPE_ENUM_W,
+ &securityEnum));
+
+ // build the constructor sig that takes SecurityCriticalScope argument
+ if (rgSigBytesSecurityCriticalEverythingCtor)
+ {
+ *pCurr++ = IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS;
+ *pCurr++ = COR_SECURITYCRITICAL_CTOR_ARGCOUNT_SCOPE_EVERYTHING; // one argument to constructor
+ *pCurr++ = ELEMENT_TYPE_VOID;
+ *pCurr++ = ELEMENT_TYPE_VALUETYPE;
+ pCurr += CorSigCompressToken(securityEnum, pCurr);
+ dwSigEverythingSize = (DWORD)(pCurr - rgSigBytesSecurityCriticalEverythingCtor);
+ _ASSERTE(dwSigEverythingSize <= COR_SECURITYCRITICAL_CTOR_SCOPE_SIG_MAX_SIZE);
+ }
+
+ // if Explicit ctor is requested
+ if (rgSigBytesSecurityCriticalExplicitCtor)
+ {
+ // build the constructor sig that has NO arguments
+ pCurr = rgSigBytesSecurityCriticalExplicitCtor;
+ *pCurr++ = IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS;
+ *pCurr++ = COR_SECURITYCRITICAL_CTOR_ARGCOUNT_NO_SCOPE; // no arguments to constructor
+ *pCurr++ = ELEMENT_TYPE_VOID;
+ dwSigExplicitSize = (DWORD)(pCurr - rgSigBytesSecurityCriticalExplicitCtor);
+ _ASSERTE(dwSigExplicitSize <= COR_SECURITYCRITICAL_CTOR_NO_SCOPE_SIG_MAX_SIZE);
+ }
+
+ErrExit:
+ return hr;
+} // NEWMERGER::RetrieveStandardSecurityCriticalMetaData
+
+//*****************************************************************************
+// Merge CustomAttributes
+//*****************************************************************************
+HRESULT NEWMERGER::MergeCustomAttributes()
+{
+
+ HRESULT hr = NOERROR;
+ CustomAttributeRec *pRecImport = NULL;
+ CustomAttributeRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ ULONG iCount;
+ ULONG i;
+ mdToken tkParentImp; // Token of attributed object (parent).
+ TOKENREC *pTokenRec; // Parent's remap.
+ mdToken tkType; // Token of attribute's type.
+ TOKENREC *pTypeRec; // Type's remap.
+ void const *pValue; // The actual value.
+ ULONG cbBlob; // Size of the value.
+ mdToken cvImp;
+ mdToken cvEmit;
+ bool fDuplicate;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ TypeRefRec *pTypeRefRec;
+ ULONG cTypeRefRecs;
+ mdToken mrSuppressMergeCheckAttributeCtor = mdTokenNil;
+ mdToken mrSecurityCriticalExplicitAttributeCtor = mdTokenNil;
+ mdToken mrSecurityCriticalEverythingAttributeCtor = mdTokenNil;
+ mdToken mrSecurityTransparentAttributeCtor = mdTokenNil;
+ mdToken mrSecurityTreatAsSafeAttributeCtor = mdTokenNil;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ // Find out the TypeRef referring to our library's System.CompilerServices.SuppressMergeCheckAttribute,
+ // System.Security.SecurityCriticalAttribute, System.Security.SecurityTransparentAttribute, and
+ // System.Security.SecurityTreatAsSafeAttibute
+ cTypeRefRecs = pMiniMdEmit->getCountTypeRefs();
+
+ { // retrieve global attribute TypeRefs
+
+ mdAssemblyRef tkMscorlib = mdTokenNil;
+ mdTypeRef securityEnum = mdTokenNil;
+
+ NewArrayHolder<BYTE> rgSigBytesSecurityCriticalEverythingCtor(new (nothrow)BYTE[COR_SECURITYCRITICAL_CTOR_SCOPE_SIG_MAX_SIZE]);
+ BYTE* pSigBytesSecurityCriticalEverythingCtor = rgSigBytesSecurityCriticalEverythingCtor.GetValue();
+ IfFailGo((pSigBytesSecurityCriticalEverythingCtor == NULL)?E_OUTOFMEMORY:S_OK);
+ DWORD dwSigEverythingSize = 0;
+
+ NewArrayHolder<BYTE> rgSigBytesSecurityCriticalExplicitCtor(new (nothrow)BYTE[COR_SECURITYCRITICAL_CTOR_NO_SCOPE_SIG_MAX_SIZE]);
+ BYTE* pSigBytesSecurityCriticalExplicitCtor = rgSigBytesSecurityCriticalExplicitCtor.GetValue();
+ IfFailGo((pSigBytesSecurityCriticalExplicitCtor == NULL)?E_OUTOFMEMORY:S_OK);
+ DWORD dwSigExplicitSize = 0;
+
+ // retrieve security critical metadata info if necessary
+ if(ISSCS_Unknown != m_isscsSecurityCritical)
+ {
+
+ hr = RetrieveStandardSecurityCriticalMetaData(
+ tkMscorlib,
+ securityEnum,
+ pSigBytesSecurityCriticalEverythingCtor,
+ dwSigEverythingSize,
+ pSigBytesSecurityCriticalExplicitCtor,
+ dwSigExplicitSize);
+
+ }
+
+ // Search for the TypeRef.
+ for (i = 1; i <= cTypeRefRecs; i++)
+ {
+ mdToken tkTmp = TokenFromRid(i,mdtTypeRef);
+
+ if (IsAttributeFromNamespace(pMiniMdEmit, tkTmp,
+ COR_COMPILERSERVICE_NAMESPACE, COR_MSCORLIB_NAME,
+ &pTypeRefRec) == S_OK)
+ {
+ LPCSTR szNameOfTypeRef;
+ IfFailGo(pMiniMdEmit->getNameOfTypeRef(pTypeRefRec, &szNameOfTypeRef));
+ if (strcmp(szNameOfTypeRef, COR_SUPPRESS_MERGE_CHECK_ATTRIBUTE) == 0)
+ {
+ hr = ImportHelper::FindMemberRef(
+ pMiniMdEmit, tkTmp,
+ COR_CTOR_METHOD_NAME,
+ NULL, 0,
+ &mrSuppressMergeCheckAttributeCtor);
+ if (S_OK == hr) continue;
+ }
+ }
+ else
+ // if we are merging security critical attributes, then look for transparent-related attributes
+ if ((ISSCS_Unknown != m_isscsSecurityCritical) &&
+ (IsAttributeFromNamespace(pMiniMdEmit, tkTmp,
+ COR_SECURITYCRITICAL_ATTRIBUTE_NAMESPACE, COR_MSCORLIB_NAME,
+ &pTypeRefRec) == S_OK))
+ {
+ LPCSTR szNameOfTypeRef;
+ IfFailGo(pMiniMdEmit->getNameOfTypeRef(pTypeRefRec, &szNameOfTypeRef));
+
+ // look for the SecurityCritical attribute
+ if (strcmp(szNameOfTypeRef, COR_SECURITYCRITICAL_ATTRIBUTE) == 0)
+ {
+ // since the SecurityCritical attribute can be either
+ // parameterless constructor or SecurityCriticalScope constructor, we
+ // look for both
+ hr = ImportHelper::FindMemberRef(
+ pMiniMdEmit, tkTmp,
+ COR_CTOR_METHOD_NAME,
+ rgSigBytesSecurityCriticalEverythingCtor.GetValue(), dwSigEverythingSize,
+ &mrSecurityCriticalEverythingAttributeCtor);
+ if (S_OK == hr) continue;
+ hr = ImportHelper::FindMemberRef(
+ pMiniMdEmit, tkTmp,
+ COR_CTOR_METHOD_NAME,
+ rgSigBytesSecurityCriticalExplicitCtor.GetValue(), dwSigExplicitSize,
+ &mrSecurityCriticalExplicitAttributeCtor);
+ if (S_OK == hr) continue;
+ }
+ else
+ // look for the SecurityTransparent attribute
+ if (strcmp(szNameOfTypeRef, COR_SECURITYTRANSPARENT_ATTRIBUTE) == 0)
+ {
+ hr = ImportHelper::FindMemberRef(
+ pMiniMdEmit, tkTmp,
+ COR_CTOR_METHOD_NAME,
+ NULL, 0,
+ &mrSecurityTransparentAttributeCtor);
+ if (S_OK == hr) continue;
+ }
+ else
+ // look for the SecurityTreatAsSafe attribute
+ if (strcmp(szNameOfTypeRef, COR_SECURITYTREATASSAFE_ATTRIBUTE) == 0)
+ {
+ hr = ImportHelper::FindMemberRef(
+ pMiniMdEmit, tkTmp,
+ COR_CTOR_METHOD_NAME,
+ NULL, 0,
+ &mrSecurityTreatAsSafeAttributeCtor);
+ if (S_OK == hr) continue;
+ }
+ }
+ hr = S_OK; // ignore failures since the attribute may not be used
+ }
+ }
+
+ // Loop over every module scope
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // We know that the filter table is not null here. Tell PREFIX that we know it.
+ PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL );
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountCustomAttributes();
+
+ // loop through all CustomAttribute
+ for (i = 1; i <= iCount; i++)
+ {
+ // compare it with the emit scope
+ IfFailGo(pMiniMdImport->GetCustomAttributeRecord(i, &pRecImport));
+ tkParentImp = pMiniMdImport->getParentOfCustomAttribute(pRecImport);
+ tkType = pMiniMdImport->getTypeOfCustomAttribute(pRecImport);
+ IfFailGo(pMiniMdImport->getValueOfCustomAttribute(pRecImport, (const BYTE **)&pValue, &cbBlob));
+
+ // only merge those CustomAttributes that are marked
+ if ( pMiniMdImport->GetFilterTable()->IsCustomAttributeMarked(TokenFromRid(i, mdtCustomAttribute)) == false)
+ continue;
+
+ // Check the type of the CustomAttribute. If it is not marked, then we don't need to move over the CustomAttributes.
+ // This will only occur for compiler defined discardable CAs during linking.
+ //
+ if ( pMiniMdImport->GetFilterTable()->IsTokenMarked(tkType) == false)
+ continue;
+
+ if ( pCurTkMap->Find(tkParentImp, &pTokenRec) )
+ {
+ // If the From token type is different from the To token's type, we have optimized the ref to def.
+ // In this case, we are dropping the CA associated with the Ref tokens.
+ //
+ if (TypeFromToken(tkParentImp) == TypeFromToken(pTokenRec->m_tkTo))
+ {
+
+ // If tkParentImp is a MemberRef and it is also mapped to a MemberRef in the merged scope with a MethodDef
+ // parent, then it is a MemberRef optimized to a MethodDef. We are keeping the MemberRef because it is a
+ // vararg call. So we can drop CAs on this MemberRef.
+ if (TypeFromToken(tkParentImp) == mdtMemberRef)
+ {
+ MemberRefRec *pTempRec;
+ IfFailGo(pMiniMdEmit->GetMemberRefRecord(RidFromToken(pTokenRec->m_tkTo), &pTempRec));
+ if (TypeFromToken(pMiniMdEmit->getClassOfMemberRef(pTempRec)) == mdtMethodDef)
+ continue;
+ }
+
+
+ if (! pCurTkMap->Find(tkType, &pTypeRec) )
+ {
+ _ASSERTE(!"CustomAttribute Type not found in output scope");
+ IfFailGo(META_E_BADMETADATA);
+ }
+
+ // Determine if we need to copy or ignore security-critical-related attributes
+ hr = MergeSecurityCriticalModuleLevelAttributes(
+ pImportData, tkParentImp, pTypeRec,
+ mrSecurityTreatAsSafeAttributeCtor, mrSecurityTransparentAttributeCtor,
+ mrSecurityCriticalExplicitAttributeCtor,
+ mrSecurityCriticalEverythingAttributeCtor);
+ IfFailGo(hr);
+ // S_FALSE means skip attribute
+ if (hr == S_FALSE) continue;
+ // S_OK means consider copying attribute
+
+ // if it's the SuppressMergeCheckAttribute, don't copy it
+ if ( pTypeRec->m_tkTo == mrSuppressMergeCheckAttributeCtor )
+ {
+ continue;
+ }
+
+ if ( pTokenRec->m_isDuplicate)
+ {
+ // Try to see if the custom value is there in the emit scope or not.
+ // If not, move it over still
+ hr = ImportHelper::FindCustomAttributeByToken(
+ pMiniMdEmit,
+ pTokenRec->m_tkTo,
+ pTypeRec->m_tkTo,
+ pValue,
+ cbBlob,
+ &cvEmit);
+
+ if ( hr == S_OK )
+ {
+ // found a match
+ // <TODO>@FUTURE: more verification??</TODO>
+ fDuplicate = true;
+ }
+ else
+ {
+ TypeRefRec *pAttributeTypeRefRec;
+ // We need to allow additive merge on TypeRef for CustomAttributes because compiler
+ // could build module but not assembly. They are hanging of Assembly level CAs on a bogus
+ // TypeRef.
+ // Also allow additive merge for CAs from CompilerServices and Microsoft.VisualC
+ if (tkParentImp == MODULEDEFTOKEN
+ || TypeFromToken(tkParentImp) == mdtTypeRef
+ || (IsAttributeFromNamespace(pMiniMdImport, tkType,
+ COR_COMPILERSERVICE_NAMESPACE, COR_MSCORLIB_NAME,
+ &pAttributeTypeRefRec) == S_OK)
+ || (IsAttributeFromNamespace(pMiniMdImport, tkType,
+ COR_MISCBITS_NAMESPACE, COR_MISCBITS_NAMESPACE,
+ &pAttributeTypeRefRec) == S_OK))
+ {
+ // clear the error
+ hr = NOERROR;
+
+ // custom value of module token! Copy over the custom value
+ goto CopyCustomAttribute;
+ }
+
+ // another case to support additive merge if the CA on MehtodDef is
+ // HandleProcessCorruptedStateExceptionsAttribute
+ if ( TypeFromToken(tkParentImp) == mdtMethodDef && tkType == pImportData->m_tkHandleProcessCorruptedStateCtor)
+ {
+ // clear the error
+ hr = NOERROR;
+
+ // custom value of module token! Copy over the custom value
+ goto CopyCustomAttribute;
+ }
+ CheckContinuableErrorEx(META_E_MD_INCONSISTENCY, pImportData, TokenFromRid(i, mdtCustomAttribute));
+ }
+ }
+ else
+ {
+CopyCustomAttribute:
+ if ((m_dwMergeFlags & DropMemberRefCAs) && TypeFromToken(pTokenRec->m_tkTo) == mdtMemberRef)
+ {
+ // CustomAttributes associated with MemberRef. If the parent of MemberRef is a MethodDef or FieldDef, drop
+ // the custom attribute.
+ MemberRefRec *pMemberRefRec;
+ IfFailGo(pMiniMdEmit->GetMemberRefRecord(RidFromToken(pTokenRec->m_tkTo), &pMemberRefRec));
+ mdToken mrParent = pMiniMdEmit->getClassOfMemberRef(pMemberRefRec);
+ if (TypeFromToken(mrParent) == mdtMethodDef || TypeFromToken(mrParent) == mdtFieldDef)
+ {
+ // Don't bother to copy over
+ continue;
+ }
+ }
+
+ // Parent is duplicated but the custom value is not. Still copy over the
+ // custom value.
+ fDuplicate = false;
+ IfFailGo(pMiniMdEmit->AddCustomAttributeRecord(&pRecEmit, (ULONG *)&cvEmit));
+ cvEmit = TokenFromRid(cvEmit, mdtCustomAttribute);
+
+ // set the parent
+ IfFailGo( pMiniMdEmit->PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Parent, pRecEmit, pTokenRec->m_tkTo) );
+ // set the type
+ IfFailGo( pMiniMdEmit->PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Type, pRecEmit, pTypeRec->m_tkTo));
+
+ // move over the CustomAttribute blob value
+ IfFailGo(pMiniMdImport->getValueOfCustomAttribute(pRecImport, (const BYTE **)&pValue, &cbBlob));
+
+ IfFailGo( pMiniMdEmit->PutBlob(TBL_CustomAttribute, CustomAttributeRec::COL_Value, pRecEmit, pValue, cbBlob));
+ IfFailGo( pMiniMdEmit->AddCustomAttributesToHash(cvEmit) );
+ }
+ cvEmit = TokenFromRid(cvEmit, mdtCustomAttribute);
+ cvImp = TokenFromRid(i, mdtCustomAttribute);
+
+ // Record the token movement
+ IfFailGo( pCurTkMap->InsertNotFound(cvImp, pTokenRec->m_isDuplicate, cvEmit, &pTokenRec) );
+ }
+ }
+ else
+ {
+
+ // either bad lookup map or bad metadata
+ _ASSERTE(!"Bad state");
+ IfFailGo( META_E_BADMETADATA );
+ }
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeCustomAttributes()
+
+//*******************************************************************************
+// Helper to check if input scope has assembly-level security transparent awareness (either SecurityTransparent or SecurityCritical)
+// SIDE EFFECT: pImportData->m_isscsSecurityCriticalStatus will be explicitly set [same value as return value]
+// SecurityCritical.Explicit attribute injection occurs for all scopes that have tags (e.g. NOT ISSCS_Unknown)
+// If the tagged scopes are all SecurityCritical.Everything, then the final attribute should be SecurityCritical.Everything
+// anyway. Otherwise, at least one SecurityCritical.Explicit tag will be used/injected
+//*******************************************************************************
+InputScopeSecurityCriticalStatus NEWMERGER::CheckInputScopeIsCritical(MergeImportData* pImportData, HRESULT& hr)
+{
+ hr = S_OK;
+
+ // the attribute should be in a known state no matter how we return from this function
+ // default to no attribute explicitly specified
+ pImportData->m_isscsSecurityCriticalStatus = ISSCS_Unknown;
+
+ mdTypeRef fakeModuleTypeRef = mdTokenNil;
+ mdAssemblyRef tkMscorlib = mdTokenNil;
+
+ // TODO: Should we remove the ability to disable merging critical attributes?
+ if (g_fRefShouldMergeCriticalChecked == FALSE)
+ {
+ // shouldn't require thread safety lock
+ g_fRefShouldMergeCritical = (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_MergeCriticalAttributes) != 0);
+ g_fRefShouldMergeCriticalChecked = TRUE;
+ }
+
+ // return no merge needed, if the merge critical attribute setting is not enabled.
+ if (!g_fRefShouldMergeCritical) return ISSCS_Unknown;
+
+ // get typeref for mscorlib
+ BYTE pbMscorlibToken[] = COR_MSCORLIB_TYPEREF;
+
+ CMiniMdRW* pImportedMiniMd = &pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd;
+
+ if (S_OK != ImportHelper::FindAssemblyRef(pImportedMiniMd,
+ COR_MSCORLIB_NAME,
+ NULL,
+ pbMscorlibToken,
+ sizeof(pbMscorlibToken),
+ asm_rmj,
+ asm_rmm,
+ asm_rup,
+ asm_rpt,
+ 0,
+ &tkMscorlib))
+ {
+ // there isn't an mscorlib ref here... we can't have the security critical attribute
+ return ISSCS_Unknown;
+ }
+
+ if (S_OK != ImportHelper::FindTypeRefByName(pImportedMiniMd,
+ tkMscorlib,
+ COR_COMPILERSERVICE_NAMESPACE,
+ MODULE_CA_TYPENAME,
+ &fakeModuleTypeRef))
+ {
+ // for now let use the fake module ref as the assembly def
+ fakeModuleTypeRef = 0x000001;
+ }
+
+ // Check the input scope for TreatAsSafe
+ if (S_OK == ImportHelper::GetCustomAttributeByName(pImportedMiniMd,
+ fakeModuleTypeRef, // This is the assembly def token
+ COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL,
+ NULL,
+ NULL))
+ {
+ pImportData->m_isscsSecurityCriticalStatus |= ISSCS_SecurityTreatAsSafe;
+ }
+
+ // Check the input scope for security transparency awareness
+ // For example, the assembly is marked SecurityTransparent, SecurityCritical(Explicit), or SecurityCritical(Everything)
+
+
+ const void *pbData = NULL; // [OUT] Put pointer to data here.
+ ULONG cbData = 0; // number of bytes in pbData
+
+ // Check if the SecurityTransparent attribute is present
+ if (S_OK == ImportHelper::GetCustomAttributeByName(pImportedMiniMd,
+ fakeModuleTypeRef, // This is the assembly def token
+ COR_SECURITYTRANSPARENT_ATTRIBUTE_FULL,
+ NULL,
+ NULL))
+ {
+ pImportData->m_isscsSecurityCriticalStatus |= ISSCS_SecurityTransparent;
+ }
+ else
+ // Check if the SecurityCritical attribute is present
+ if (S_OK == ImportHelper::GetCustomAttributeByName(pImportedMiniMd,
+ fakeModuleTypeRef, // This is the assembly def token
+ COR_SECURITYCRITICAL_ATTRIBUTE_FULL,
+ &pbData,
+ &cbData))
+ {
+ // find out if critical everything or explicit
+
+ // default to critical
+ pImportData->m_isscsSecurityCriticalStatus = ISSCS_SecurityCritical;
+
+ BYTE rgSecurityCriticalEverythingCtorValue[] = COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EVERYTHING;
+ // if value is non-0 (i.e. 1), then mark as SecurityCritical everything, otherwise, explicit
+ if (NULL != pbData && cbData == 8 &&
+ memcmp(rgSecurityCriticalEverythingCtorValue, pbData, cbData) == 0)
+ {
+ pImportData->m_isscsSecurityCriticalStatus |= ISSCS_SecurityCriticalEverything;
+ }
+ else
+ {
+ pImportData->m_isscsSecurityCriticalStatus |= ISSCS_SecurityCriticalExplicit;
+ }
+ }
+
+ return pImportData->m_isscsSecurityCriticalStatus;
+} // HRESULT NEWMERGER::CheckInputScopeIsCritical()
+
+//*******************************************************************************
+// Helper to merge security critical annotations across assemblies and types
+//*******************************************************************************
+HRESULT NEWMERGER::MergeSecurityCriticalAttributes()
+{
+ // if no assembly-level critical attributes were specified, then none are needed,
+ // and no need to do special attribute merging
+ if (ISSCS_Unknown == m_isscsSecurityCritical)
+ {
+ return S_OK;
+ }
+ // or if the global-scope already has TAS/Critical.Everything, then ignore individual type fixes
+ else if ((ISSCS_SECURITYCRITICAL_LEGACY & m_isscsSecurityCriticalAllScopes) == ISSCS_SECURITYCRITICAL_LEGACY)
+ {
+ return S_OK;
+ }
+
+ HRESULT hr = S_OK;
+
+ CMiniMdRW* emit = GetMiniMdEmit();
+ // The attribute we want to decorate all of the types with has not been defined.
+ mdMemberRef tkSecurityCriticalEverythingAttribute = mdTokenNil;
+ mdMemberRef tkSecurityTreatAsSafeAttribute = mdTokenNil;
+ mdMemberRef tkSecuritySafeCriticalAttribute = mdTokenNil;
+
+ mdAssemblyRef tkMscorlib = mdTokenNil;
+ mdTypeRef fakeModuleTypeRef = mdTokenNil;
+ mdTypeRef securityEnum = mdTokenNil;
+
+ DWORD dwSigSize;
+ BYTE* rgSigBytesSecurityCriticalExplicitCtor = 0;
+ DWORD dwSigSize_TEMP;
+
+ BYTE rgSigBytesTreatAsSafeCtor[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 0x00, ELEMENT_TYPE_VOID};
+
+ BYTE rgSecurityCriticalEverythingCtorValue[] = COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EVERYTHING;
+
+
+ mdTypeRef tkSecurityCriticalEverythingAttributeType = mdTokenNil;
+ mdTypeRef tkSecurityTreatAsSafeAttributeType = mdTokenNil;
+
+ NewArrayHolder<BYTE> rgSigBytesSecurityCriticalEverythingCtor(new (nothrow)BYTE[COR_SECURITYCRITICAL_CTOR_SCOPE_SIG_MAX_SIZE]);
+ BYTE* pSigBytesSecurityCriticalEverythingCtor = rgSigBytesSecurityCriticalEverythingCtor.GetValue();
+ IfFailGo((pSigBytesSecurityCriticalEverythingCtor == NULL)?E_OUTOFMEMORY:S_OK);
+
+ IfFailGo(RetrieveStandardSecurityCriticalMetaData(
+ tkMscorlib,
+ securityEnum,
+ pSigBytesSecurityCriticalEverythingCtor,
+ dwSigSize,
+ rgSigBytesSecurityCriticalExplicitCtor,
+ dwSigSize_TEMP));
+
+ if (S_OK != ImportHelper::FindTypeRefByName(emit,
+ tkMscorlib,
+ COR_COMPILERSERVICE_NAMESPACE,
+ MODULE_CA_TYPENAME,
+ &fakeModuleTypeRef))
+ {
+ // for now let use the fake module ref as the assembly def
+ fakeModuleTypeRef = 0x000001;
+ }
+
+ IfFailGo(m_pRegMetaEmit->DefineTypeRefByName(
+ tkMscorlib, COR_SECURITYCRITICAL_ATTRIBUTE_FULL_W, &tkSecurityCriticalEverythingAttributeType));
+
+ IfFailGo(m_pRegMetaEmit->DefineMemberRef(tkSecurityCriticalEverythingAttributeType,
+ COR_CONSTRUCTOR_METADATA_IDENTIFIER,
+ rgSigBytesSecurityCriticalEverythingCtor.GetValue(),
+ dwSigSize,
+ &tkSecurityCriticalEverythingAttribute));
+
+ IfFailGo(m_pRegMetaEmit->DefineTypeRefByName(
+ tkMscorlib, COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL_W,
+ &tkSecurityTreatAsSafeAttributeType));
+
+ IfFailGo(m_pRegMetaEmit->DefineMemberRef(tkSecurityTreatAsSafeAttributeType,
+ COR_CONSTRUCTOR_METADATA_IDENTIFIER,
+ rgSigBytesTreatAsSafeCtor,
+ sizeof(rgSigBytesTreatAsSafeCtor),
+ &tkSecurityTreatAsSafeAttribute));
+
+
+ // place this block in a new scope so that we can safely goto past it
+ {
+ mdTypeRef tkSecuritySafeCriticalAttributeType = mdTokenNil;
+ if (FAILED (hr = m_pRegMetaEmit->DefineTypeRefByName(tkMscorlib, COR_SECURITYSAFECRITICAL_ATTRIBUTE_FULL_W,
+ &tkSecuritySafeCriticalAttributeType)))
+ {
+ _ASSERTE(!"Couldn't Emit a Typeref for SafeCritical attribute");
+ return hr;
+ }
+
+ BYTE rgSigBytesSafeCriticalCtor[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 0x00, ELEMENT_TYPE_VOID};
+ if (FAILED(hr = m_pRegMetaEmit->DefineMemberRef(tkSecuritySafeCriticalAttributeType,
+ W(".ctor"),
+ rgSigBytesSafeCriticalCtor,
+ sizeof(rgSigBytesSafeCriticalCtor),
+ &tkSecuritySafeCriticalAttribute)))
+ {
+ _ASSERTE(!"Couldn't Emit a MemberRef for SafeCritical attribute .ctor");
+ return hr;
+ }
+ }
+
+
+ for (MergeImportData* pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // if the import is marked TAS, then we need to explicitly mark each type as TAS
+ // if the import is marked Crit/Everything, then we need to explicitly mark each type as Crit
+ // if the import is not marked at all, then we need to explicitly mark each type TAS/Crit
+
+ // if the import is marked ONLY Crit/Explicit or ONLY Transparent then we can ignore it
+ if (ISSCS_SecurityTransparent == pImportData->m_isscsSecurityCriticalStatus ||
+ ISSCS_SecurityCriticalExplicit == pImportData->m_isscsSecurityCriticalStatus) continue;
+
+ // Run through the scopes that need to have their types decorated with this attribute
+ MDTOKENMAP*pCurTKMap = pImportData->m_pMDTokenMap;
+ BYTE rgTreatAsSafeCtorValue[] = COR_SECURITYTREATASSAFE_ATTRIBUTE_VALUE;
+
+ BOOL fMarkEachTokenAsCritical = FALSE;
+ // if the import is unmarked or marked Crit/Everything,
+ // then we need to explicitly mark each token as Crit
+ // Unless the the global scope already has SecurityCritical.Everything
+ if (((ISSCS_SecurityCriticalEverything & m_isscsSecurityCriticalAllScopes) != ISSCS_SecurityCriticalEverything) &&
+ (((ISSCS_SecurityCriticalEverything & pImportData->m_isscsSecurityCriticalStatus) == ISSCS_SecurityCriticalEverything ) ||
+
+ // OR this scope is NOT transparent or critical-explicit
+ (ISSCS_SecurityTransparent & pImportData->m_isscsSecurityCriticalStatus) == 0 ||
+ (ISSCS_SecurityCritical & pImportData->m_isscsSecurityCriticalStatus) == 0 ||
+
+ // OR this scope is UNKNOWN
+ (ISSCS_Unknown == (ISSCS_SECURITYCRITICAL_FLAGS & pImportData->m_isscsSecurityCriticalStatus))))
+ {
+ fMarkEachTokenAsCritical = TRUE;
+ }
+
+ BOOL fMarkEachTokenAsSafe = FALSE;
+ // if the import is unmarked or marked TAS,
+ // then we need to explicitly mark each token as TAS
+ // Unless the the global scope already has SecurityTreatAsSafe
+ if (((ISSCS_SecurityTreatAsSafe & m_isscsSecurityCriticalAllScopes) != ISSCS_SecurityTreatAsSafe) &&
+ ((ISSCS_SecurityTreatAsSafe & pImportData->m_isscsSecurityCriticalStatus) ||
+ ISSCS_Unknown == (pImportData->m_isscsSecurityCriticalStatus & ISSCS_SECURITYCRITICAL_FLAGS)))
+ {
+ fMarkEachTokenAsSafe = TRUE;
+ }
+
+ BYTE rgSafeCriticalCtorValue[] = {0x01, 0x00, 0x00 ,0x00};
+
+ for (int i = 0; i < pCurTKMap->Count(); i++)
+ {
+ TOKENREC* pRec = pCurTKMap->Get(i);
+ BOOL fInjectSecurityAttributes = FALSE;
+
+ // skip empty records
+ if (pRec->IsEmpty()) continue;
+
+ // If this scope contained a typeref that was resolved to a typedef, let's not mark it. We'll let the owner
+ // of the actual typedef decide if that type should be marked.
+ if ((TypeFromToken(pRec->m_tkFrom) == mdtTypeRef) && (TypeFromToken(pRec->m_tkTo) == mdtTypeDef))
+ continue;
+
+ // Same for method refs/method defs
+ if ((TypeFromToken(pRec->m_tkFrom) == mdtMemberRef) && (TypeFromToken(pRec->m_tkTo) == mdtMethodDef))
+ continue;
+
+ // check for typedefs, but don't put this on the global typedef
+ if ((TypeFromToken(pRec->m_tkTo) == mdtTypeDef) && (pRec->m_tkTo != TokenFromRid(1, mdtTypeDef)))
+ {
+ // by default we will inject
+ fInjectSecurityAttributes = TRUE;
+ // except for Enums
+ DWORD dwClassAttrs = 0;
+ mdTypeRef crExtends = mdTokenNil;
+
+ if (FAILED(hr = m_pRegMetaEmit->GetTypeDefProps(pRec->m_tkTo, NULL, NULL, 0 , &dwClassAttrs, &crExtends)))
+ {
+ // TODO: should we fail ??
+ }
+
+ // check for Enum types
+ if (!IsNilToken(crExtends) && (TypeFromToken(crExtends)==mdtTypeRef))
+ {
+ // get the namespace and the name for this token
+ CMiniMdRW *pMiniMd = GetMiniMdEmit();
+ TypeRefRec *pTypeRefRec;
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(crExtends), &pTypeRefRec));
+ LPCSTR szNamespace;
+ LPCSTR szName;
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace));;
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szName));
+ // check for System.Enum
+ BOOL bIsEnum = (!strcmp(szNamespace,"System"))&&(!strcmp(szName,"Enum"));
+ if (bIsEnum)
+ {
+ fInjectSecurityAttributes = FALSE;
+ }
+ }
+ }
+ else // check for global method defs
+ if (TypeFromToken(pRec->m_tkTo) == mdtMethodDef)
+ {
+ int isGlobal = 0;
+ if (!FAILED(m_pRegMetaEmit->IsGlobal(pRec->m_tkTo, &isGlobal)))
+ {
+ // check for global methods
+ if (isGlobal != 0)
+ {
+ fInjectSecurityAttributes = TRUE;
+ }
+ }
+ }
+
+ if (fInjectSecurityAttributes)
+ {
+ // check to see if the token already has a custom attribute
+ const void *pbData = NULL; // [OUT] Put pointer to data here.
+ ULONG cbData = 0;
+
+ if (fMarkEachTokenAsCritical)
+ {
+ // Check if the Type already has SecurityCritical
+ BOOL fInjectSecurityCriticalEverything = TRUE;
+ if (S_OK == m_pRegMetaEmit->GetCustomAttributeByName(pRec->m_tkTo, COR_SECURITYCRITICAL_ATTRIBUTE_FULL_W, &pbData, &cbData))
+ {
+ // if value is non-0 (i.e. 1), then it is SecurityCritical.Everything - so do not inject another
+ fInjectSecurityCriticalEverything = !(NULL != pbData && cbData == 8 &&
+ memcmp(rgSecurityCriticalEverythingCtorValue, pbData, cbData) == 0);
+ }
+
+ // either inject or overwrite SecurityCritical.Everything
+ if (fInjectSecurityCriticalEverything)
+ {
+ IfFailGo(m_pRegMetaEmit->DefineCustomAttribute(
+ pRec->m_tkTo, tkSecurityCriticalEverythingAttribute,
+ rgSecurityCriticalEverythingCtorValue, // Use this if you need specific custom attribute data (presence of the attribute isn't enough)
+ sizeof(rgSecurityCriticalEverythingCtorValue), // Length of your custom attribute data
+ NULL));
+ }
+ }
+
+ // If the Type does NOT already have TAS then add it
+ if (fMarkEachTokenAsSafe &&
+ S_OK != m_pRegMetaEmit->GetCustomAttributeByName(pRec->m_tkTo, COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL_W, &pbData, &cbData))
+ {
+ IfFailGo(m_pRegMetaEmit->DefineCustomAttribute(
+ pRec->m_tkTo, tkSecurityTreatAsSafeAttribute,
+ rgTreatAsSafeCtorValue, // Use this if you need specific custom attribute data (presence of the attribute isn't enough)
+ sizeof(rgTreatAsSafeCtorValue), // Length of your custom attribute data
+ NULL));
+ }
+
+ hr = m_pRegMetaEmit->DefineCustomAttribute(pRec->m_tkTo, tkSecuritySafeCriticalAttribute,
+ rgSafeCriticalCtorValue, // Use this if you need specific custom attribute data (presence of the attribute isn't enough)
+ sizeof(rgSafeCriticalCtorValue), // Length of your custom attribute data
+ NULL);
+
+ }
+ }
+ }
+
+ // If the global scope is not Transparent, we should emit SecurityCritical.Explicit || Everything
+ if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityTransparent) != ISSCS_SecurityTransparent &&
+ (m_isscsSecurityCritical & ISSCS_SecurityCriticalEverything) != ISSCS_SecurityCriticalExplicit)
+ {
+ BOOL fEmitSecurityEverything = FALSE;
+ // in the case of Unmarked and TAS/Unmarked, we need to emit the SecurityCritical.Everything attribute
+ // if it hasn't already been emitted
+ if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityCriticalEverything) ==
+ ISSCS_SecurityCriticalEverything)
+ {
+ fEmitSecurityEverything = TRUE;
+ }
+ // otherwise, emit the SecurityCritical.Explicit attribute
+
+ BOOL fSecurityCriticalExists = FALSE;
+ // check to see if the assembly already has the appropriate SecurityCritical attribute
+ // [from one of the input scopes]
+ const void *pbData = NULL;
+ ULONG cbData = 0;
+ if (S_OK == ImportHelper::GetCustomAttributeByName(emit,
+ fakeModuleTypeRef, // This is the assembly def token
+ COR_SECURITYCRITICAL_ATTRIBUTE_FULL,
+ &pbData,
+ &cbData))
+ {
+ // find out if critical everything or explicit
+ // default to critical
+ // if value is non-0 (i.e. 1), then mark as SecurityCritical everything, otherwise, explicit
+ if (NULL != pbData && cbData == 8 &&
+ memcmp(rgSecurityCriticalEverythingCtorValue, pbData, cbData) == 0)
+ {
+ if (!fEmitSecurityEverything)
+ {
+ _ASSERTE(!"Unexpected SecurityCritical.Everything attribute detected");
+ IfFailGo(META_E_BADMETADATA);
+ }
+ }
+ else
+ {
+ if (fEmitSecurityEverything)
+ {
+ _ASSERTE(!"Unexpected SecurityCritical.Explicit attribute detected");
+ IfFailGo(META_E_BADMETADATA);
+ }
+ }
+ fSecurityCriticalExists = TRUE;
+ }
+
+ if (!fSecurityCriticalExists)
+ {
+ // retrieve the type and CustomAttribute
+ mdCustomAttribute tkSecurityCriticalAttributeExplicit;
+
+ mdTypeRef tkSecurityCriticalExplicitAttributeType = mdTokenNil;
+
+ IfFailGo(m_pRegMetaEmit->DefineTypeRefByName(
+ tkMscorlib, COR_SECURITYCRITICAL_ATTRIBUTE_FULL_W,
+ &tkSecurityCriticalExplicitAttributeType));
+
+ BYTE rgSigBytesSecurityCriticalExplicitCtorLocal[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 0x00, ELEMENT_TYPE_VOID};
+ BYTE rgSecurityCriticalExplicitCtorValue[] = COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EXPLICIT;
+
+ IfFailGo(m_pRegMetaEmit->DefineMemberRef(tkSecurityCriticalExplicitAttributeType,
+ COR_CONSTRUCTOR_METADATA_IDENTIFIER,
+ rgSigBytesSecurityCriticalExplicitCtorLocal,
+ sizeof(rgSigBytesSecurityCriticalExplicitCtorLocal),
+ &tkSecurityCriticalAttributeExplicit));
+
+ IfFailGo(m_pRegMetaEmit->DefineCustomAttribute(
+ fakeModuleTypeRef,
+ fEmitSecurityEverything?tkSecurityCriticalEverythingAttribute:tkSecurityCriticalAttributeExplicit,
+ fEmitSecurityEverything?rgSecurityCriticalEverythingCtorValue:rgSecurityCriticalExplicitCtorValue,
+ fEmitSecurityEverything?sizeof(rgSecurityCriticalEverythingCtorValue):sizeof(rgSecurityCriticalExplicitCtorValue),
+ NULL));
+
+ }
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeSecurityCriticalAttributes()
+
+//*******************************************************************************
+// Helper to copy an InterfaceImpl record
+//*******************************************************************************
+HRESULT NEWMERGER::CopyInterfaceImpl(
+ InterfaceImplRec *pRecEmit, // [IN] the emit record to fill
+ MergeImportData *pImportData, // [IN] the importing context
+ InterfaceImplRec *pRecImp) // [IN] the record to import
+{
+ HRESULT hr;
+ mdToken tkParent;
+ mdToken tkInterface;
+ CMiniMdRW *pMiniMdEmit = GetMiniMdEmit();
+ CMiniMdRW *pMiniMdImp;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+
+ tkParent = pMiniMdImp->getClassOfInterfaceImpl(pRecImp);
+ tkInterface = pMiniMdImp->getInterfaceOfInterfaceImpl(pRecImp);
+
+ IfFailGo( pCurTkMap->Remap(tkParent, &tkParent) );
+ IfFailGo( pCurTkMap->Remap(tkInterface, &tkInterface) );
+
+ IfFailGo( pMiniMdEmit->PutToken( TBL_InterfaceImpl, InterfaceImplRec::COL_Class, pRecEmit, tkParent) );
+ IfFailGo( pMiniMdEmit->PutToken( TBL_InterfaceImpl, InterfaceImplRec::COL_Interface, pRecEmit, tkInterface) );
+
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::CopyInterfaceImpl()
+
+
+//*****************************************************************************
+// Merge Assembly table
+//*****************************************************************************
+HRESULT NEWMERGER::MergeAssembly()
+{
+ HRESULT hr = NOERROR;
+ AssemblyRec *pRecImport = NULL;
+ AssemblyRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ LPCUTF8 szTmp;
+ const BYTE *pbTmp;
+ ULONG cbTmp;
+ ULONG iRecord;
+ TOKENREC *pTokenRec;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ if (!pMiniMdImport->getCountAssemblys())
+ goto ErrExit; // There is no Assembly in the import scope to merge.
+
+ // Copy the Assembly map record to the Emit scope and send a token remap notifcation
+ // to the client. No duplicate checking needed since the Assembly can be present in
+ // only one scope and there can be atmost one entry.
+ IfFailGo(pMiniMdImport->GetAssemblyRecord(1, &pRecImport));
+ IfFailGo(pMiniMdEmit->AddAssemblyRecord(&pRecEmit, &iRecord));
+
+ pRecEmit->Copy(pRecImport);
+
+ IfFailGo(pMiniMdImport->getPublicKeyOfAssembly(pRecImport, &pbTmp, &cbTmp));
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_Assembly, AssemblyRec::COL_PublicKey, pRecEmit,
+ pbTmp, cbTmp));
+
+ IfFailGo(pMiniMdImport->getNameOfAssembly(pRecImport, &szTmp));
+ IfFailGo(pMiniMdEmit->PutString(TBL_Assembly, AssemblyRec::COL_Name, pRecEmit, szTmp));
+
+ IfFailGo(pMiniMdImport->getLocaleOfAssembly(pRecImport, &szTmp));
+ IfFailGo(pMiniMdEmit->PutString(TBL_Assembly, AssemblyRec::COL_Locale, pRecEmit, szTmp));
+
+ // record the token movement.
+ IfFailGo(pCurTkMap->InsertNotFound(
+ TokenFromRid(1, mdtAssembly),
+ false,
+ TokenFromRid(iRecord, mdtAssembly),
+ &pTokenRec));
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeAssembly()
+
+
+
+
+//*****************************************************************************
+// Merge File table
+//*****************************************************************************
+HRESULT NEWMERGER::MergeFiles()
+{
+ HRESULT hr = NOERROR;
+ FileRec *pRecImport = NULL;
+ FileRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ LPCUTF8 szTmp;
+ const void *pbTmp;
+ ULONG cbTmp;
+ ULONG iCount;
+ ULONG i;
+ ULONG iRecord;
+ TOKENREC *pTokenRec;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountFiles();
+
+ // Loop through all File records and copy them to the Emit scope.
+ // Since there can only be one File table in all the scopes combined,
+ // there isn't any duplicate checking that needs to be done.
+ for (i = 1; i <= iCount; i++)
+ {
+ IfFailGo(pMiniMdImport->GetFileRecord(i, &pRecImport));
+ IfFailGo(pMiniMdEmit->AddFileRecord(&pRecEmit, &iRecord));
+
+ pRecEmit->Copy(pRecImport);
+
+ IfFailGo(pMiniMdImport->getNameOfFile(pRecImport, &szTmp));
+ IfFailGo(pMiniMdEmit->PutString(TBL_File, FileRec::COL_Name, pRecEmit, szTmp));
+
+ IfFailGo(pMiniMdImport->getHashValueOfFile(pRecImport, (const BYTE **)&pbTmp, &cbTmp));
+ IfFailGo(pMiniMdEmit->PutBlob(TBL_File, FileRec::COL_HashValue, pRecEmit, pbTmp, cbTmp));
+
+ // record the token movement.
+ IfFailGo(pCurTkMap->InsertNotFound(
+ TokenFromRid(i, mdtFile),
+ false,
+ TokenFromRid(iRecord, mdtFile),
+ &pTokenRec));
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeFiles()
+
+
+//*****************************************************************************
+// Merge ExportedType table
+//*****************************************************************************
+HRESULT NEWMERGER::MergeExportedTypes()
+{
+ HRESULT hr = NOERROR;
+ ExportedTypeRec *pRecImport = NULL;
+ ExportedTypeRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ LPCUTF8 szTmp;
+ mdToken tkTmp;
+ ULONG iCount;
+ ULONG i;
+ ULONG iRecord;
+ TOKENREC *pTokenRec;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountExportedTypes();
+
+ // Loop through all ExportedType records and copy them to the Emit scope.
+ // Since there can only be one ExportedType table in all the scopes combined,
+ // there isn't any duplicate checking that needs to be done.
+ for (i = 1; i <= iCount; i++)
+ {
+ IfFailGo(pMiniMdImport->GetExportedTypeRecord(i, &pRecImport));
+ IfFailGo(pMiniMdEmit->AddExportedTypeRecord(&pRecEmit, &iRecord));
+
+ pRecEmit->Copy(pRecImport);
+
+ IfFailGo(pMiniMdImport->getTypeNameOfExportedType(pRecImport, &szTmp));
+ IfFailGo(pMiniMdEmit->PutString(TBL_ExportedType, ExportedTypeRec::COL_TypeName, pRecEmit, szTmp));
+
+ IfFailGo(pMiniMdImport->getTypeNamespaceOfExportedType(pRecImport, &szTmp));
+ IfFailGo(pMiniMdEmit->PutString(TBL_ExportedType, ExportedTypeRec::COL_TypeNamespace, pRecEmit, szTmp));
+
+ tkTmp = pMiniMdImport->getImplementationOfExportedType(pRecImport);
+ IfFailGo(pCurTkMap->Remap(tkTmp, &tkTmp));
+ IfFailGo(pMiniMdEmit->PutToken(TBL_ExportedType, ExportedTypeRec::COL_Implementation,
+ pRecEmit, tkTmp));
+
+
+ // record the token movement.
+ IfFailGo(pCurTkMap->InsertNotFound(
+ TokenFromRid(i, mdtExportedType),
+ false,
+ TokenFromRid(iRecord, mdtExportedType),
+ &pTokenRec));
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeExportedTypes()
+
+
+//*****************************************************************************
+// Merge ManifestResource table
+//*****************************************************************************
+HRESULT NEWMERGER::MergeManifestResources()
+{
+ HRESULT hr = NOERROR;
+ ManifestResourceRec *pRecImport = NULL;
+ ManifestResourceRec *pRecEmit = NULL;
+ CMiniMdRW *pMiniMdImport;
+ CMiniMdRW *pMiniMdEmit;
+ LPCUTF8 szTmp;
+ mdToken tkTmp;
+ ULONG iCount;
+ ULONG i;
+ ULONG iRecord;
+ TOKENREC *pTokenRec;
+
+ MergeImportData *pImportData;
+ MDTOKENMAP *pCurTkMap;
+
+ pMiniMdEmit = GetMiniMdEmit();
+
+ for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData)
+ {
+ // for each import scope
+ pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+
+ // set the current MDTokenMap
+ pCurTkMap = pImportData->m_pMDTokenMap;
+ iCount = pMiniMdImport->getCountManifestResources();
+
+ // Loop through all ManifestResource records and copy them to the Emit scope.
+ // Since there can only be one ManifestResource table in all the scopes combined,
+ // there isn't any duplicate checking that needs to be done.
+ for (i = 1; i <= iCount; i++)
+ {
+ IfFailGo(pMiniMdImport->GetManifestResourceRecord(i, &pRecImport));
+ IfFailGo(pMiniMdEmit->AddManifestResourceRecord(&pRecEmit, &iRecord));
+
+ pRecEmit->Copy(pRecImport);
+
+ IfFailGo(pMiniMdImport->getNameOfManifestResource(pRecImport, &szTmp));
+ IfFailGo(pMiniMdEmit->PutString(TBL_ManifestResource, ManifestResourceRec::COL_Name,
+ pRecEmit, szTmp));
+
+ tkTmp = pMiniMdImport->getImplementationOfManifestResource(pRecImport);
+ IfFailGo(pCurTkMap->Remap(tkTmp, &tkTmp));
+ IfFailGo(pMiniMdEmit->PutToken(TBL_ManifestResource, ManifestResourceRec::COL_Implementation,
+ pRecEmit, tkTmp));
+
+ // record the token movement.
+ IfFailGo(pCurTkMap->InsertNotFound(
+ TokenFromRid(i, mdtManifestResource),
+ false,
+ TokenFromRid(iRecord, mdtManifestResource),
+ &pTokenRec));
+ }
+ }
+ErrExit:
+ return hr;
+} // HRESULT NEWMERGER::MergeManifestResources()
+
+
+
+
+
+//*****************************************************************************
+// Error handling. Call back to host to see what they want to do.
+//*****************************************************************************
+HRESULT NEWMERGER::OnError(
+ HRESULT hrIn, // The error HR we're reporting.
+ MergeImportData *pImportData, // The input scope with the error.
+ mdToken token) // The token with the error.
+{
+ // This function does a QI and a Release on every call. However, it should be
+ // called very infrequently, and lets the scope just keep a generic handler.
+ IMetaDataError *pIErr = NULL;
+ IUnknown *pHandler = pImportData->m_pHandler;
+ CMiniMdRW *pMiniMd = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd);
+ CQuickArray<WCHAR> rName; // Name of the TypeDef in unicode.
+ LPCUTF8 szTypeName;
+ LPCUTF8 szNSName;
+ TypeDefRec *pTypeRec;
+ int iLen; // Length of a name.
+ mdToken tkParent;
+ HRESULT hr = NOERROR;
+
+ if (pHandler && pHandler->QueryInterface(IID_IMetaDataError, (void**)&pIErr)==S_OK)
+ {
+ switch (hrIn)
+ {
+
+ case META_E_PARAM_COUNTS:
+ case META_E_METHD_NOT_FOUND:
+ case META_E_METHDIMPL_INCONSISTENT:
+ {
+ LPCUTF8 szMethodName;
+ MethodRec *pMethodRec;
+
+ // Method name.
+ _ASSERTE(TypeFromToken(token) == mdtMethodDef);
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(token), &pMethodRec));
+ IfFailGo(pMiniMd->getNameOfMethod(pMethodRec, &szMethodName));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzMethodName, szMethodName);
+ IfNullGo(wzMethodName);
+
+ // Type and its name.
+ IfFailGo( pMiniMd->FindParentOfMethodHelper(token, &tkParent) );
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName));
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rName.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName);
+
+ PostError(hrIn, (LPWSTR) rName.Ptr(), wzMethodName, token);
+ break;
+ }
+ case META_E_FIELD_NOT_FOUND:
+ {
+ LPCUTF8 szFieldName;
+ FieldRec *pFieldRec;
+
+ // Field name.
+ _ASSERTE(TypeFromToken(token) == mdtFieldDef);
+ IfFailGo(pMiniMd->GetFieldRecord(RidFromToken(token), &pFieldRec));
+ IfFailGo(pMiniMd->getNameOfField(pFieldRec, &szFieldName));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzFieldName, szFieldName);
+ IfNullGo(wzFieldName);
+
+ // Type and its name.
+ IfFailGo( pMiniMd->FindParentOfFieldHelper(token, &tkParent) );
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName));
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rName.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName);
+
+ PostError(hrIn, (LPWSTR) rName.Ptr(), wzFieldName, token);
+ break;
+ }
+ case META_E_EVENT_NOT_FOUND:
+ {
+ LPCUTF8 szEventName;
+ EventRec *pEventRec;
+
+ // Event name.
+ _ASSERTE(TypeFromToken(token) == mdtEvent);
+ IfFailGo(pMiniMd->GetEventRecord(RidFromToken(token), &pEventRec));
+ IfFailGo(pMiniMd->getNameOfEvent(pEventRec, &szEventName));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzEventName, szEventName);
+ IfNullGo(wzEventName);
+
+ // Type and its name.
+ IfFailGo( pMiniMd->FindParentOfEventHelper(token, &tkParent) );
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName));
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rName.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName);
+
+ PostError(hrIn, (LPWSTR) rName.Ptr(), wzEventName, token);
+ break;
+ }
+ case META_E_PROP_NOT_FOUND:
+ {
+ LPCUTF8 szPropertyName;
+ PropertyRec *pPropertyRec;
+
+ // Property name.
+ _ASSERTE(TypeFromToken(token) == mdtProperty);
+ IfFailGo(pMiniMd->GetPropertyRecord(RidFromToken(token), &pPropertyRec));
+ IfFailGo(pMiniMd->getNameOfProperty(pPropertyRec, &szPropertyName));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzPropertyName, szPropertyName);
+ IfNullGo(wzPropertyName);
+
+ // Type and its name.
+ IfFailGo( pMiniMd->FindParentOfPropertyHelper(token, &tkParent) );
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName));
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rName.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName);
+
+ PostError(hrIn, (LPWSTR) rName.Ptr(), wzPropertyName, token);
+ break;
+ }
+ case META_S_PARAM_MISMATCH:
+ {
+ LPCUTF8 szMethodName;
+ MethodRec *pMethodRec;
+ mdToken tkMethod;
+
+ // Method name.
+ _ASSERTE(TypeFromToken(token) == mdtParamDef);
+ IfFailGo( pMiniMd->FindParentOfParamHelper(token, &tkMethod) );
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkMethod), &pMethodRec));
+ IfFailGo(pMiniMd->getNameOfMethod(pMethodRec, &szMethodName));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzMethodName, szMethodName);
+ IfNullGo(wzMethodName);
+
+ // Type and its name.
+ IfFailGo( pMiniMd->FindParentOfMethodHelper(token, &tkParent) );
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName));
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rName.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName);
+
+ // use the error hresult so that we can post the correct error.
+ PostError(META_E_PARAM_MISMATCH, wzMethodName, (LPWSTR) rName.Ptr(), token);
+ break;
+ }
+ case META_E_INTFCEIMPL_NOT_FOUND:
+ {
+ InterfaceImplRec *pRec; // The InterfaceImpl
+ mdToken tkIface; // Token of the implemented interface.
+ CQuickArray<WCHAR> rIface; // Name of the Implemented Interface in unicode.
+ TypeRefRec *pRef; // TypeRef record when II is a typeref.
+ InterfaceImplRec *pInterfaceImplRec;
+
+ // Get the record.
+ _ASSERTE(TypeFromToken(token) == mdtInterfaceImpl);
+ IfFailGo(pMiniMd->GetInterfaceImplRecord(RidFromToken(token), &pRec));
+ // Get the name of the class.
+ tkParent = pMiniMd->getClassOfInterfaceImpl(pRec);
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName));
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rName.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName);
+
+ // Get the name of the implemented interface.
+ IfFailGo(pMiniMd->GetInterfaceImplRecord(RidFromToken(token), &pInterfaceImplRec));
+ tkIface = pMiniMd->getInterfaceOfInterfaceImpl(pInterfaceImplRec);
+ if (TypeFromToken(tkIface) == mdtTypeDef)
+ { // If it is a typedef...
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkIface), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName));
+ }
+ else
+ { // If it is a typeref...
+ _ASSERTE(TypeFromToken(tkIface) == mdtTypeRef);
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkIface), &pRef));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pRef, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pRef, &szNSName));
+ }
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rIface.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rIface.Ptr(), iLen+1, szNSName, szTypeName);
+
+
+ PostError(hrIn, (LPWSTR) rName.Ptr(), (LPWSTR)rIface.Ptr(), token);
+ break;
+ }
+ case META_E_CLASS_LAYOUT_INCONSISTENT:
+ case META_E_METHOD_COUNTS:
+ case META_E_FIELD_COUNTS:
+ case META_E_EVENT_COUNTS:
+ case META_E_PROPERTY_COUNTS:
+ {
+ // get the type name.
+ _ASSERTE(TypeFromToken(token) == mdtTypeDef);
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(token), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName));
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rName.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName);
+
+ PostError(hrIn, (LPWSTR) rName.Ptr(), token);
+ break;
+ }
+ case META_E_GENERICPARAM_INCONSISTENT:
+ {
+ // If token is type, get type name; if method, get method name.
+ LPWSTR wzName;
+ LPCUTF8 szMethodName;
+ MethodRec *pMethodRec;
+
+ if ((TypeFromToken(token) == mdtMethodDef))
+ {
+ // Get the method name.
+ IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(token), &pMethodRec));
+ IfFailGo(pMiniMd->getNameOfMethod(pMethodRec, &szMethodName));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzMethodName, szMethodName);
+ IfNullGo(wzMethodName);
+ wzName = wzMethodName;
+ }
+ else
+ {
+ // Get the type name.
+ _ASSERTE(TypeFromToken(token) == mdtTypeDef);
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(token), &pTypeRec));
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName));
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rName.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName);
+ wzName = (LPWSTR)rName.Ptr();
+ }
+
+ PostError(hrIn, wzName, token);
+ break;
+ }
+ case META_E_TYPEDEF_MISSING:
+ {
+ TypeRefRec *pRef; // TypeRef record when II is a typeref.
+
+ // Get the record.
+ _ASSERTE(TypeFromToken(token) == mdtTypeRef);
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(token), &pRef));
+ IfFailGo(pMiniMd->getNameOfTypeRef(pRef, &szTypeName));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pRef, &szNSName));
+
+ // Put namespace + name together.
+ iLen = ns::GetFullLength(szNSName, szTypeName);
+ IfFailGo(rName.ReSizeNoThrow(iLen+1));
+ ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName);
+
+
+ PostError(hrIn, (LPWSTR) rName.Ptr(), token);
+ break;
+ }
+ default:
+ {
+ PostError(hrIn, token);
+ break;
+ }
+ }
+ hr = pIErr->OnError(hrIn, token);
+ }
+ else
+ hr = S_FALSE;
+ErrExit:
+ if (pIErr)
+ pIErr->Release();
+ return (hr);
+} // NEWMERGER::OnError
+
+#endif //FEATURE_METADATA_EMIT_ALL
diff --git a/src/md/compiler/newmerger.h b/src/md/compiler/newmerger.h
new file mode 100644
index 0000000000..fc89ab7f61
--- /dev/null
+++ b/src/md/compiler/newmerger.h
@@ -0,0 +1,256 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// NewMerger.h
+//
+
+//
+// Contains utility code for MD directory
+//
+//*****************************************************************************
+#ifndef __NEWMERGER__h__
+#define __NEWMERGER__h__
+
+class RegMeta;
+
+class MDTOKENMAP;
+
+// module-level awareness of Security critical annotions
+typedef BYTE InputScopeSecurityCriticalStatus;
+#define ISSCS_Unknown 0x0
+#define ISSCS_SecurityCritical 0x1
+#define ISSCS_SecurityCriticalEverything (ISSCS_SecurityCritical | 0x2)
+#define ISSCS_SecurityCriticalExplicit (ISSCS_SecurityCritical)
+#define ISSCS_SecurityTransparent 0x4
+#define ISSCS_SecurityTreatAsSafe 0x8
+#define ISSCS_SECURITYCRITICAL_LEGACY (ISSCS_SecurityCriticalEverything | ISSCS_SecurityTreatAsSafe)
+#define ISSCS_SECURITYCRITICAL_FLAGS (ISSCS_SecurityCriticalEverything | ISSCS_SecurityTransparent)
+
+//*********************************************************************
+// MergeImportData
+//*********************************************************************
+class MergeImportData
+{
+public:
+ RegMeta *m_pRegMetaImport;
+ IUnknown *m_pHandler;
+ IMapToken *m_pHostMapToken;
+ MDTOKENMAP *m_pMDTokenMap;
+ MergeImportData *m_pNextImportData;
+
+ mdMemberRef m_tkSuppressMergeCheckCtor; // caches the SuppressMergeCheckAttribute's .ctor token
+ mdMemberRef m_tkHandleProcessCorruptedStateCtor; // caches the memberRef token to HandleProcessCorruptedStateExceptionsAttribute's .ctor token
+
+ // import contains assembly-level SecurityTransparent or SecurityCritical
+ InputScopeSecurityCriticalStatus m_isscsSecurityCriticalStatus;
+#if _DEBUG
+ int m_iImport; // debug only. This is the ith import for merge.
+#endif // _DEBUG
+};
+
+//*********************************************************************
+// MergeTypeData
+//*********************************************************************
+struct MergeTypeData
+{
+ ULONG m_cMethods;
+ ULONG m_cFields;
+ ULONG m_cEvents;
+ ULONG m_cProperties;
+ BOOL m_bSuppressMergeCheck;
+};
+
+
+//*********************************************************************
+// Class to handle merge
+//*********************************************************************
+class NEWMERGER
+{
+ friend class RegMeta;
+public:
+ NEWMERGER();
+ ~NEWMERGER();
+
+ HRESULT Init(RegMeta *pRegMetaDest);
+
+ HRESULT AddImport(
+ IMetaDataImport2 *pImport, // [IN] The scope to be merged.
+ IMapToken *pHostMapToken, // [IN] Host IMapToken interface to receive token remap notification
+ IUnknown *pHandler); // [IN] An object to receive to receive error notification.
+
+ HRESULT Merge(MergeFlags flags, CorRefToDefCheck optimizeRefToDef);
+
+protected:
+
+ CMiniMdRW *GetMiniMdEmit();
+
+ HRESULT InitMergeTypeData();
+
+ HRESULT MergeTypeDefNamesOnly();
+ HRESULT MergeModuleRefs();
+ HRESULT MergeAssemblyRefs();
+ HRESULT MergeTypeRefs();
+ HRESULT CompleteMergeTypeDefs();
+
+ HRESULT CopyTypeDefPartially(
+ TypeDefRec *pRecEmit, // [IN] the emit record to fill
+ CMiniMdRW *pMiniMdImport, // [IN] the importing scope
+ TypeDefRec *pRecImp); // [IN] the record to import
+
+ // helpers for merging tables
+ HRESULT MergeModule( );
+ HRESULT MergeTypeDefChildren();
+ HRESULT MergeInterfaceImpls( );
+ HRESULT MergeMemberRefs( );
+ HRESULT MergePinvoke();
+
+ HRESULT MergeConstants( );
+ HRESULT MergeCustomAttributes( );
+ HRESULT MergeFieldMarshals( );
+ HRESULT MergeDeclSecuritys( );
+ HRESULT MergeClassLayouts( );
+ HRESULT MergeFieldLayouts( );
+ HRESULT MergeFieldRVAs();
+ HRESULT MergeMethodImpls( );
+ HRESULT MergeStandAloneSigs();
+ HRESULT MergeMethodSpecs();
+ HRESULT MergeTypeSpecs();
+ HRESULT MergeSourceFiles( );
+ HRESULT MergeBlocks( );
+ HRESULT MergeScopes( );
+ HRESULT MergeLocalVariables( );
+ HRESULT MergeStrings( );
+
+ HRESULT MergeAssembly();
+ HRESULT MergeFiles();
+ HRESULT MergeExportedTypes();
+ HRESULT MergeManifestResources();
+
+ // helpers for SecurityCritical-related merging
+ InputScopeSecurityCriticalStatus CheckInputScopeIsCritical(MergeImportData* pImportData, HRESULT& hr);
+ HRESULT RetrieveStandardSecurityCriticalMetaData(
+ mdAssemblyRef& tkMscorlib,
+ mdTypeRef& securityEnum,
+ BYTE*& rgSigBytesSecurityCriticalEverythingCtor,
+ DWORD& dwSigEverythingSize,
+ BYTE*& rgSigBytesSecurityCriticalExplicitCtor,
+ DWORD& dwSigExplicitSize);
+
+ HRESULT MergeSecurityCriticalModuleLevelAttributes(
+ MergeImportData* pImportData,
+ mdToken tkParentImp, TOKENREC* pTypeRec,
+ mdToken mrSecurityTreatAsSafeAttributeCtor,
+ mdToken mrSecurityTransparentAttributeCtor,
+ mdToken mrSecurityCriticalExplicitAttributeCtor,
+ mdToken mrSecurityCriticalEverythingAttributeCtor);
+ HRESULT MergeSecurityCriticalAttributes();
+
+ // copy over a interfaceimpl record
+ HRESULT CopyInterfaceImpl(
+ InterfaceImplRec *pRecEmit, // [IN] the emit record to fill
+ MergeImportData *pImportData, // [IN] the importing context
+ InterfaceImplRec *pRecImp); // [IN] the record to import
+
+ // verification helpers
+ HRESULT VerifyMethods(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit);
+ HRESULT VerifyFields(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit);
+ HRESULT VerifyEvents(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit);
+ HRESULT VerifyProperties(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit);
+ HRESULT VerifyParams(MergeImportData *pImportData, mdMethodDef mdImp, mdMethodDef mdEmit);
+ HRESULT VerifyGenericParams(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit);
+ HRESULT VerifyGenericParamConstraints(MergeImportData *pImportData, mdGenericParam gpImp, mdGenericParam gpEmit);
+
+ // Copy helpers
+ HRESULT CopyMethods(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit);
+ HRESULT CopyFields(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit);
+ HRESULT CopyEvents(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit);
+ HRESULT CopyProperties(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit);
+ HRESULT CopyParams(MergeImportData *pImportData, mdMethodDef mdImp, mdMethodDef mdEmit);
+ HRESULT CopyGenericParams(MergeImportData *pImportData, mdToken tkImp, mdToken tkEmit);
+ HRESULT CopyGenericParamConstraints(MergeImportData *pImportData, mdGenericParam gpImp, mdGenericParam gpEmit);
+
+ HRESULT CopyMethod(
+ MergeImportData *pImportData, // [IN] import scope
+ MethodRec *pRecImp, // [IN] the record to import
+ MethodRec *pRecEmit); // [IN] the emit record to fill
+
+ HRESULT CopyField(
+ MergeImportData *pImportData, // [IN] import scope
+ FieldRec *pRecImp, // [IN] the record to import
+ FieldRec *pRecEmit); // [IN] the emit record to fill
+
+ HRESULT CopyEvent(
+ MergeImportData *pImportData, // [IN] import scope
+ EventRec *pRecImp, // [IN] the record to import
+ EventRec *pRecEmit); // [IN] the emit record to fill
+
+ HRESULT CopyProperty(
+ MergeImportData *pImportData, // [IN] import scope
+ PropertyRec *pRecImp, // [IN] the record to import
+ PropertyRec *pRecEmit); // [IN] the emit record to fill
+
+ HRESULT CopyParam(
+ MergeImportData *pImportData, // [IN] import scope
+ ParamRec *pRecImp, // [IN] the record to import
+ ParamRec *pRecEmit); // [IN] the emit record to fill
+
+ HRESULT CopyMethodSemantics(
+ MergeImportData *pImportData,
+ mdToken tkImport, // Event or property in the import scope
+ mdToken tkEmit); // corresponding event or property in the emitting scope
+
+ HRESULT VerifyMethod(
+ MergeImportData *pImportData,
+ mdMethodDef mdImp, // [IN] the emit record to fill
+ mdMethodDef mdEmit); // [IN] the record to import
+
+ HRESULT OnError(HRESULT hr, MergeImportData *pImportData, mdToken token);
+
+private:
+ RegMeta *m_pRegMetaEmit;
+ MergeImportData *m_pImportDataList;
+ MergeImportData **m_pImportDataTail;
+ MergeFlags m_dwMergeFlags;
+ BOOL m_fDupCheck;
+ CorRefToDefCheck m_optimizeRefToDef;
+ // the combined value of the Security Critical input scopes (e.g. UNION of each scope's attributes)
+ // if ANY of the scopes have a bit set, then we must do some merging
+ InputScopeSecurityCriticalStatus m_isscsSecurityCritical;
+ // the common values of the Security Critical input scopes (e.g. INTERSECTION of each scope's attributes)
+ // if all scopes have the same bit set, then we can emit one bit at the final output scope
+ InputScopeSecurityCriticalStatus m_isscsSecurityCriticalAllScopes;
+
+ CDynArray<MergeTypeData> m_rMTDs;
+#if _DEBUG
+ int m_iImport; // debug only. To count how many import scopes to be merged.
+#endif // _DEBUG
+};
+
+
+#define CheckContinuableErrorEx(EXPR, HANDLER, TOKEN) \
+{ \
+ HRESULT hrOnErr, hrExpr; \
+ hrExpr = EXPR; \
+ \
+ hrOnErr = OnError(hrExpr, HANDLER, TOKEN); \
+ if (hrOnErr != S_OK) \
+ { \
+ if (hrOnErr == S_FALSE) \
+ { \
+ hr = hrExpr; \
+ } \
+ else if (SUCCEEDED(hrOnErr)) \
+ { \
+ hr = E_UNEXPECTED; \
+ } \
+ else if (FAILED(hrOnErr)) \
+ { \
+ hr = hrOnErr; \
+ } \
+ IfFailGo(hr); \
+ } \
+}
+
+
+#endif // __NEWMERGER__h__
diff --git a/src/md/compiler/regmeta.cpp b/src/md/compiler/regmeta.cpp
new file mode 100644
index 0000000000..230d1e4ff0
--- /dev/null
+++ b/src/md/compiler/regmeta.cpp
@@ -0,0 +1,1588 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// RegMeta.cpp
+//
+
+//
+// Implementation for meta data public interface methods.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "metadata.h"
+#include "corerror.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+#include "filtermanager.h"
+#include "mdperf.h"
+#include "switches.h"
+#include "posterror.h"
+#include "stgio.h"
+#include "sstring.h"
+
+#include "mdinternalrw.h"
+
+
+#include <metamodelrw.h>
+
+#define DEFINE_CUSTOM_NODUPCHECK 1
+#define DEFINE_CUSTOM_DUPCHECK 2
+#define SET_CUSTOM 3
+
+#if defined(_DEBUG) && defined(_TRACE_REMAPS)
+#define LOGGING
+#endif
+#include <log.h>
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4102)
+#endif
+
+RegMeta::RegMeta() :
+ m_pStgdb(0),
+ m_pStgdbFreeList(NULL),
+ m_pUnk(0),
+ m_pFilterManager(NULL),
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+ m_pInternalImport(NULL),
+#endif
+ m_pSemReadWrite(NULL),
+ m_fOwnSem(false),
+ m_bRemap(false),
+ m_bSaveOptimized(false),
+ m_hasOptimizedRefToDef(false),
+ m_pHandler(0),
+ m_fIsTypeDefDirty(false),
+ m_fIsMemberDefDirty(false),
+ m_fStartedEE(false),
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ m_pCorHost(NULL),
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+ m_pAppDomain(NULL),
+ m_OpenFlags(0),
+ m_cRef(0),
+ m_pFreeThreadedMarshaler(NULL),
+ m_bCached(false),
+ m_trLanguageType(0),
+ m_SetAPICaller(EXTERNAL_CALLER),
+ m_ModuleType(ValidatorModuleTypeInvalid),
+ m_pVEHandler(0),
+ m_bKeepKnownCa(false),
+ m_pCorProfileData(NULL),
+ m_ReorderingOptions(NoReordering)
+#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
+ , m_safeToDeleteStgdb(true)
+#endif
+{
+ memset(&m_OptionValue, 0, sizeof(OptionValue));
+
+#ifdef _DEBUG
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaBreak))
+ {
+ _ASSERTE(!"RegMeta()");
+ }
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_KeepKnownCA))
+ m_bKeepKnownCa = true;
+#endif // _DEBUG
+
+} // RegMeta::RegMeta()
+
+RegMeta::~RegMeta()
+{
+ BEGIN_CLEANUP_ENTRYPOINT;
+
+ _ASSERTE(!m_bCached);
+
+ HRESULT hr = S_OK;
+
+ LOCKWRITENORET();
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+ // This should have worked if we've cached the public interface in the past
+ _ASSERTE(SUCCEEDED(hr) || (m_pInternalImport == NULL) || (m_pInternalImport->GetCachedPublicInterface(false) == NULL));
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+ if (SUCCEEDED(hr))
+ {
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+ if (m_pInternalImport != NULL)
+ {
+ // RegMeta is going away. Make sure we clear up the pointer from MDInternalRW to this RegMeta.
+ if (FAILED(m_pInternalImport->SetCachedPublicInterface(NULL)))
+ { // Do nothing on error
+ }
+ m_pInternalImport = NULL;
+ m_fOwnSem = false;
+ }
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+ UNLOCKWRITE();
+ }
+
+ if (m_pFreeThreadedMarshaler)
+ {
+ m_pFreeThreadedMarshaler->Release();
+ m_pFreeThreadedMarshaler = NULL;
+ }
+
+ if (m_pSemReadWrite && m_fOwnSem)
+ delete m_pSemReadWrite;
+
+ // If this RegMeta is a wrapper on an external StgDB, release it.
+ if (IsOfExternalStgDB(m_OpenFlags))
+ {
+ _ASSERTE(m_pUnk != NULL); // Owning IUnknown for external StgDB.
+ if (m_pUnk)
+ m_pUnk->Release();
+ m_pUnk = 0;
+ }
+ else
+ { // Not a wrapper, so free our StgDB.
+ _ASSERTE(m_pUnk == NULL);
+ // It's possible m_pStdbg is NULL in OOM scenarios
+ if (m_pStgdb != NULL)
+ delete m_pStgdb;
+ m_pStgdb = 0;
+ }
+
+ // Delete the old copies of Stgdb list. This is the list track all of the
+ // old snapshuts with ReOpenWithMemory call.
+ CLiteWeightStgdbRW *pCur;
+ while (m_pStgdbFreeList)
+ {
+ pCur = m_pStgdbFreeList;
+ m_pStgdbFreeList = m_pStgdbFreeList->m_pNextStgdb;
+ delete pCur;
+ }
+
+ if (m_pVEHandler)
+ m_pVEHandler->Release();
+
+ // If This RegMeta spun up the runtime (probably to process security
+ // attributes), shut it down now.
+ if (m_fStartedEE)
+ {
+ m_pAppDomain->Release();
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ m_pCorHost->Stop();
+ m_pCorHost->Release();
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+ }
+
+ if (m_pFilterManager != NULL)
+ delete m_pFilterManager;
+
+
+ if (m_OptionValue.m_RuntimeVersion != NULL)
+ delete[] m_OptionValue.m_RuntimeVersion;
+
+ END_CLEANUP_ENTRYPOINT;
+
+} // RegMeta::~RegMeta()
+
+HRESULT RegMeta::SetOption(OptionValue *pOptionValue)
+{
+ _ASSERTE(pOptionValue);
+ char* pszRuntimeVersion = NULL;
+
+ if (pOptionValue->m_RuntimeVersion != NULL)
+ {
+ SIZE_T dwBufferSize = strlen(pOptionValue->m_RuntimeVersion) + 1; // +1 for null
+ pszRuntimeVersion = new (nothrow) char[dwBufferSize];
+ if (pszRuntimeVersion == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ strcpy_s(pszRuntimeVersion, dwBufferSize, pOptionValue->m_RuntimeVersion);
+ }
+
+ memcpy(&m_OptionValue, pOptionValue, sizeof(OptionValue));
+ m_OptionValue.m_RuntimeVersion = pszRuntimeVersion;
+
+ return S_OK;
+}// SetOption
+
+
+//*****************************************************************************
+// Initialize with an existing stgdb.
+//*****************************************************************************
+__checkReturn
+HRESULT
+RegMeta::InitWithStgdb(
+ IUnknown *pUnk, // The IUnknown that owns the life time for the existing stgdb
+ CLiteWeightStgdbRW *pStgdb) // existing light weight stgdb
+{
+ // RegMeta created this way will not create a read/write lock semaphore.
+ HRESULT hr = S_OK;
+
+ _ASSERTE(m_pStgdb == NULL);
+ m_tdModule = COR_GLOBAL_PARENT_TOKEN;
+ m_pStgdb = pStgdb;
+
+ m_OpenFlags = ofExternalStgDB;
+
+ // remember the owner of the light weight stgdb
+ // AddRef it to ensure the lifetime
+ //
+ m_pUnk = pUnk;
+ m_pUnk->AddRef();
+ IfFailGo(m_pStgdb->m_MiniMd.GetOption(&m_OptionValue));
+ErrExit:
+ return hr;
+} // RegMeta::InitWithStgdb
+
+#ifdef FEATURE_METADATA_EMIT
+
+//*****************************************************************************
+// call stgdb InitNew
+//*****************************************************************************
+__checkReturn
+HRESULT
+RegMeta::CreateNewMD()
+{
+ HRESULT hr = NOERROR;
+
+ m_OpenFlags = ofWrite;
+
+ // Allocate our m_pStgdb.
+ _ASSERTE(m_pStgdb == NULL);
+ IfNullGo(m_pStgdb = new (nothrow) CLiteWeightStgdbRW);
+
+ // Initialize the new, empty database.
+
+ // First tell the new database what sort of metadata to create
+ m_pStgdb->m_MiniMd.m_OptionValue.m_MetadataVersion = m_OptionValue.m_MetadataVersion;
+ m_pStgdb->m_MiniMd.m_OptionValue.m_InitialSize = m_OptionValue.m_InitialSize;
+ IfFailGo(m_pStgdb->InitNew());
+
+ // Set up the Module record.
+ ULONG iRecord;
+ ModuleRec *pModule;
+ GUID mvid;
+ IfFailGo(m_pStgdb->m_MiniMd.AddModuleRecord(&pModule, &iRecord));
+ IfFailGo(CoCreateGuid(&mvid));
+ IfFailGo(m_pStgdb->m_MiniMd.PutGuid(TBL_Module, ModuleRec::COL_Mvid, pModule, mvid));
+
+ // Add the dummy module typedef which we are using to parent global items.
+ TypeDefRec *pRecord;
+ IfFailGo(m_pStgdb->m_MiniMd.AddTypeDefRecord(&pRecord, &iRecord));
+ m_tdModule = TokenFromRid(iRecord, mdtTypeDef);
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_TypeDef, TypeDefRec::COL_Name, pRecord, COR_WMODULE_CLASS));
+ IfFailGo(m_pStgdb->m_MiniMd.SetOption(&m_OptionValue));
+
+ if (IsThreadSafetyOn())
+ {
+ m_pSemReadWrite = new (nothrow) UTSemReadWrite();
+ IfNullGo(m_pSemReadWrite);
+ IfFailGo(m_pSemReadWrite->Init());
+ m_fOwnSem = true;
+
+ INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
+ }
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+ // initialize the embedded merger
+ m_newMerger.Init(this);
+#endif //FEATURE_METADATA_EMIT_ALL
+
+ErrExit:
+ return hr;
+} // RegMeta::CreateNewMD
+
+#endif //FEATURE_METADATA_EMIT
+
+//*****************************************************************************
+// call stgdb OpenForRead
+//*****************************************************************************
+HRESULT RegMeta::OpenExistingMD(
+ LPCWSTR szDatabase, // Name of database.
+ void *pData, // Data to open on top of, 0 default.
+ ULONG cbData, // How big is the data.
+ ULONG dwOpenFlags) // Flags for the open.
+{
+ HRESULT hr = NOERROR;
+ void *pbData = pData; // Pointer to original or copied data.
+
+
+
+ m_OpenFlags = dwOpenFlags;
+
+ if (!IsOfReOpen(dwOpenFlags))
+ {
+ // Allocate our m_pStgdb, if we should.
+ _ASSERTE(m_pStgdb == NULL);
+ IfNullGo( m_pStgdb = new (nothrow) CLiteWeightStgdbRW );
+ }
+
+ IfFailGo( m_pStgdb->OpenForRead(
+ szDatabase,
+ pbData,
+ cbData,
+ m_OpenFlags) );
+
+ if (m_pStgdb->m_MiniMd.m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0 &&
+ m_pStgdb->m_MiniMd.m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0)
+ m_OptionValue.m_MetadataVersion = MDVersion1;
+
+ else
+ m_OptionValue.m_MetadataVersion = MDVersion2;
+
+
+
+ IfFailGo( m_pStgdb->m_MiniMd.SetOption(&m_OptionValue) );
+
+ if (IsThreadSafetyOn())
+ {
+ m_pSemReadWrite = new (nothrow) UTSemReadWrite();
+ IfNullGo(m_pSemReadWrite);
+ IfFailGo(m_pSemReadWrite->Init());
+ m_fOwnSem = true;
+
+ INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
+ }
+
+ if (!IsOfReOpen(dwOpenFlags))
+ {
+#ifdef FEATURE_METADATA_EMIT_ALL
+ // initialize the embedded merger
+ m_newMerger.Init(this);
+#endif //FEATURE_METADATA_EMIT_ALL
+
+ // There must always be a Global Module class and its the first entry in
+ // the TypeDef table.
+ m_tdModule = TokenFromRid(1, mdtTypeDef);
+ }
+
+ErrExit:
+
+ return hr;
+} //RegMeta::OpenExistingMD
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+HRESULT RegMeta::OpenExistingMD(
+ IMDCustomDataSource* pDataSource, // Name of database.
+ ULONG dwOpenFlags) // Flags to control open.
+{
+ HRESULT hr = NOERROR;
+
+ m_OpenFlags = dwOpenFlags;
+
+ if (!IsOfReOpen(dwOpenFlags))
+ {
+ // Allocate our m_pStgdb, if we should.
+ _ASSERTE(m_pStgdb == NULL);
+ IfNullGo(m_pStgdb = new (nothrow)CLiteWeightStgdbRW);
+ }
+
+ IfFailGo(m_pStgdb->OpenForRead(
+ pDataSource,
+ m_OpenFlags));
+
+ if (m_pStgdb->m_MiniMd.m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0 &&
+ m_pStgdb->m_MiniMd.m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0)
+ m_OptionValue.m_MetadataVersion = MDVersion1;
+
+ else
+ m_OptionValue.m_MetadataVersion = MDVersion2;
+
+
+
+ IfFailGo(m_pStgdb->m_MiniMd.SetOption(&m_OptionValue));
+
+ if (IsThreadSafetyOn())
+ {
+ m_pSemReadWrite = new (nothrow)UTSemReadWrite();
+ IfNullGo(m_pSemReadWrite);
+ IfFailGo(m_pSemReadWrite->Init());
+ m_fOwnSem = true;
+
+ INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
+ }
+
+ if (!IsOfReOpen(dwOpenFlags))
+ {
+#ifdef FEATURE_METADATA_EMIT_ALL
+ // initialize the embedded merger
+ m_newMerger.Init(this);
+#endif //FEATURE_METADATA_EMIT_ALL
+
+ // There must always be a Global Module class and its the first entry in
+ // the TypeDef table.
+ m_tdModule = TokenFromRid(1, mdtTypeDef);
+ }
+
+ErrExit:
+
+ return hr;
+} //RegMeta::OpenExistingMD
+#endif // FEATURE_METADATA_CUSTOM_DATA_SOURCE
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+//*****************************************************************************
+// Gets a cached Internal importer, if available.
+//
+// Arguments:
+// fWithLock - if true, takes a reader lock.
+// If false, assumes caller is handling the synchronization.
+//
+// Returns:
+// A cached Internal importer, which gets addreffed. Caller must release!
+// If no importer is set, returns NULL
+//
+// Notes:
+// This function also does not trigger the creation of Internal interface.
+// Set the cached importer via code:RegMeta.SetCachedInternalInterface
+//
+// Implements internal API code:IMetaDataHelper::GetCachedInternalInterface.
+//*****************************************************************************
+IUnknown* RegMeta::GetCachedInternalInterface(BOOL fWithLock)
+{
+ IUnknown *pRet = NULL;
+ HRESULT hr = S_OK;
+
+ if (fWithLock)
+ {
+ LOCKREAD();
+
+ pRet = m_pInternalImport;
+ }
+ else
+ {
+ pRet = m_pInternalImport;
+ }
+ if (pRet) pRet->AddRef();
+
+ErrExit:
+
+ return pRet;
+} //RegMeta::GetCachedInternalInterface
+
+//*****************************************************************************
+// Set the cached Internal interface. This function will return an Error is the
+// current cached internal interface is not empty and trying set a non-empty internal
+// interface. One RegMeta will only associated
+// with one Internal Object. Unless we have bugs somewhere else. It will QI on the
+// IUnknown for the IMDInternalImport. If this failed, error will be returned.
+// Note: Caller should take a write lock
+//
+// This does addref the importer (the public and private importers maintain
+// weak references to each other).
+//
+// Implements internal API code:IMetaDataHelper::SetCachedInternalInterface.
+//*****************************************************************************
+HRESULT RegMeta::SetCachedInternalInterface(IUnknown *pUnk)
+{
+ HRESULT hr = NOERROR;
+ IMDInternalImport *pInternal = NULL;
+
+ if (pUnk)
+ {
+ if (m_pInternalImport)
+ {
+ _ASSERTE(!"Bad state!");
+ }
+ IfFailRet( pUnk->QueryInterface(IID_IMDInternalImport, (void **) &pInternal) );
+
+ // Should be non-null
+ _ASSERTE(pInternal);
+ m_pInternalImport = pInternal;
+
+ // We don't want to add ref the internal interface, so undo the AddRef() from the QI.
+ pInternal->Release();
+ }
+ else
+ {
+ // Internal interface is going away before the public interface. Take ownership on the
+ // reader writer lock.
+ m_fOwnSem = true;
+ m_pInternalImport = NULL;
+ }
+ return hr;
+} // RegMeta::SetCachedInternalInterface
+
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+//*****************************************************************************
+// IUnknown
+//*****************************************************************************
+
+ULONG RegMeta::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+} // ULONG RegMeta::AddRef()
+
+
+HRESULT
+RegMeta::QueryInterface(
+ REFIID riid,
+ void ** ppUnk)
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+ int fIsInterfaceRW = false;
+ *ppUnk = 0;
+
+ if (riid == IID_IUnknown)
+ {
+ *ppUnk = (IUnknown *)(IMetaDataImport2 *)this;
+ }
+ else if (riid == IID_IMDCommon)
+ {
+ *ppUnk = (IMDCommon *)this;
+ }
+ else if (riid == IID_IMetaDataImport)
+ {
+ *ppUnk = (IMetaDataImport2 *)this;
+ }
+ else if (riid == IID_IMetaDataImport2)
+ {
+ *ppUnk = (IMetaDataImport2 *)this;
+ }
+ else if (riid == IID_IMetaDataAssemblyImport)
+ {
+ *ppUnk = (IMetaDataAssemblyImport *)this;
+ }
+ else if (riid == IID_IMetaDataTables)
+ {
+ *ppUnk = static_cast<IMetaDataTables *>(this);
+ }
+ else if (riid == IID_IMetaDataTables2)
+ {
+ *ppUnk = static_cast<IMetaDataTables2 *>(this);
+ }
+
+#ifndef FEATURE_METADATA_STANDALONE_WINRT
+ else if (riid == IID_IMetaDataInfo)
+ {
+ *ppUnk = static_cast<IMetaDataInfo *>(this);
+ }
+#endif //!FEATURE_METADATA_STANDALONE_WINRT
+
+#ifdef FEATURE_METADATA_EMIT
+ else if (riid == IID_IMetaDataEmit)
+ {
+ *ppUnk = (IMetaDataEmit2 *)this;
+ fIsInterfaceRW = true;
+ }
+ else if (riid == IID_IMetaDataEmit2)
+ {
+ *ppUnk = (IMetaDataEmit2 *)this;
+ fIsInterfaceRW = true;
+ }
+ else if (riid == IID_IMetaDataAssemblyEmit)
+ {
+ *ppUnk = (IMetaDataAssemblyEmit *)this;
+ fIsInterfaceRW = true;
+ }
+#endif //FEATURE_METADATA_EMIT
+
+#if defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR)
+ else if (riid == IID_IMetaDataValidate)
+ {
+ *ppUnk = (IMetaDataValidate *)this;
+ }
+#endif //defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR)
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+ else if (riid == IID_IMetaDataFilter)
+ {
+ *ppUnk = (IMetaDataFilter *)this;
+ }
+#endif //FEATURE_METADATA_EMIT_ALL
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+ else if (riid == IID_IMetaDataHelper)
+ {
+ *ppUnk = (IMetaDataHelper *)this;
+ }
+ else if (riid == IID_IMDInternalEmit)
+ {
+ *ppUnk = static_cast<IMDInternalEmit *>(this);
+ }
+ else if (riid == IID_IGetIMDInternalImport)
+ {
+ *ppUnk = static_cast<IGetIMDInternalImport *>(this);
+ }
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+#if defined(FEATURE_METADATA_EMIT) && defined(FEATURE_METADATA_INTERNAL_APIS)
+ else if (riid == IID_IMetaDataEmitHelper)
+ {
+ *ppUnk = (IMetaDataEmitHelper *)this;
+ fIsInterfaceRW = true;
+ }
+#endif //FEATURE_METADATA_EMIT && FEATURE_METADATA_INTERNAL_APIS
+
+#ifdef FEATURE_METADATA_IN_VM
+#ifdef FEATURE_COMINTEROP
+ else if (riid == IID_IMarshal)
+ {
+ // We will only repond to this interface if scope is opened for ReadOnly
+ if (IsOfReadOnly(m_OpenFlags))
+ {
+ if (m_pFreeThreadedMarshaler == NULL)
+ {
+ // Guard ourselves against first time QI on IMarshal from two different threads..
+ LOCKWRITE();
+ if (m_pFreeThreadedMarshaler == NULL)
+ {
+ // First time! Create the FreeThreadedMarshaler
+ IfFailGo(CoCreateFreeThreadedMarshaler((IUnknown *)(IMetaDataEmit2 *)this, &m_pFreeThreadedMarshaler));
+ }
+ }
+
+ _ASSERTE(m_pFreeThreadedMarshaler != NULL);
+
+ IfFailGo(m_pFreeThreadedMarshaler->QueryInterface(riid, ppUnk));
+
+ // AddRef has happened in the QueryInterface and thus should just return
+ goto ErrExit;
+ }
+ else
+ {
+ IfFailGo(E_NOINTERFACE);
+ }
+ }
+#endif // FEATURE_COMINTEROP
+#ifdef FEATURE_PREJIT
+ else if (riid == IID_IMetaDataCorProfileData)
+ {
+ *ppUnk = (IMetaDataCorProfileData *)this;
+ }
+ else if (riid == IID_IMDInternalMetadataReorderingOptions)
+ {
+ *ppUnk = (IMDInternalMetadataReorderingOptions *)this;
+ }
+#endif //FEATURE_PREJIT
+#endif //FEATURE_METADATA_IN_VM
+ else
+ {
+ IfFailGo(E_NOINTERFACE);
+ }
+
+ if (fIsInterfaceRW && IsOfReadOnly(m_OpenFlags))
+ {
+ // They are asking for a read/write interface and this scope was
+ // opened as Read-Only
+
+ *ppUnk = NULL;
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+ }
+
+ if (fIsInterfaceRW)
+ {
+ LOCKWRITENORET();
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_pStgdb->m_MiniMd.ConvertToRW();
+ }
+
+ if (FAILED(hr))
+ {
+ *ppUnk = NULL;
+ goto ErrExit;
+ }
+ }
+
+ AddRef();
+ErrExit:
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::QueryInterface
+
+#ifndef FEATURE_METADATA_STANDALONE_WINRT
+
+//---------------------------------------------------------------------------------------
+//
+// Returns the memory region of the mapped file and type of its mapping. The choice of the file mapping type
+// for each scope is CLR implementation specific and user cannot explicitly set it.
+//
+// The memory is valid only as long as the underlying MetaData scope is opened (there's a reference to
+// a MetaData interface for this scope).
+//
+// Implements public API code:IMetaDataInfo::GetFileMapping.
+//
+// Arguments:
+// ppvData - Fills with pointer to the start of the mapped file.
+// pcbData - Fills with the size of the mapped memory region (for flat-mapping it is the size of the
+// file).
+// pdwMappingType - Fills with type of file mapping (code:CorFileMapping).
+// Current CLR implementation returns always code:fmFlat. The other value(s) are reserved for future
+// usage. See code:StgIO::MapFileToMem#CreateFileMapping_SEC_IMAGE for more details.
+//
+// Return Value:
+// S_OK - All output data are filled.
+// COR_E_NOTSUPPORTED - CLR cannot (or doesn't want to) provide the memory region.
+// This can happen when:
+// - The MetaData scope was opened with flag code:ofWrite or code:ofCopyMemory.
+// Note: code:ofCopyMemory could be supported in future CLR versions. For example if we change
+// code:CLiteWeightStgdbRW::OpenForRead to copy whole file (or add a new flag ofCopyWholeFile).
+// - The MetaData scope was opened without flag code:ofReadOnly.
+// Note: We could support this API without code:ofReadOnly flag in future CLR versions. We just
+// need some test coverage and user scenario for it.
+// - Only MetaData part of the file was opened using code:OpenScopeOnMemory.
+// - The file is not NT PE file (e.g. it is NT OBJ = .obj file produced by managed C++).
+// E_INVALIDARG - NULL was passed as an argument value.
+//
+HRESULT
+RegMeta::GetFileMapping(
+ const void ** ppvData,
+ ULONGLONG * pcbData,
+ DWORD * pdwMappingType)
+{
+ HRESULT hr = S_OK;
+
+ if ((ppvData == NULL) || (pcbData == NULL) || (pdwMappingType == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ // Note: Some of the following checks are duplicit (as some combinations are invalid and ensured by CLR
+ // implementation), but it is easier to check them all
+
+ // OpenScope flags have to be (ofRead | ofReadOnly) and not ofCopyMemory
+ // (as code:CLiteWeightStgdbRW::OpenForRead will copy only the MetaData part of the file)
+ if (((m_OpenFlags & ofReadWriteMask) != ofRead) ||
+ ((m_OpenFlags & ofReadOnly) == 0) ||
+ ((m_OpenFlags & ofCopyMemory) != 0))
+ {
+ IfFailGo(COR_E_NOTSUPPORTED);
+ }
+ // The file has to be NT PE file (not CLDB = managed C++ .obj file) and we have to have its full mapping
+ // (see code:CLiteWeightStgdbRW::OpenForRead)
+ if ((m_pStgdb->m_pImage == NULL) ||
+ (m_pStgdb->m_dwImageSize == 0) ||
+ (m_pStgdb->GetFileType() != FILETYPE_NTPE))
+ {
+ IfFailGo(COR_E_NOTSUPPORTED);
+ }
+ if (m_pStgdb->m_pStgIO->GetFlags() != DBPROP_TMODEF_READ)
+ {
+ IfFailGo(COR_E_NOTSUPPORTED);
+ }
+ // The file has to be flat-mapped, or copied to memory (file mapping code:MTYPE_IMAGE is not currently
+ // supported - see code:StgIO::MapFileToMem#CreateFileMapping_SEC_IMAGE)
+ // Note: Only small files (<=64K) are copied to memory - see code:StgIO::MapFileToMem#CopySmallFiles
+ if ((m_pStgdb->m_pStgIO->GetMemoryMappedType() != MTYPE_FLAT) &&
+ (m_pStgdb->m_pStgIO->GetMemoryMappedType() != MTYPE_NOMAPPING))
+ {
+ IfFailGo(COR_E_NOTSUPPORTED);
+ }
+ // All necessary conditions are satisfied
+
+ *ppvData = m_pStgdb->m_pImage;
+ *pcbData = m_pStgdb->m_dwImageSize;
+ // We checked that the file was flat-mapped above
+ *pdwMappingType = fmFlat;
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ *ppvData = NULL;
+ *pcbData = 0;
+ *pdwMappingType = 0;
+ }
+
+ return hr;
+} // RegMeta::GetFileMapping
+
+#endif //!FEATURE_METADATA_STANDALONE_WINRT
+
+//------------------------------------------------------------------------------
+// Metadata dump
+//
+#ifdef _DEBUG
+
+#define STRING_BUFFER_LEN 1024
+#define ENUM_BUFFER_SIZE 10
+
+int DumpMD_Write(__in __in_z const char *str)
+{
+ OutputDebugStringA(str);
+ return 0; // strlen(str);
+} // int DumpMD_Write()
+
+int DumpMD_WriteLine(__in __in_z const char *str)
+{
+ OutputDebugStringA(str);
+ OutputDebugStringA("\n");
+ return 0; // strlen(str);
+} // int DumpMD_Write()
+
+int DumpMD_VWriteMarker(__in __in_z const char *str, va_list marker)
+{
+ CQuickBytes m_output;
+
+ int count = -1;
+ int i = 1;
+ HRESULT hr;
+
+ while (count < 0)
+ {
+ if (FAILED(hr = m_output.ReSizeNoThrow(STRING_BUFFER_LEN * i)))
+ return 0;
+ count = _vsnprintf((char *)m_output.Ptr(), STRING_BUFFER_LEN * i, str, marker);
+ i *= 2;
+ }
+ OutputDebugStringA((LPCSTR)m_output.Ptr());
+ return count;
+} // int DumpMD_VWriteMarker()
+
+int DumpMD_VWrite(__in __in_z const char *str, ...)
+{
+ va_list marker;
+ int count;
+
+ va_start(marker, str);
+ count = DumpMD_VWriteMarker(str, marker);
+ va_end(marker);
+ return count;
+} // int DumpMD_VWrite()
+
+int DumpMD_VWriteLine(__in __in_z const char *str, ...)
+{
+ va_list marker;
+ int count;
+
+ va_start(marker, str);
+ count = DumpMD_VWriteMarker(str, marker);
+ DumpMD_Write("\n");
+ va_end(marker);
+ return count;
+} // int DumpMD_VWriteLine()
+
+
+const char *DumpMD_DumpRawNameOfType(RegMeta *pMD, ULONG iType)
+{
+ if (iType <= iRidMax)
+ {
+ const char *pNameTable;
+ pMD->GetTableInfo(iType, 0,0,0,0, &pNameTable);
+ return pNameTable;
+ }
+ else
+ // Is the field a coded token?
+ if (iType <= iCodedTokenMax)
+ {
+ int iCdTkn = iType - iCodedToken;
+ const char *pNameCdTkn;
+ pMD->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn);
+ return pNameCdTkn;
+ }
+
+ // Fixed type.
+ switch (iType)
+ {
+ case iBYTE:
+ return "BYTE";
+ case iSHORT:
+ return "short";
+ case iUSHORT:
+ return "USHORT";
+ case iLONG:
+ return "long";
+ case iULONG:
+ return "ULONG";
+ case iSTRING:
+ return "string";
+ case iGUID:
+ return "GUID";
+ case iBLOB:
+ return "blob";
+ }
+ // default:
+ static char buf[30];
+ sprintf_s(buf, NumItems(buf), "unknown type 0x%02x", iType);
+ return buf;
+} // const char *DumpMD_DumpRawNameOfType()
+
+void DumpMD_DumpRawCol(RegMeta *pMD, ULONG ixTbl, ULONG ixCol, ULONG rid, bool bStats)
+{
+ ULONG ulType; // Type of a column.
+ ULONG ulVal; // Value of a column.
+ LPCUTF8 pString; // Pointer to a string.
+ const void *pBlob; // Pointer to a blob.
+ ULONG cb; // Size of something.
+
+ pMD->GetColumn(ixTbl, ixCol, rid, &ulVal);
+ pMD->GetColumnInfo(ixTbl, ixCol, 0, 0, &ulType, 0);
+
+ if (ulType <= iRidMax)
+ {
+ const char *pNameTable;
+ pMD->GetTableInfo(ulType, 0,0,0,0, &pNameTable);
+ DumpMD_VWrite("%s[%x]", pNameTable, ulVal);
+ }
+ else
+ // Is the field a coded token?
+ if (ulType <= iCodedTokenMax)
+ {
+ int iCdTkn = ulType - iCodedToken;
+ const char *pNameCdTkn;
+ pMD->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn);
+ DumpMD_VWrite("%s[%08x]", pNameCdTkn, ulVal);
+ }
+ else
+ {
+ // Fixed type.
+ switch (ulType)
+ {
+ case iBYTE:
+ DumpMD_VWrite("%02x", ulVal);
+ break;
+ case iSHORT:
+ case iUSHORT:
+ DumpMD_VWrite("%04x", ulVal);
+ break;
+ case iLONG:
+ case iULONG:
+ DumpMD_VWrite("%08x", ulVal);
+ break;
+ case iSTRING:
+ DumpMD_VWrite("string#%x", ulVal);
+ if (bStats && ulVal)
+ {
+ pMD->GetString(ulVal, &pString);
+ cb = (ULONG) strlen(pString) + 1;
+ DumpMD_VWrite("(%d)", cb);
+ }
+ break;
+ case iGUID:
+ DumpMD_VWrite("guid#%x", ulVal);
+ if (bStats && ulVal)
+ {
+ DumpMD_VWrite("(16)");
+ }
+ break;
+ case iBLOB:
+ DumpMD_VWrite("blob#%x", ulVal);
+ if (bStats && ulVal)
+ {
+ pMD->GetBlob(ulVal, &cb, &pBlob);
+ cb += 1;
+ if (cb > 128)
+ cb += 1;
+ if (cb > 16535)
+ cb += 1;
+ DumpMD_VWrite("(%d)", cb);
+ }
+ break;
+ default:
+ DumpMD_VWrite("unknown type 0x%04x", ulVal);
+ break;
+ }
+ }
+} // void DumpMD_DumpRawCol()
+
+ULONG DumpMD_DumpRawColStats(RegMeta *pMD, ULONG ixTbl, ULONG ixCol, ULONG cRows)
+{
+ ULONG rslt = 0;
+ ULONG ulType; // Type of a column.
+ ULONG ulVal; // Value of a column.
+ LPCUTF8 pString; // Pointer to a string.
+ const void *pBlob; // Pointer to a blob.
+ ULONG cb; // Size of something.
+
+ pMD->GetColumnInfo(ixTbl, ixCol, 0, 0, &ulType, 0);
+
+ if (IsHeapType(ulType))
+ {
+ for (ULONG rid=1; rid<=cRows; ++rid)
+ {
+ pMD->GetColumn(ixTbl, ixCol, rid, &ulVal);
+ // Fixed type.
+ switch (ulType)
+ {
+ case iSTRING:
+ if (ulVal)
+ {
+ pMD->GetString(ulVal, &pString);
+ cb = (ULONG) strlen(pString);
+ rslt += cb + 1;
+ }
+ break;
+ case iGUID:
+ if (ulVal)
+ rslt += 16;
+ break;
+ case iBLOB:
+ if (ulVal)
+ {
+ pMD->GetBlob(ulVal, &cb, &pBlob);
+ rslt += cb + 1;
+ if (cb > 128)
+ rslt += 1;
+ if (cb > 16535)
+ rslt += 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return rslt;
+} // ULONG DumpMD_DumpRawColStats()
+
+int DumpMD_DumpHex(
+ const char *szPrefix, // String prefix for first line.
+ const void *pvData, // The data to print.
+ ULONG cbData, // Bytes of data to print.
+ int bText=1, // If true, also dump text.
+ ULONG nLine=16) // Bytes per line to print.
+{
+ const BYTE *pbData = static_cast<const BYTE*>(pvData);
+ ULONG i; // Loop control.
+ ULONG nPrint; // Number to print in an iteration.
+ ULONG nSpace; // Spacing calculations.
+ ULONG nPrefix; // Size of the prefix.
+ ULONG nLines=0; // Number of lines printed.
+ const char *pPrefix; // For counting spaces in the prefix.
+
+ // Round down to 8 characters.
+ nLine = nLine & ~0x7;
+
+ for (nPrefix=0, pPrefix=szPrefix; *pPrefix; ++pPrefix)
+ {
+ if (*pPrefix == '\t')
+ nPrefix = (nPrefix + 8) & ~7;
+ else
+ ++nPrefix;
+ }
+ //nPrefix = strlen(szPrefix);
+ do
+ { // Write the line prefix.
+ if (szPrefix)
+ DumpMD_VWrite("%s:", szPrefix);
+ else
+ DumpMD_VWrite("%*s:", nPrefix, "");
+ szPrefix = 0;
+ ++nLines;
+
+ // Calculate spacing.
+ nPrint = min(cbData, nLine);
+ nSpace = nLine - nPrint;
+
+ // dump in hex.
+ for(i=0; i<nPrint; i++)
+ {
+ if ((i&7) == 0)
+ DumpMD_Write(" ");
+ DumpMD_VWrite("%02x ", pbData[i]);
+ }
+ if (bText)
+ {
+ // Space out to the text spot.
+ if (nSpace)
+ DumpMD_VWrite("%*s", nSpace*3+nSpace/8, "");
+ // Dump in text.
+ DumpMD_Write(">");
+ for(i=0; i<nPrint; i++)
+ DumpMD_VWrite("%c", (isprint(pbData[i])) ? pbData[i] : ' ');
+ // Space out the text, and finish the line.
+ DumpMD_VWrite("%*s<", nSpace, "");
+ }
+ DumpMD_VWriteLine("");
+
+ // Next data to print.
+ cbData -= nPrint;
+ pbData += nPrint;
+ }
+ while (cbData > 0);
+
+ return nLines;
+} // int DumpMD_DumpHex()
+
+void DumpMD_DisplayUserStrings(
+ RegMeta *pMD) // The scope to dump.
+{
+ HCORENUM stringEnum = NULL; // string enumerator.
+ mdString Strings[ENUM_BUFFER_SIZE]; // String tokens from enumerator.
+ CQuickArray<WCHAR> rUserString; // Buffer to receive string.
+ WCHAR *szUserString; // Working pointer into buffer.
+ ULONG chUserString; // Size of user string.
+ CQuickArray<char> rcBuf; // Buffer to hold the BLOB version of the string.
+ char *szBuf; // Working pointer into buffer.
+ ULONG chBuf; // Saved size of the user string.
+ ULONG count; // Items returned from enumerator.
+ ULONG totalCount = 1; // Running count of strings.
+ bool bUnprint = false; // Is an unprintable character found?
+ HRESULT hr; // A result.
+ while (SUCCEEDED(hr = pMD->EnumUserStrings( &stringEnum,
+ Strings, NumItems(Strings), &count)) &&
+ count > 0)
+ {
+ if (totalCount == 1)
+ { // If only one, it is the NULL string, so don't print it.
+ DumpMD_WriteLine("User Strings");
+ DumpMD_WriteLine("-------------------------------------------------------");
+ }
+ for (ULONG i = 0; i < count; i++, totalCount++)
+ {
+ do { // Try to get the string into the existing buffer.
+ hr = pMD->GetUserString( Strings[i], rUserString.Ptr(),(ULONG32)rUserString.MaxSize(), &chUserString);
+ if (hr == CLDB_S_TRUNCATION)
+ { // Buffer wasn't big enough, try to enlarge it.
+ if (FAILED(rUserString.ReSizeNoThrow(chUserString)))
+ DumpMD_VWriteLine("malloc failed: %#8x.", E_OUTOFMEMORY);
+ continue;
+ }
+ } while (0);
+ if (FAILED(hr)) DumpMD_VWriteLine("GetUserString failed: %#8x.", hr);
+
+ szUserString = rUserString.Ptr();
+ chBuf = chUserString;
+
+ DumpMD_VWrite("%08x : (%2d) L\"", Strings[i], chUserString);
+ while (chUserString)
+ {
+ switch (*szUserString)
+ {
+ case 0:
+ DumpMD_Write("\\0"); break;
+ case W('\r'):
+ DumpMD_Write("\\r"); break;
+ case W('\n'):
+ DumpMD_Write("\\n"); break;
+ case W('\t'):
+ DumpMD_Write("\\t"); break;
+ default:
+ if (iswprint(*szUserString))
+ DumpMD_VWrite("%lc", *szUserString);
+ else
+ {
+ bUnprint = true;
+ DumpMD_Write(".");
+ }
+ break;
+ }
+ ++szUserString;
+ --chUserString;
+ }
+ DumpMD_WriteLine("\"");
+
+ // Print the user string as a blob if an unprintable character is found.
+ if (bUnprint)
+ {
+ bUnprint = false;
+ szUserString = rUserString.Ptr();
+ // REVISIT_TODO: ReSizeNoThrow can fail. Check its return value and add an error path.
+ rcBuf.ReSizeNoThrow(81); //(chBuf * 5 + 1);
+ szBuf = rcBuf.Ptr();
+ ULONG j,k;
+ DumpMD_WriteLine("\t\tUser string has unprintables, hex format below:");
+ for (j = 0,k=0; j < chBuf; j++)
+ {
+ // See rcBuf.ResSizeNoThrow(81) above
+ sprintf_s (&szBuf[k*5],81-(k*5), "%04x ", szUserString[j]);
+ k++;
+ if((k==16)||(j == (chBuf-1)))
+ {
+ szBuf[k*5] = '\0';
+ DumpMD_VWriteLine("\t\t%s", szBuf);
+ k=0;
+ }
+ }
+ }
+ }
+ }
+ if (stringEnum)
+ pMD->CloseEnum(stringEnum);
+} // void MDInfo::DisplayUserStrings()
+
+void DumpMD_DumpRawHeaps(
+ RegMeta *pMD) // The scope to dump.
+{
+ HRESULT hr; // A result.
+ ULONG ulSize; // Bytes in a heap.
+ const BYTE *pData; // Pointer to a blob.
+ ULONG cbData; // Size of a blob.
+ ULONG oData; // Offset of current blob.
+ char rcPrefix[30]; // To format line prefix.
+
+ pMD->GetBlobHeapSize(&ulSize);
+ DumpMD_VWriteLine("");
+ DumpMD_VWriteLine("Blob Heap: %d(%#x) bytes", ulSize,ulSize);
+ oData = 0;
+ do
+ {
+ pMD->GetBlob(oData, &cbData, (const void**)&pData);
+ sprintf_s(rcPrefix, NumItems(rcPrefix), "%5x,%-2x", oData, cbData);
+ DumpMD_DumpHex(rcPrefix, pData, cbData);
+ hr = pMD->GetNextBlob(oData, &oData);
+ }
+ while (hr == S_OK);
+
+ pMD->GetStringHeapSize(&ulSize);
+ DumpMD_VWriteLine("");
+ DumpMD_VWriteLine("String Heap: %d(%#x) bytes", ulSize,ulSize);
+ oData = 0;
+ const char *pString;
+ do
+ {
+ pMD->GetString(oData, &pString);
+ sprintf_s(rcPrefix, NumItems(rcPrefix), "%08x", oData);
+ DumpMD_DumpHex(rcPrefix, pString, (ULONG)strlen(pString)+1);
+ if (*pString != 0)
+ DumpMD_VWrite("%08x: %s\n", oData, pString);
+ hr = pMD->GetNextString(oData, &oData);
+ }
+ while (hr == S_OK);
+ DumpMD_VWriteLine("");
+
+ DumpMD_DisplayUserStrings(pMD);
+
+} // void DumpMD_DumpRawHeaps()
+
+
+void DumpMD_DumpRaw(RegMeta *pMD, int iDump, bool bStats)
+{
+ ULONG cTables; // Tables in the database.
+ ULONG cCols; // Columns in a table.
+ ULONG cRows; // Rows in a table.
+ ULONG cbRow; // Bytes in a row of a table.
+ ULONG iKey; // Key column of a table.
+ const char *pNameTable; // Name of a table.
+ ULONG oCol; // Offset of a column.
+ ULONG cbCol; // Size of a column.
+ ULONG ulType; // Type of a column.
+ const char *pNameColumn; // Name of a column.
+ ULONG ulSize;
+
+ pMD->GetNumTables(&cTables);
+
+ pMD->GetStringHeapSize(&ulSize);
+ DumpMD_VWrite("Strings: %d(%#x)", ulSize, ulSize);
+ pMD->GetBlobHeapSize(&ulSize);
+ DumpMD_VWrite(", Blobs: %d(%#x)", ulSize, ulSize);
+ pMD->GetGuidHeapSize(&ulSize);
+ DumpMD_VWrite(", Guids: %d(%#x)", ulSize, ulSize);
+ pMD->GetUserStringHeapSize(&ulSize);
+ DumpMD_VWriteLine(", User strings: %d(%#x)", ulSize, ulSize);
+
+ for (ULONG ixTbl = 0; ixTbl < cTables; ++ixTbl)
+ {
+ pMD->GetTableInfo(ixTbl, &cbRow, &cRows, &cCols, &iKey, &pNameTable);
+
+ if (cRows == 0 && iDump < 3)
+ continue;
+
+ if (iDump >= 2)
+ DumpMD_VWriteLine("=================================================");
+ DumpMD_VWriteLine("%2d: %-20s cRecs:%5d(%#x), cbRec:%3d(%#x), cbTable:%6d(%#x)",
+ ixTbl, pNameTable, cRows, cRows, cbRow, cbRow, cbRow * cRows, cbRow * cRows);
+
+ if (iDump < 2)
+ continue;
+
+ // Dump column definitions for the table.
+ ULONG ixCol;
+ for (ixCol=0; ixCol<cCols; ++ixCol)
+ {
+ pMD->GetColumnInfo(ixTbl, ixCol, &oCol, &cbCol, &ulType, &pNameColumn);
+
+ DumpMD_VWrite(" col %2x:%c %-12s oCol:%2x, cbCol:%x, %-7s",
+ ixCol, ((ixCol==iKey)?'*':' '), pNameColumn, oCol, cbCol, DumpMD_DumpRawNameOfType(pMD, ulType));
+
+ if (bStats)
+ {
+ ulSize = DumpMD_DumpRawColStats(pMD, ixTbl, ixCol, cRows);
+ if (ulSize)
+ DumpMD_VWrite("(%d)", ulSize);
+ }
+ DumpMD_VWriteLine("");
+ }
+
+ if (iDump < 3)
+ continue;
+
+ // Dump the rows.
+ for (ULONG rid = 1; rid <= cRows; ++rid)
+ {
+ if (rid == 1)
+ DumpMD_VWriteLine("-------------------------------------------------");
+ DumpMD_VWrite(" %3x == ", rid);
+ for (ixCol=0; ixCol < cCols; ++ixCol)
+ {
+ if (ixCol) DumpMD_VWrite(", ");
+ DumpMD_VWrite("%d:", ixCol);
+ DumpMD_DumpRawCol(pMD, ixTbl, ixCol, rid, bStats);
+ }
+ DumpMD_VWriteLine("");
+ }
+ }
+
+ DumpMD_DumpRawHeaps(pMD);
+
+} // void DumpMD_DumpRaw()
+
+
+int DumpMD_impl(RegMeta *pMD)
+{
+ DumpMD_DumpRaw(pMD, 3, false);
+ return 0;
+}
+
+int DumpMD(UINT_PTR iMD)
+{
+ RegMeta *pMD = reinterpret_cast<RegMeta*>(iMD);
+ return DumpMD_impl(pMD);
+}
+
+#endif //_DEBUG
+
+//*****************************************************************************
+// Using the existing RegMeta and reopen with another chuck of memory. Make sure that all stgdb
+// is still kept alive.
+//*****************************************************************************
+HRESULT RegMeta::ReOpenWithMemory(
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData, // [in] Size of the data pointed to by pData.
+ DWORD dwReOpenFlags) // [in] ReOpen flags
+{
+ HRESULT hr = NOERROR;
+
+ // Only allow the ofCopyMemory and ofTakeOwnership flags
+ if (dwReOpenFlags != 0 && ((dwReOpenFlags & (~(ofCopyMemory|ofTakeOwnership))) > 0))
+ return E_INVALIDARG;
+
+ LOCKWRITE();
+
+
+ // put the current m_pStgdb to the free list
+ m_pStgdb->m_pNextStgdb = m_pStgdbFreeList;
+ m_pStgdbFreeList = m_pStgdb;
+ m_pStgdb = new (nothrow) CLiteWeightStgdbRW;
+ IfNullGo( m_pStgdb );
+ IfFailGo( OpenExistingMD(0 /* szFileName */, const_cast<void*>(pData), cbData, ofReOpen|dwReOpenFlags /* flags */) );
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+ // We've created a new Stgdb, but may still have an Internal Importer hanging around accessing the old Stgdb.
+ // The free list ensures we don't have a dangling pointer, but the
+ // If we have a corresponding InternalInterface, need to clear it because it's now using stale data.
+ // Others will need to update their Internal interface to get the new data.
+ {
+ HRESULT hrIgnore = SetCachedInternalInterface(NULL);
+ (void)hrIgnore; //prevent "unused variable" error from GCC
+ _ASSERTE(hrIgnore == NOERROR); // clearing the cached interface should always succeed.
+ }
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+ // we are done!
+ErrExit:
+ if (FAILED(hr))
+ {
+ // recover to the old state
+ if (m_pStgdb)
+ delete m_pStgdb;
+ m_pStgdb = m_pStgdbFreeList;
+ m_pStgdbFreeList = m_pStgdbFreeList->m_pNextStgdb;
+ }
+#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
+ else
+ {
+ if( !(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_MD_PreserveDebuggerMetadataMemory)) && IsSafeToDeleteStgdb())
+ {
+ // now that success is assured, delete the old block of memory
+ // This isn't normally a safe operation because we would have given out
+ // internal pointers to the memory. However when this feature is enabled
+ // we track calls that might have given out internal pointers. If none
+ // of the APIs were ever called then we can safely delete.
+ CLiteWeightStgdbRW* pStgdb = m_pStgdbFreeList;
+ m_pStgdbFreeList = m_pStgdbFreeList->m_pNextStgdb;
+ delete pStgdb;
+ }
+
+ MarkSafeToDeleteStgdb(); // As of right now, no APIs have given out internal pointers
+ // to the newly allocated stgdb
+ }
+#endif
+
+ return hr;
+} // RegMeta::ReOpenWithMemory
+
+
+//*****************************************************************************
+// This function returns the requested public interface based on the given
+// internal import interface.
+// A common path to call this is updating the matedata for dynamic modules.
+//*****************************************************************************
+STDAPI MDReOpenMetaDataWithMemoryEx(
+ void *pImport, // [IN] Given scope. public interfaces
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData, // [in] Size of the data pointed to by pData.
+ DWORD dwReOpenFlags) // [in] Flags for ReOpen
+{
+ HRESULT hr = S_OK;
+ IUnknown *pUnk = (IUnknown *) pImport;
+ IMetaDataImport2 *pMDImport = NULL;
+ RegMeta *pRegMeta = NULL;
+
+ _ASSERTE(pImport);
+
+ IfFailGo( pUnk->QueryInterface(IID_IMetaDataImport2, (void **) &pMDImport) );
+ pRegMeta = (RegMeta*) pMDImport;
+
+ IfFailGo( pRegMeta->ReOpenWithMemory(pData, cbData, dwReOpenFlags) );
+
+ErrExit:
+ if (pMDImport)
+ pMDImport->Release();
+
+ return hr;
+} // MDReOpenMetaDataWithMemoryEx
+
+STDAPI MDReOpenMetaDataWithMemory(
+ void *pImport, // [IN] Given scope. public interfaces
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData) // [in] Size of the data pointed to by pData.
+{
+ return MDReOpenMetaDataWithMemoryEx(pImport, pData, cbData, 0);
+}
+
+// --------------------------------------------------------------------------------------
+//
+// Zeros used by public APIs as return value (or pointer to this memory) for invalid input.
+// It is used by methods:
+// * code:RegMeta::GetPublicApiCompatibilityZeros, and
+// * code:RegMeta::GetPublicApiCompatibilityZerosOfSize.
+//
+const BYTE
+RegMeta::s_rgMetaDataPublicApiCompatibilityZeros[64] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+// --------------------------------------------------------------------------------------
+//
+// Returns pointer to zeros of size (cbSize).
+// Used by public APIs to return compatible values with previous releases.
+//
+const BYTE *
+RegMeta::GetPublicApiCompatibilityZerosOfSize(UINT32 cbSize)
+{
+ if (cbSize <= sizeof(s_rgMetaDataPublicApiCompatibilityZeros))
+ {
+ return s_rgMetaDataPublicApiCompatibilityZeros;
+ }
+ _ASSERTE(!"Dangerous call to this method! Reconsider fixing the caller.");
+ return NULL;
+} // RegMeta::GetPublicApiCompatibilityZerosOfSize
+
+
+
+
+//
+// returns the "built for" version of a metadata scope.
+//
+HRESULT RegMeta::GetVersionString( // S_OK or error.
+ LPCSTR *pVer) // [OUT] Put version string here.
+{
+ _ASSERTE(pVer != NULL);
+ HRESULT hr;
+ LOCKREAD();
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ if (m_pStgdb->m_pvMd != NULL)
+ {
+#endif
+ *pVer = reinterpret_cast<const char*>(reinterpret_cast<const STORAGESIGNATURE*>(m_pStgdb->m_pvMd)->pVersion);
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ }
+ else
+ {
+ //This emptry string matches the fallback behavior we have in other places that query the version string.
+ //From what I can tell the only caller to this method is the code that tests if we need to apply the WinMD adapter
+ //and it checks if pVer == "WindowsRuntime". We don't support the debugger custom metadata source scenario with WinMDs (yet?)
+ //so we intend for that check to return FALSE.
+ *pVer = "";
+ }
+#endif
+ hr = S_OK;
+ ErrExit:
+ return hr;
+}
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+//*****************************************************************************
+// IGetIMDInternalImport methods
+//*****************************************************************************
+HRESULT RegMeta::GetIMDInternalImport(
+ IMDInternalImport ** ppIMDInternalImport // [OUT] Buffer to receive IMDInternalImport*
+ )
+{
+ HRESULT hr = S_OK;
+ MDInternalRW *pInternalRW = NULL;
+ bool isLockedForWrite = false;
+ IUnknown *pIUnkInternal = NULL;
+ IUnknown *pThis = (IMetaDataImport2*)this;
+
+ pIUnkInternal = this->GetCachedInternalInterface(TRUE);
+ if (pIUnkInternal)
+ {
+ // there is already a cached Internal interface. GetCachedInternalInterface does add ref the
+ // returned interface
+ IfFailGo(pIUnkInternal->QueryInterface(IID_IMDInternalImport, (void**)ppIMDInternalImport));
+ goto ErrExit;
+ }
+
+ if (this->IsThreadSafetyOn())
+ {
+ _ASSERTE( this->GetReaderWriterLock() );
+ IfFailGo(this->GetReaderWriterLock()->LockWrite());
+ isLockedForWrite = true;
+ }
+
+ // check again. Maybe someone else beat us to setting the internal interface while we are waiting
+ // for the write lock. Don't need to grab the read lock since we already have the write lock.
+ pIUnkInternal = this->GetCachedInternalInterface(FALSE);
+ if (pIUnkInternal)
+ {
+ // there is already a cached Internal interface. GetCachedInternalInterface does add ref the
+ // returned interface
+ IfFailGo(pIUnkInternal->QueryInterface(IID_IMDInternalImport, (void**)ppIMDInternalImport));
+ goto ErrExit;
+ }
+
+ // now create the compressed object
+ IfNullGo( pInternalRW = new (nothrow) MDInternalRW );
+ IfFailGo( pInternalRW->InitWithStgdb(pThis, this->GetMiniStgdb() ) );
+
+ // make the public object and the internal object point to each other.
+ _ASSERTE( pInternalRW->GetReaderWriterLock() == NULL &&
+ (! this->IsThreadSafetyOn() || this->GetReaderWriterLock() != NULL ));
+ IfFailGo( this->SetCachedInternalInterface(static_cast<IMDInternalImportENC*>(pInternalRW)) );
+ IfFailGo( pInternalRW->SetCachedPublicInterface(pThis));
+ IfFailGo( pInternalRW->SetReaderWriterLock(this->GetReaderWriterLock() ));
+ IfFailGo( pInternalRW->QueryInterface(IID_IMDInternalImport, (void**)ppIMDInternalImport));
+
+ErrExit:
+ if (isLockedForWrite == true)
+ this->GetReaderWriterLock()->UnlockWrite();
+ if (pIUnkInternal)
+ pIUnkInternal->Release();
+ if (pInternalRW)
+ pInternalRW->Release();
+ if (FAILED(hr))
+ {
+ if (ppIMDInternalImport)
+ *ppIMDInternalImport = 0;
+ }
+ return hr;
+}
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
diff --git a/src/md/compiler/regmeta.h b/src/md/compiler/regmeta.h
new file mode 100644
index 0000000000..cb7bae17b5
--- /dev/null
+++ b/src/md/compiler/regmeta.h
@@ -0,0 +1,2123 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// RegMeta.h
+//
+
+//
+// This is the code for the MetaData coclass including both the emit and
+// import API's.
+//
+// This provides an implementation of the public Metadata interfaces via the RegMeta class. It's
+// primarily intended for use by tools such as compilers, debuggers, and profilers.
+//*****************************************************************************
+#ifndef __RegMeta__h__
+#define __RegMeta__h__
+
+#include <metamodelrw.h>
+#include <corperm.h>
+#include "../inc/mdlog.h"
+#include "utsem.h"
+
+#include "newmerger.h"
+
+#include "rwutil.h"
+#include "mdperf.h"
+#include <ivehandler.h>
+
+#include "sigparser.h"
+#ifdef FEATURE_FUSION
+#include "fusion.h"
+#endif
+
+#include "winmdinterfaces.h"
+
+class FilterManager;
+
+// Support for symbol binding meta data. This is a custom value hung off of
+// the Module entry. The CORDBG_SYMBOL_URL needs to be allocated on top of
+// a buffer large enough to hold it.
+//
+#define SZ_CORDBG_SYMBOL_URL W("DebugSymbolUrlData")
+
+struct CORDBG_SYMBOL_URL
+{
+ GUID FormatID; // ID of the format type.
+ WCHAR rcName[2]; // Variable sized name of the item.
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:6305) // "Potential mismatch between sizeof and countof quantities"
+#endif
+
+ ULONG Size() const
+ {
+ return (ULONG)(sizeof(GUID) + ((wcslen(rcName) + 1) * 2));
+ }
+
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+};
+
+
+//
+// Internal open flags.
+//
+#define ofExternalStgDB ofReserved1
+#define IsOfExternalStgDB(x) ((x) & ofExternalStgDB)
+#define ofReOpen ofReserved2
+#define IsOfReOpen(x) ((x) & ofReOpen)
+
+
+// Set API caller type
+enum SetAPICallerType
+{
+ DEFINE_API = 0x1,
+ EXTERNAL_CALLER = 0x2
+};
+
+// Define the record entry for the table over which ValidateMetaData iterates over.
+// Add a forward declaration for RegMeta.
+class RegMeta;
+typedef HRESULT (RegMeta::*ValidateRecordFunction)(RID);
+
+// Support for security attributes. Bundles of attributes (they look much like
+// custom attributes) are passed into a single API (DefineSecurityAttributeSet)
+// where they're processed and written into the metadata as one or more opaque
+// blobs of data.
+struct CORSEC_ATTR
+{
+ CORSEC_ATTR *pNext; // Next structure in list or NULL.
+ mdToken tkObj; // The object to put the value on.
+ mdMemberRef tkCtor; // The security attribute constructor.
+ mdTypeRef tkTypeRef; // Ref to the security attribute type.
+ mdAssemblyRef tkAssemblyRef; // Ref to the assembly containing the security attribute class.
+ void const *pCustomAttribute; // The custom value data.
+ ULONG cbCustomAttribute; // The custom value data length.
+};
+
+// Support for "Pseudo Custom Attributes".
+struct CCustAttrHashKey
+{
+ mdToken tkType; // Token of the custom attribute type.
+ int ca; // flag indicating what the ca is.
+};
+
+class CCustAttrHash : public CClosedHashEx<CCustAttrHashKey, CCustAttrHash>
+{
+ typedef CCustAttrHashKey T;
+
+ using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::Hash;
+ using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::Compare;
+ using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::Status;
+ using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::SetStatus;
+ using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::GetKey;
+
+public:
+ CCustAttrHash(int iBuckets=37) : CClosedHashEx<CCustAttrHashKey,CCustAttrHash>(iBuckets) {}
+ unsigned int Hash(const T *pData);
+ unsigned int Compare(const T *p1, T *p2);
+ ELEMENTSTATUS Status(T *pEntry);
+ void SetStatus(T *pEntry, ELEMENTSTATUS s);
+ void* GetKey(T *pEntry);
+};
+
+class MDInternalRW;
+struct CaArg;
+struct CaNamedArg;
+
+#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
+#define REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED() MarkUnsafeToDeleteStgdb()
+#else
+#define REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED()
+#endif
+
+// The RegMeta class implements the public metadata interfaces.
+//
+// Notes:
+// This object is primarily consumed by tools, not the runtime itself. (The runtime should be
+// using the internal metadata interfaces, not the public ones implemented here).
+// The VM uses it for IMetaDataEmit* interfaces. Define* methods are not exposed to the VM
+// (eg for Reflection) otherwise.
+// This object is a thin veneer exposing a public interface on top of the real storage.
+// The runtime also has internal interfaces to access that storage.
+//
+// This is included outside of md\compiler; so be very careful about using #ifdef to change class layout
+// (adding removing interfaces changes class layout)
+//
+// This exists in both the full and standalone versions of the metadata.
+//
+
+class RegMeta :
+ public IMetaDataImport2,
+ public IMetaDataAssemblyImport,
+ public IMetaDataTables2
+
+#ifndef FEATURE_METADATA_STANDALONE_WINRT
+ , public IMetaDataInfo
+#endif
+
+#ifdef FEATURE_METADATA_EMIT
+ , public IMetaDataEmit2
+ , public IMetaDataAssemblyEmit
+#endif
+
+#ifdef FEATURE_METADATA_VALIDATOR
+ , public IMetaDataValidate
+#endif
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+ , public IMetaDataFilter
+#endif
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+ , public IMetaDataHelper
+ , public IMDInternalEmit
+ , public IGetIMDInternalImport
+#endif
+
+#if defined(FEATURE_METADATA_EMIT) && defined(FEATURE_METADATA_INTERNAL_APIS)
+ , public IMetaDataEmitHelper
+#endif
+
+#if defined(FEATURE_METADATA_IN_VM) && defined(FEATURE_PREJIT)
+ , public IMetaDataCorProfileData
+ , public IMDInternalMetadataReorderingOptions
+#endif
+ , public IMDCommon
+{
+ friend class NEWMERGER;
+ friend class CImportTlb;
+ friend class MDInternalRW;
+ friend class MDInternalRO;
+ friend HRESULT TranslateSigHelper(
+ IMDInternalImport* pImport,
+ IMDInternalImport* pAssemImport,
+ const void* pbHashValue,
+ ULONG cbHashValue,
+ PCCOR_SIGNATURE pbSigBlob,
+ ULONG cbSigBlob,
+ IMetaDataAssemblyEmit* pAssemEmit,
+ IMetaDataEmit* emit,
+ CQuickBytes* pqkSigEmit,
+ ULONG* pcbSig);
+public:
+//*****************************************************************************
+// IUnknown methods
+//*****************************************************************************
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
+ STDMETHODIMP_(ULONG) AddRef(void);
+ STDMETHODIMP_(ULONG) Release(void);
+
+//*****************************************************************************
+// IMetaDataImport methods
+//*****************************************************************************
+ void STDMETHODCALLTYPE CloseEnum(HCORENUM hEnum);
+ STDMETHODIMP CountEnum(HCORENUM hEnum, ULONG *pulCount);
+ STDMETHODIMP ResetEnum(HCORENUM hEnum, ULONG ulPos);
+ STDMETHODIMP EnumTypeDefs(HCORENUM *phEnum, mdTypeDef rTypeDefs[],
+ ULONG cMax, ULONG *pcTypeDefs);
+ STDMETHODIMP EnumInterfaceImpls(HCORENUM *phEnum, mdTypeDef td,
+ mdInterfaceImpl rImpls[], ULONG cMax,
+ ULONG* pcImpls);
+ STDMETHODIMP EnumTypeRefs(HCORENUM *phEnum, mdTypeRef rTypeRefs[],
+ ULONG cMax, ULONG* pcTypeRefs);
+ STDMETHODIMP FindTypeDefByName( // S_OK or error.
+ LPCWSTR szTypeDef, // [IN] Name of the Type.
+ mdToken tdEncloser, // [IN] TypeDef/TypeRef of Enclosing class.
+ mdTypeDef *ptd); // [OUT] Put the TypeDef token here.
+
+ STDMETHODIMP GetScopeProps( // S_OK or error.
+ __out_ecount_opt (cchName) LPWSTR szName, // [OUT] Put name here.
+ ULONG cchName, // [IN] Size of name buffer in wide chars.
+ ULONG *pchName, // [OUT] Put size of name (wide chars) here.
+ GUID *pmvid); // [OUT] Put MVID here.
+
+ STDMETHODIMP GetModuleFromScope( // S_OK.
+ mdModule *pmd); // [OUT] Put mdModule token here.
+
+ STDMETHODIMP GetTypeDefProps( // S_OK or error.
+ mdTypeDef td, // [IN] TypeDef token for inquiry.
+ __out_ecount_opt (cchTypeDef) LPWSTR szTypeDef, // [OUT] Put name here.
+ ULONG cchTypeDef, // [IN] size of name buffer in wide chars.
+ ULONG *pchTypeDef, // [OUT] put size of name (wide chars) here.
+ DWORD *pdwTypeDefFlags, // [OUT] Put flags here.
+ mdToken *ptkExtends); // [OUT] Put base class TypeDef/TypeRef here.
+
+ STDMETHODIMP GetInterfaceImplProps( // S_OK or error.
+ mdInterfaceImpl iiImpl, // [IN] InterfaceImpl token.
+ mdTypeDef *pClass, // [OUT] Put implementing class token here.
+ mdToken *ptkIface); // [OUT] Put implemented interface token here.
+
+ STDMETHODIMP GetTypeRefProps(
+ mdTypeRef tr, // S_OK or error.
+ mdToken *ptkResolutionScope, // [OUT] Resolution scope, mdModuleRef or mdAssemblyRef.
+ __out_ecount_opt (cchName) LPWSTR szName, // [OUT] Name buffer.
+ ULONG cchName, // [IN] Size of Name buffer.
+ ULONG *pchName); // [OUT] Actual size of Name.
+
+ // This access global shared state to looks across multiple metadata scopes that would
+ // otherwise be independent.
+ STDMETHODIMP ResolveTypeRef(mdTypeRef tr, REFIID riid, IUnknown **ppIScope, mdTypeDef *ptd);
+
+ STDMETHODIMP EnumMembers( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumMembersWithName( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumMethods( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdMethodDef rMethods[], // [OUT] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumMethodsWithName( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdMethodDef rMethods[], // [OU] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumFields( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdFieldDef rFields[], // [OUT] Put FieldDefs here.
+ ULONG cMax, // [IN] Max FieldDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumFieldsWithName( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdFieldDef rFields[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+
+ STDMETHODIMP EnumParams( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdParamDef rParams[], // [OUT] Put ParamDefs here.
+ ULONG cMax, // [IN] Max ParamDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumMemberRefs( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tkParent, // [IN] Parent token to scope the enumeration.
+ mdMemberRef rMemberRefs[], // [OUT] Put MemberRefs here.
+ ULONG cMax, // [IN] Max MemberRefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumMethodImpls( // S_OK, S_FALSE, or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdToken rMethodBody[], // [OUT] Put Method Body tokens here.
+ mdToken rMethodDecl[], // [OUT] Put Method Declaration tokens here.
+ ULONG cMax, // [IN] Max tokens to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumPermissionSets( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] if !NIL, token to scope the enumeration.
+ DWORD dwActions, // [IN] if !0, return only these actions.
+ mdPermission rPermission[], // [OUT] Put Permissions here.
+ ULONG cMax, // [IN] Max Permissions to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP FindMember(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdToken *pmb); // [OUT] matching memberdef
+
+ STDMETHODIMP FindMethod(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmb); // [OUT] matching memberdef
+
+ STDMETHODIMP FindField(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdFieldDef *pmb); // [OUT] matching memberdef
+
+ STDMETHODIMP FindMemberRef(
+ mdTypeRef td, // [IN] given typeRef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMemberRef *pmr); // [OUT] matching memberref
+
+ STDMETHODIMP GetMethodProps(
+ mdMethodDef mb, // The method for which to get props.
+ mdTypeDef *pClass, // Put method's class here.
+ __out_ecount_opt (cchMethod) LPWSTR szMethod, // Put method's name here.
+ ULONG cchMethod, // Size of szMethod buffer in wide chars.
+ ULONG *pchMethod, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG *pulCodeRVA, // [OUT] codeRVA
+ DWORD *pdwImplFlags); // [OUT] Impl. Flags
+
+ STDMETHODIMP GetMemberRefProps( // S_OK or error.
+ mdMemberRef mr, // [IN] given memberref
+ mdToken *ptk, // [OUT] Put classref or classdef here.
+ __out_ecount_opt (cchMember) LPWSTR szMember, // [OUT] buffer to fill for member's name
+ ULONG cchMember, // [IN] the count of char of szMember
+ ULONG *pchMember, // [OUT] actual count of char in member name
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to meta data blob value
+ ULONG *pbSig); // [OUT] actual size of signature blob
+
+ STDMETHODIMP EnumProperties( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdProperty rProperties[], // [OUT] Put Properties here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcProperties); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumEvents( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdEvent rEvents[], // [OUT] Put events here.
+ ULONG cMax, // [IN] Max events to put.
+ ULONG *pcEvents); // [OUT] Put # put here.
+
+ STDMETHODIMP GetEventProps( // S_OK, S_FALSE, or error.
+ mdEvent ev, // [IN] event token
+ mdTypeDef *pClass, // [OUT] typedef containing the event declarion.
+ LPCWSTR szEvent, // [OUT] Event name
+ ULONG cchEvent, // [IN] the count of wchar of szEvent
+ ULONG *pchEvent, // [OUT] actual count of wchar for event's name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType, // [OUT] EventType class
+ mdMethodDef *pmdAddOn, // [OUT] AddOn method of the event
+ mdMethodDef *pmdRemoveOn, // [OUT] RemoveOn method of the event
+ mdMethodDef *pmdFire, // [OUT] Fire method of the event
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the event
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod); // [OUT] total number of other method of this event
+
+ STDMETHODIMP EnumMethodSemantics( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdToken rEventProp[], // [OUT] Put Event/Property here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcEventProp); // [OUT] Put # put here.
+
+ STDMETHODIMP GetMethodSemantics( // S_OK, S_FALSE, or error.
+ mdMethodDef mb, // [IN] method token
+ mdToken tkEventProp, // [IN] event/property token.
+ DWORD *pdwSemanticsFlags); // [OUT] the role flags for the method/propevent pair
+
+ STDMETHODIMP GetClassLayout(
+ mdTypeDef td, // [IN] give typedef
+ DWORD *pdwPackSize, // [OUT] 1, 2, 4, 8, or 16
+ COR_FIELD_OFFSET rFieldOffset[], // [OUT] field offset array
+ ULONG cMax, // [IN] size of the array
+ ULONG *pcFieldOffset, // [OUT] needed array size
+ ULONG *pulClassSize); // [OUT] the size of the class
+
+ STDMETHODIMP GetFieldMarshal(
+ mdToken tk, // [IN] given a field's memberdef
+ PCCOR_SIGNATURE *ppvNativeType, // [OUT] native type of this field
+ ULONG *pcbNativeType); // [OUT] the count of bytes of *ppvNativeType
+
+ STDMETHODIMP GetRVA( // S_OK or error.
+ mdToken tk, // Member for which to set offset
+ ULONG *pulCodeRVA, // The offset
+ DWORD *pdwImplFlags); // the implementation flags
+
+ STDMETHODIMP 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.
+
+ STDMETHODIMP GetSigFromToken( // S_OK or error.
+ mdSignature mdSig, // [IN] Signature token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token.
+ ULONG *pcbSig); // [OUT] return size of signature.
+
+ STDMETHODIMP GetModuleRefProps( // S_OK or error.
+ mdModuleRef mur, // [IN] moduleref token.
+ __out_ecount_opt (cchName) LPWSTR szName, // [OUT] buffer to fill with the moduleref name.
+ ULONG cchName, // [IN] size of szName in wide characters.
+ ULONG *pchName); // [OUT] actual count of characters in the name.
+
+ STDMETHODIMP EnumModuleRefs( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdModuleRef rModuleRefs[], // [OUT] put modulerefs here.
+ ULONG cmax, // [IN] max memberrefs to put.
+ ULONG *pcModuleRefs); // [OUT] put # put here.
+
+ STDMETHODIMP GetTypeSpecFromToken( // S_OK or error.
+ mdTypeSpec typespec, // [IN] TypeSpec token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to TypeSpec signature
+ ULONG *pcbSig); // [OUT] return size of signature.
+
+ STDMETHODIMP GetNameFromToken( // S_OK or error.
+ mdToken tk, // [IN] Token to get name from. Must have a name.
+ MDUTF8CSTR *pszUtf8NamePtr); // [OUT] Return pointer to UTF8 name in heap.
+
+ STDMETHODIMP EnumUnresolvedMethods( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken rMethods[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP GetUserString( // S_OK or error.
+ mdString stk, // [IN] String token.
+ __out_ecount_opt (cchString) LPWSTR szString, // [OUT] Copy of string.
+ ULONG cchString, // [IN] Max chars of room in szString.
+ ULONG *pchString); // [OUT] How many chars in actual string.
+
+ STDMETHODIMP GetPinvokeMap( // S_OK or error.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ __out_ecount_opt (cchImportName) LPWSTR szImportName, // [OUT] Import name.
+ ULONG cchImportName, // [IN] Size of the name buffer.
+ ULONG *pchImportName, // [OUT] Actual number of characters stored.
+ mdModuleRef *pmrImportDLL); // [OUT] ModuleRef token for the target DLL.
+
+ STDMETHODIMP EnumSignatures( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdSignature rSignatures[], // [OUT] put signatures here.
+ ULONG cmax, // [IN] max signatures to put.
+ ULONG *pcSignatures); // [OUT] put # put here.
+
+ STDMETHODIMP EnumTypeSpecs( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdTypeSpec rTypeSpecs[], // [OUT] put TypeSpecs here.
+ ULONG cmax, // [IN] max TypeSpecs to put.
+ ULONG *pcTypeSpecs); // [OUT] put # put here.
+
+ STDMETHODIMP EnumUserStrings( // S_OK or error.
+ HCORENUM *phEnum, // [IN/OUT] pointer to the enum.
+ mdString rStrings[], // [OUT] put Strings here.
+ ULONG cmax, // [IN] max Strings to put.
+ ULONG *pcStrings); // [OUT] put # put here.
+
+ STDMETHODIMP GetParamForMethodIndex( // S_OK or error.
+ mdMethodDef md, // [IN] Method token.
+ ULONG ulParamSeq, // [IN] Parameter sequence.
+ mdParamDef *ppd); // [IN] Put Param token here.
+
+ STDMETHODIMP GetCustomAttributeByName( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCWSTR szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData); // [OUT] Put size of data here.
+
+ STDMETHODIMP EnumCustomAttributes( // S_OK or error.
+ HCORENUM *phEnum, // [IN, OUT] COR enumerator.
+ mdToken tk, // [IN] Token to scope the enumeration, 0 for all.
+ mdToken tkType, // [IN] Type of interest, 0 for all.
+ mdCustomAttribute rCustomAttributes[], // [OUT] Put custom attribute tokens here.
+ ULONG cMax, // [IN] Size of rCustomAttributes.
+ ULONG *pcCustomAttributes); // [OUT, OPTIONAL] Put count of token values here.
+
+ STDMETHODIMP GetCustomAttributeProps( // S_OK or error.
+ mdCustomAttribute cv, // [IN] CustomAttribute token.
+ mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here.
+ mdToken *ptkType, // [OUT, OPTIONAL] Put AttrType token here.
+ void const **ppBlob, // [OUT, OPTIONAL] Put pointer to data here.
+ ULONG *pcbSize); // [OUT, OPTIONAL] Put size of date here.
+
+ STDMETHODIMP FindTypeRef( // S_OK or error.
+ mdToken tkResolutionScope, // ResolutionScope.
+ LPCWSTR szName, // [IN] TypeRef name.
+ mdTypeRef *ptr); // [OUT] matching TypeRef.
+
+ STDMETHODIMP GetMemberProps(
+ mdToken mb, // The member for which to get props.
+ mdTypeDef *pClass, // Put member's class here.
+ __out_ecount_opt (cchMember) LPWSTR szMember, // Put member's name here.
+ ULONG cchMember, // Size of szMember buffer in wide chars.
+ ULONG *pchMember, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG *pulCodeRVA, // [OUT] codeRVA
+ DWORD *pdwImplFlags, // [OUT] Impl. Flags
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppValue, // [OUT] constant value
+ ULONG *pcbValue); // [OUT] size of constant value
+
+ STDMETHODIMP GetFieldProps(
+ mdFieldDef mb, // The field for which to get props.
+ mdTypeDef *pClass, // Put field's class here.
+ __out_ecount_opt (cchField) LPWSTR szField, // Put field's name here.
+ ULONG cchField, // Size of szField buffer in wide chars.
+ ULONG *pchField, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppValue, // [OUT] constant value
+ ULONG *pcbValue); // [OUT] size of constant value
+
+ STDMETHODIMP GetPropertyProps( // S_OK, S_FALSE, or error.
+ mdProperty prop, // [IN] property token
+ mdTypeDef *pClass, // [OUT] typedef containing the property declarion.
+ LPCWSTR szProperty, // [OUT] Property name
+ ULONG cchProperty, // [IN] the count of wchar of szProperty
+ ULONG *pchProperty, // [OUT] actual count of wchar for property name
+ DWORD *pdwPropFlags, // [OUT] property flags.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob
+ ULONG *pbSig, // [OUT] count of bytes in *ppvSig
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppDefaultValue, // [OUT] constant value
+ ULONG *pcbValue, // [OUT] size of constant value
+ mdMethodDef *pmdSetter, // [OUT] setter method of the property
+ mdMethodDef *pmdGetter, // [OUT] getter method of the property
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the property
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod); // [OUT] total number of other method of this property
+
+ STDMETHODIMP GetParamProps( // S_OK or error.
+ mdParamDef tk, // [IN]The Parameter.
+ mdMethodDef *pmd, // [OUT] Parent Method token.
+ ULONG *pulSequence, // [OUT] Parameter sequence.
+ __out_ecount_opt (cchName) LPWSTR szName, // [OUT] Put name here.
+ ULONG cchName, // [OUT] Size of name buffer.
+ ULONG *pchName, // [OUT] Put actual size of name here.
+ DWORD *pdwAttr, // [OUT] Put flags here.
+ DWORD *pdwCPlusTypeFlag, // [OUT] Flag for value type. selected ELEMENT_TYPE_*.
+ UVCP_CONSTANT *ppValue, // [OUT] Constant value.
+ ULONG *pcbValue); // [OUT] size of constant value
+
+ STDMETHODIMP_(BOOL) IsValidToken( // True or False.
+ mdToken tk); // [IN] Given token.
+
+ STDMETHODIMP GetNestedClassProps( // S_OK or error.
+ mdTypeDef tdNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptdEnclosingClass); // [OUT] EnclosingClass token.
+
+ STDMETHODIMP GetNativeCallConvFromSig( // S_OK or error.
+ void const *pvSig, // [IN] Pointer to signature.
+ ULONG cbSig, // [IN] Count of signature bytes.
+ ULONG *pCallConv); // [OUT] Put calling conv here (see CorPinvokemap).
+
+ STDMETHODIMP IsGlobal( // S_OK or error.
+ mdToken pd, // [IN] Type, Field, or Method token.
+ int *pbGlobal); // [OUT] Put 1 if global, 0 otherwise.
+
+//*****************************************************************************
+// IMetaDataImport2 methods
+//*****************************************************************************
+ STDMETHODIMP GetGenericParamProps( // S_OK or error.
+ mdGenericParam gp, // [IN] GenericParam
+ ULONG *pulParamSeq, // [OUT] Index of the type parameter
+ DWORD *pdwParamFlags, // [OUT] Flags, for future use (e.g. variance)
+ mdToken *ptOwner, // [OUT] Owner (TypeDef or MethodDef)
+ DWORD *reserved, // [OUT] For future use (e.g. non-type parameters)
+ __out_ecount_opt (cchName) LPWSTR wzname, // [OUT] Put name here
+ ULONG cchName, // [IN] Size of buffer
+ ULONG *pchName); // [OUT] Put size of name (wide chars) here.
+
+ STDMETHODIMP GetGenericParamConstraintProps( // S_OK or error.
+ mdGenericParamConstraint gpc, // [IN] GenericParamConstraint
+ mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained
+ mdToken *ptkConstraintType); // [OUT] TypeDef/Ref/Spec constraint
+
+ STDMETHODIMP GetMethodSpecProps(
+ 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
+
+ STDMETHODIMP EnumGenericParams( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] TypeDef or MethodDef whose generic parameters are requested
+ mdGenericParam rGenericParams[], // [OUT] Put GenericParams here.
+ ULONG cMax, // [IN] Max GenericParams to put.
+ ULONG *pcGenericParams); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumGenericParamConstraints( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdGenericParam tk, // [IN] GenericParam whose constraints are requested
+ mdGenericParamConstraint rGenericParamConstraints[], // [OUT] Put GenericParamConstraints here.
+ ULONG cMax, // [IN] Max GenericParamConstraints to put.
+ ULONG *pcGenericParamConstraints); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumMethodSpecs(
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] MethodDef or MemberRef whose MethodSpecs are requested
+ mdMethodSpec rMethodSpecs[], // [OUT] Put MethodSpecs here.
+ ULONG cMax, // [IN] Max tokens to put.
+ ULONG *pcMethodSpecs); // [OUT] Put actual count here.
+
+ STDMETHODIMP GetPEKind( // S_OK or error.
+ DWORD* pdwPEKind, // [OUT] The kind of PE (0 - not a PE)
+ DWORD* pdwMAchine); // [OUT] Machine as defined in NT header
+
+ STDMETHODIMP GetVersionString( // S_OK or error.
+ __out_ecount_opt (cchBufSize) LPWSTR pwzBuf, // [OUT] Put version string here.
+ DWORD cchBufSize, // [IN] size of the buffer, in wide chars
+ DWORD *pchBufSize); // [OUT] Size of the version string, wide chars, including terminating nul.
+
+//*****************************************************************************
+// IMetaDataAssemblyImport
+//*****************************************************************************
+ STDMETHODIMP GetAssemblyProps( // S_OK or error.
+ 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.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with assembly's simply name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ DWORD *pdwAssemblyFlags); // [OUT] Flags.
+
+ STDMETHODIMP GetAssemblyRefProps( // S_OK or error.
+ 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.
+ __out_ecount_part_opt(cchName, *pchName)LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ const void **ppbHashValue, // [OUT] Hash blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ DWORD *pdwAssemblyRefFlags); // [OUT] Flags.
+
+ STDMETHODIMP GetFileProps( // S_OK or error.
+ mdFile mdf, // [IN] The File for which to get the properties.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in 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.
+
+ STDMETHODIMP GetExportedTypeProps( // S_OK or error.
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags); // [OUT] Flags.
+
+ STDMETHODIMP GetManifestResourceProps( // S_OK or error.
+ mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties.
+ __out_ecount_part_opt(cchName, *pchName)LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in 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.
+
+ STDMETHODIMP EnumAssemblyRefs( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdAssemblyRef rAssemblyRefs[], // [OUT] Put AssemblyRefs here.
+ ULONG cMax, // [IN] Max AssemblyRefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumFiles( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdFile rFiles[], // [OUT] Put Files here.
+ ULONG cMax, // [IN] Max Files to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumExportedTypes( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdExportedType rExportedTypes[], // [OUT] Put ExportedTypes here.
+ ULONG cMax, // [IN] Max ExportedTypes to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumManifestResources( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdManifestResource rManifestResources[], // [OUT] Put ManifestResources here.
+ ULONG cMax, // [IN] Max Resources to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP FindExportedTypeByName( // S_OK or error
+ LPCWSTR szName, // [IN] Name of the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] Enclosing ExportedType.
+ mdExportedType *ptkExportedType); // [OUT] Put the ExportedType token here.
+
+ STDMETHODIMP FindManifestResourceByName(// S_OK or error
+ LPCWSTR szName, // [IN] Name of the ManifestResource.
+ mdManifestResource *ptkManifestResource); // [OUT] Put the ManifestResource token here.
+
+ STDMETHODIMP GetAssemblyFromScope( // S_OK or error
+ mdAssembly *ptkAssembly); // [OUT] Put token here.
+
+ // This uses Fusion to lookup, so it's E_NOTIMPL in the standalone versions.
+ STDMETHODIMP FindAssembliesByName( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
+ IUnknown *ppIUnk[], // [OUT] put IMetaDataAssemblyImport pointers here
+ ULONG cMax, // [IN] The max number to put
+ ULONG *pcAssemblies); // [OUT] The number of assemblies returned.
+
+#ifdef FEATURE_METADATA_EMIT
+//*****************************************************************************
+// IMetaDataEmit
+//*****************************************************************************
+ STDMETHODIMP DefineMethod( // S_OK or error.
+ mdTypeDef td, // Parent TypeDef
+ LPCWSTR szName, // Name of member
+ DWORD dwMethodFlags, // Member attributes
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ ULONG ulCodeRVA,
+ DWORD dwImplFlags,
+ mdMethodDef *pmd); // Put member token here
+
+ STDMETHODIMP DefineMethodImpl( // S_OK or error.
+ mdTypeDef td, // [IN] The class implementing the method
+ mdToken tkBody, // [IN] Method body, MethodDef or MethodRef
+ mdToken tkDecl); // [IN] Method declaration, MethodDef or MethodRef
+
+ STDMETHODIMP SetMethodImplFlags( // [IN] S_OK or error.
+ mdMethodDef md, // [IN] Method for which to set impl flags
+ DWORD dwImplFlags);
+
+ STDMETHODIMP SetFieldRVA( // [IN] S_OK or error.
+ mdFieldDef fd, // [IN] Field for which to set offset
+ ULONG ulRVA); // [IN] The offset
+
+ STDMETHODIMP DefineTypeRefByName( // S_OK or error.
+ mdToken tkResolutionScope, // [IN] ModuleRef or AssemblyRef.
+ LPCWSTR szName, // [IN] Name of the TypeRef.
+ mdTypeRef *ptr); // [OUT] Put TypeRef token here.
+
+ STDMETHODIMP DefineImportType( // S_OK or error.
+ IMetaDataAssemblyImport *pAssemImport, // [IN] Assemby containing the TypeDef.
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ IMetaDataImport *pImport, // [IN] Scope containing the TypeDef.
+ mdTypeDef tdImport, // [IN] The imported TypeDef.
+ IMetaDataAssemblyEmit *pAssemEmit, // [IN] Assembly into which the TypeDef is imported.
+ mdTypeRef *ptr); // [OUT] Put TypeRef token here.
+
+ STDMETHODIMP DefineMemberRef( // S_OK or error
+ mdToken tkImport, // [IN] ClassRef or ClassDef importing a member.
+ LPCWSTR szName, // [IN] member's name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMemberRef *pmr); // [OUT] memberref token
+
+ STDMETHODIMP DefineImportMember( // S_OK or error.
+ IMetaDataAssemblyImport *pAssemImport, // [IN] Assemby containing the Member.
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ IMetaDataImport *pImport, // [IN] Import scope, with member.
+ mdToken mbMember, // [IN] Member in import scope.
+ IMetaDataAssemblyEmit *pAssemEmit, // [IN] Assembly into which the Member is imported.
+ mdToken tkImport, // [IN] Classref or classdef in emit scope.
+ mdMemberRef *pmr); // [OUT] Put member ref here.
+
+ STDMETHODIMP DefineEvent(
+ mdTypeDef td, // [IN] the class/interface on which the event is being defined
+ LPCWSTR szEvent, // [IN] Name of the event
+ DWORD dwEventFlags, // [IN] CorEventAttr
+ mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef(to the Event class
+ mdMethodDef mdAddOn, // [IN] required add method
+ mdMethodDef mdRemoveOn, // [IN] required remove method
+ mdMethodDef mdFire, // [IN] optional fire method
+ mdMethodDef rmdOtherMethods[], // [IN] optional array of other methods associate with the event
+ mdEvent *pmdEvent); // [OUT] output event token
+
+ STDMETHODIMP SetClassLayout(
+ mdTypeDef td, // [IN] typedef
+ DWORD dwPackSize, // [IN] packing size specified as 1, 2, 4, 8, or 16
+ COR_FIELD_OFFSET rFieldOffsets[], // [IN] array of layout specification
+ ULONG ulClassSize); // [IN] size of the class
+
+ STDMETHODIMP DeleteClassLayout(
+ mdTypeDef td); // [IN] typdef token
+
+ STDMETHODIMP SetFieldMarshal(
+ mdToken tk, // [IN] given a fieldDef or paramDef token
+ PCCOR_SIGNATURE pvNativeType, // [IN] native type specification
+ ULONG cbNativeType); // [IN] count of bytes of pvNativeType
+
+ STDMETHODIMP DeleteFieldMarshal(
+ mdToken tk); // [IN] fieldDef or paramDef token to be deleted.
+
+ STDMETHODIMP DefinePermissionSet(
+ mdToken tk, // [IN] the object to be decorated.
+ DWORD dwAction, // [IN] CorDeclSecurity.
+ void const *pvPermission, // [IN] permission blob.
+ ULONG cbPermission, // [IN] count of bytes of pvPermission.
+ mdPermission *ppm); // [OUT] returned permission token.
+
+ STDMETHODIMP SetRVA( // [IN] S_OK or error.
+ mdToken md, // [IN] MethodDef for which to set offset
+ ULONG ulRVA); // [IN] The offset#endif
+
+ STDMETHODIMP GetTokenFromSig( // [IN] S_OK or error.
+ PCCOR_SIGNATURE pvSig, // [IN] Signature to define.
+ ULONG cbSig, // [IN] Size of signature data.
+ mdSignature *pmsig); // [OUT] returned signature token.
+
+ STDMETHODIMP DefineModuleRef( // S_OK or error.
+ LPCWSTR szName, // [IN] DLL name
+ mdModuleRef *pmur); // [OUT] returned module ref token
+
+ STDMETHODIMP SetParent( // S_OK or error.
+ mdMemberRef mr, // [IN] Token for the ref to be fixed up.
+ mdToken tk); // [IN] The ref parent.
+
+ STDMETHODIMP GetTokenFromTypeSpec( // S_OK or error.
+ PCCOR_SIGNATURE pvSig, // [IN] ArraySpec Signature to define.
+ ULONG cbSig, // [IN] Size of signature data.
+ mdTypeSpec *ptypespec); // [OUT] returned TypeSpec token.
+
+ STDMETHODIMP SaveToMemory( // S_OK or error.
+ void *pbData, // [OUT] Location to write data.
+ ULONG cbData); // [IN] Max size of data buffer.
+
+ STDMETHODIMP DefineUserString( // S_OK or error.
+ LPCWSTR szString, // [IN] User literal string.
+ ULONG cchString, // [IN] Length of string.
+ mdString *pstk); // [OUT] String token.
+
+ STDMETHODIMP DeleteToken( // Return code.
+ mdToken tkObj); // [IN] The token to be deleted
+
+ STDMETHODIMP SetTypeDefProps( // S_OK or error.
+ mdTypeDef td, // [IN] The TypeDef.
+ DWORD dwTypeDefFlags, // [IN] TypeDef flags.
+ mdToken tkExtends, // [IN] Base TypeDef or TypeRef.
+ mdToken rtkImplements[]); // [IN] Implemented interfaces.
+
+ STDMETHODIMP DefineNestedType( // S_OK or error.
+ LPCWSTR szTypeDef, // [IN] Name of TypeDef
+ DWORD dwTypeDefFlags, // [IN] CustomAttribute flags
+ mdToken tkExtends, // [IN] extends this TypeDef or typeref
+ mdToken rtkImplements[], // [IN] Implements interfaces
+ mdTypeDef tdEncloser, // [IN] TypeDef token of the enclosing type.
+ mdTypeDef *ptd); // [OUT] Put TypeDef token here
+
+ STDMETHODIMP SetMethodProps( // S_OK or error.
+ mdMethodDef md, // [IN] The MethodDef.
+ DWORD dwMethodFlags, // [IN] Method attributes.
+ ULONG ulCodeRVA, // [IN] Code RVA.
+ DWORD dwImplFlags); // [IN] MethodImpl flags.
+
+ STDMETHODIMP SetEventProps( // S_OK or error.
+ mdEvent ev, // [IN] The event token.
+ DWORD dwEventFlags, // [IN] CorEventAttr.
+ mdToken tkEventType, // [IN] A reference (mdTypeRef or mdTypeRef) to the Event class.
+ mdMethodDef mdAddOn, // [IN] Add method.
+ mdMethodDef mdRemoveOn, // [IN] Remove method.
+ mdMethodDef mdFire, // [IN] Fire method.
+ mdMethodDef rmdOtherMethods[]); // [IN] Array of other methods associated with the event.
+
+ STDMETHODIMP SetPermissionSetProps( // S_OK or error.
+ mdToken tk, // [IN] The object to be decorated.
+ DWORD dwAction, // [IN] CorDeclSecurity.
+ void const *pvPermission, // [IN] Permission blob.
+ ULONG cbPermission, // [IN] Count of bytes of pvPermission.
+ mdPermission *ppm); // [OUT] Permission token.
+
+ STDMETHODIMP DefinePinvokeMap( // Return code.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD dwMappingFlags, // [IN] Flags used for mapping.
+ LPCWSTR szImportName, // [IN] Import name.
+ mdModuleRef mrImportDLL); // [IN] ModuleRef token for the target DLL.
+
+ STDMETHODIMP SetPinvokeMap( // Return code.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD dwMappingFlags, // [IN] Flags used for mapping.
+ LPCWSTR szImportName, // [IN] Import name.
+ mdModuleRef mrImportDLL); // [IN] ModuleRef token for the target DLL.
+
+ STDMETHODIMP DeletePinvokeMap( // Return code.
+ mdToken tk); // [IN]FieldDef or MethodDef.
+
+ STDMETHODIMP DefineCustomAttribute( // Return code.
+ mdToken tkOwner, // [IN] The object to put the value on.
+ mdToken tkCtor, // [IN] Constructor of the CustomAttribute type (MemberRef/MethodDef).
+ void const *pCustomAttribute, // [IN] The custom value data.
+ ULONG cbCustomAttribute, // [IN] The custom value data length.
+ mdCustomAttribute *pcv); // [OUT] The custom value token value on return.
+
+ STDMETHODIMP SetCustomAttributeValue( // Return code.
+ mdCustomAttribute pcv, // [IN] The custom value token whose value to replace.
+ void const *pCustomAttribute, // [IN] The custom value data.
+ ULONG cbCustomAttribute); // [IN] The custom value data length.
+
+ STDMETHODIMP DefineField( // S_OK or error.
+ mdTypeDef td, // Parent TypeDef
+ LPCWSTR szName, // Name of member
+ DWORD dwFieldFlags, // Member attributes
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] constant value
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdFieldDef *pmd); // [OUT] Put member token here
+
+ STDMETHODIMP DefineProperty(
+ mdTypeDef td, // [IN] the class/interface on which the property is being defined
+ LPCWSTR szProperty, // [IN] Name of the property
+ DWORD dwPropFlags, // [IN] CorPropertyAttr
+ PCCOR_SIGNATURE pvSig, // [IN] the required type signature
+ ULONG cbSig, // [IN] the size of the type signature blob
+ DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] constant value
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdMethodDef mdSetter, // [IN] optional setter of the property
+ mdMethodDef mdGetter, // [IN] optional getter of the property
+ mdMethodDef rmdOtherMethods[], // [IN] an optional array of other methods
+ mdProperty *pmdProp); // [OUT] output property token
+
+ STDMETHODIMP DefineParam(
+ mdMethodDef md, // [IN] Owning method
+ ULONG ulParamSeq, // [IN] Which param
+ LPCWSTR szName, // [IN] Optional param name
+ DWORD dwParamFlags, // [IN] Optional param flags
+ DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] constant value
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdParamDef *ppd); // [OUT] Put param token here
+
+ STDMETHODIMP SetFieldProps( // S_OK or error.
+ mdFieldDef fd, // [IN] The FieldDef.
+ DWORD dwFieldFlags, // [IN] Field attributes.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchValue); // [IN] size of constant value (string, in wide chars).
+
+ STDMETHODIMP SetPropertyProps( // S_OK or error.
+ mdProperty pr, // [IN] Property token.
+ DWORD dwPropFlags, // [IN] CorPropertyAttr.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdMethodDef mdSetter, // [IN] Setter of the property.
+ mdMethodDef mdGetter, // [IN] Getter of the property.
+ mdMethodDef rmdOtherMethods[]); // [IN] Array of other methods.
+
+ STDMETHODIMP SetParamProps( // Return code.
+ mdParamDef pd, // [IN] Param token.
+ LPCWSTR szName, // [IN] Param name.
+ DWORD dwParamFlags, // [IN] Param flags.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for value type. selected ELEMENT_TYPE_*.
+ void const *pValue, // [OUT] Constant value.
+ ULONG cchValue); // [IN] size of constant value (string, in wide chars).
+
+ STDMETHODIMP ApplyEditAndContinue( // S_OK or error.
+ IUnknown *pImport); // [IN] Metadata from the delta PE.
+
+ // Specialized Custom Attributes for security.
+ STDMETHODIMP DefineSecurityAttributeSet(// Return code.
+ mdToken tkObj, // [IN] Class or method requiring security attributes.
+ COR_SECATTR rSecAttrs[], // [IN] Array of security attribute descriptions.
+ ULONG cSecAttrs, // [IN] Count of elements in above array.
+ ULONG *pulErrorAttr); // [OUT] On error, index of attribute causing problem.
+
+ STDMETHODIMP TranslateSigWithScope(
+ IMetaDataAssemblyImport *pAssemImport, // [IN] assembly importing interface
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ IMetaDataImport *import, // [IN] importing interface
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit *pAssemEmti, // [IN] emit assembly interface
+ IMetaDataEmit *emit, // [IN] emit interface
+ PCOR_SIGNATURE pvTranslatedSig, // [OUT] buffer to hold translated signature
+ ULONG cbTranslatedSigMax,
+ ULONG *pcbTranslatedSig); // [OUT] count of bytes in the translated signature
+
+//*****************************************************************************
+// IMetaDataEmit2 methods
+//*****************************************************************************
+ STDMETHODIMP SetModuleProps( // S_OK or error.
+ LPCWSTR szName); // [IN] If not NULL, the name to set.
+
+ STDMETHODIMP Save( // S_OK or error.
+ LPCWSTR szFile, // [IN] The filename to save to.
+ DWORD dwSaveFlags); // [IN] Flags for the save.
+
+ STDMETHODIMP SaveToStream( // S_OK or error.
+ IStream *pIStream, // [IN] A writable stream to save to.
+ DWORD dwSaveFlags); // [IN] Flags for the save.
+
+ STDMETHODIMP GetSaveSize( // S_OK or error.
+ CorSaveSize fSave, // [IN] cssAccurate or cssQuick.
+ DWORD *pdwSaveSize); // [OUT] Put the size here.
+
+ STDMETHODIMP Merge( // S_OK or error.
+ IMetaDataImport *pImport, // [IN] The scope to be merged.
+ IMapToken *pHostMapToken, // [IN] Host IMapToken interface to receive token remap notification
+ IUnknown *pHandler); // [IN] An object to receive to receive error notification.
+
+ STDMETHODIMP MergeEnd(); // S_OK or error.
+
+ STDMETHODIMP DefineMethodSpec( // S_OK or error
+ mdToken tkImport, // [IN] MethodDef or MemberRef
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodSpec *pmi); // [OUT] method instantiation token
+
+ STDMETHODIMP DefineTypeDef( // S_OK or error.
+ LPCWSTR szTypeDef, // [IN] Name of TypeDef
+ DWORD dwTypeDefFlags, // [IN] CustomAttribute flags
+ mdToken tkExtends, // [IN] extends this TypeDef or typeref
+ mdToken rtkImplements[], // [IN] Implements interfaces
+ mdTypeDef *ptd); // [OUT] Put TypeDef token here
+
+ STDMETHODIMP SetHandler( // S_OK.
+ IUnknown *pUnk); // [IN] The new error handler.
+
+ STDMETHODIMP GetDeltaSaveSize( // S_OK or error.
+ CorSaveSize fSave, // [IN] cssAccurate or cssQuick.
+ DWORD *pdwSaveSize); // [OUT] Put the size here.
+
+ STDMETHODIMP SaveDelta( // S_OK or error.
+ LPCWSTR szFile, // [IN] The filename to save to.
+ DWORD dwSaveFlags); // [IN] Flags for the save.
+
+ STDMETHODIMP SaveDeltaToStream( // S_OK or error.
+ IStream *pIStream, // [IN] A writable stream to save to.
+ DWORD dwSaveFlags); // [IN] Flags for the save.
+
+ STDMETHODIMP SaveDeltaToMemory( // S_OK or error.
+ void *pbData, // [OUT] Location to write data.
+ ULONG cbData); // [IN] Max size of data buffer.
+
+ STDMETHODIMP ResetENCLog(); // S_OK or error.
+
+ STDMETHODIMP DefineGenericParam( // S_OK or error.
+ mdToken tk, // [IN] TypeDef or MethodDef
+ ULONG ulParamSeq, // [IN] Index of the type parameter
+ DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance)
+ LPCWSTR szname, // [IN] Name
+ DWORD reserved, // [IN] For future use (e.g. non-type parameters)
+ mdToken rtkConstraints[], // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec)
+ mdGenericParam *pgp); // [OUT] Put GenericParam token here
+
+ STDMETHODIMP SetGenericParamProps( // S_OK or error.
+ mdGenericParam gp, // [IN] GenericParam
+ DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance)
+ LPCWSTR szName, // [IN] Optional name
+ DWORD reserved, // [IN] For future use (e.g. non-type parameters)
+ mdToken rtkConstraints[]); // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec)
+
+//*****************************************************************************
+// IMetaDataAssemblyEmit
+//*****************************************************************************
+ STDMETHODIMP DefineAssembly( // S_OK or error.
+ const void *pbPublicKey, // [IN] Public key of the assembly.
+ ULONG cbPublicKey, // [IN] Count of bytes in the public key.
+ ULONG ulHashAlgId, // [IN] Hash Algorithm.
+ LPCWSTR szName, // [IN] Name of the assembly.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ DWORD dwAssemblyFlags, // [IN] Flags.
+ mdAssembly *pma); // [OUT] Returned Assembly token.
+
+ STDMETHODIMP DefineAssemblyRef( // S_OK or error.
+ const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly.
+ ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token.
+ LPCWSTR szName, // [IN] Name of the assembly being referenced.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwAssemblyRefFlags, // [IN] Token for Execution Location.
+ mdAssemblyRef *pmar); // [OUT] Returned AssemblyRef token.
+
+ STDMETHODIMP DefineFile( // S_OK or error.
+ LPCWSTR szName, // [IN] Name of the file.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwFileFlags, // [IN] Flags.
+ mdFile *pmf); // [OUT] Returned File token.
+
+ STDMETHODIMP DefineExportedType( // S_OK or error.
+ LPCWSTR szName, // [IN] Name of the Com Type.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef tkTypeDef, // [IN] TypeDef token within the file.
+ DWORD dwExportedTypeFlags, // [IN] Flags.
+ mdExportedType *pmct); // [OUT] Returned ExportedType token.
+
+ STDMETHODIMP DefineManifestResource( // S_OK or error.
+ LPCWSTR szName, // [IN] Name of the resource.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource.
+ DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file.
+ DWORD dwResourceFlags, // [IN] Flags.
+ mdManifestResource *pmmr); // [OUT] Returned ManifestResource token.
+
+ STDMETHODIMP SetAssemblyProps( // S_OK or error.
+ mdAssembly pma, // [IN] Assembly token.
+ const void *pbPublicKey, // [IN] Public key of the assembly.
+ ULONG cbPublicKey, // [IN] Count of bytes in the public key.
+ ULONG ulHashAlgId, // [IN] Hash Algorithm.
+ LPCWSTR szName, // [IN] Name of the assembly.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ DWORD dwAssemblyFlags); // [IN] Flags.
+
+ STDMETHODIMP SetAssemblyRefProps( // S_OK or error.
+ mdAssemblyRef ar, // [IN] AssemblyRefToken.
+ const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly.
+ ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token.
+ LPCWSTR szName, // [IN] Name of the assembly being referenced.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwAssemblyRefFlags); // [IN] Token for Execution Location.
+
+ STDMETHODIMP SetFileProps( // S_OK or error.
+ mdFile file, // [IN] File token.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwFileFlags); // [IN] Flags.
+
+ STDMETHODIMP SetExportedTypeProps( // S_OK or error.
+ mdExportedType ct, // [IN] ExportedType token.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef tkTypeDef, // [IN] TypeDef token within the file.
+ DWORD dwExportedTypeFlags); // [IN] Flags.
+
+ STDMETHODIMP SetManifestResourceProps( // S_OK or error.
+ mdManifestResource mr, // [IN] ManifestResource token.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource.
+ DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file.
+ DWORD dwResourceFlags); // [IN] Flags.
+
+#endif //FEATURE_METADATA_EMIT
+
+#ifdef FEATURE_METADATA_VALIDATOR
+//*****************************************************************************
+// IMetaDataValidator
+//*****************************************************************************
+
+ STDMETHODIMP ValidatorInit(
+ DWORD dwModuleType, // [IN] Specifies whether the module is a PE file or an obj.
+ IUnknown * pUnk); // [IN] Validation error handler.
+
+ STDMETHODIMP ValidateMetaData();
+#endif //FEATURE_METADATA_VALIDATOR
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+//*****************************************************************************
+// IMetaDataFilter
+//*****************************************************************************
+ STDMETHODIMP UnmarkAll(); // unmark everything in a module
+
+ STDMETHODIMP MarkToken(
+ mdToken tk); // [IN] Token to be marked
+
+ STDMETHODIMP IsTokenMarked(
+ mdToken tk, // [IN] Token to be checked
+ BOOL *pIsMarked); // [OUT] TRUE if token is marked
+
+#endif //FEATURE_METADATA_EMIT_ALL
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+//*****************************************************************************
+// IMetaDataEmitHelper
+//*****************************************************************************
+ STDMETHODIMP DefineMethodSemanticsHelper(
+ mdToken tkAssociation, // [IN] property or event token
+ DWORD dwFlags, // [IN] semantics
+ mdMethodDef md); // [IN] method to associated with
+
+ STDMETHODIMP SetFieldLayoutHelper( // Return hresult.
+ mdFieldDef fd, // [IN] field to associate the layout info
+ ULONG ulOffset); // [IN] the offset for the field
+
+ STDMETHODIMP DefineEventHelper(
+ mdTypeDef td, // [IN] the class/interface on which the event is being defined
+ LPCWSTR szEvent, // [IN] Name of the event
+ DWORD dwEventFlags, // [IN] CorEventAttr
+ mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef) to the Event class
+ mdEvent *pmdEvent); // [OUT] output event token
+
+ STDMETHODIMP AddDeclarativeSecurityHelper(
+ mdToken tk, // [IN] Parent token (typedef/methoddef)
+ DWORD dwAction, // [IN] Security action (CorDeclSecurity)
+ void const *pValue, // [IN] Permission set blob
+ DWORD cbValue, // [IN] Byte count of permission set blob
+ mdPermission*pmdPermission); // [OUT] Output permission token
+
+ STDMETHODIMP SetResolutionScopeHelper( // Return hresult.
+ mdTypeRef tr, // [IN] TypeRef record to update
+ mdToken rs); // [IN] new ResolutionScope
+
+ STDMETHODIMP SetManifestResourceOffsetHelper( // Return hresult.
+ mdManifestResource mr, // [IN] The manifest token
+ ULONG ulOffset); // [IN] new offset
+
+ STDMETHODIMP SetTypeParent( // Return hresult.
+ mdTypeDef td, // [IN] Type definition
+ mdToken tkExtends); // [IN] parent type
+
+ STDMETHODIMP AddInterfaceImpl( // Return hresult.
+ mdTypeDef td, // [IN] Type definition
+ mdToken tkInterface); // [IN] interface type
+
+//*****************************************************************************
+// IMDInternalEmit
+//*****************************************************************************
+
+ STDMETHODIMP ChangeMvid( // S_OK or error.
+ REFGUID newMvid); // GUID to use as the MVID
+
+ STDMETHOD(SetMDUpdateMode)(
+ ULONG updateMode, ULONG *pPreviousUpdateMode);
+
+//*****************************************************************************
+// IMetaDataHelper
+//*****************************************************************************
+ STDMETHODIMP GetMetadata( // Result.
+ ULONG ulSelect, // [IN] Selector.
+ void **ppData); // [OUT] Put pointer to data here.
+
+ STDMETHODIMP_(IUnknown *) GetCachedInternalInterface(BOOL fWithLock); // S_OK or error
+ STDMETHODIMP SetCachedInternalInterface(IUnknown *pUnk); // S_OK or error
+ STDMETHODIMP SetReaderWriterLock(UTSemReadWrite * pSem)
+ {
+ _ASSERTE(m_pSemReadWrite == NULL);
+ m_pSemReadWrite = pSem;
+ INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
+ return NOERROR;
+ }
+ STDMETHODIMP_(UTSemReadWrite *) GetReaderWriterLock() { return m_pSemReadWrite; }
+
+#ifndef FEATURE_METADATA_EMIT
+ // This method is also part of IMetaDataEmit interface, do not declare it twice
+ STDMETHODIMP TranslateSigWithScope(
+ IMetaDataAssemblyImport *pAssemImport, // [IN] assembly importing interface
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ IMetaDataImport *import, // [IN] importing interface
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit *pAssemEmti, // [IN] emit assembly interface
+ IMetaDataEmit *emit, // [IN] emit interface
+ PCOR_SIGNATURE pvTranslatedSig, // [OUT] buffer to hold translated signature
+ ULONG cbTranslatedSigMax,
+ ULONG *pcbTranslatedSig); // [OUT] count of bytes in the translated signature
+#endif //!FEATURE_METADATA_EMIT
+
+ //*****************************************************************************
+ // IGetIMDInternalImport methods
+ //*****************************************************************************
+ STDMETHOD(GetIMDInternalImport) (
+ IMDInternalImport ** ppIMDInternalImport // [OUT] Buffer to receive IMDInternalImport*
+ );
+
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+//*****************************************************************************
+// IMetaDataTables
+//*****************************************************************************
+
+ // Fills size (*pcbStringsHeapSize) of internal strings heap (#String).
+ // Returns S_OK or error code. Fills *pcbStringsHeapSize with 0 on error.
+ // Implements public API code:IMetaDataTables::GetStringHeapSize.
+ STDMETHODIMP GetStringHeapSize(
+ __out ULONG *pcbStringsHeapSize); // [OUT] Size of the string heap.
+
+ // Fills size (*pcbBlobsHeapSize) of blobs heap (#Blob).
+ // Returns S_OK or error code. Fills *pcbBlobsHeapSize with 0 on error.
+ // Implements public API code:IMetaDataTables::GetBlobHeapSize.
+ STDMETHODIMP GetBlobHeapSize(
+ __out ULONG *pcbBlobsHeapSize); // [OUT] Size of the blob heap.
+
+ // Fills size (*pcbGuidsHeapSize) of guids heap (#GUID).
+ // Returns S_OK or error code. Fills *pcbGuidsHeapSize with 0 on error.
+ // Implements public API code:IMetaDataTables::GetGuidHeapSize.
+ STDMETHODIMP GetGuidHeapSize(
+ __out ULONG *pcbGuidsHeapSize); // [OUT] Size of the Guid heap.
+
+ // Fills size (*pcbUserStringsHeapSize) of user strings heap (#US) (referenced from IL).
+ // Returns S_OK or error code. Fills *pcbUserStringsHeapSize with 0 on error.
+ // Implements public API code:IMetaDataTables::GetUserStringHeapSize.
+ // Backward compatibility: returns S_OK even if the string doesn't have odd number of bytes as specified
+ // in CLI ECMA specification.
+ STDMETHODIMP GetUserStringHeapSize(
+ __out ULONG *pcbUserStringsHeapSize); // [OUT] Size of the user string heap.
+
+ // Implements public API code:IMetaDataTables::GetNumTables.
+ STDMETHODIMP GetNumTables(
+ __out ULONG *pcTables); // [OUT] Count of tables.
+
+ // Implements public API code:IMetaDataTables::GetNumTables.
+ STDMETHODIMP GetTableIndex(
+ ULONG token, // [IN] Token for which to get table index.
+ __out ULONG *pixTbl); // [OUT] Put table index here.
+
+ // Implements public API code:IMetaDataTables::GetTableInfo.
+ STDMETHODIMP GetTableInfo(
+ ULONG ixTbl, // [IN] Which table.
+ ULONG *pcbRow, // [OUT] Size of a row, bytes.
+ ULONG *pcRows, // [OUT] Number of rows.
+ ULONG *pcCols, // [OUT] Number of columns in each row.
+ ULONG *piKey, // [OUT] Key column, or -1 if none.
+ const char **ppName); // [OUT] Name of the table.
+
+ // Implements public API code:IMetaDataTables::GetColumnInfo.
+ STDMETHODIMP GetColumnInfo(
+ ULONG ixTbl, // [IN] Which Table.
+ ULONG ixCol, // [IN] Which Column in the table.
+ ULONG *poCol, // [OUT] Offset of the column in the row.
+ ULONG *pcbCol, // [OUT] Size of a column, bytes.
+ ULONG *pType, // [OUT] Type of the column.
+ const char **ppName); // [OUT] Name of the Column.
+
+ // Implements public API code:IMetaDataTables::GetCodedTokenInfo.
+ STDMETHODIMP GetCodedTokenInfo(
+ ULONG ixCdTkn, // [IN] Which kind of coded token.
+ ULONG *pcTokens, // [OUT] Count of tokens.
+ ULONG **ppTokens, // [OUT] List of tokens.
+ const char **ppName); // [OUT] Name of the CodedToken.
+
+ // Implements public API code:IMetaDataTables::GetRow.
+ STDMETHODIMP GetRow(
+ ULONG ixTbl, // [IN] Which table.
+ ULONG rid, // [IN] Which row.
+ void **ppRow); // [OUT] Put pointer to row here.
+
+ // Implements public API code:IMetaDataTables::GetColumn.
+ STDMETHODIMP GetColumn(
+ ULONG ixTbl, // [IN] Which table.
+ ULONG ixCol, // [IN] Which column.
+ ULONG rid, // [IN] Which row.
+ ULONG *pVal); // [OUT] Put the column contents here.
+
+ //#GetString_IMetaDataTables
+ // Fills internal null-terminated string (*pszString) at index ixString from string heap (#String).
+ // Returns S_OK (even for index 0) or error code (if index is invalid, fills *pszString with NULL then).
+ // Implements public API code:IMetaDataTables::GetString.
+ STDMETHODIMP GetString(
+ ULONG ixString, // [IN] Value from a string column.
+ const char **pszString); // [OUT] Put a pointer to the string here.
+
+ //#GetBlob_IMetaDataTables
+ // Fills blob entry (*ppvData of size *pcbDataSize) at index ixBlob from blob heap (#Blob).
+ // Returns S_OK (even for index 0) or error code (if index is invalid, fills NULL and o then).
+ // Implements public API code:IMetaDataTables::GetBlob.
+ STDMETHODIMP GetBlob(
+ ULONG ixBlob, // [IN] Value from a blob column.
+ ULONG *pcbDataSize, // [OUT] Put size of the blob here.
+ const void **ppvData); // [OUT] Put a pointer to the blob here.
+
+ //#GetGuid_IMetaDataTables
+ // Fills guid (*ppGuid) at index ixGuid from guid heap (#GUID).
+ // Returns S_OK and fills *ppGuid. Returns S_OK even for (invalid) index 0 (fills *ppGuid with pointer
+ // to zeros then).
+ // Retruns error code (if index is invalid except 0, fills NULL and o then).
+ // Implements public API code:IMetaDataTables::GetGuid.
+ // Backward compatibility: returns S_OK even if the index is 0 which is invalid as specified in CLI ECMA
+ // specification. In that case returns pointer to GUID from zeros.
+ STDMETHODIMP GetGuid(
+ ULONG ixGuid, // [IN] Value from a guid column.
+ const GUID **ppGuid); // [OUT] Put a pointer to the GUID here.
+
+ //#GetUserString_IMetaDataTables
+ // Fills user string (*ppvData of size *pcbDataSize) at index ixUserString.
+ // Returns S_OK (even for index 0) or error code (if index is invalid, fills NULL and o then).
+ // Implements public API code:IMetaDataTables::GetUserString.
+ STDMETHODIMP GetUserString(
+ ULONG ixUserString, // [IN] Value from a UserString column.
+ __out ULONG *pcbData, // [OUT] Put size of the UserString here.
+ __deref_out_opt const void **ppData); // [OUT] Put a pointer to the UserString here.
+
+ //#GetNextString_IMetaDataTables
+ // Fills index of string (*pixNextString) from the internal strings heap (#String) starting behind
+ // string at index ixString.
+ // Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextString with 0 on S_FALSE.
+ // Implements public API code:IMetaDataTables::.GetNextString.
+ STDMETHODIMP GetNextString(
+ ULONG ixString, // [IN] Value from a string column.
+ __out ULONG *pixNextString); // [OUT] Put the index of the next string here.
+
+ //#GetNextBlob_IMetaDataTables
+ // Fills index of blob (*pixNextBlob) from the blobs heap (#Blob) starting behind blob at index ixBlob.
+ // Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextBlob with 0 on S_FALSE.
+ // Implements public API code:IMetaDataTables::GetNextString.
+ STDMETHODIMP GetNextBlob(
+ ULONG ixBlob, // [IN] Value from a blob column.
+ __out ULONG *pixNextBlob); // [OUT] Put the index of the next blob here.
+
+ //#GetNextGuid_IMetaDataTables
+ // Fills index of guid (*pixNextGuid) from the guids heap (#GUID) starting behind guid at index ixGuid.
+ // Returns S_OK or S_FALSE (if the new index is invalid). Fills *pixNextGuid with 0 on S_FALSE.
+ // Implements public API code:IMetaDataTables::GetNextGuid.
+ // Backward compatibility: returns S_OK even if the guid index (ixGuid) is 0 which is invalid as
+ // specified in CLI ECMA specification.
+ STDMETHODIMP GetNextGuid(
+ ULONG ixGuid, // [IN] Value from a guid column.
+ __out ULONG *pixNextGuid); // [OUT] Put the index of the next guid here.
+
+ //#GetNextUserString_IMetaDataTables
+ // Fills index of user string (*pixNextUserString) from the user strings heap (#US) starting behind string
+ // at index ixUserString.
+ // Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextUserString with 0 on S_FALSE.
+ // Implements public API code:IMetaDataTables::GetNextUserString.
+ // Backward compatibility: returns S_OK even if the string doesn't have odd number of bytes as specified
+ // in CLI ECMA specification.
+ STDMETHODIMP GetNextUserString(
+ ULONG ixUserString, // [IN] Value from a UserString column.
+ __out ULONG *ixpNextUserString); // [OUT] Put the index of the next user string here.
+
+ // Implements public API code:IMetaDataTables2::GetMetaDataStorage.
+ STDMETHODIMP GetMetaDataStorage(
+ const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB').
+ ULONG *pcbMd); // [OUT] put size of the stream here.
+
+ // Implements public API code:IMetaDataTables2::GetMetaDataStreamInfo.
+ STDMETHODIMP GetMetaDataStreamInfo( // Get info about the MD stream.
+ 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.
+
+#ifndef FEATURE_METADATA_STANDALONE_WINRT
+
+//*****************************************************************************
+// IMetaDataInfo
+//*****************************************************************************
+
+ // Returns the memory region of the mapped file and type of its mapping. The choice of the file mapping
+ // type for each scope is CLR implementation specific and user cannot explicitly set it.
+ //
+ // The memory is valid only as long as the underlying MetaData scope is opened (there's a reference to
+ // a MetaData interface for this scope).
+ //
+ // Returns S_OK, COR_E_NOTSUPPORTED (.obj files, etc.), or E_INVALIDARG (if NULL is passed).
+ // Implements public API code:IMetaDataInfo::GetFileMapping.
+ STDMETHODIMP GetFileMapping(
+ const void ** ppvData, // [out] Pointer to the start of the mapped file.
+ ULONGLONG * pcbData, // [out] Size of the mapped memory region..
+ DWORD * pdwMappingType); // [out] Type of file mapping (code:CorFileMapping).
+
+#endif //!FEATURE_METADATA_STANDALONE_WINRT
+
+#if defined(FEATURE_METADATA_IN_VM) && defined(FEATURE_PREJIT)
+
+//*****************************************************************************
+// IMetaDataCorProfileData
+//*****************************************************************************
+
+ STDMETHOD(SetCorProfileData)(
+ CorProfileData *pProfileData); // [IN] Pointer to profile data
+
+//*****************************************************************************
+// IMDInternalMetadataReorderingOptions
+//*****************************************************************************
+
+ STDMETHOD(SetMetaDataReorderingOptions)(
+ MetaDataReorderingOptions options); // [IN] metadata reordering options
+
+#endif //FEATURE_METADATA_IN_VM && FEATURE_PREJIT
+
+//*****************************************************************************
+// IMDCommon methods
+//*****************************************************************************
+ STDMETHOD_(IMetaModelCommon*, GetMetaModelCommon)()
+ {
+ return GetMiniMd();
+ }
+
+ STDMETHOD_(IMetaModelCommonRO*, GetMetaModelCommonRO)()
+ {
+ if (GetMiniMd()->IsWritable())
+ {
+ _ASSERTE(!"IMetaModelCommonRO methods cannot be used because this importer is writable.");
+ return NULL;
+ }
+ return GetMiniMd();
+ }
+
+
+ // returns the "built for" version of a metadata scope.
+ __checkReturn
+ STDMETHOD(GetVersionString)( // S_OK or error.
+ LPCSTR *pVer); // [OUT] Put version string here.
+
+//*****************************************************************************
+// Helpers.
+//*****************************************************************************
+
+ HRESULT MarkAll(); // mark everything in a module
+
+//*****************************************************************************
+// Open / Create support.
+//*****************************************************************************
+
+ RegMeta();
+ virtual ~RegMeta();
+
+ HRESULT SetOption(OptionValue *pOptionValue);
+
+ // HRESULT Init();
+ // void Cleanup();
+
+ HRESULT InitWithStgdb(
+ IUnknown *pUnk, // The IUnknown that owns the life time for the existing stgdb
+ CLiteWeightStgdbRW *pStgdb); // existing light weight stgdb
+
+ ULONG GetRefCount() { return m_cRef; }
+ HRESULT AddToCache();
+ static HRESULT FindCachedReadOnlyEntry(LPCWSTR szName, DWORD dwOpenFlags, RegMeta **ppMeta);
+ BOOL IsReadOnly() { return IsOfReadOnly(m_OpenFlags); }
+ BOOL IsCopyMemory() { return IsOfCopyMemory(m_OpenFlags); }
+
+
+ // helper function to reopen RegMeta with a new chuck of memory
+ HRESULT ReOpenWithMemory(
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData, // [in] ReOpen flags
+ DWORD dwReOpenFlags); // [in] Size of the data pointed to by pData.
+
+ HRESULT CreateNewMD();
+
+ HRESULT OpenExistingMD(
+ LPCWSTR szDatabase, // Name of database.
+ void *pbData, // Data to open on top of, 0 default.
+ ULONG cbData, // How big is the data.
+ ULONG dwFlags); // Flags to control open.
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ HRESULT OpenExistingMD(
+ IMDCustomDataSource* pDataSource, // Name of database.
+ ULONG dwFlags); // Flags to control open.
+#endif
+
+ FORCEINLINE CLiteWeightStgdbRW* GetMiniStgdb() { return m_pStgdb; }
+ FORCEINLINE CMiniMdRW* GetMiniMd() { return &m_pStgdb->m_MiniMd; }
+
+//*****************************************************************************
+
+ bool IsTypeDefDirty() { return m_fIsTypeDefDirty;}
+ void SetTypeDefDirty(bool fDirty) { m_fIsTypeDefDirty = fDirty;}
+
+ bool IsMemberDefDirty() { return m_fIsMemberDefDirty;}
+ void SetMemberDefDirty(bool fDirty) { m_fIsMemberDefDirty = fDirty;}
+
+ FORCEINLINE BOOL IsThreadSafetyOn()
+ {
+ return (m_OptionValue.m_ThreadSafetyOptions & MDThreadSafetyOn) == MDThreadSafetyOn;
+ }
+
+ LPCWSTR GetNameOfDBFile() { return (m_pStgdb->m_wszFileName == NULL) ? W("") : m_pStgdb->m_wszFileName; }
+ DWORD GetLowFileTimeOfDBFile() { return m_pStgdb->m_dwDatabaseLFT; }
+ DWORD GetLowFileSizeOfDBFile() { return m_pStgdb->m_dwDatabaseLFS; }
+protected:
+ // Helper functions used for implementation of MetaData APIs.
+ HRESULT RefToDefOptimization();
+
+ FORCEINLINE BOOL PreserveLocalRefs(CorLocalRefPreservation localRefType)
+ {
+ return (m_OptionValue.m_LocalRefPreservation & localRefType) == localRefType;
+ }
+
+ HRESULT PreSave();
+ HRESULT ProcessFilter();
+ HRESULT ProcessFilterWorker();
+
+ // Initialize the EE
+ HRESULT StartupEE();
+
+ // Define a TypeRef given the name.
+ enum eCheckDups {eCheckDefault=0, eCheckNo=1, eCheckYes=2};
+
+ HRESULT _DefinePermissionSet(
+ mdToken tk, // [IN] the object to be decorated.
+ DWORD dwAction, // [IN] CorDeclSecurity.
+ void const *pvPermission, // [IN] permission blob.
+ ULONG cbPermission, // [IN] count of bytes of pvPermission.
+ mdPermission *ppm); // [OUT] returned permission token.
+
+ HRESULT _DefineTypeRef(
+ mdToken tkResolutionScope, // [IN] ModuleRef or AssemblyRef.
+ const void *szName, // [IN] Name of the TypeRef.
+ BOOL isUnicode, // [IN] Specifies whether the URL is unicode.
+ mdTypeRef *ptk, // [OUT] Put mdTypeRef here.
+ eCheckDups eCheck=eCheckDefault); // [IN] Specifies whether to check for duplicates.
+
+ // Define MethodSemantics
+ HRESULT _DefineMethodSemantics( // S_OK or error.
+ USHORT usAttr, // [IN] CorMethodSemanticsAttr
+ mdMethodDef md, // [IN] Method
+ mdToken tkAssoc, // [IN] Association
+ BOOL bClear); // [IN] Specifies whether to delete the existing records.
+
+ HRESULT _SaveToStream( // S_OK or error.
+ IStream *pIStream, // [IN] A writable stream to save to.
+ DWORD dwSaveFlags); // [IN] Flags for the save.
+
+ HRESULT _SetRVA( // [IN] S_OK or error.
+ mdToken md, // [IN] Member for which to set offset
+ ULONG ulCodeRVA, // [IN] The offset
+ DWORD dwImplFlags);
+
+ HRESULT _DefineEvent( // Return hresult.
+ mdTypeDef td, // [IN] the class/interface on which the event is being defined
+ LPCWSTR szEvent, // [IN] Name of the event
+ DWORD dwEventFlags, // [IN] CorEventAttr
+ mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef) to the Event class
+ mdEvent *pmdEvent); // [OUT] output event token
+
+ // Creates and sets a row in the InterfaceImpl table. Optionally clear
+ // pre-existing records for the owning class.
+ HRESULT _SetImplements( // S_OK or error.
+ mdToken rTk[], // Array of TypeRef or TypeDef tokens for implemented interfaces.
+ mdTypeDef td, // Implementing TypeDef.
+ BOOL bClear); // Specifies whether to clear the existing records.
+
+ // Sets flags, name and constraints for a single GenericParam record
+ HRESULT _SetGenericParamProps( // S_OK or error.
+ mdGenericParam tkGP, // [IN] Formal parameter token
+ GenericParamRec *pGenericParam, // [IN] GenericParam record ptr
+ DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance)
+ LPCWSTR szName, // [IN] Optional name
+ DWORD reserved, // [IN] For future use (e.g. non-type parameters)
+ mdToken rtkConstraints[]); // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec)
+
+ HRESULT _SetTypeDefProps( // S_OK or error.
+ mdTypeDef td, // [IN] The TypeDef.
+ DWORD dwTypeDefFlags, // [IN] TypeDef flags.
+ mdToken tkExtends, // [IN] Base TypeDef or TypeRef.
+ mdToken rtkImplements[]); // [IN] Implemented interfaces.
+
+ HRESULT _SetEventProps1( // Return hresult.
+ mdEvent ev, // [IN] Event token.
+ DWORD dwEventFlags, // [IN] Event flags.
+ mdToken tkEventType); // [IN] Event type class.
+
+ HRESULT _SetEventProps2( // Return hresult.
+ mdEvent ev, // [IN] Event token.
+ mdMethodDef mdAddOn, // [IN] Add method.
+ mdMethodDef mdRemoveOn, // [IN] Remove method.
+ mdMethodDef mdFire, // [IN] Fire method.
+ mdMethodDef rmdOtherMethods[], // [IN] An array of other methods.
+ BOOL bClear); // [IN] Specifies whether to clear the existing MethodSemantics records.
+
+ HRESULT _SetPermissionSetProps( // Return hresult.
+ mdPermission tkPerm, // [IN] Permission token.
+ DWORD dwAction, // [IN] CorDeclSecurity.
+ void const *pvPermission, // [IN] Permission blob.
+ ULONG cbPermission); // [IN] Count of bytes of pvPermission.
+
+ HRESULT _DefinePinvokeMap( // Return hresult.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD dwMappingFlags, // [IN] Flags used for mapping.
+ LPCWSTR szImportName, // [IN] Import name.
+ mdModuleRef mrImportDLL); // [IN] ModuleRef token for the target DLL.
+
+ HRESULT _DefineSetConstant( // Return hresult.
+ mdToken tk, // [IN] Parent token.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchString, // [IN] Size of string in wide chars, or -1 for default.
+ BOOL bSearch); // [IN] Specifies whether to search for an existing record.
+
+ HRESULT _SetMethodProps( // S_OK or error.
+ mdMethodDef md, // [IN] The MethodDef.
+ DWORD dwMethodFlags, // [IN] Method attributes.
+ ULONG ulCodeRVA, // [IN] Code RVA.
+ DWORD dwImplFlags); // [IN] MethodImpl flags.
+
+ HRESULT _SetFieldProps( // S_OK or error.
+ mdFieldDef fd, // [IN] The FieldDef.
+ DWORD dwFieldFlags, // [IN] Field attributes.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchValue); // [IN] size of constant value (string, in wide chars).
+
+ HRESULT _SetClassLayout( // S_OK or error.
+ mdTypeDef td, // [IN] The class.
+ ULONG dwPackSize, // [IN] The packing size.
+ ULONG ulClassSize); // [IN, OPTIONAL] The class size.
+
+ HRESULT _SetFieldOffset( // S_OK or error.
+ mdFieldDef fd, // [IN] The field.
+ ULONG ulOffset); // [IN] The offset of the field.
+
+ HRESULT _SetPropertyProps( // S_OK or error.
+ mdProperty pr, // [IN] Property token.
+ DWORD dwPropFlags, // [IN] CorPropertyAttr.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdMethodDef mdSetter, // [IN] Setter of the property.
+ mdMethodDef mdGetter, // [IN] Getter of the property.
+ mdMethodDef rmdOtherMethods[]); // [IN] Array of other methods.
+
+ HRESULT _SetParamProps( // Return code.
+ mdParamDef pd, // [IN] Param token.
+ LPCWSTR szName, // [IN] Param name.
+ DWORD dwParamFlags, // [IN] Param flags.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for value type. selected ELEMENT_TYPE_*.
+ void const *pValue, // [OUT] Constant value.
+ ULONG cchValue); // [IN] size of constant value (string, in wide chars).
+
+ HRESULT _SetAssemblyProps( // S_OK or error.
+ mdAssembly pma, // [IN] Assembly token.
+ const void *pbOriginator, // [IN] Originator of the assembly.
+ ULONG cbOriginator, // [IN] Count of bytes in the Originator blob.
+ ULONG ulHashAlgId, // [IN] Hash Algorithm.
+ LPCWSTR szName, // [IN] Name of the assembly.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ DWORD dwAssemblyFlags); // [IN] Flags.
+
+ HRESULT _SetAssemblyRefProps( // S_OK or error.
+ mdAssemblyRef ar, // [IN] AssemblyRefToken.
+ const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly.
+ ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token.
+ LPCWSTR szName, // [IN] Name of the assembly being referenced.
+ const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwAssemblyRefFlags); // [IN] Token for Execution Location.
+
+ HRESULT _SetFileProps( // S_OK or error.
+ mdFile file, // [IN] File token.
+ const void *pbHashValue, // [IN] Hash Blob.
+ ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob.
+ DWORD dwFileFlags) ; // [IN] Flags.
+
+ HRESULT _SetExportedTypeProps( // S_OK or error.
+ mdExportedType ct, // [IN] ExportedType token.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef tkTypeDef, // [IN] TypeDef token within the file.
+ DWORD dwExportedTypeFlags); // [IN] Flags.
+
+ HRESULT _SetManifestResourceProps( // S_OK or error.
+ mdManifestResource mr, // [IN] ManifestResource token.
+ mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource.
+ DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file.
+ DWORD dwResourceFlags); // [IN] Flags.
+
+ HRESULT _DefineTypeDef( // S_OK or error.
+ LPCWSTR szTypeDef, // [IN] Name of TypeDef
+ DWORD dwTypeDefFlags, // [IN] CustomAttribute flags
+ mdToken tkExtends, // [IN] extends this TypeDef or typeref
+ mdToken rtkImplements[], // [IN] Implements interfaces
+ mdTypeDef tdEncloser, // [IN] TypeDef token of the Enclosing Type.
+ mdTypeDef *ptd); // [OUT] Put TypeDef token here
+
+ HRESULT _SetFieldMarshal(
+ mdToken tk, // [IN] given a fieldDef or paramDef token
+ PCCOR_SIGNATURE pvNativeType, // [IN] native type specification
+ ULONG cbNativeType); // [IN] count of bytes of pvNativeType
+
+ HRESULT _IsKnownCustomAttribute( // S_OK, S_FALSE, or error.
+ mdToken tkType, // [IN] Token of custom attribute's type.
+ int *pca); // [OUT] Put value from KnownCustAttr enum here.
+
+ HRESULT _DefineModuleRef( // S_OK or error.
+ LPCWSTR szName, // [IN] DLL name
+ mdModuleRef *pmur); // [OUT] returned module ref token
+
+ HRESULT _HandleKnownCustomAttribute( // S_OK or error.
+ mdToken tkObj, // [IN] Object being attributed.
+ const void *pData, // [IN] Custom Attribute data blob.
+ ULONG cbData, // [IN] Count of bytes in the data.
+ int ca, // [IN] Value from KnownCustAttr enum.
+ int *bKeep); // [OUT} Keep the known CA?
+
+ HRESULT _HandleNativeTypeCustomAttribute(// S_OK or error.
+ mdToken tkObj, // Object being attributed.
+ CaArg *pArgs, // Pointer to args.
+ CaNamedArg *pNamedArgs, // Pointer to named args.
+ CQuickArray<BYTE> &qNativeType); // Native type is built here.
+
+ // Find a given param of a Method.
+ HRESULT _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.
+
+ // Given the signature, return the token for signature.
+ HRESULT _GetTokenFromSig( // S_OK or error.
+ PCCOR_SIGNATURE pvSig, // [IN] Signature to define.
+ ULONG cbSig, // [IN] Size of signature data.
+ mdSignature *pmsig); // [OUT] returned signature token.
+
+ // Turn the specified internal flags on.
+ HRESULT _TurnInternalFlagsOn( // S_OK or error.
+ mdToken tkObj, // [IN] Target object whose internal flags are targeted.
+ DWORD flags); // [IN] Specifies flags to be turned on.
+
+ // This routine eliminates duplicates from the given list of InterfaceImpl tokens
+ // to be defined. It checks for duplicates against the database only if the
+ // TypeDef for which these tokens are being defined is not a new one.
+ HRESULT _InterfaceImplDupProc( // S_OK or error.
+ mdToken rTk[], // Array of TypeRef or TypeDef tokens for implemented interfaces.
+ mdTypeDef td, // Implementing TypeDef.
+ CQuickBytes *pcqbTk); // Quick Byte object for placing the array of unique tokens.
+
+ // Helper : convert a text field signature to a com format
+ HRESULT _ConvertTextElementTypeToComSig(// Return hresult.
+ IMetaDataEmit *emit, // [IN] emit interface.
+ BOOL fCreateTrIfNotFound, // [IN] create typeref if not found or fail out?
+ LPCSTR *ppOneArgSig, // [IN|OUT] class file format signature. On exit, it will be next arg starting point
+ CQuickBytes *pqbNewSig, // [OUT] place holder for COM+ signature
+ ULONG cbStart, // [IN] bytes that are already in pqbNewSig
+ ULONG *pcbCount); // [OUT] count of bytes put into the QuickBytes buffer
+
+ HRESULT _CheckCmodForCallConv( // S_OK, -1 if found, or error.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature to check.
+ ULONG *pcbTotal, // [OUT] Put bytes consumed here.
+ ULONG *pCallConv); // [OUT] If found, put calling convention here.
+
+ HRESULT _SearchOneArgForCallConv( // S_OK, -1 if found, or error.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature to check.
+ ULONG *pcbTotal, // [OUT] Put bytes consumed here.
+ ULONG *pCallConv); // [OUT] If found, put calling convention here.
+
+
+
+ int inline IsGlobalMethodParent(mdTypeDef *ptd)
+ {
+ if (IsGlobalMethodParentTk(*ptd))
+ {
+ *ptd = m_tdModule;
+ return (true);
+ }
+ return (false);
+ }
+
+ int inline IsGlobalMethodParentToken(mdTypeDef td)
+ {
+ return (!IsNilToken(m_tdModule) && td == m_tdModule);
+ }
+
+ FORCEINLINE BOOL IsENCOn()
+ {
+ _ASSERTE( ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC) ==
+ m_pStgdb->m_MiniMd.IsENCOn() );
+ return (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC;
+ }
+
+ FORCEINLINE BOOL IsIncrementalOn()
+ {
+ return (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateIncremental;
+ }
+
+ FORCEINLINE BOOL CheckDups(CorCheckDuplicatesFor checkdup)
+ {
+ return ((m_OptionValue.m_DupCheck & checkdup) ||
+ (m_OptionValue.m_UpdateMode == MDUpdateIncremental ||
+ m_OptionValue.m_UpdateMode == MDUpdateENC) );
+ }
+
+ FORCEINLINE HRESULT UpdateENCLog(mdToken tk, CMiniMdRW::eDeltaFuncs funccode = CMiniMdRW::eDeltaFuncDefault)
+ {
+ _ASSERTE( ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC) ==
+ m_pStgdb->m_MiniMd.IsENCOn() );
+ return m_pStgdb->m_MiniMd.UpdateENCLog(tk, funccode);
+ }
+
+ FORCEINLINE HRESULT UpdateENCLog2(ULONG ixTbl, ULONG iRid, CMiniMdRW::eDeltaFuncs funccode = CMiniMdRW::eDeltaFuncDefault)
+ {
+ _ASSERTE( ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC) ==
+ m_pStgdb->m_MiniMd.IsENCOn() );
+ return m_pStgdb->m_MiniMd.UpdateENCLog2(ixTbl, iRid, funccode);
+ }
+
+ FORCEINLINE bool IsCallerDefine() { return m_SetAPICaller == DEFINE_API; }
+ FORCEINLINE void SetCallerDefine() { m_SetAPICaller = DEFINE_API; }
+ FORCEINLINE bool IsCallerExternal() { return m_SetAPICaller == EXTERNAL_CALLER; }
+ FORCEINLINE void SetCallerExternal() { m_SetAPICaller = EXTERNAL_CALLER; }
+
+#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
+ bool IsSafeToDeleteStgdb()
+ {
+ return m_safeToDeleteStgdb && m_pStgdb->m_MiniMd.IsSafeToDelete();
+ }
+
+ FORCEINLINE void MarkUnsafeToDeleteStgdb() { m_safeToDeleteStgdb = false; }
+ FORCEINLINE void MarkSafeToDeleteStgdb() { m_safeToDeleteStgdb = true; }
+#endif
+
+ // Define Validate methods for all tables.
+#undef MiniMdTable
+#define MiniMdTable(x) HRESULT Validate##x(RID rid);
+ MiniMdTables()
+
+ // Validate a record in a generic sense using Meta-Meta data.
+ STDMETHODIMP ValidateRecord(ULONG ixTbl, ULONG ulRow);
+
+ // Validate if the signature is properly formed with regards to the
+ // compression scheme.
+ STDMETHODIMP ValidateSigCompression(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig); // [IN] Size in bytes of the signature.
+
+ // Validate one argument given the offset to the beginning of the
+ // argument, size of the full signature and the currentl offset value.
+ STDMETHODIMP ValidateOneArg(
+ mdToken tk, // [IN] Token whose signature is being processed.
+ PCCOR_SIGNATURE &pbSig, // [IN] Pointer to the beginning of argument.
+ ULONG cbSig, // [IN] Size in bytes of the full signature.
+ ULONG *pulCurByte, // [IN/OUT] Current offset into the signature..
+ ULONG *pulNSentinels, // [IN/OUT] Number of sentinels
+ BOOL bNoVoidAllowed); // [IN] Flag indicating whether "void" is disallowed for this arg
+
+ // Validate the given Method signature.
+ STDMETHODIMP ValidateMethodSig(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size in bytes of the signature.
+ DWORD dwFlags); // [IN] Method flags.
+
+ // Validate the given Field signature.
+ STDMETHODIMP ValidateFieldSig(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig); // [IN] Size in bytes of the signature.
+
+ // Validate the given MethodSpec signature.
+ STDMETHODIMP ValidateMethodSpecSig(
+ mdMethodSpec tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size in bytes of the signature.
+ ULONG *ulArity); // [OUT] Arity of the instantiation.
+
+
+protected:
+
+ // This scope's Stgdb. This stores the actual data which the class then exposes.
+ // This storage may be shared by an internal metadata object too.
+ // This is read-write so that the RegMeta class can implement the emit interfaces.
+ CLiteWeightStgdbRW *m_pStgdb;
+
+ CLiteWeightStgdbRW *m_pStgdbFreeList; // This scope's Stgdb.
+ mdTypeDef m_tdModule; // The global module.
+ IUnknown *m_pUnk; // The IUnknown that owns the Stgdb.
+ FilterManager *m_pFilterManager; // Contains helper functions for marking
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+ // Pointer to internal interface. This is a weak reference (it doesn't addref/release).
+ IMDInternalImport *m_pInternalImport;
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+ UTSemReadWrite *m_pSemReadWrite;
+ bool m_fOwnSem;
+
+ unsigned m_bRemap : 1; // If true, there is a token mapper.
+ unsigned m_bSaveOptimized : 1; // If true, save optimization has been done.
+ unsigned m_hasOptimizedRefToDef : 1; // true if we have performed ref to def optimization
+ IUnknown *m_pHandler;
+ bool m_fIsTypeDefDirty; // This flag is set when the TypeRef to TypeDef map is not valid
+ bool m_fIsMemberDefDirty; // This flag is set when the MemberRef to MemberDef map is not valid
+ bool m_fStartedEE; // Set when EE runtime has been started up.
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ ICorRuntimeHost *m_pCorHost; // Hosting environment for EE runtime.
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+ IUnknown *m_pAppDomain; // AppDomain in which managed security code will be run.
+
+private:
+ ULONG m_OpenFlags; // Open time flags.
+
+ LONG m_cRef; // Ref count.
+#ifdef FEATURE_METADATA_EMIT_ALL
+ NEWMERGER m_newMerger; // class for handling merge
+#endif //FEATURE_METADATA_EMIT_ALL
+ IUnknown *m_pFreeThreadedMarshaler; // FreeThreadedMarshaler
+
+#ifdef FEATURE_METADATA_PERF_STATS
+ MDCompilerPerf m_MDCompilerPerf; // Compiler perf object to store all stats.
+#endif
+
+ // If true, cached in list of global scopes. This is very dangerous because it may allow
+ // unpredictable state sharing between seemingly unrelated dispensers.
+ bool m_bCached;
+
+ OptionValue m_OptionValue;
+
+ mdTypeRef m_trLanguageType;
+
+ // Specifies whether the caller of the Set API is one of the Define functions
+ // or an external API. This allows for performance optimization in the Set APIs
+ // by not checking for Duplicates in certain cases.
+ SetAPICallerType m_SetAPICaller;
+
+ CorValidatorModuleType m_ModuleType;
+ IVEHandler *m_pVEHandler;
+#ifndef FEATURE_CORECLR
+ ValidateRecordFunction m_ValidateRecordFunctionTable[TBL_COUNT];
+#endif
+ CCustAttrHash m_caHash; // Hashed list of custom attribute types seen.
+
+ bool m_bKeepKnownCa; // Should all known CA's be kept?
+
+ CorProfileData *m_pCorProfileData;
+
+ MetaDataReorderingOptions m_ReorderingOptions;
+
+#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
+ bool m_safeToDeleteStgdb; // This starts out true, but gets set to FALSE if we detect
+ // a RegMeta API call that might have given out an internal pointer.
+ // There is an equivalent state in MiniMD, and both must be
+ // TRUE in order to delete safely.
+#endif
+
+ HRESULT _ValidateErrorHelper(
+ HRESULT VECode,
+ VEContext Context);
+
+ HRESULT _ValidateErrorHelper(
+ HRESULT VECode,
+ VEContext Context,
+ ULONG ulVal1);
+
+ HRESULT _ValidateErrorHelper(
+ HRESULT VECode,
+ VEContext Context,
+ ULONG ulVal1,
+ ULONG ulVal2);
+
+ HRESULT _ValidateErrorHelper(
+ HRESULT VECode,
+ VEContext Context,
+ ULONG ulVal1,
+ ULONG ulVal2,
+ ULONG ulVal3);
+
+private:
+ // Returns pointer to zeros of size (cbSize).
+ // Used by public APIs to return compatible values with previous releases.
+ static const BYTE *GetPublicApiCompatibilityZerosOfSize(UINT32 cbSize);
+ // Returns pointer to zeros typed as type T.
+ // Used by public APIs to return compatible values with previous releases.
+ template<class T>
+ T *GetPublicApiCompatibilityZeros()
+ {
+ static_assert_no_msg(sizeof(T) <= sizeof(s_rgMetaDataPublicApiCompatibilityZeros));
+ return reinterpret_cast<T *>(s_rgMetaDataPublicApiCompatibilityZeros);
+ }
+ // Zeros used by public APIs as return value (or pointer to this memory) for invalid input.
+ // It is used by methods:
+ // * code:RegMeta::GetPublicApiCompatibilityZeros, and
+ // * code:RegMeta::GetPublicApiCompatibilityZerosOfSize.
+ static const BYTE s_rgMetaDataPublicApiCompatibilityZeros[64];
+
+}; // class RegMeta
+
+
+#endif // __RegMeta__h__
diff --git a/src/md/compiler/regmeta_compilersupport.cpp b/src/md/compiler/regmeta_compilersupport.cpp
new file mode 100644
index 0000000000..0bea06699b
--- /dev/null
+++ b/src/md/compiler/regmeta_compilersupport.cpp
@@ -0,0 +1,506 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// RegMeta.cpp
+//
+
+//
+// Implementation for meta data public interface methods.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "metadata.h"
+#include "corerror.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+#include "filtermanager.h"
+#include "mdperf.h"
+#include "switches.h"
+#include "posterror.h"
+#include "stgio.h"
+#include "sstring.h"
+
+#include <metamodelrw.h>
+
+#define DEFINE_CUSTOM_NODUPCHECK 1
+#define DEFINE_CUSTOM_DUPCHECK 2
+#define SET_CUSTOM 3
+
+#if defined(_DEBUG) && defined(_TRACE_REMAPS)
+#define LOGGING
+#endif
+#include <log.h>
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4102)
+#endif
+
+#ifdef FEATURE_METADATA_EMIT
+
+//*****************************************************************************
+// Merge the pImport scope to this scope
+//*****************************************************************************
+STDMETHODIMP RegMeta::Merge( // S_OK or error.
+ IMetaDataImport *pImport, // [IN] The scope to be merged.
+ IMapToken *pHostMapToken, // [IN] Host IMapToken interface to receive token remap notification
+ IUnknown *pHandler) // [IN] An object to receive to receive error notification.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ IMetaDataImport2 *pI2=NULL;
+
+ LOG((LOGMD, "RegMeta::Merge(0x%08x, 0x%08x)\n", pImport, pHandler));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(VerifyNotWinMD(pImport, "IMetaDataEmit::Merge(): merging with a .winmd file not supported."));
+
+ IfFailGo(pImport->QueryInterface(IID_IMetaDataImport2, (void**)&pI2));
+ m_hasOptimizedRefToDef = false;
+
+ // track this import
+ IfFailGo( m_newMerger.AddImport(pI2, pHostMapToken, pHandler) );
+
+ErrExit:
+ if (pI2)
+ pI2->Release();
+ STOP_MD_PERF(Merge);
+ END_ENTRYPOINT_NOTHROW;
+
+ return (hr);
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::Merge
+
+
+//*****************************************************************************
+// real merge takes place here
+//*****************************************************************************
+STDMETHODIMP RegMeta::MergeEnd() // S_OK or error.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::MergeEnd()\n"));
+ START_MD_PERF();
+ LOCKWRITE();
+ // Merge happens here!!
+
+ // <REVISIT_TODO>bug 16719. Merge itself is doing a lots of small changes in literally
+ // dozens of places. It would be to hard to maintain and would cause code
+ // bloat to auto-grow the tables. So instead, we've opted to just expand
+ // the world right away and avoid the trouble.</REVISIT_TODO>
+ IfFailGo(m_pStgdb->m_MiniMd.ExpandTables());
+
+ IfFailGo(m_newMerger.Merge(m_OptionValue.m_MergeOptions, m_OptionValue.m_RefToDefCheck) );
+
+ErrExit:
+ STOP_MD_PERF(MergeEnd);
+ END_ENTRYPOINT_NOTHROW;
+
+ return (hr);
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::MergeEnd
+
+
+//*****************************************************************************
+// As the Stgdb object to get the save size for the metadata delta.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetDeltaSaveSize( // S_OK or error.
+ CorSaveSize fSave, // [IN] cssAccurate or cssQuick.
+ DWORD *pdwSaveSize) // [OUT] Put the size here.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // Make sure we're in EnC mode
+ if (!IsENCOn())
+ {
+ _ASSERTE(!"Not in EnC mode!");
+ IfFailGo(META_E_NOT_IN_ENC_MODE);
+ }
+
+ m_pStgdb->m_MiniMd.EnableDeltaMetadataGeneration();
+ hr = GetSaveSize(fSave, pdwSaveSize);
+ m_pStgdb->m_MiniMd.DisableDeltaMetadataGeneration();
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::GetDeltaSaveSize
+
+//*****************************************************************************
+// Saves a metadata delta to a file of a given name.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SaveDelta( // S_OK or error.
+ LPCWSTR szFile, // [IN] The filename to save to.
+ DWORD dwSaveFlags) // [IN] Flags for the save.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+
+ // Make sure we're in EnC mode
+ if (!IsENCOn())
+ {
+ _ASSERTE(!"Not in EnC mode!");
+ IfFailGo(META_E_NOT_IN_ENC_MODE);
+ }
+
+
+
+ m_pStgdb->m_MiniMd.EnableDeltaMetadataGeneration();
+ hr = Save(szFile, dwSaveFlags);
+ m_pStgdb->m_MiniMd.DisableDeltaMetadataGeneration();
+
+ErrExit:
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::SaveDelta
+
+//*****************************************************************************
+// Saves a metadata delta to a stream.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SaveDeltaToStream( // S_OK or error.
+ IStream *pIStream, // [IN] A writable stream to save to.
+ DWORD dwSaveFlags) // [IN] Flags for the save.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // Make sure we're in EnC mode
+ if (!IsENCOn())
+ {
+ _ASSERTE(!"Not in EnC mode!");
+ IfFailGo(META_E_NOT_IN_ENC_MODE);
+ }
+
+
+
+ m_pStgdb->m_MiniMd.EnableDeltaMetadataGeneration();
+ hr = SaveToStream(pIStream, dwSaveFlags);
+ m_pStgdb->m_MiniMd.DisableDeltaMetadataGeneration();
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::SaveDeltaToStream
+
+//*****************************************************************************
+// Saves a copy of the scope into the memory buffer provided. The buffer size
+// must be at least as large as the GetSaveSize value.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SaveDeltaToMemory( // S_OK or error.
+ void *pbData, // [OUT] Location to write data.
+ ULONG cbData) // [IN] Max size of data buffer.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // Make sure we're in EnC mode
+ if (!IsENCOn())
+ {
+ _ASSERTE(!"Not in EnC mode!");
+ IfFailGo(META_E_NOT_IN_ENC_MODE);
+ }
+
+
+ m_pStgdb->m_MiniMd.EnableDeltaMetadataGeneration();
+ hr = SaveToMemory(pbData, cbData);
+ m_pStgdb->m_MiniMd.DisableDeltaMetadataGeneration();
+
+ErrExit:
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::SaveDeltaToMemory
+
+//*****************************************************************************
+// Resets the current edit and continue session
+//
+// Implements public API code:IMetaDataEmit2::ResetENCLog.
+//*****************************************************************************
+STDMETHODIMP
+RegMeta::ResetENCLog()
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // Make sure we're in EnC mode
+ if (!IsENCOn())
+ {
+ _ASSERTE(!"Not in EnC mode!");
+ IfFailGo(META_E_NOT_IN_ENC_MODE);
+ }
+
+ IfFailGo(m_pStgdb->m_MiniMd.ResetENCLog());
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::ResetENCLog
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+
+// Helper for code:RegMeta::ProcessFilter
+HRESULT RegMeta::ProcessFilterWorker()
+{
+ HRESULT hr = S_OK;
+
+ CMiniMdRW *pMiniMd; // The MiniMd with the data.
+ RegMeta *pMetaNew = NULL;
+ CMapToken *pMergeMap = NULL;
+ IMapToken *pMapNew = NULL;
+ MergeTokenManager *pCompositHandler = NULL;
+ IMapToken *pHostMapToken = NULL;
+
+ // For convenience.
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+ IfNullGo( pMiniMd->GetFilterTable() );
+ _ASSERTE(pMiniMd->GetFilterTable()->Count() != 0); // caller verified this
+
+ // Yes, client has used filter to specify what are the metadata needed.
+ // We will create another instance of RegMeta and make this module an imported module
+ // to be merged into the new RegMeta. We will provide the handler to track all of the token
+ // movements. We will replace the merged light weight stgdb to this RegMeta..
+ // Then we will need to fix up the MergeTokenManager with this new movement.
+ // The reason that we decide to choose this approach is because it will be more complicated
+ // and very likely less efficient to fix up the signature blob pool and then compact all of the pools!
+ //
+
+ // Create a new RegMeta.
+ pMetaNew = new (nothrow) RegMeta();
+ IfNullGo( pMetaNew );
+ pMetaNew->AddRef();
+ IfFailGo(pMetaNew->SetOption(&m_OptionValue));
+
+
+ // Remember the open type.
+ IfFailGo(pMetaNew->CreateNewMD());
+ IfFailGo(pMetaNew->AddToCache());
+
+ // Ignore the error return by setting handler
+ hr = pMetaNew->SetHandler(m_pHandler);
+
+ // create the IMapToken to receive token remap information from merge
+ pMergeMap = new (nothrow) CMapToken;
+ IfNullGo( pMergeMap );
+
+ // use merge to filter out the unneeded data. But we need to keep COMType and also need to drop off the
+ // CustomAttributes that associated with MemberRef with parent MethodDef
+ //
+ pMetaNew->m_hasOptimizedRefToDef = false;
+ IfFailGo( pMetaNew->m_newMerger.AddImport(this, pMergeMap, NULL) );
+ IfFailGo( pMetaNew->m_pStgdb->m_MiniMd.ExpandTables());
+ IfFailGo( pMetaNew->m_newMerger.Merge((MergeFlags)(MergeManifest | DropMemberRefCAs | NoDupCheck), MDRefToDefDefault) );
+
+ // Now we need to recalculate the token movement
+ //
+ if (m_newMerger.m_pImportDataList)
+ {
+
+ // This is the case the filter is applied to merged emit scope. We need calculate how this implicit merge
+ // affects the original merge remap. Basically we need to walk all the m_pTkMapList in the merger and replace
+ // the to token to the most recent to token.
+ //
+ MDTOKENMAP *pMDTokenMapList;
+
+ pMDTokenMapList = m_newMerger.m_pImportDataList->m_pMDTokenMap;
+
+ MDTOKENMAP *pMap;
+ TOKENREC *pTKRec;
+ ULONG i;
+ mdToken tkFinalTo;
+ ModuleRec *pMod;
+ ModuleRec *pModNew;
+ LPCUTF8 szName;
+
+ // update each import map from merge to have the m_tkTo points to the final mapped to token
+ for (pMap = pMDTokenMapList; pMap; pMap = pMap->m_pNextMap)
+ {
+ // update each record
+ for (i = 0; i < (ULONG) (pMap->Count()); i++)
+ {
+ TOKENREC *pRecTo;
+ pTKRec = pMap->Get(i);
+ if ( pMergeMap->Find( pTKRec->m_tkTo, &pRecTo ) )
+ {
+ // This record is kept by the filter and the tkTo is changed
+ pRecTo->m_isFoundInImport = true;
+ tkFinalTo = pRecTo->m_tkTo;
+ pTKRec->m_tkTo = tkFinalTo;
+ pTKRec->m_isDeleted = false;
+
+ // send the notification now. Because after merge, we may have everything in order and
+ // won't send another set of notification.
+ //
+ LOG((LOGMD, "TokenRemap in RegMeta::ProcessFilter (IMapToken 0x%08x): from 0x%08x to 0x%08x\n", pMap->m_pMap, pTKRec->m_tkFrom, pTKRec->m_tkTo));
+
+ pMap->m_pMap->Map(pTKRec->m_tkFrom, pTKRec->m_tkTo);
+ }
+ else
+ {
+ // This record is pruned by the filter upon save
+ pTKRec->m_isDeleted = true;
+ }
+ }
+ }
+
+ // now walk the pMergeMap and check to see if there is any entry that is not set to true for m_isFoundInImport.
+ // These are the records that from calling DefineXXX methods directly on the Emitting scope!
+ if (m_pHandler)
+ m_pHandler->QueryInterface(IID_IMapToken, (void **)&pHostMapToken);
+ if (pHostMapToken)
+ {
+ for (i = 0; i < (ULONG) (pMergeMap->m_pTKMap->Count()); i++)
+ {
+ pTKRec = pMergeMap->m_pTKMap->Get(i);
+ if (pTKRec->m_isFoundInImport == false)
+ {
+ LOG((LOGMD, "TokenRemap in RegMeta::ProcessFilter (default IMapToken 0x%08x): from 0x%08x to 0x%08x\n", pHostMapToken, pTKRec->m_tkFrom, pTKRec->m_tkTo));
+
+ // send the notification on the IMapToken from SetHandler of this RegMeta
+ pHostMapToken->Map(pTKRec->m_tkFrom, pTKRec->m_tkTo);
+ }
+ }
+ }
+
+ // Preserve module name across merge.
+ IfFailGo(m_pStgdb->m_MiniMd.GetModuleRecord(1, &pMod));
+ IfFailGo(pMetaNew->m_pStgdb->m_MiniMd.GetModuleRecord(1, &pModNew));
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfModule(pMod, &szName));
+ IfFailGo(pMetaNew->m_pStgdb->m_MiniMd.PutString(TBL_Module, ModuleRec::COL_Name, pModNew, szName));
+
+ // now swap the stgdb but keep the merger...
+ _ASSERTE( !IsOfExternalStgDB(m_OpenFlags) );
+
+ CLiteWeightStgdbRW * pStgdbTmp = m_pStgdb;
+ m_pStgdb = pMetaNew->m_pStgdb;
+ pMetaNew->m_pStgdb = pStgdbTmp;
+ // Update RuntimeVersion string pointers to point to the owning RegMeta string (the strings are 2 copies of the same string content)
+ m_pStgdb->m_MiniMd.m_OptionValue.m_RuntimeVersion = m_OptionValue.m_RuntimeVersion;
+ pMetaNew->m_pStgdb->m_MiniMd.m_OptionValue.m_RuntimeVersion = pMetaNew->m_OptionValue.m_RuntimeVersion;
+ }
+ else
+ {
+ // swap the Stgdb
+ CLiteWeightStgdbRW * pStgdbTmp = m_pStgdb;
+ m_pStgdb = pMetaNew->m_pStgdb;
+ pMetaNew->m_pStgdb = pStgdbTmp;
+ // Update RuntimeVersion string pointers to point to the owning RegMeta string (the strings are 2 copies of the same string content)
+ m_pStgdb->m_MiniMd.m_OptionValue.m_RuntimeVersion = m_OptionValue.m_RuntimeVersion;
+ pMetaNew->m_pStgdb->m_MiniMd.m_OptionValue.m_RuntimeVersion = pMetaNew->m_OptionValue.m_RuntimeVersion;
+
+ // Client either open an existing scope and apply the filter mechanism, or client define the scope and then
+ // apply the filter mechanism.
+
+ // In this case, host better has supplied the handler!!
+ _ASSERTE( m_bRemap && m_pHandler);
+ IfFailGo( m_pHandler->QueryInterface(IID_IMapToken, (void **) &pMapNew) );
+
+
+ {
+ // Send the notification of token movement now because after merge we may not move tokens again
+ // and thus no token notification will be send.
+ MDTOKENMAP *pMap = pMergeMap->m_pTKMap;
+ TOKENREC *pTKRec;
+ ULONG i;
+
+ for (i=0; i < (ULONG) (pMap->Count()); i++)
+ {
+ pTKRec = pMap->Get(i);
+ pMap->m_pMap->Map(pTKRec->m_tkFrom, pTKRec->m_tkTo);
+ }
+
+ }
+
+
+ // What we need to do here is create a IMapToken that will replace the original handler. This new IMapToken
+ // upon called will first map the from token to the most original from token.
+ //
+ pCompositHandler = new (nothrow) MergeTokenManager(pMergeMap->m_pTKMap, NULL);
+ IfNullGo( pCompositHandler );
+
+ // now update the following field to hold on to the real IMapToken supplied by our client by SetHandler
+ if (pMergeMap->m_pTKMap->m_pMap)
+ pMergeMap->m_pTKMap->m_pMap->Release();
+ _ASSERTE(pMapNew);
+ pMergeMap->m_pTKMap->m_pMap = pMapNew;
+
+ // ownership transferred
+ pMergeMap = NULL;
+ pMapNew = NULL;
+
+ // now you want to replace all of the IMapToken set by calling SetHandler to this new MergeTokenManager
+ IfFailGo( m_pStgdb->m_MiniMd.SetHandler(pCompositHandler) );
+
+ m_pHandler = pCompositHandler;
+
+ // ownership transferred
+ pCompositHandler = NULL;
+ }
+
+ // Force a ref to def optimization because the remap information was stored in the thrown away CMiniMdRW
+ m_hasOptimizedRefToDef = false;
+ IfFailGo( RefToDefOptimization() );
+
+ErrExit:
+ if (pHostMapToken)
+ pHostMapToken->Release();
+ if (pMetaNew)
+ pMetaNew->Release();
+ if (pMergeMap)
+ pMergeMap->Release();
+ if (pCompositHandler)
+ pCompositHandler->Release();
+ if (pMapNew)
+ pMapNew->Release();
+
+ return hr;
+} // RegMeta::ProcessFilter
+
+#endif //FEATURE_METADATA_EMIT_ALL
+
+#endif //FEATURE_METADATA_EMIT
diff --git a/src/md/compiler/regmeta_emit.cpp b/src/md/compiler/regmeta_emit.cpp
new file mode 100644
index 0000000000..22d2979343
--- /dev/null
+++ b/src/md/compiler/regmeta_emit.cpp
@@ -0,0 +1,2116 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: RegMeta_IMetaDataImport.cpp
+//
+
+//
+// Some methods of code:RegMeta class which implement public API interfaces:
+// * code:IMetaDataEmit
+// * code:IMetaDataEmit2
+//
+// ======================================================================================
+
+#include "stdafx.h"
+#include "regmeta.h"
+#include "metadata.h"
+#include "corerror.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+#include "filtermanager.h"
+#include "mdperf.h"
+#include "switches.h"
+#include "posterror.h"
+#include "stgio.h"
+#include "sstring.h"
+
+#include <metamodelrw.h>
+
+#define DEFINE_CUSTOM_NODUPCHECK 1
+#define DEFINE_CUSTOM_DUPCHECK 2
+#define SET_CUSTOM 3
+
+#if defined(_DEBUG) && defined(_TRACE_REMAPS)
+#define LOGGING
+#endif
+#include <log.h>
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4102)
+#endif
+
+#ifdef FEATURE_METADATA_EMIT
+
+//*****************************************************************************
+// Set module properties on a scope.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetModuleProps( // S_OK or error.
+ LPCWSTR szName) // [IN] If not NULL, the name to set.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ModuleRec *pModule; // The module record to modify.
+
+ LOG((LOGMD, "RegMeta::SetModuleProps(%S)\n", MDSTR(szName)));
+
+
+ START_MD_PERF()
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetModuleRecord(1, &pModule));
+ if (szName != NULL)
+ {
+ LPCWSTR szFile = NULL;
+ size_t cchFile;
+
+ SplitPathInterior(szName, NULL, 0, NULL, 0, &szFile, &cchFile, NULL, 0);
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_Module, ModuleRec::COL_Name, pModule, szFile));
+ }
+
+ IfFailGo(UpdateENCLog(TokenFromRid(1, mdtModule)));
+
+ErrExit:
+
+ STOP_MD_PERF(SetModuleProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::SetModuleProps()
+
+//*****************************************************************************
+// Saves a scope to a file of a given name.
+//*****************************************************************************
+STDMETHODIMP RegMeta::Save( // S_OK or error.
+ LPCWSTR szFile, // [IN] The filename to save to.
+ DWORD dwSaveFlags) // [IN] Flags for the save.
+{
+ HRESULT hr=S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::Save(%S, 0x%08x)\n", MDSTR(szFile), dwSaveFlags));
+ START_MD_PERF()
+ LOCKWRITE();
+
+ // Check reserved param..
+ if (dwSaveFlags != 0)
+ IfFailGo (E_INVALIDARG);
+ IfFailGo(PreSave());
+ IfFailGo(m_pStgdb->Save(szFile, dwSaveFlags));
+
+ // Reset m_bSaveOptimized, this is to handle the incremental and ENC
+ // scenerios where one may do multiple saves.
+ _ASSERTE(m_bSaveOptimized && !m_pStgdb->m_MiniMd.IsPreSaveDone());
+ m_bSaveOptimized = false;
+
+#if defined(_DEBUG)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
+ {
+ int DumpMD_impl(RegMeta *pMD);
+ DumpMD_impl(this);
+ }
+#endif // _DEBUG
+
+ErrExit:
+
+ STOP_MD_PERF(Save);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::Save()
+
+//*****************************************************************************
+// Saves a scope to a stream.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SaveToStream( // S_OK or error.
+ IStream *pIStream, // [IN] A writable stream to save to.
+ DWORD dwSaveFlags) // [IN] Flags for the save.
+{
+ HRESULT hr=S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOCKWRITE();
+
+ LOG((LOGMD, "RegMeta::SaveToStream(0x%08x, 0x%08x)\n", pIStream, dwSaveFlags));
+ START_MD_PERF()
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ hr = _SaveToStream(pIStream, dwSaveFlags);
+
+ STOP_MD_PERF(SaveToStream);
+
+#if defined(_DEBUG)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
+ {
+ int DumpMD_impl(RegMeta *pMD);
+ DumpMD_impl(this);
+ }
+#endif // _DEBUG
+
+ErrExit:
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::SaveToStream()
+
+//*****************************************************************************
+// Saves a scope to a stream.
+//*****************************************************************************
+HRESULT RegMeta::_SaveToStream( // S_OK or error.
+ IStream *pIStream, // [IN] A writable stream to save to.
+ DWORD dwSaveFlags) // [IN] Flags for the save.
+{
+ HRESULT hr=S_OK;
+
+ IfFailGo(PreSave());
+ IfFailGo( m_pStgdb->SaveToStream(pIStream, m_ReorderingOptions, m_pCorProfileData) );
+
+ // Reset m_bSaveOptimized, this is to handle the incremental and ENC
+ // scenerios where one may do multiple saves.
+ _ASSERTE(m_bSaveOptimized && !m_pStgdb->m_MiniMd.IsPreSaveDone());
+ m_bSaveOptimized = false;
+
+ErrExit:
+
+ return hr;
+} // STDMETHODIMP RegMeta::_SaveToStream()
+
+//*****************************************************************************
+// Saves a copy of the scope into the memory buffer provided. The buffer size
+// must be at least as large as the GetSaveSize value.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SaveToMemory( // S_OK or error.
+ void *pbData, // [OUT] Location to write data.
+ ULONG cbData) // [IN] Max size of data buffer.
+{
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ IStream *pStream = 0; // Working pointer for save.
+
+ LOG((LOGMD, "MD RegMeta::SaveToMemory(0x%08x, 0x%08x)\n",
+ pbData, cbData));
+ START_MD_PERF();
+
+#ifdef _DEBUG
+ ULONG cbActual; // Size of the real data.
+ IfFailGo(GetSaveSize(cssAccurate, &cbActual));
+ _ASSERTE(cbData >= cbActual);
+#endif
+
+ { // cannot lock before the debug statement. Because GetSaveSize is also a public API which will take the Write lock.
+ LOCKWRITE();
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+ // Create a stream interface on top of the user's data buffer, then simply
+ // call the save to stream method.
+ IfFailGo(CInMemoryStream::CreateStreamOnMemory(pbData, cbData, &pStream));
+ IfFailGo(_SaveToStream(pStream, 0));
+ }
+ErrExit:
+ if (pStream)
+ pStream->Release();
+ STOP_MD_PERF(SaveToMemory);
+ END_ENTRYPOINT_NOTHROW;
+
+ return (hr);
+} // STDMETHODIMP RegMeta::SaveToMemory()
+
+//*****************************************************************************
+// As the Stgdb object to get the save size for the scope.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetSaveSize( // S_OK or error.
+ CorSaveSize fSave, // [IN] cssAccurate or cssQuick.
+ DWORD *pdwSaveSize) // [OUT] Put the size here.
+{
+ HRESULT hr=S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ FilterTable *ft = NULL;
+
+ LOG((LOGMD, "RegMeta::GetSaveSize(0x%08x, 0x%08x)\n", fSave, pdwSaveSize));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ ft = m_pStgdb->m_MiniMd.GetFilterTable();
+ IfNullGo(ft);
+
+ if (m_pStgdb->m_MiniMd.m_UserStringHeap.GetUnalignedSize() == 0)
+ {
+ if (!IsENCDelta(m_pStgdb->m_MiniMd.m_OptionValue.m_UpdateMode) &&
+ !m_pStgdb->m_MiniMd.IsMinimalDelta())
+ {
+ BYTE rgData[] = {' ', 0, 0};
+ UINT32 nIndex;
+ IfFailGo(m_pStgdb->m_MiniMd.PutUserString(
+ MetaData::DataBlob(rgData, sizeof(rgData)),
+ &nIndex));
+ // Make sure this user string is marked
+ if (ft->Count() != 0)
+ {
+ IfFailGo( m_pFilterManager->MarkNewUserString(TokenFromRid(nIndex, mdtString)));
+ }
+ }
+ }
+
+
+ if (ft->Count() != 0)
+ {
+ int iCount;
+
+ // There is filter table. Linker is using /opt:ref.
+ // Make sure that we are marking the AssemblyDef token!
+ iCount = m_pStgdb->m_MiniMd.getCountAssemblys();
+ _ASSERTE(iCount <= 1);
+
+ if (iCount)
+ {
+ IfFailGo(m_pFilterManager->Mark(TokenFromRid(iCount, mdtAssembly)));
+ }
+ }
+#ifdef FEATURE_METADATA_EMIT_ALL
+ else if (m_newMerger.m_pImportDataList)
+ {
+ // always pipe through another pass of merge to drop unnecessary ref for linker.
+ MarkAll();
+ }
+#endif //FEATURE_METADATA_EMIT_ALL
+
+ IfFailGo(PreSave());
+
+ hr = m_pStgdb->GetSaveSize(fSave, (UINT32 *)pdwSaveSize, m_ReorderingOptions, m_pCorProfileData);
+
+ErrExit:
+ STOP_MD_PERF(GetSaveSize);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::GetSaveSize
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+
+//*****************************************************************************
+// Unmark everything in this module
+//
+// Implements public API code:IMetaDataFilter::UnmarkAll.
+//*****************************************************************************
+HRESULT RegMeta::UnmarkAll()
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+ int i;
+ int iCount;
+ TypeDefRec *pRec;
+ ULONG ulEncloser;
+ NestedClassRec *pNestedClass;
+ CustomAttributeRec *pCARec;
+ mdToken tkParent;
+ int iStart, iEnd;
+
+ LOG((LOGMD, "RegMeta::UnmarkAll\n"));
+
+ START_MD_PERF();
+ LOCKWRITE();
+
+#if 0
+ // We cannot enable this check. Because our tests are depending on this.. Sigh..
+ if (m_pFilterManager != NULL)
+ {
+ // UnmarkAll has been called before
+ IfFailGo( META_E_HAS_UNMARKALL );
+ }
+#endif // 0
+
+ // calculate the TypeRef and TypeDef mapping here
+ //
+ IfFailGo( RefToDefOptimization() );
+
+ // unmark everything in the MiniMd.
+ IfFailGo( m_pStgdb->m_MiniMd.UnmarkAll() );
+
+ // instantiate the filter manager
+ m_pFilterManager = new (nothrow) FilterManager( &(m_pStgdb->m_MiniMd) );
+ IfNullGo( m_pFilterManager );
+
+ // Mark all public typedefs.
+ iCount = m_pStgdb->m_MiniMd.getCountTypeDefs();
+
+ // Mark all of the public TypeDef. We need to skip over the <Module> typedef
+ for (i = 2; i <= iCount; i++)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(i, &pRec));
+ if (m_OptionValue.m_LinkerOption == MDNetModule)
+ {
+ // Client is asking us to keep private type as well.
+ IfFailGo( m_pFilterManager->Mark(TokenFromRid(i, mdtTypeDef)) );
+ }
+ else if (i != 1)
+ {
+ // when client is not set to MDNetModule, global functions/fields won't be keep by default
+ //
+ if (IsTdPublic(pRec->GetFlags()))
+ {
+ IfFailGo( m_pFilterManager->Mark(TokenFromRid(i, mdtTypeDef)) );
+ }
+ else if ( IsTdNestedPublic(pRec->GetFlags()) ||
+ IsTdNestedFamily(pRec->GetFlags()) ||
+ IsTdNestedFamORAssem(pRec->GetFlags()) )
+ {
+ // This nested class would potentially be visible outside, either
+ // directly or through inheritence. If the enclosing class is
+ // marked, this nested class must be marked.
+ //
+ IfFailGo(m_pStgdb->m_MiniMd.FindNestedClassHelper(TokenFromRid(i, mdtTypeDef), &ulEncloser));
+ _ASSERTE( !InvalidRid(ulEncloser) &&
+ "Bad metadata for nested type!" );
+ IfFailGo(m_pStgdb->m_MiniMd.GetNestedClassRecord(ulEncloser, &pNestedClass));
+ tkParent = m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pNestedClass);
+ if ( m_pStgdb->m_MiniMd.GetFilterTable()->IsTypeDefMarked(tkParent))
+ IfFailGo( m_pFilterManager->Mark(TokenFromRid(i, mdtTypeDef)) );
+ }
+ }
+ }
+
+ if (m_OptionValue.m_LinkerOption == MDNetModule)
+ {
+ // Mark global function if NetModule. We will not keep _Delete method.
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(1, &pRec));
+ iStart = m_pStgdb->m_MiniMd.getMethodListOfTypeDef(pRec);
+ IfFailGo(m_pStgdb->m_MiniMd.getEndMethodListOfTypeDef(1, (RID *)&iEnd));
+ for ( i = iStart; i < iEnd; i ++ )
+ {
+ RID rid;
+ MethodRec *pMethodRec;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRid(i, &rid));
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(rid, &pMethodRec));
+
+ // check the name
+ if (IsMdRTSpecialName(pMethodRec->GetFlags()))
+ {
+ LPCUTF8 szName;
+ IfFailGo(m_pStgdb->m_MiniMd.getNameOfMethod(pMethodRec, &szName));
+
+ // Only mark method if not a _Deleted method
+ if (strcmp(szName, COR_DELETED_NAME_A) != 0)
+ IfFailGo( m_pFilterManager->Mark( TokenFromRid( rid, mdtMethodDef) ) );
+ }
+ else
+ {
+ //
+ if (!IsMiForwardRef(pMethodRec->GetImplFlags()) ||
+ IsMiRuntime(pMethodRec->GetImplFlags()) ||
+ IsMdPinvokeImpl(pMethodRec->GetFlags()) )
+
+ IfFailGo( m_pFilterManager->Mark( TokenFromRid( rid, mdtMethodDef) ) );
+ }
+ }
+ }
+
+ // mark the module property
+ IfFailGo( m_pFilterManager->Mark(TokenFromRid(1, mdtModule)) );
+
+ // We will also keep all of the TypeRef that has any CustomAttribute hang off it.
+ iCount = m_pStgdb->m_MiniMd.getCountCustomAttributes();
+
+ // Mark all of the TypeRef used by CA's
+ for (i = 1; i <= iCount; i++)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(i, &pCARec));
+ tkParent = m_pStgdb->m_MiniMd.getParentOfCustomAttribute(pCARec);
+ if (TypeFromToken(tkParent) == mdtTypeRef)
+ {
+ m_pFilterManager->Mark(tkParent);
+ }
+ }
+ErrExit:
+
+ STOP_MD_PERF(UnmarkAll);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::UnmarkAll
+
+#endif //FEATURE_METADATA_EMIT_ALL
+
+//*****************************************************************************
+// Mark everything in this module
+//*****************************************************************************
+HRESULT RegMeta::MarkAll()
+{
+ HRESULT hr = NOERROR;
+
+ // mark everything in the MiniMd.
+ IfFailGo( m_pStgdb->m_MiniMd.MarkAll() );
+
+ // instantiate the filter manager if not instantiated
+ if (m_pFilterManager == NULL)
+ {
+ m_pFilterManager = new (nothrow) FilterManager( &(m_pStgdb->m_MiniMd) );
+ IfNullGo( m_pFilterManager );
+ }
+ErrExit:
+
+ return hr;
+} // HRESULT RegMeta::MarkAll
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+
+//*****************************************************************************
+// Mark the transitive closure of a token
+//@todo GENERICS: What about GenericParam, MethodSpec?
+//
+// Implements public API code:IMetaDataFilter::MarkToken.
+//*****************************************************************************
+STDMETHODIMP RegMeta::MarkToken( // Return code.
+ mdToken tk) // [IN] token to be Marked
+{
+ HRESULT hr = NOERROR;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // LOG((LOGMD, "RegMeta::MarkToken(0x%08x)\n", tk));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ if (m_pStgdb->m_MiniMd.GetFilterTable() == NULL || m_pFilterManager == NULL)
+ {
+ // UnmarkAll has not been called. Everything is considered marked.
+ // No need to do anything extra!
+ IfFailGo( META_E_MUST_CALL_UNMARKALL );
+ }
+
+ switch ( TypeFromToken(tk) )
+ {
+ case mdtTypeDef:
+ case mdtMethodDef:
+ case mdtFieldDef:
+ case mdtMemberRef:
+ case mdtTypeRef:
+ case mdtTypeSpec:
+ case mdtMethodSpec:
+ case mdtSignature:
+ case mdtString:
+#if _DEBUG
+ if (TypeFromToken(tk) == mdtTypeDef)
+ {
+ TypeDefRec *pType;
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tk), &pType));
+ LPCSTR szTypeDefName;
+ if (m_pStgdb->m_MiniMd.getNameOfTypeDef(pType, &szTypeDefName) == S_OK)
+ {
+ LOG((LOGMD, "MarkToken: Host is marking typetoken 0x%08x with name <%s>\n", tk, szTypeDefName));
+ }
+ }
+ else
+ if (TypeFromToken(tk) == mdtMethodDef)
+ {
+ MethodRec *pMeth;
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMeth));
+ LPCSTR szMethodName;
+ if (m_pStgdb->m_MiniMd.getNameOfMethod(pMeth, &szMethodName) == S_OK)
+ {
+ LOG((LOGMD, "MarkToken: Host is marking methodtoken 0x%08x with name <%s>\n", tk, szMethodName));
+ }
+ }
+ else
+ if (TypeFromToken(tk) == mdtFieldDef)
+ {
+ FieldRec *pField;
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pField));
+ LPCSTR szFieldName;
+ if (m_pStgdb->m_MiniMd.getNameOfField(pField, &szFieldName) == S_OK)
+ {
+ LOG((LOGMD, "MarkToken: Host is marking field token 0x%08x with name <%s>\n", tk, szFieldName));
+ }
+ }
+ else
+ {
+ LOG((LOGMD, "MarkToken: Host is marking token 0x%08x\n", tk));
+ }
+#endif // _DEBUG
+ if (!IsValidToken(tk))
+ IfFailGo( E_INVALIDARG );
+
+ IfFailGo( m_pFilterManager->Mark(tk) );
+ break;
+
+ case mdtBaseType:
+ // no need to mark base type
+ goto ErrExit;
+
+ default:
+ _ASSERTE(!"Bad token type!");
+ hr = E_INVALIDARG;
+ break;
+ }
+ErrExit:
+
+ STOP_MD_PERF(MarkToken);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::MarkToken
+
+//*****************************************************************************
+// Unmark everything in this module
+//@todo GENERICS: What about GenericParam, MethodSpec?
+//
+// Implements public API code:IMetaDataFilter::IsTokenMarked.
+//*****************************************************************************
+HRESULT RegMeta::IsTokenMarked(
+ mdToken tk, // [IN] Token to check if marked or not
+ BOOL *pIsMarked) // [OUT] true if token is marked
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ FilterTable *pFilter = NULL;
+
+ LOG((LOGMD, "RegMeta::IsTokenMarked(0x%08x)\n", tk));
+ START_MD_PERF();
+ LOCKREAD();
+
+ pFilter = m_pStgdb->m_MiniMd.GetFilterTable();
+ IfNullGo( pFilter );
+
+ if (!IsValidToken(tk))
+ IfFailGo( E_INVALIDARG );
+
+ switch ( TypeFromToken(tk) )
+ {
+ case mdtTypeRef:
+ *pIsMarked = pFilter->IsTypeRefMarked(tk);
+ break;
+ case mdtTypeDef:
+ *pIsMarked = pFilter->IsTypeDefMarked(tk);
+ break;
+ case mdtFieldDef:
+ *pIsMarked = pFilter->IsFieldMarked(tk);
+ break;
+ case mdtMethodDef:
+ *pIsMarked = pFilter->IsMethodMarked(tk);
+ break;
+ case mdtParamDef:
+ *pIsMarked = pFilter->IsParamMarked(tk);
+ break;
+ case mdtMemberRef:
+ *pIsMarked = pFilter->IsMemberRefMarked(tk);
+ break;
+ case mdtCustomAttribute:
+ *pIsMarked = pFilter->IsCustomAttributeMarked(tk);
+ break;
+ case mdtPermission:
+ *pIsMarked = pFilter->IsDeclSecurityMarked(tk);
+ break;
+ case mdtSignature:
+ *pIsMarked = pFilter->IsSignatureMarked(tk);
+ break;
+ case mdtEvent:
+ *pIsMarked = pFilter->IsEventMarked(tk);
+ break;
+ case mdtProperty:
+ *pIsMarked = pFilter->IsPropertyMarked(tk);
+ break;
+ case mdtModuleRef:
+ *pIsMarked = pFilter->IsModuleRefMarked(tk);
+ break;
+ case mdtTypeSpec:
+ *pIsMarked = pFilter->IsTypeSpecMarked(tk);
+ break;
+ case mdtInterfaceImpl:
+ *pIsMarked = pFilter->IsInterfaceImplMarked(tk);
+ break;
+ case mdtString:
+ default:
+ _ASSERTE(!"Bad token type!");
+ hr = E_INVALIDARG;
+ break;
+ }
+ErrExit:
+
+ STOP_MD_PERF(IsTokenMarked);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::IsTokenMarked
+
+#endif //FEATURE_METADATA_EMIT_ALL
+
+//*****************************************************************************
+// Create and populate a new TypeDef record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::DefineTypeDef( // S_OK or error.
+ LPCWSTR szTypeDef, // [IN] Name of TypeDef
+ DWORD dwTypeDefFlags, // [IN] CustomAttribute flags
+ mdToken tkExtends, // [IN] extends this TypeDef or typeref
+ mdToken rtkImplements[], // [IN] Implements interfaces
+ mdTypeDef *ptd) // [OUT] Put TypeDef token here
+{
+ HRESULT hr = S_OK; // A result.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::DefineTypeDef(%S, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ MDSTR(szTypeDef), dwTypeDefFlags, tkExtends,
+ rtkImplements, ptd));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ _ASSERTE(!IsTdNested(dwTypeDefFlags));
+
+ IfFailGo(_DefineTypeDef(szTypeDef, dwTypeDefFlags,
+ tkExtends, rtkImplements, mdTokenNil, ptd));
+ErrExit:
+ STOP_MD_PERF(DefineTypeDef);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::DefineTypeDef()
+
+
+//*****************************************************************************
+// Implements public API code:IMetaDataFilter::SetHandler.
+//*****************************************************************************
+STDMETHODIMP RegMeta::SetHandler( // S_OK.
+ IUnknown *pUnk) // [IN] The new error handler.
+{
+ HRESULT hr = S_OK; // A result.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ IMapToken *pIMap = NULL;
+
+ LOG((LOGMD, "RegMeta::SetHandler(0x%08x)\n", pUnk));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ m_pHandler = pUnk;
+
+ // Ignore the error return by SetHandler
+ IfFailGo(m_pStgdb->m_MiniMd.SetHandler(pUnk));
+
+ // Figure out up front if remap is supported.
+ if (pUnk)
+ pUnk->QueryInterface(IID_IMapToken, (PVOID *) &pIMap);
+ m_bRemap = (pIMap != 0);
+ if (pIMap)
+ pIMap->Release();
+
+ErrExit:
+
+ STOP_MD_PERF(SetHandler);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::SetHandler()
+
+//*******************************************************************************
+// Internal helper functions.
+//*******************************************************************************
+
+//*******************************************************************************
+// Perform optimizations of the metadata prior to saving.
+//*******************************************************************************
+HRESULT RegMeta::PreSave() // Return code.
+{
+ HRESULT hr = S_OK; // A result.
+ CMiniMdRW *pMiniMd; // The MiniMd with the data.
+ unsigned bRemapOld = m_bRemap;
+ MergeTokenManager *ptkMgr = NULL;
+
+ // For convenience.
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ // If the code has already been optimized there is nothing to do.
+ if (m_bSaveOptimized)
+ goto ErrExit;
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+ if (m_newMerger.m_pImportDataList != NULL)
+ {
+ // This is the linker scenario. We we have IMap for each scope. We will create an instance of our own mapper
+ // who knows how to send notification back to host!
+
+ // cache the host provided handler to the end our MergeTokenManager
+
+ ptkMgr = new (nothrow) MergeTokenManager(m_newMerger.m_pImportDataList->m_pMDTokenMap, m_pHandler);
+ IfNullGo(ptkMgr);
+ hr = m_pStgdb->m_MiniMd.SetHandler(ptkMgr);
+ _ASSERTE(SUCCEEDED(hr));
+ }
+#endif //FEATURE_METADATA_EMIT_ALL
+
+ IfFailGo(RefToDefOptimization());
+
+ // we need to update MethodImpl table here with ref to def result
+ if (pMiniMd->GetMemberRefToMemberDefMap() != NULL)
+ {
+ MethodImplRec *pMethodImplRec;
+ mdToken tkMethodBody;
+ mdToken tkMethodDecl;
+ mdToken newTK;
+ ULONG cMethodImplRecs; // Count of MemberRefs.
+ ULONG iMI;
+
+ cMethodImplRecs = pMiniMd->getCountMethodImpls();
+ // Enum through all member ref's looking for ref's to internal things.
+ for (iMI = 1; iMI <= cMethodImplRecs; iMI++)
+ { // Get a MethodImpl.
+ IfFailGo(pMiniMd->GetMethodImplRecord(iMI, &pMethodImplRec));
+ tkMethodBody = pMiniMd->getMethodBodyOfMethodImpl(pMethodImplRec);
+ if (TypeFromToken(tkMethodBody) == mdtMemberRef)
+ {
+ // did it get remapped to a def
+ newTK = *(pMiniMd->GetMemberRefToMemberDefMap()->Get(RidFromToken(tkMethodBody)));
+ if (!IsNilToken(newTK))
+ {
+ // yes... fix up the value...
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl,
+ MethodImplRec::COL_MethodBody,
+ pMethodImplRec,
+ newTK));
+ }
+ }
+ // do the same thing for MethodDecl
+ tkMethodDecl = pMiniMd->getMethodDeclarationOfMethodImpl(pMethodImplRec);
+ if (TypeFromToken(tkMethodDecl) == mdtMemberRef)
+ {
+ // did it get remapped to a def
+ newTK = *(pMiniMd->GetMemberRefToMemberDefMap()->Get(RidFromToken(tkMethodDecl)));
+ if (!IsNilToken(newTK))
+ {
+ // yes... fix up the value...
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl,
+ MethodImplRec::COL_MethodDeclaration,
+ pMethodImplRec,
+ newTK));
+ }
+ }
+ }
+ }
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+ IfFailGo(ProcessFilter());
+
+ if (m_newMerger.m_pImportDataList != NULL)
+ {
+ // Allocate a token mapper object that will be used for phase 1 if there is not Handler but
+ // linker has provided the IMapToken
+ //
+ m_bRemap = true;
+ }
+#endif //FEATURE_METADATA_EMIT_ALL
+
+ // reget the minimd because it can be swapped in the call of ProcessFilter
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // Don't repeat this process again.
+ m_bSaveOptimized = true;
+
+ // call get save size to trigger the PreSaveXXX on MetaModelRW class.
+ IfFailGo(m_pStgdb->m_MiniMd.PreSave(m_ReorderingOptions, m_pCorProfileData));
+
+ErrExit:
+ if (ptkMgr != NULL)
+ {
+ // recovery the initial state
+ hr = m_pStgdb->m_MiniMd.SetHandler(NULL);
+ ptkMgr->Release();
+ }
+
+ m_bRemap = bRemapOld;
+
+ return hr;
+} // RegMeta::PreSave
+
+//*******************************************************************************
+// Perform optimizations of ref to def
+//*******************************************************************************
+HRESULT RegMeta::RefToDefOptimization()
+{
+ mdToken mfdef; // Method or Field Def.
+ LPCSTR szName; // MemberRef or TypeRef name.
+ const COR_SIGNATURE *pvSig; // Signature of the MemberRef.
+ ULONG cbSig; // Size of the signature blob.
+ HRESULT hr = S_OK; // A result.
+ ULONG iMR; // For iterating MemberRefs.
+ CMiniMdRW *pMiniMd; // The MiniMd with the data.
+ ULONG cMemberRefRecs; // Count of MemberRefs.
+ MemberRefRec *pMemberRefRec; // A MemberRefRec.
+
+
+
+ START_MD_PERF();
+
+ // the Ref to Def map is still up-to-date
+ if (IsMemberDefDirty() == false && IsTypeDefDirty() == false && m_hasOptimizedRefToDef == true)
+ goto ErrExit;
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // The basic algorithm here is:
+ //
+ // calculate all of the TypeRef to TypeDef map and store it at TypeRefToTypeDefMap
+ // for each MemberRef mr
+ // {
+ // get the parent of mr
+ // if (parent of mr is a TypeRef and has been mapped to a TypeDef)
+ // {
+ // Remap MemberRef to MemberDef
+ // }
+ // }
+ //
+ // There are several places where errors are eaten, since this whole thing is
+ // an optimization step and not doing it would still be valid.
+ //
+
+ // Ensure the size
+ // initialize the token remap manager. This class will track all of the Refs to Defs map and also
+ // token movements due to removing pointer tables or sorting.
+ //
+ if ( pMiniMd->GetTokenRemapManager() == NULL)
+ {
+
+ IfFailGo( pMiniMd->InitTokenRemapManager() );
+ }
+ else
+ {
+ IfFailGo( pMiniMd->GetTokenRemapManager()->ClearAndEnsureCapacity(pMiniMd->getCountTypeRefs(), pMiniMd->getCountMemberRefs()));
+ }
+
+ // If this is the first time or more TypeDef has been introduced, recalculate the TypeRef to TypeDef map
+ if (IsTypeDefDirty() || m_hasOptimizedRefToDef == false)
+ {
+ IfFailGo( pMiniMd->CalculateTypeRefToTypeDefMap() );
+ }
+
+ // If this is the first time or more memberdefs has been introduced, recalculate the TypeRef to TypeDef map
+ if (IsMemberDefDirty() || m_hasOptimizedRefToDef == false)
+ {
+ mdToken tkParent;
+ cMemberRefRecs = pMiniMd->getCountMemberRefs();
+
+ // Enum through all member ref's looking for ref's to internal things.
+ for (iMR = 1; iMR<=cMemberRefRecs; iMR++)
+ { // Get a MemberRef.
+ IfFailGo(pMiniMd->GetMemberRefRecord(iMR, &pMemberRefRec));
+
+ // If not member of the TypeRef, skip it.
+ tkParent = pMiniMd->getClassOfMemberRef(pMemberRefRec);
+
+ if ( TypeFromToken(tkParent) == mdtMethodDef )
+ {
+ // always track the map even though it is already in the original scope
+ *(pMiniMd->GetMemberRefToMemberDefMap()->Get(iMR)) = tkParent;
+ continue;
+ }
+
+ if ( TypeFromToken(tkParent) != mdtTypeRef && TypeFromToken(tkParent) != mdtTypeDef )
+ {
+ // this has been either optimized to mdtMethodDef, mdtFieldDef or referring to
+ // ModuleRef
+ continue;
+ }
+
+ // In the case of global function, we have tkParent as m_tdModule.
+ // We will always do the optmization.
+ if (TypeFromToken(tkParent) == mdtTypeRef)
+ {
+ // If we're preserving local typerefs, skip this token
+ if (PreserveLocalRefs(MDPreserveLocalTypeRef))
+ {
+ continue;
+ }
+
+ // The parent is a TypeRef. We need to check to see if this TypeRef is optimized to a TypeDef
+ tkParent = *(pMiniMd->GetTypeRefToTypeDefMap()->Get(RidFromToken(tkParent)) );
+ // tkParent = pMapTypeRefToTypeDef[RidFromToken(tkParent)];
+ if ( RidFromToken(tkParent) == 0)
+ {
+ continue;
+ }
+ }
+
+ // If we're preserving local memberrefs, skip this token
+ if (PreserveLocalRefs(MDPreserveLocalMemberRef))
+ {
+ continue;
+ }
+
+ // Get the name and signature of this mr.
+ IfFailGo(pMiniMd->getNameOfMemberRef(pMemberRefRec, &szName));
+ IfFailGo(pMiniMd->getSignatureOfMemberRef(pMemberRefRec, &pvSig, &cbSig));
+
+ // Look for a member with the same def. Might not be found if it is
+ // inherited from a base class.
+ //<TODO>@future: this should support inheritence checking.
+ // Look for a member with the same name and signature.</TODO>
+ hr = ImportHelper::FindMember(pMiniMd, tkParent, szName, pvSig, cbSig, &mfdef);
+ if (hr != S_OK)
+ {
+ #if _TRACE_REMAPS
+ // Log the failure.
+ LOG((LF_METADATA, LL_INFO10, "Member %S//%S.%S not found\n", szNamespace, szTDName, rcMRName));
+ #endif
+ continue;
+ }
+
+ // We will only record this if mfdef is a methoddef. We don't support
+ // parent of MemberRef as fielddef. As if we can optimize MemberRef to FieldDef,
+ // we can remove this row.
+ //
+ if ( (TypeFromToken(mfdef) == mdtMethodDef) &&
+ (m_bRemap || tkParent == m_tdModule ) )
+ {
+ // Always change the parent if it is the global function.
+ // Or change the parent if we have a remap that we can send notification.
+ //
+ IfFailGo(pMiniMd->PutToken(TBL_MemberRef, MemberRefRec::COL_Class, pMemberRefRec, mfdef));
+ }
+
+ // We will always track the changes. In MiniMd::PreSaveFull, we will use this map to send
+ // notification to our host if there is any IMapToken provided.
+ //
+ *(pMiniMd->GetMemberRefToMemberDefMap()->Get(iMR)) = mfdef;
+
+ } // EnumMemberRefs
+ }
+
+ // Reset return code from likely search failures.
+ hr = S_OK;
+
+ SetMemberDefDirty(false);
+ SetTypeDefDirty(false);
+ m_hasOptimizedRefToDef = true;
+ErrExit:
+ STOP_MD_PERF(RefToDefOptimization);
+
+ return hr;
+} // RegMeta::RefToDefOptimization
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+
+//*****************************************************************************
+// Process filter
+//*****************************************************************************
+HRESULT RegMeta::ProcessFilter()
+{
+ HRESULT hr = S_OK;
+
+ CMiniMdRW *pMiniMd; // The MiniMd with the data.
+
+ START_MD_PERF();
+
+ // For convenience.
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+ IfNullGo( pMiniMd->GetFilterTable() );
+ if ( pMiniMd->GetFilterTable()->Count() == 0 )
+ {
+ // there is no filter
+ goto ErrExit;
+ }
+ hr = ProcessFilterWorker();
+
+ErrExit:
+ STOP_MD_PERF(ProcessFilter);
+
+ return hr;
+} // RegMeta::ProcessFilter
+
+#endif //FEATURE_METADATA_EMIT_ALL
+
+//*****************************************************************************
+// Define a TypeRef given the fully qualified name.
+//*****************************************************************************
+HRESULT RegMeta::_DefineTypeRef(
+ mdToken tkResolutionScope, // [IN] ModuleRef or AssemblyRef.
+ const void *szName, // [IN] Name of the TypeRef.
+ BOOL isUnicode, // [IN] Specifies whether the URL is unicode.
+ mdTypeRef *ptk, // [OUT] Put mdTypeRef here.
+ eCheckDups eCheck) // [IN] Specifies whether to check for duplicates.
+{
+ HRESULT hr = S_OK;
+ LPUTF8 szUTF8FullQualName;
+ CQuickBytes qbNamespace;
+ CQuickBytes qbName;
+ int bSuccess;
+ ULONG ulStringLen;
+
+
+
+
+ _ASSERTE(ptk && szName);
+ _ASSERTE (TypeFromToken(tkResolutionScope) == mdtModule ||
+ TypeFromToken(tkResolutionScope) == mdtModuleRef ||
+ TypeFromToken(tkResolutionScope) == mdtAssemblyRef ||
+ TypeFromToken(tkResolutionScope) == mdtTypeRef ||
+ tkResolutionScope == mdTokenNil);
+
+ if (isUnicode)
+ {
+ UTF8STR((LPCWSTR)szName, szUTF8FullQualName);
+ }
+ else
+ {
+ szUTF8FullQualName = (LPUTF8)szName;
+ }
+ PREFIX_ASSUME(szUTF8FullQualName != NULL);
+
+ ulStringLen = (ULONG)(strlen(szUTF8FullQualName) + 1);
+ IfFailGo(qbNamespace.ReSizeNoThrow(ulStringLen));
+ IfFailGo(qbName.ReSizeNoThrow(ulStringLen));
+ bSuccess = ns::SplitPath(szUTF8FullQualName,
+ (LPUTF8)qbNamespace.Ptr(),
+ ulStringLen,
+ (LPUTF8)qbName.Ptr(),
+ ulStringLen);
+ _ASSERTE(bSuccess);
+
+ // Search for existing TypeRef record.
+ if (eCheck==eCheckYes || (eCheck==eCheckDefault && CheckDups(MDDupTypeRef)))
+ {
+ hr = ImportHelper::FindTypeRefByName(&(m_pStgdb->m_MiniMd), tkResolutionScope,
+ (LPCUTF8)qbNamespace.Ptr(),
+ (LPCUTF8)qbName.Ptr(), ptk);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ {
+ hr = S_OK;
+ goto NormalExit;
+ }
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto NormalExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ // Create TypeRef record.
+ TypeRefRec *pRecord;
+ RID iRecord;
+
+ IfFailGo(m_pStgdb->m_MiniMd.AddTypeRefRecord(&pRecord, &iRecord));
+
+ // record the more defs are introduced.
+ SetTypeDefDirty(true);
+
+ // Give token back to caller.
+ *ptk = TokenFromRid(iRecord, mdtTypeRef);
+
+ // Set the fields of the TypeRef record.
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeRef, TypeRefRec::COL_Namespace,
+ pRecord, (LPUTF8)qbNamespace.Ptr()));
+
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeRef, TypeRefRec::COL_Name,
+ pRecord, (LPUTF8)qbName.Ptr()));
+
+ if (!IsNilToken(tkResolutionScope))
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_TypeRef, TypeRefRec::COL_ResolutionScope,
+ pRecord, tkResolutionScope));
+ IfFailGo(UpdateENCLog(*ptk));
+
+ // Hash the name.
+ IfFailGo(m_pStgdb->m_MiniMd.AddNamedItemToHash(TBL_TypeRef, *ptk, (LPUTF8)qbName.Ptr(), 0));
+
+ErrExit:
+ ;
+NormalExit:
+
+ return hr;
+} // HRESULT RegMeta::_DefineTypeRef()
+
+//*******************************************************************************
+// Define MethodSemantics
+//*******************************************************************************
+HRESULT RegMeta::_DefineMethodSemantics( // S_OK or error.
+ USHORT usAttr, // [IN] CorMethodSemanticsAttr.
+ mdMethodDef md, // [IN] Method.
+ mdToken tkAssoc, // [IN] Association.
+ BOOL bClear) // [IN] Specifies whether to delete the exisiting entries.
+{
+ HRESULT hr = S_OK;
+ MethodSemanticsRec *pRecord = 0;
+ MethodSemanticsRec *pRecord1; // Use this to recycle a MethodSemantics record.
+ RID iRecord;
+ HENUMInternal hEnum;
+
+
+
+ _ASSERTE(TypeFromToken(md) == mdtMethodDef || IsNilToken(md));
+ _ASSERTE(RidFromToken(tkAssoc));
+ memset(&hEnum, 0, sizeof(HENUMInternal));
+
+ // Clear all matching records by setting association to a Nil token.
+ if (bClear)
+ {
+ RID i;
+
+ IfFailGo( m_pStgdb->m_MiniMd.FindMethodSemanticsHelper(tkAssoc, &hEnum) );
+ while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&i))
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodSemanticsRecord(i, &pRecord1));
+ if (usAttr == pRecord1->GetSemantic())
+ {
+ pRecord = pRecord1;
+ iRecord = i;
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodSemantics,
+ MethodSemanticsRec::COL_Association, pRecord, mdPropertyNil));
+ // In Whidbey, we should create ENC log record here.
+ }
+ }
+ }
+ // If setting (not just clearing) the association, do that now.
+ if (!IsNilToken(md))
+ {
+ // Create a new record required
+ if (pRecord == NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddMethodSemanticsRecord(&pRecord, &iRecord));
+ }
+
+ // Save the data.
+ pRecord->SetSemantic(usAttr);
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodSemantics,
+ MethodSemanticsRec::COL_Method, pRecord, md));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodSemantics,
+ MethodSemanticsRec::COL_Association, pRecord, tkAssoc));
+
+ // regardless if we reuse the record or create the record, add the MethodSemantics to the hash
+ IfFailGo( m_pStgdb->m_MiniMd.AddMethodSemanticsToHash(iRecord) );
+
+ // Create log record for non-token table.
+ IfFailGo(UpdateENCLog2(TBL_MethodSemantics, iRecord));
+ }
+
+ErrExit:
+ HENUMInternal::ClearEnum(&hEnum);
+
+ return hr;
+} // HRESULT RegMeta::_DefineMethodSemantics()
+
+//*******************************************************************************
+// Turn the specified internal flags on.
+//*******************************************************************************
+HRESULT RegMeta::_TurnInternalFlagsOn( // S_OK or error.
+ mdToken tkObj, // [IN] Target object whose internal flags are targetted.
+ DWORD flags) // [IN] Specifies flags to be turned on.
+{
+ HRESULT hr;
+ MethodRec *pMethodRec;
+ FieldRec *pFieldRec;
+ TypeDefRec *pTypeDefRec;
+
+ switch (TypeFromToken(tkObj))
+ {
+ case mdtMethodDef:
+ IfFailRet(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkObj), &pMethodRec));
+ pMethodRec->AddFlags(flags);
+ break;
+ case mdtFieldDef:
+ IfFailRet(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tkObj), &pFieldRec));
+ pFieldRec->AddFlags(flags);
+ break;
+ case mdtTypeDef:
+ IfFailRet(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tkObj), &pTypeDefRec));
+ pTypeDefRec->AddFlags(flags);
+ break;
+ default:
+ _ASSERTE(!"Not supported token type!");
+ return E_INVALIDARG;
+ }
+ return S_OK;
+} // RegMeta::_TurnInternalFlagsOn
+
+//*****************************************************************************
+// Helper: Set the properties on the given TypeDef token.
+//*****************************************************************************
+HRESULT RegMeta::_SetTypeDefProps( // S_OK or error.
+ mdTypeDef td, // [IN] The TypeDef.
+ DWORD dwTypeDefFlags, // [IN] TypeDef flags.
+ mdToken tkExtends, // [IN] Base TypeDef or TypeRef.
+ mdToken rtkImplements[]) // [IN] Implemented interfaces.
+{
+ HRESULT hr = S_OK; // A result.
+ BOOL bClear = IsENCOn() || IsCallerExternal(); // Specifies whether to clear the InterfaceImpl records.
+ TypeDefRec *pRecord; // New TypeDef record.
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+ _ASSERTE(TypeFromToken(tkExtends) == mdtTypeDef || TypeFromToken(tkExtends) == mdtTypeRef || TypeFromToken(tkExtends) == mdtTypeSpec ||
+ IsNilToken(tkExtends) || tkExtends == ULONG_MAX);
+
+ // Get the record.
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pRecord));
+
+ if (dwTypeDefFlags != ULONG_MAX)
+ {
+ // No one should try to set the reserved flags explicitly.
+ _ASSERTE((dwTypeDefFlags & (tdReservedMask&~tdRTSpecialName)) == 0);
+ // Clear the reserved flags from the flags passed in.
+ dwTypeDefFlags &= (~tdReservedMask);
+ // Preserve the reserved flags stored.
+ dwTypeDefFlags |= (pRecord->GetFlags() & tdReservedMask);
+ // Set the flags.
+ pRecord->SetFlags(dwTypeDefFlags);
+ }
+ if (tkExtends != ULONG_MAX)
+ {
+ if (IsNilToken(tkExtends))
+ tkExtends = mdTypeDefNil;
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_TypeDef, TypeDefRec::COL_Extends,
+ pRecord, tkExtends));
+ }
+
+ // Implemented interfaces.
+ if (rtkImplements)
+ IfFailGo(_SetImplements(rtkImplements, td, bClear));
+
+ IfFailGo(UpdateENCLog(td));
+ErrExit:
+ return hr;
+} // HRESULT RegMeta::_SetTypeDefProps()
+
+//******************************************************************************
+// Creates and sets a row in the InterfaceImpl table. Optionally clear
+// pre-existing records for the owning class.
+//******************************************************************************
+HRESULT RegMeta::_SetImplements( // S_OK or error.
+ mdToken rTk[], // Array of TypeRef or TypeDef or TypeSpec tokens for implemented interfaces.
+ mdTypeDef td, // Implementing TypeDef.
+ BOOL bClear) // Specifies whether to clear the existing records.
+{
+ HRESULT hr = S_OK;
+ ULONG i = 0;
+ ULONG j;
+ InterfaceImplRec *pInterfaceImpl;
+ RID iInterfaceImpl;
+ RID ridStart;
+ RID ridEnd;
+ CQuickBytes cqbTk;
+ const mdToken *pTk;
+ bool fIsTableVirtualSortValid;
+
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && rTk);
+ _ASSERTE(!m_bSaveOptimized && "Cannot change records after PreSave() and before Save().");
+
+ // Clear all exising InterfaceImpl records by setting the parent to Nil.
+ if (bClear)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetInterfaceImplsForTypeDef(
+ RidFromToken(td), &ridStart, &ridEnd));
+ for (j = ridStart; j < ridEnd; j++)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetInterfaceImplRecord(
+ m_pStgdb->m_MiniMd.GetInterfaceImplRid(j),
+ &pInterfaceImpl));
+ _ASSERTE (td == m_pStgdb->m_MiniMd.getClassOfInterfaceImpl(pInterfaceImpl));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_InterfaceImpl, InterfaceImplRec::COL_Class,
+ pInterfaceImpl, mdTypeDefNil));
+ }
+ }
+
+ // Eliminate duplicates from the array passed in.
+ if (CheckDups(MDDupInterfaceImpl))
+ {
+ IfFailGo(_InterfaceImplDupProc(rTk, td, &cqbTk));
+ pTk = (mdToken *)cqbTk.Ptr();
+ }
+ else
+ pTk = rTk;
+
+ // Get the state of InterfaceImpl table's VirtualSort
+ fIsTableVirtualSortValid = m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_InterfaceImpl);
+ // Loop for each implemented interface.
+ while (!IsNilToken(pTk[i]))
+ {
+ _ASSERTE(TypeFromToken(pTk[i]) == mdtTypeRef || TypeFromToken(pTk[i]) == mdtTypeDef
+ || TypeFromToken(pTk[i]) == mdtTypeSpec);
+
+ // Create the interface implementation record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddInterfaceImplRecord(&pInterfaceImpl, &iInterfaceImpl));
+
+ // Set data.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_InterfaceImpl, InterfaceImplRec::COL_Class,
+ pInterfaceImpl, td));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_InterfaceImpl, InterfaceImplRec::COL_Interface,
+ pInterfaceImpl, pTk[i]));
+ // Had the table valid VirtualSort?
+ if (fIsTableVirtualSortValid)
+ { // Validate table's VistualSort after adding 1 record and store its
+ // new validation state
+ IfFailGo(m_pStgdb->m_MiniMd.ValidateVirtualSortAfterAddRecord(
+ TBL_InterfaceImpl,
+ &fIsTableVirtualSortValid));
+ }
+
+ i++;
+
+ IfFailGo(UpdateENCLog(TokenFromRid(mdtInterfaceImpl, iInterfaceImpl)));
+ }
+ErrExit:
+
+ return hr;
+} // HRESULT RegMeta::_SetImplements()
+
+//******************************************************************************
+// This routine eliminates duplicates from the given list of InterfaceImpl tokens
+// to be defined. It checks for duplicates against the database only if the
+// TypeDef for which these tokens are being defined is not a new one.
+//******************************************************************************
+HRESULT RegMeta::_InterfaceImplDupProc( // S_OK or error.
+ mdToken rTk[], // Array of TypeRef or TypeDef or TypeSpec tokens for implemented interfaces.
+ mdTypeDef td, // Implementing TypeDef.
+ CQuickBytes *pcqbTk) // Quick Byte object for placing the array of unique tokens.
+{
+ HRESULT hr = S_OK;
+ ULONG i = 0;
+ ULONG iUniqCount = 0;
+ BOOL bDupFound;
+
+ while (!IsNilToken(rTk[i]))
+ {
+ _ASSERTE(TypeFromToken(rTk[i]) == mdtTypeRef || TypeFromToken(rTk[i]) == mdtTypeDef
+ || TypeFromToken(rTk[i]) == mdtTypeSpec);
+ bDupFound = false;
+
+ // Eliminate duplicates from the input list of tokens by looking within the list.
+ for (ULONG j = 0; j < iUniqCount; j++)
+ {
+ if (rTk[i] == ((mdToken *)pcqbTk->Ptr())[j])
+ {
+ bDupFound = true;
+ break;
+ }
+ }
+
+ // If no duplicate is found record it in the list.
+ if (!bDupFound)
+ {
+ IfFailGo(pcqbTk->ReSizeNoThrow((iUniqCount+1) * sizeof(mdToken)));
+ ((mdToken *)pcqbTk->Ptr())[iUniqCount] = rTk[i];
+ iUniqCount++;
+ }
+ i++;
+ }
+
+ // Create a Nil token to signify the end of list.
+ IfFailGo(pcqbTk->ReSizeNoThrow((iUniqCount+1) * sizeof(mdToken)));
+ ((mdToken *)pcqbTk->Ptr())[iUniqCount] = mdTokenNil;
+ErrExit:
+
+ return hr;
+} // HRESULT RegMeta::_InterfaceImplDupProc()
+
+//*******************************************************************************
+// helper to define event
+//*******************************************************************************
+HRESULT RegMeta::_DefineEvent( // Return hresult.
+ mdTypeDef td, // [IN] the class/interface on which the event is being defined
+ LPCWSTR szEvent, // [IN] Name of the event
+ DWORD dwEventFlags, // [IN] CorEventAttr
+ mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef) to the Event class
+ mdEvent *pmdEvent) // [OUT] output event token
+{
+ HRESULT hr = S_OK;
+ EventRec *pEventRec = NULL;
+ RID iEventRec;
+ EventMapRec *pEventMap;
+ RID iEventMap;
+ mdEvent mdEv;
+ LPUTF8 szUTF8Event;
+ UTF8STR(szEvent, szUTF8Event);
+ PREFIX_ASSUME(szUTF8Event != NULL);
+
+
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && td != mdTypeDefNil);
+ _ASSERTE(IsNilToken(tkEventType) || TypeFromToken(tkEventType) == mdtTypeDef ||
+ TypeFromToken(tkEventType) == mdtTypeRef || TypeFromToken(tkEventType) == mdtTypeSpec);
+ _ASSERTE(szEvent && pmdEvent);
+
+ if (CheckDups(MDDupEvent))
+ {
+ hr = ImportHelper::FindEvent(&(m_pStgdb->m_MiniMd), td, szUTF8Event, pmdEvent);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(*pmdEvent), &pEventRec));
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ if (! pEventRec)
+ {
+ // Create a new map if one doesn't exist already, else retrieve the existing one.
+ // The event map must be created before the EventRecord, the new event map will
+ // be pointing past the first event record.
+ IfFailGo(m_pStgdb->m_MiniMd.FindEventMapFor(RidFromToken(td), &iEventMap));
+ if (InvalidRid(iEventMap))
+ {
+ // Create new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddEventMapRecord(&pEventMap, &iEventMap));
+ // Set parent.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_EventMap,
+ EventMapRec::COL_Parent, pEventMap, td));
+ IfFailGo(UpdateENCLog2(TBL_EventMap, iEventMap));
+ }
+ else
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventMapRecord(iEventMap, &pEventMap));
+ }
+
+ // Create a new event record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddEventRecord(&pEventRec, &iEventRec));
+
+ // Set output parameter.
+ *pmdEvent = TokenFromRid(iEventRec, mdtEvent);
+
+ // Add Event to EventMap.
+ IfFailGo(m_pStgdb->m_MiniMd.AddEventToEventMap(RidFromToken(iEventMap), iEventRec));
+
+ IfFailGo(UpdateENCLog2(TBL_EventMap, iEventMap, CMiniMdRW::eDeltaEventCreate));
+ }
+
+ mdEv = *pmdEvent;
+
+ // Set data
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Event, EventRec::COL_Name, pEventRec, szUTF8Event));
+ IfFailGo(_SetEventProps1(*pmdEvent, dwEventFlags, tkEventType));
+
+ // Add the <Event token, typedef token> to the lookup table
+ if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Event))
+ IfFailGo( m_pStgdb->m_MiniMd.AddEventToLookUpTable(*pmdEvent, td) );
+
+ IfFailGo(UpdateENCLog(*pmdEvent));
+
+ErrExit:
+
+ return hr;
+} // HRESULT RegMeta::_DefineEvent()
+
+
+//******************************************************************************
+// Set the specified properties on the Event Token.
+//******************************************************************************
+HRESULT RegMeta::_SetEventProps1( // Return hresult.
+ mdEvent ev, // [IN] Event token.
+ DWORD dwEventFlags, // [IN] Event flags.
+ mdToken tkEventType) // [IN] Event type class.
+{
+ EventRec *pRecord;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(TypeFromToken(ev) == mdtEvent && RidFromToken(ev));
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(ev), &pRecord));
+ if (dwEventFlags != ULONG_MAX)
+ {
+ // Don't let caller set reserved bits
+ dwEventFlags &= ~evReservedMask;
+ // Preserve reserved bits.
+ dwEventFlags |= (pRecord->GetEventFlags() & evReservedMask);
+
+ pRecord->SetEventFlags(static_cast<USHORT>(dwEventFlags));
+ }
+ if (!IsNilToken(tkEventType))
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_Event, EventRec::COL_EventType,
+ pRecord, tkEventType));
+ErrExit:
+ return hr;
+} // HRESULT RegMeta::_SetEventProps1()
+
+//******************************************************************************
+// Set the specified properties on the given Event token.
+//******************************************************************************
+HRESULT RegMeta::_SetEventProps2( // Return hresult.
+ mdEvent ev, // [IN] Event token.
+ mdMethodDef mdAddOn, // [IN] Add method.
+ mdMethodDef mdRemoveOn, // [IN] Remove method.
+ mdMethodDef mdFire, // [IN] Fire method.
+ mdMethodDef rmdOtherMethods[], // [IN] An array of other methods.
+ BOOL bClear) // [IN] Specifies whether to clear the existing MethodSemantics records.
+{
+ EventRec *pRecord;
+ HRESULT hr = S_OK;
+
+
+
+ _ASSERTE(TypeFromToken(ev) == mdtEvent && RidFromToken(ev));
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(ev), &pRecord));
+
+ // Remember the AddOn method.
+ if (!IsNilToken(mdAddOn))
+ {
+ _ASSERTE(TypeFromToken(mdAddOn) == mdtMethodDef);
+ IfFailGo(_DefineMethodSemantics(msAddOn, mdAddOn, ev, bClear));
+ }
+
+ // Remember the RemoveOn method.
+ if (!IsNilToken(mdRemoveOn))
+ {
+ _ASSERTE(TypeFromToken(mdRemoveOn) == mdtMethodDef);
+ IfFailGo(_DefineMethodSemantics(msRemoveOn, mdRemoveOn, ev, bClear));
+ }
+
+ // Remember the fire method.
+ if (!IsNilToken(mdFire))
+ {
+ _ASSERTE(TypeFromToken(mdFire) == mdtMethodDef);
+ IfFailGo(_DefineMethodSemantics(msFire, mdFire, ev, bClear));
+ }
+
+ // Store all of the other methods.
+ if (rmdOtherMethods)
+ {
+ int i = 0;
+ mdMethodDef mb;
+
+ while (1)
+ {
+ mb = rmdOtherMethods[i++];
+ if (IsNilToken(mb))
+ break;
+ _ASSERTE(TypeFromToken(mb) == mdtMethodDef);
+ IfFailGo(_DefineMethodSemantics(msOther, mb, ev, bClear));
+
+ // The first call would've cleared all the existing ones.
+ bClear = false;
+ }
+ }
+ErrExit:
+
+ return hr;
+} // HRESULT RegMeta::_SetEventProps2()
+
+//******************************************************************************
+// Set Permission on the given permission token.
+//******************************************************************************
+HRESULT RegMeta::_SetPermissionSetProps( // Return hresult.
+ mdPermission tkPerm, // [IN] Permission token.
+ DWORD dwAction, // [IN] CorDeclSecurity.
+ void const *pvPermission, // [IN] Permission blob.
+ ULONG cbPermission) // [IN] Count of bytes of pvPermission.
+{
+ DeclSecurityRec *pRecord;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(TypeFromToken(tkPerm) == mdtPermission && cbPermission != ULONG_MAX);
+ _ASSERTE(dwAction && dwAction <= dclMaximumValue);
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(tkPerm), &pRecord));
+
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_DeclSecurity, DeclSecurityRec::COL_PermissionSet,
+ pRecord, pvPermission, cbPermission));
+ErrExit:
+ return hr;
+} // HRESULT RegMeta::_SetPermissionSetProps()
+
+//******************************************************************************
+// Define or set value on a constant record.
+//******************************************************************************
+HRESULT RegMeta::_DefineSetConstant( // Return hresult.
+ mdToken tk, // [IN] Parent token.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchString, // [IN] Size of string in wide chars, or -1 for default.
+ BOOL bSearch) // [IN] Specifies whether to search for an existing record.
+{
+ HRESULT hr = S_OK;
+
+
+
+ if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END &&
+ dwCPlusTypeFlag != ULONG_MAX) &&
+ (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING ||
+ dwCPlusTypeFlag == ELEMENT_TYPE_CLASS))))
+ {
+ ConstantRec *pConstRec = 0;
+ RID iConstRec;
+ ULONG cbBlob;
+ ULONG ulValue = 0;
+
+ if (bSearch)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.FindConstantHelper(tk, &iConstRec));
+ if (!InvalidRid(iConstRec))
+ IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(iConstRec, &pConstRec));
+ }
+ if (! pConstRec)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.AddConstantRecord(&pConstRec, &iConstRec));
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_Constant, ConstantRec::COL_Parent,
+ pConstRec, tk));
+ IfFailGo( m_pStgdb->m_MiniMd.AddConstantToHash(iConstRec) );
+ }
+
+ // Add values to the various columns of the constant value row.
+ pConstRec->SetType(static_cast<BYTE>(dwCPlusTypeFlag));
+ if (!pValue)
+ pValue = &ulValue;
+ cbBlob = _GetSizeOfConstantBlob(dwCPlusTypeFlag, (void *)pValue, cchString);
+ if (cbBlob > 0)
+ {
+#if BIGENDIAN
+ void *pValueTemp;
+ pValueTemp = (void *)alloca(cbBlob);
+ IfFailGo(m_pStgdb->m_MiniMd.SwapConstant(pValue, dwCPlusTypeFlag, pValueTemp, cbBlob));
+ pValue = pValueTemp;
+#endif
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Constant, ConstantRec::COL_Value,
+ pConstRec, pValue, cbBlob));
+ }
+
+
+ // Create log record for non-token record.
+ IfFailGo(UpdateENCLog2(TBL_Constant, iConstRec));
+ }
+ErrExit:
+
+ return hr;
+} // HRESULT RegMeta::_DefineSetConstant()
+
+
+//*****************************************************************************
+// Helper: Set the properties on the given Method token.
+//*****************************************************************************
+HRESULT RegMeta::_SetMethodProps( // S_OK or error.
+ mdMethodDef md, // [IN] The MethodDef.
+ DWORD dwMethodFlags, // [IN] Method attributes.
+ ULONG ulCodeRVA, // [IN] Code RVA.
+ DWORD dwImplFlags) // [IN] MethodImpl flags.
+{
+ MethodRec *pRecord;
+ HRESULT hr = S_OK;
+
+ _ASSERTE(TypeFromToken(md) == mdtMethodDef && RidFromToken(md));
+
+ // Get the Method record.
+ IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pRecord));
+
+ // Set the data.
+ if (dwMethodFlags != ULONG_MAX)
+ {
+ // Preserve the reserved flags stored already and always keep the mdRTSpecialName
+ dwMethodFlags |= (pRecord->GetFlags() & mdReservedMask);
+
+ // Set the flags.
+ pRecord->SetFlags(static_cast<USHORT>(dwMethodFlags));
+ }
+ if (ulCodeRVA != ULONG_MAX)
+ pRecord->SetRVA(ulCodeRVA);
+ if (dwImplFlags != ULONG_MAX)
+ pRecord->SetImplFlags(static_cast<USHORT>(dwImplFlags));
+
+ IfFailGo(UpdateENCLog(md));
+ErrExit:
+ return hr;
+} // HRESULT RegMeta::_SetMethodProps()
+
+
+//*****************************************************************************
+// Helper: Set the properties on the given Field token.
+//*****************************************************************************
+HRESULT RegMeta::_SetFieldProps( // S_OK or error.
+ mdFieldDef fd, // [IN] The FieldDef.
+ DWORD dwFieldFlags, // [IN] Field attributes.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchValue) // [IN] size of constant value (string, in wide chars).
+{
+#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER
+ return E_NOTIMPL;
+#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+ FieldRec *pRecord;
+ HRESULT hr = S_OK;
+ int bHasDefault = false; // If defining a constant, in this call.
+
+ _ASSERTE (TypeFromToken(fd) == mdtFieldDef && RidFromToken(fd));
+
+ // Get the Field record.
+ IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fd), &pRecord));
+
+ // See if there is a Constant.
+ if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END &&
+ dwCPlusTypeFlag != ULONG_MAX) &&
+ (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING ||
+ dwCPlusTypeFlag == ELEMENT_TYPE_CLASS))))
+ {
+ if (dwFieldFlags == ULONG_MAX)
+ dwFieldFlags = pRecord->GetFlags();
+ dwFieldFlags |= fdHasDefault;
+
+ bHasDefault = true;
+ }
+
+ // Set the flags.
+ if (dwFieldFlags != ULONG_MAX)
+ {
+ if ( IsFdHasFieldRVA(dwFieldFlags) && !IsFdHasFieldRVA(pRecord->GetFlags()) )
+ {
+ // This will trigger field RVA to be created if it is not yet created!
+ _SetRVA(fd, 0, 0);
+ }
+
+ // Preserve the reserved flags stored.
+ dwFieldFlags |= (pRecord->GetFlags() & fdReservedMask);
+ // Set the flags.
+ pRecord->SetFlags(static_cast<USHORT>(dwFieldFlags));
+ }
+
+ IfFailGo(UpdateENCLog(fd));
+
+ // Set the Constant.
+ if (bHasDefault)
+ {
+ BOOL bSearch = IsCallerExternal() || IsENCOn();
+ IfFailGo(_DefineSetConstant(fd, dwCPlusTypeFlag, pValue, cchValue, bSearch));
+ }
+
+ErrExit:
+ return hr;
+#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER
+} // RegMeta::_SetFieldProps
+
+//*****************************************************************************
+// Helper: Set the properties on the given Property token.
+//*****************************************************************************
+HRESULT RegMeta::_SetPropertyProps( // S_OK or error.
+ mdProperty pr, // [IN] Property token.
+ DWORD dwPropFlags, // [IN] CorPropertyAttr.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for value type, selected ELEMENT_TYPE_*
+ void const *pValue, // [IN] Constant value.
+ ULONG cchValue, // [IN] size of constant value (string, in wide chars).
+ mdMethodDef mdSetter, // [IN] Setter of the property.
+ mdMethodDef mdGetter, // [IN] Getter of the property.
+ mdMethodDef rmdOtherMethods[]) // [IN] Array of other methods.
+{
+ PropertyRec *pRecord;
+ BOOL bClear = IsCallerExternal() || IsENCOn() || IsIncrementalOn();
+ HRESULT hr = S_OK;
+ int bHasDefault = false; // If true, constant value this call.
+
+
+
+ _ASSERTE(TypeFromToken(pr) == mdtProperty && RidFromToken(pr));
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(pr), &pRecord));
+
+ if (dwPropFlags != ULONG_MAX)
+ {
+ // Clear the reserved flags from the flags passed in.
+ dwPropFlags &= (~prReservedMask);
+ }
+ // See if there is a constant.
+ if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END &&
+ dwCPlusTypeFlag != ULONG_MAX) &&
+ (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING ||
+ dwCPlusTypeFlag == ELEMENT_TYPE_CLASS))))
+ {
+ if (dwPropFlags == ULONG_MAX)
+ dwPropFlags = pRecord->GetPropFlags();
+ dwPropFlags |= prHasDefault;
+
+ bHasDefault = true;
+ }
+ if (dwPropFlags != ULONG_MAX)
+ {
+ // Preserve the reserved flags.
+ dwPropFlags |= (pRecord->GetPropFlags() & prReservedMask);
+ // Set the flags.
+ pRecord->SetPropFlags(static_cast<USHORT>(dwPropFlags));
+ }
+
+ // store the getter (or clear out old one).
+ if (mdGetter != ULONG_MAX)
+ {
+ _ASSERTE(TypeFromToken(mdGetter) == mdtMethodDef || IsNilToken(mdGetter));
+ IfFailGo(_DefineMethodSemantics(msGetter, mdGetter, pr, bClear));
+ }
+
+ // Store the setter (or clear out old one).
+ if (mdSetter != ULONG_MAX)
+ {
+ _ASSERTE(TypeFromToken(mdSetter) == mdtMethodDef || IsNilToken(mdSetter));
+ IfFailGo(_DefineMethodSemantics(msSetter, mdSetter, pr, bClear));
+ }
+
+ // Store all of the other methods.
+ if (rmdOtherMethods)
+ {
+ int i = 0;
+ mdMethodDef mb;
+
+ while (1)
+ {
+ mb = rmdOtherMethods[i++];
+ if (IsNilToken(mb))
+ break;
+ _ASSERTE(TypeFromToken(mb) == mdtMethodDef);
+ IfFailGo(_DefineMethodSemantics(msOther, mb, pr, bClear));
+
+ // The first call to _DefineMethodSemantics would've cleared all the records
+ // that match with msOther and pr.
+ bClear = false;
+ }
+ }
+
+ IfFailGo(UpdateENCLog(pr));
+
+ // Set the constant.
+ if (bHasDefault)
+ {
+ BOOL bSearch = IsCallerExternal() || IsENCOn() || IsIncrementalOn();
+ IfFailGo(_DefineSetConstant(pr, dwCPlusTypeFlag, pValue, cchValue, bSearch));
+ }
+
+ErrExit:
+
+ return hr;
+} // HRESULT RegMeta::_SetPropertyProps()
+
+
+//*****************************************************************************
+// Helper: This routine sets properties on the given Param token.
+//*****************************************************************************
+HRESULT RegMeta::_SetParamProps( // Return code.
+ mdParamDef pd, // [IN] Param token.
+ LPCWSTR szName, // [IN] Param name.
+ DWORD dwParamFlags, // [IN] Param flags.
+ DWORD dwCPlusTypeFlag, // [IN] Flag for value type. selected ELEMENT_TYPE_*.
+ void const *pValue, // [OUT] Constant value.
+ ULONG cchValue) // [IN] size of constant value (string, in wide chars).
+{
+ HRESULT hr = S_OK;
+ ParamRec *pRecord;
+ int bHasDefault = false; // Is there a default for this call.
+
+ _ASSERTE(TypeFromToken(pd) == mdtParamDef && RidFromToken(pd));
+
+ IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(pd), &pRecord));
+
+ // Set the properties.
+ if (szName != NULL)
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_Param, ParamRec::COL_Name, pRecord, szName));
+ }
+
+ if (dwParamFlags != ULONG_MAX)
+ {
+ // No one should try to set the reserved flags explicitly.
+ _ASSERTE((dwParamFlags & pdReservedMask) == 0);
+ // Clear the reserved flags from the flags passed in.
+ dwParamFlags &= (~pdReservedMask);
+ }
+ // See if there is a constant.
+ if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END &&
+ dwCPlusTypeFlag != ULONG_MAX) &&
+ (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING ||
+ dwCPlusTypeFlag == ELEMENT_TYPE_CLASS))))
+ {
+ if (dwParamFlags == ULONG_MAX)
+ dwParamFlags = pRecord->GetFlags();
+ dwParamFlags |= pdHasDefault;
+
+ bHasDefault = true;
+ }
+ // Set the flags.
+ if (dwParamFlags != ULONG_MAX)
+ {
+ // Preserve the reserved flags stored.
+ dwParamFlags |= (pRecord->GetFlags() & pdReservedMask);
+ // Set the flags.
+ pRecord->SetFlags(static_cast<USHORT>(dwParamFlags));
+ }
+
+ // ENC log for the param record.
+ IfFailGo(UpdateENCLog(pd));
+
+ // Defer setting the constant until after the ENC log for the param. Due to the way that
+ // parameter records are re-ordered, ENC needs the param record log entry to be IMMEDIATELY
+ // after the param added function.
+
+ // Set the constant.
+ if (bHasDefault)
+ {
+ BOOL bSearch = IsCallerExternal() || IsENCOn();
+ IfFailGo(_DefineSetConstant(pd, dwCPlusTypeFlag, pValue, cchValue, bSearch));
+ }
+
+ErrExit:
+ return hr;
+} // HRESULT RegMeta::_SetParamProps()
+
+//*****************************************************************************
+// Create and populate a new TypeDef record.
+//*****************************************************************************
+HRESULT RegMeta::_DefineTypeDef( // S_OK or error.
+ LPCWSTR szTypeDef, // [IN] Name of TypeDef
+ DWORD dwTypeDefFlags, // [IN] CustomAttribute flags
+ mdToken tkExtends, // [IN] extends this TypeDef or typeref
+ mdToken rtkImplements[], // [IN] Implements interfaces
+ mdTypeDef tdEncloser, // [IN] TypeDef token of the Enclosing Type.
+ mdTypeDef *ptd) // [OUT] Put TypeDef token here
+{
+ HRESULT hr = S_OK; // A result.
+ TypeDefRec *pRecord = NULL; // New TypeDef record.
+ RID iRecord; // New TypeDef RID.
+ CQuickBytes qbNamespace; // Namespace buffer.
+ CQuickBytes qbName; // Name buffer.
+ LPUTF8 szTypeDefUTF8; // Full name in UTF8.
+ ULONG ulStringLen; // Length of the TypeDef string.
+ int bSuccess; // Return value for SplitPath().
+
+
+
+ _ASSERTE(IsTdAutoLayout(dwTypeDefFlags) || IsTdSequentialLayout(dwTypeDefFlags) || IsTdExplicitLayout(dwTypeDefFlags));
+
+ _ASSERTE(ptd);
+ _ASSERTE(TypeFromToken(tkExtends) == mdtTypeRef || TypeFromToken(tkExtends) == mdtTypeDef || TypeFromToken(tkExtends) == mdtTypeSpec
+ || IsNilToken(tkExtends));
+ _ASSERTE(szTypeDef && *szTypeDef);
+ _ASSERTE(IsNilToken(tdEncloser) || IsTdNested(dwTypeDefFlags));
+
+ UTF8STR(szTypeDef, szTypeDefUTF8);
+ PREFIX_ASSUME(szTypeDefUTF8 != NULL);
+
+ ulStringLen = (ULONG)(strlen(szTypeDefUTF8) + 1);
+ IfFailGo(qbNamespace.ReSizeNoThrow(ulStringLen));
+ IfFailGo(qbName.ReSizeNoThrow(ulStringLen));
+ bSuccess = ns::SplitPath(szTypeDefUTF8,
+ (LPUTF8)qbNamespace.Ptr(),
+ ulStringLen,
+ (LPUTF8)qbName.Ptr(),
+ ulStringLen);
+ _ASSERTE(bSuccess);
+
+ if (CheckDups(MDDupTypeDef))
+ {
+ // Check for existence. Do a query by namespace and name.
+ hr = ImportHelper::FindTypeDefByName(&(m_pStgdb->m_MiniMd),
+ (LPCUTF8)qbNamespace.Ptr(),
+ (LPCUTF8)qbName.Ptr(),
+ tdEncloser,
+ ptd);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(*ptd), &pRecord));
+ // <TODO>@FUTURE: Should we check to see if the GUID passed is correct?</TODO>
+ }
+ else
+ {
+ hr = META_S_DUPLICATE;
+ goto ErrExit;
+ }
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ if (!pRecord)
+ {
+ // Create the new record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddTypeDefRecord(&pRecord, &iRecord));
+
+ // Invalidate the ref to def optimization since more def is introduced
+ SetTypeDefDirty(true);
+
+ if (!IsNilToken(tdEncloser))
+ {
+ NestedClassRec *pNestedClassRec;
+ RID iNestedClassRec;
+
+ // Create a new NestedClass record.
+ IfFailGo(m_pStgdb->m_MiniMd.AddNestedClassRecord(&pNestedClassRec, &iNestedClassRec));
+ // Set the NestedClass value.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_NestedClass, NestedClassRec::COL_NestedClass,
+ pNestedClassRec, TokenFromRid(iRecord, mdtTypeDef)));
+ // Set the NestedClass value.
+ IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_NestedClass, NestedClassRec::COL_EnclosingClass,
+ pNestedClassRec, tdEncloser));
+
+ IfFailGo( m_pStgdb->m_MiniMd.AddNestedClassToHash(iNestedClassRec) );
+
+ // Create the log record for the non-token record.
+ IfFailGo(UpdateENCLog2(TBL_NestedClass, iNestedClassRec));
+ }
+
+ // Give token back to caller.
+ *ptd = TokenFromRid(iRecord, mdtTypeDef);
+ }
+
+ // Set the namespace and name.
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeDef, TypeDefRec::COL_Name,
+ pRecord, (LPCUTF8)qbName.Ptr()));
+ IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeDef, TypeDefRec::COL_Namespace,
+ pRecord, (LPCUTF8)qbNamespace.Ptr()));
+
+ SetCallerDefine();
+ IfFailGo(_SetTypeDefProps(*ptd, dwTypeDefFlags, tkExtends, rtkImplements));
+ErrExit:
+ SetCallerExternal();
+
+ return hr;
+} // RegMeta::_DefineTypeDef
+
+#endif //FEATURE_METADATA_EMIT
+
+#if defined(FEATURE_METADATA_IN_VM) && defined(FEATURE_PREJIT)
+
+//******************************************************************************
+//--- IMetaDataCorProfileData
+//******************************************************************************
+
+HRESULT RegMeta::SetCorProfileData(
+ CorProfileData *pProfileData) // [IN] Pointer to profile data
+{
+ m_pCorProfileData = pProfileData;
+
+ return S_OK;
+}
+
+//******************************************************************************
+//--- IMDInternalMetadataReorderingOptions
+//******************************************************************************
+
+HRESULT RegMeta::SetMetaDataReorderingOptions(
+ MetaDataReorderingOptions options) // [IN] Metadata reordering options
+{
+ m_ReorderingOptions = options;
+
+ return S_OK;
+}
+
+#endif //defined(FEATURE_METADATA_IN_VM) && defined(FEATURE_PREJIT)
diff --git a/src/md/compiler/regmeta_imetadatatables.cpp b/src/md/compiler/regmeta_imetadatatables.cpp
new file mode 100644
index 0000000000..a36ccfc38b
--- /dev/null
+++ b/src/md/compiler/regmeta_imetadatatables.cpp
@@ -0,0 +1,719 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: RegMeta_IMetaDataTables.cpp
+//
+
+//
+// Methods of code:RegMeta class which implement public API interfaces code:IMetaDataTables:
+// * code:RegMeta::GetStringHeapSize
+// * code:RegMeta::GetBlobHeapSize
+// * code:RegMeta::GetGuidHeapSize
+// * code:RegMeta::GetUserStringHeapSize
+//
+// * code:RegMeta#GetString_IMetaDataTables
+// * code:RegMeta#GetBlob_IMetaDataTables
+// * code:RegMeta#GetGuid_IMetaDataTables
+// * code:RegMeta#GetUserString_IMetaDataTables
+//
+// * code:RegMeta::GetNextString
+// * code:RegMeta::GetNextBlob
+// * code:RegMeta::GetNextGuid
+// * code:RegMeta::GetNextUserString
+//
+// * code:RegMeta::GetNumTables
+// * code:RegMeta::GetTableIndex
+// * code:RegMeta::GetTableInfo
+// * code:RegMeta::GetColumnInfo
+// * code:RegMeta::GetCodedTokenInfo
+// * code:RegMeta::GetRow
+// * code:RegMeta::GetColumn
+//
+// Methods of code:RegMeta class which implement public API interfaces code:IMetaDataTables2:
+// * code:RegMeta::GetMetaDataStorage
+// * code:RegMeta::GetMetaDataStreamInfo
+//
+// ======================================================================================
+
+#include "stdafx.h"
+#include "regmeta.h"
+
+// --------------------------------------------------------------------------------------
+//
+// Fills size (*pcbStringsHeapSize) of internal strings heap (#String).
+// Returns S_OK or error code. Fills *pcbStringsHeapSize with 0 on error.
+// Implements public API code:IMetaDataTables::GetStringHeapSize.
+//
+HRESULT
+RegMeta::GetStringHeapSize(
+ __out ULONG *pcbStringsHeapSize) // [OUT] Size of the string heap.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pcbStringsHeapSize = m_pStgdb->m_MiniMd.m_StringHeap.GetUnalignedSize();
+
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetStringHeapSize
+
+// --------------------------------------------------------------------------------------
+//
+// Fills size (*pcbBlobsHeapSize) of blobs heap (#Blob).
+// Returns S_OK or error code. Fills *pcbBlobsHeapSize with 0 on error.
+// Implements public API code:IMetaDataTables::GetBlobHeapSize.
+//
+HRESULT
+RegMeta::GetBlobHeapSize(
+ __out ULONG *pcbBlobsHeapSize) // [OUT] Size of the blob heap.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pcbBlobsHeapSize = m_pStgdb->m_MiniMd.m_BlobHeap.GetUnalignedSize();
+
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetBlobHeapSize
+
+// --------------------------------------------------------------------------------------
+//
+// Fills size (*pcbGuidsHeapSize) of guids heap (#GUID).
+// Returns S_OK or error code. Fills *pcbGuidsHeapSize with 0 on error.
+// Implements public API code:IMetaDataTables::GetGuidHeapSize.
+//
+HRESULT
+RegMeta::GetGuidHeapSize(
+ __out ULONG *pcbGuidsHeapSize) // [OUT] Size of the Guid heap.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pcbGuidsHeapSize = m_pStgdb->m_MiniMd.m_GuidHeap.GetSize();
+
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetGuidHeapSize
+
+// --------------------------------------------------------------------------------------
+//
+// Fills size (*pcbUserStringsHeapSize) of user strings heap (#US) (referenced from IL).
+// Returns S_OK or error code. Fills *pcbUserStringsHeapSize with 0 on error.
+// Implements public API code:IMetaDataTables::GetUserStringHeapSize.
+//
+HRESULT
+RegMeta::GetUserStringHeapSize(
+ __out ULONG *pcbUserStringsHeapSize) // [OUT] Size of the user string heap.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pcbUserStringsHeapSize = m_pStgdb->m_MiniMd.m_UserStringHeap.GetUnalignedSize();
+
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetUserStringHeapSize
+
+// --------------------------------------------------------------------------------------
+//
+//#GetString_IMetaDataTables
+//
+// Fills internal null-terminated string (*pszString) at index ixString from string heap (#String).
+// Returns S_OK (even for index 0) or error code (if index is invalid, fills *pszString with NULL then).
+// Implements public API code:IMetaDataTables::GetString.
+//
+HRESULT RegMeta::GetString(
+ ULONG ixString, // [IN] Value from a string column.
+ const char **pszString) // [OUT] Put a pointer to the string here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ IfFailGo(m_pStgdb->m_MiniMd.getString(
+ ixString,
+ pszString));
+
+ _ASSERTE(hr == S_OK);
+ goto Exit;
+ErrExit:
+ *pszString = NULL;
+Exit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetString
+
+// --------------------------------------------------------------------------------------
+//
+//#GetBlob_IMetaDataTables
+//
+// Fills blob entry (*ppvData of size *pcbDataSize) at index ixBlob from blob heap (#Blob).
+// Returns S_OK (even for index 0) or error code (if index is invalid, fills NULL and o then).
+// Implements public API code:IMetaDataTables::GetBlob.
+//
+HRESULT RegMeta::GetBlob(
+ ULONG ixBlob, // [IN] Value from a blob column.
+ __out ULONG *pcbDataSize, // [OUT] Put size of the blob here.
+ __deref_out const void **ppvData) // [OUT] Put a pointer to the blob here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MetaData::DataBlob dataBlob;
+ IfFailGo(m_pStgdb->m_MiniMd.getBlob(ixBlob, &dataBlob));
+
+ *ppvData = (const void *)dataBlob.GetDataPointer();
+ *pcbDataSize = dataBlob.GetSize();
+
+ _ASSERTE(hr == S_OK);
+ goto Exit;
+ErrExit:
+ *ppvData = NULL;
+ *pcbDataSize = 0;
+Exit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetBlob
+
+// --------------------------------------------------------------------------------------
+//
+//#GetGuid_IMetaDataTables
+//
+// Fills guid (*ppGuid) at index ixGuid from guid heap (#GUID).
+// Returns S_OK and fills *ppGuid. Returns S_OK even for (invalid) index 0 (fills *ppGuid with pointer to
+// zeros then).
+// Retruns error code (if index is invalid except 0, fills NULL and o then).
+// Implements public API code:IMetaDataTables::GetGuid.
+//
+// Backward compatibility: returns S_OK even if the index is 0 which is invalid as specified in CLI ECMA
+// specification. In that case returns pointer to GUID from zeros.
+//
+HRESULT RegMeta::GetGuid(
+ ULONG ixGuid, // [IN] Value from a guid column.
+ const GUID **ppGuid) // [OUT] Put a pointer to the GUID here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+
+ if (ixGuid == 0)
+ {
+ // Return zeros
+ *ppGuid = GetPublicApiCompatibilityZeros<const GUID>();
+ hr = S_OK;
+ }
+ else
+ {
+ IfFailGo(m_pStgdb->m_MiniMd.m_GuidHeap.GetGuid(
+ ixGuid,
+ ppGuid));
+ }
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetGuid
+
+// --------------------------------------------------------------------------------------
+//
+//#GetUserString_IMetaDataTables
+//
+// Fills user string (*ppvData of size *pcbDataSize) at index ixUserString.
+// Returns S_OK (even for index 0) or error code (if index is invalid, fills NULL and o then).
+// Implements public API code:IMetaDataTables::GetUserString.
+//
+HRESULT
+RegMeta::GetUserString(
+ ULONG ixUserString, // [IN] Value from a UserString column.
+ __out ULONG *pcbDataSize, // [OUT] Put size of the UserString here.
+ __deref_out_opt const void **ppvData) // [OUT] Put a pointer to the UserString here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ MetaData::DataBlob userString;
+ IfFailGo(m_pStgdb->m_MiniMd.GetUserString(ixUserString, &userString));
+ _ASSERTE(hr == S_OK);
+
+ *ppvData = (const void *)userString.GetDataPointer();
+ *pcbDataSize = userString.GetSize();
+
+ goto Exit;
+ErrExit:
+ *ppvData = NULL;
+ *pcbDataSize = 0;
+Exit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetUserString
+
+// --------------------------------------------------------------------------------------
+//
+//#GetNextString_IMetaDataTables
+//
+// Fills index of string (*pixNextString) from the internal strings heap (#String) starting behind string
+// at index ixString.
+// Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextString with 0 on S_FALSE.
+// Implements public API code:IMetaDataTables::GetNextString.
+//
+HRESULT
+RegMeta::GetNextString(
+ ULONG ixString, // [IN] Value from a string column.
+ __out ULONG *pixNextString) // [OUT] Put the index of the next string here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+
+ // Get string at index ixString
+ LPCSTR szString;
+ IfFailGo(m_pStgdb->m_MiniMd.m_StringHeap.GetString(
+ ixString,
+ &szString));
+ _ASSERTE(hr == S_OK);
+
+ // Get index behind the string - doesn't overflow, because string heap was verified
+ UINT32 ixNextString;
+ ixNextString = ixString + (UINT32)(strlen(szString) + 1);
+
+ // Verify that the next index is in the string heap
+ if (!m_pStgdb->m_MiniMd.m_StringHeap.IsValidIndex(ixNextString))
+ { // The next index is invalid
+ goto ErrExit;
+ }
+ // The next index is valid
+ *pixNextString = ixNextString;
+ goto Exit;
+
+ErrExit:
+ // Fill output parameters on error
+ *pixNextString = 0;
+ // Return S_FALSE if either of the string indexes is invalid (backward API compatibility)
+ hr = S_FALSE;
+Exit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetNextString
+
+// --------------------------------------------------------------------------------------
+//
+//#GetNextBlob_IMetaDataTables
+//
+// Fills index of blob (*pixNextBlob) from the blobs heap (#Blob) starting behind blob at index ixBlob.
+// Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextBlob with 0 on S_FALSE.
+// Implements public API code:IMetaDataTables::GetNextString.
+//
+HRESULT
+RegMeta::GetNextBlob(
+ ULONG ixBlob, // [IN] Value from a blob column.
+ __out ULONG *pixNextBlob) // [OUT] Put the index of the next blob here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+
+ // Get blob at index ixBlob (verifies that the blob is in the blob heap)
+ MetaData::DataBlob blob;
+ IfFailGo(m_pStgdb->m_MiniMd.m_BlobHeap.GetBlobWithSizePrefix(
+ ixBlob,
+ &blob));
+ _ASSERTE(hr == S_OK);
+
+ // Get index behind the blob - doesn't overflow, because the blob is in the blob heap
+ UINT32 ixNextBlob;
+ ixNextBlob = ixBlob + blob.GetSize();
+
+ // Verify that the next index is in the blob heap
+ if (!m_pStgdb->m_MiniMd.m_BlobHeap.IsValidIndex(ixNextBlob))
+ { // The next index is invalid
+ goto ErrExit;
+ }
+ // The next index is valid
+ *pixNextBlob = ixNextBlob;
+ goto Exit;
+
+ErrExit:
+ // Fill output parameters on error
+ *pixNextBlob = 0;
+ // Return S_FALSE if either of the string indexes is invalid (backward API compatibility)
+ hr = S_FALSE;
+Exit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetNextBlob
+
+// --------------------------------------------------------------------------------------
+//
+//#GetNextGuid_IMetaDataTables
+//
+// Fills index of guid (*pixNextGuid) from the guids heap (#GUID) starting behind guid at index ixGuid.
+// Returns S_OK or S_FALSE (if the new index is invalid). Fills *pixNextGuid with 0 on S_FALSE.
+// Implements public API code:IMetaDataTables::GetNextGuid.
+//
+// Backward compatibility: returns S_OK even if the guid index (ixGuid) is 0 which is invalid as
+// specified in CLI ECMA specification.
+//
+HRESULT
+RegMeta::GetNextGuid(
+ ULONG ixGuid, // [IN] Value from a guid column.
+ __out ULONG *pixNextGuid) // [OUT] Put the index of the next guid here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ S_UINT32 ixNextGuid = S_UINT32(ixGuid) + S_UINT32(1);
+ if (ixNextGuid.IsOverflow())
+ { // It's invalid index (UINT32_MAX)
+ goto ErrExit;
+ }
+ if (!m_pStgdb->m_MiniMd.m_GuidHeap.IsValidIndex(ixNextGuid.Value()))
+ { // The next index is invalid
+ goto ErrExit;
+ }
+ _ASSERTE(hr == S_OK);
+ // The next index is valid
+ *pixNextGuid = ixNextGuid.Value();
+ goto Exit;
+
+ErrExit:
+ // Fill output parameters on error
+ *pixNextGuid = 0;
+ // Return S_FALSE if next guid index is invalid (backward API compatibility)
+ hr = S_FALSE;
+Exit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetNextGuid
+
+// --------------------------------------------------------------------------------------
+//
+//#GetNextUserString_IMetaDataTables
+//
+// Fills index of user string (*pixNextUserString) from the user strings heap (#US) starting behind string
+// at index ixUserString.
+// Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextUserString with 0 on S_FALSE.
+// Implements public API code:IMetaDataTables::GetNextUserString.
+//
+// Backward compatibility: returns S_OK even if the string doesn't have odd number of bytes as specified
+// in CLI ECMA specification.
+//
+HRESULT
+RegMeta::GetNextUserString(
+ ULONG ixUserString, // [IN] Value from a UserString column.
+ __out ULONG *pixNextUserString) // [OUT] Put the index of the next user string here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+
+ // Get user string at index ixUserString (verifies that the user string is in the heap)
+ MetaData::DataBlob userString;
+ IfFailGo(m_pStgdb->m_MiniMd.m_UserStringHeap.GetBlobWithSizePrefix(
+ ixUserString,
+ &userString));
+ _ASSERTE(hr == S_OK);
+
+ // Get index behind the user string - doesn't overflow, because the user string is in the heap
+ UINT32 ixNextUserString;
+ ixNextUserString = ixUserString + userString.GetSize();
+
+ // Verify that the next index is in the user string heap
+ if (!m_pStgdb->m_MiniMd.m_UserStringHeap.IsValidIndex(ixNextUserString))
+ { // The next index is invalid
+ goto ErrExit;
+ }
+ // The next index is valid
+ *pixNextUserString = ixNextUserString;
+ goto Exit;
+
+ErrExit:
+ // Fill output parameters on error
+ *pixNextUserString = 0;
+ // Return S_FALSE if either of the string indexes is invalid (backward API compatibility)
+ hr = S_FALSE;
+Exit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetNextUserString
+
+// --------------------------------------------------------------------------------------
+//
+// Implements public API code:IMetaDataTables::GetNumTables.
+//
+HRESULT
+RegMeta::GetNumTables(
+ __out ULONG *pcTables) // [OUT] Count of tables.
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // These are for dumping metadata information.
+ // We probably don't need to do any lock here.
+
+ *pcTables = m_pStgdb->m_MiniMd.GetCountTables();
+ END_ENTRYPOINT_NOTHROW;
+
+ return S_OK;
+} // RegMeta::GetNumTables
+
+// --------------------------------------------------------------------------------------
+//
+// Implements public API code:IMetaDataTables::GetTableIndex.
+//
+HRESULT
+RegMeta::GetTableIndex(
+ ULONG token, // [IN] Token for which to get table index.
+ __out ULONG *pixTbl) // [OUT] Put table index here.
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // These are for dumping metadata information.
+ // We probably don't need to do any lock here.
+
+ *pixTbl = CMiniMdRW::GetTableForToken(token);
+ END_ENTRYPOINT_NOTHROW;
+
+ return S_OK;
+} // RegMeta::GetTableIndex
+
+// --------------------------------------------------------------------------------------
+//
+// Implements public API code:IMetaDataTables::GetTableInfo.
+//
+HRESULT
+RegMeta::GetTableInfo(
+ ULONG ixTbl, // [IN] Which table.
+ ULONG *pcbRow, // [OUT] Size of a row, bytes.
+ ULONG *pcRows, // [OUT] Number of rows.
+ ULONG *pcCols, // [OUT] Number of columns in each row.
+ ULONG *piKey, // [OUT] Key column, or -1 if none.
+ const char **ppName) // [OUT] Name of the table.
+{
+ HRESULT hr = S_OK;
+ CMiniTableDef *pTbl = NULL;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // These are for dumping metadata information.
+ // We probably don't need to do any lock here.
+
+ if (ixTbl >= m_pStgdb->m_MiniMd.GetCountTables())
+ IfFailGo(E_INVALIDARG);
+ pTbl = &m_pStgdb->m_MiniMd.m_TableDefs[ixTbl];
+ if (pcbRow != NULL)
+ *pcbRow = pTbl->m_cbRec;
+ if (pcRows != NULL)
+ *pcRows = m_pStgdb->m_MiniMd.GetCountRecs(ixTbl);
+ if (pcCols != NULL)
+ *pcCols = pTbl->m_cCols;
+ if (piKey != NULL)
+ *piKey = (pTbl->m_iKey == (BYTE) -1) ? -1 : pTbl->m_iKey;
+ if (ppName != NULL)
+ *ppName = g_Tables[ixTbl].m_pName;
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetTableInfo
+
+// --------------------------------------------------------------------------------------
+//
+// Implements public API code:IMetaDataTables::GetColumnInfo.
+//
+HRESULT
+RegMeta::GetColumnInfo(
+ ULONG ixTbl, // [IN] Which Table
+ ULONG ixCol, // [IN] Which Column in the table
+ ULONG *poCol, // [OUT] Offset of the column in the row.
+ ULONG *pcbCol, // [OUT] Size of a column, bytes.
+ ULONG *pType, // [OUT] Type of the column.
+ const char **ppName) // [OUT] Name of the Column.
+{
+ HRESULT hr = S_OK;
+ CMiniTableDef *pTbl = NULL;
+ CMiniColDef *pCol = NULL;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // These are for dumping metadata information.
+ // We probably don't need to do any lock here.
+
+ if (ixTbl >= m_pStgdb->m_MiniMd.GetCountTables())
+ IfFailGo(E_INVALIDARG);
+ pTbl = &m_pStgdb->m_MiniMd.m_TableDefs[ixTbl];
+ if (ixCol >= pTbl->m_cCols)
+ IfFailGo(E_INVALIDARG);
+ pCol = &pTbl->m_pColDefs[ixCol];
+ if (poCol != NULL)
+ *poCol = pCol->m_oColumn;
+ if (pcbCol != NULL)
+ *pcbCol = pCol->m_cbColumn;
+ if (pType != NULL)
+ *pType = pCol->m_Type;
+ if (ppName != NULL)
+ *ppName = g_Tables[ixTbl].m_pColNames[ixCol];
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetColumnInfo
+
+// --------------------------------------------------------------------------------------
+//
+// Implements public API code:IMetaDataTables::GetCodedTokenInfo.
+//
+HRESULT
+RegMeta::GetCodedTokenInfo(
+ ULONG ixCdTkn, // [IN] Which kind of coded token.
+ ULONG *pcTokens, // [OUT] Count of tokens.
+ ULONG **ppTokens, // [OUT] List of tokens.
+ const char **ppName) // [OUT] Name of the CodedToken.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // These are for dumping metadata information.
+ // We probably don't need to do any lock here.
+
+ // Validate arguments.
+ if (ixCdTkn >= CDTKN_COUNT)
+ IfFailGo(E_INVALIDARG);
+
+ if (pcTokens != NULL)
+ *pcTokens = g_CodedTokens[ixCdTkn].m_cTokens;
+ if (ppTokens != NULL)
+ *ppTokens = (ULONG*)g_CodedTokens[ixCdTkn].m_pTokens;
+ if (ppName != NULL)
+ *ppName = g_CodedTokens[ixCdTkn].m_pName;
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetCodedTokenInfo
+
+// --------------------------------------------------------------------------------------
+//
+// Implements public API code:IMetaDataTables::GetRow.
+//
+HRESULT
+RegMeta::GetRow(
+ ULONG ixTbl, // [IN] Which table.
+ ULONG rid, // [IN] Which row.
+ void **ppRow) // [OUT] Put pointer to row here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // These are for dumping metadata information.
+ // We probably don't need to do any lock here.
+
+ // Validate arguments.
+ if (ixTbl >= m_pStgdb->m_MiniMd.GetCountTables())
+ IfFailGo(E_INVALIDARG);
+ if (rid == 0 || rid > m_pStgdb->m_MiniMd.m_Schema.m_cRecs[ixTbl])
+ IfFailGo(E_INVALIDARG);
+
+ // Get the row.
+ IfFailGo(m_pStgdb->m_MiniMd.getRow(ixTbl, rid, ppRow));
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetRow
+
+// --------------------------------------------------------------------------------------
+//
+// Implements public API code:IMetaDataTables::GetColumn.
+//
+HRESULT
+RegMeta::GetColumn(
+ ULONG ixTbl, // [IN] Which table.
+ ULONG ixCol, // [IN] Which column.
+ ULONG rid, // [IN] Which row.
+ ULONG *pVal) // [OUT] Put the column contents here.
+{
+ HRESULT hr = S_OK;
+ CMiniColDef *pCol = NULL;
+ CMiniTableDef *pTbl = NULL;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // These are for dumping metadata information.
+ // We probably don't need to do any lock here.
+
+ void *pRow = NULL; // Row with data.
+
+ // Validate arguments.
+ if (ixTbl >= m_pStgdb->m_MiniMd.GetCountTables())
+ IfFailGo(E_INVALIDARG);
+ pTbl = &m_pStgdb->m_MiniMd.m_TableDefs[ixTbl];
+ if (ixCol >= pTbl->m_cCols)
+ IfFailGo(E_INVALIDARG);
+ if (rid == 0 || rid > m_pStgdb->m_MiniMd.m_Schema.m_cRecs[ixTbl])
+ IfFailGo(E_INVALIDARG);
+
+ // Get the row.
+ IfFailGo(m_pStgdb->m_MiniMd.getRow(ixTbl, rid, &pRow));
+
+ // Is column a token column?
+ pCol = &pTbl->m_pColDefs[ixCol];
+ if (pCol->m_Type <= iCodedTokenMax)
+ {
+ *pVal = m_pStgdb->m_MiniMd.GetToken(ixTbl, ixCol, pRow);
+ }
+ else
+ {
+ *pVal = m_pStgdb->m_MiniMd.GetCol(ixTbl, ixCol, pRow);
+ }
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetColumn
+
+// --------------------------------------------------------------------------------------
+//
+// Implements public API code:IMetaDataTables2::GetMetaDataStorage.
+//
+HRESULT
+RegMeta::GetMetaDataStorage(
+ const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB').
+ ULONG *pcbMd) // [OUT] put size of the stream here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+ REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+
+ hr = m_pStgdb->GetRawData(ppvMd, pcbMd);
+
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetMetaDataStorage
+
+// --------------------------------------------------------------------------------------
+//
+// Implements public API code:IMetaDataTables2::GetMetaDataStreamInfo.
+//
+HRESULT
+RegMeta::GetMetaDataStreamInfo(
+ 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 = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+ REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+
+ hr = m_pStgdb->GetRawStreamInfo(ix, ppchName, ppv, pcb);
+
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetMetaDataStreamInfo
diff --git a/src/md/compiler/regmeta_import.cpp b/src/md/compiler/regmeta_import.cpp
new file mode 100644
index 0000000000..dd260b42a8
--- /dev/null
+++ b/src/md/compiler/regmeta_import.cpp
@@ -0,0 +1,1204 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: RegMeta_IMetaDataImport.cpp
+//
+
+//
+// Methods of code:RegMeta class which implement public API interfaces:
+// * code:IMetaDataImport
+// * code:IMetaDataImport2
+//
+// ======================================================================================
+
+#include "stdafx.h"
+#include "regmeta.h"
+#include "metadata.h"
+#include "corerror.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+#include "filtermanager.h"
+#include "mdperf.h"
+#include "switches.h"
+#include "posterror.h"
+#include "stgio.h"
+#include "sstring.h"
+
+#include <metamodelrw.h>
+
+#define DEFINE_CUSTOM_NODUPCHECK 1
+#define DEFINE_CUSTOM_DUPCHECK 2
+#define SET_CUSTOM 3
+
+#if defined(_DEBUG) && defined(_TRACE_REMAPS)
+#define LOGGING
+#endif
+#include <log.h>
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4102)
+#endif
+
+//*****************************************************************************
+// determine if a token is valid or not
+//*****************************************************************************
+BOOL RegMeta::IsValidToken( // true if tk is valid token
+ mdToken tk) // [IN] token to be checked
+{
+ BOOL fRet = FALSE;
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_VOIDRET;
+
+ LOCKREADNORET();
+
+ // If acquiring the lock failed...
+ IfFailGo(hr);
+
+ fRet = m_pStgdb->m_MiniMd._IsValidToken(tk);
+
+ErrExit:
+ END_ENTRYPOINT_VOIDRET;
+ return fRet;
+} // RegMeta::IsValidToken
+
+//*****************************************************************************
+// Close an enumerator.
+//*****************************************************************************
+void __stdcall RegMeta::CloseEnum(
+ HCORENUM hEnum) // The enumerator.
+{
+ BEGIN_CLEANUP_ENTRYPOINT;
+
+ LOG((LOGMD, "RegMeta::CloseEnum(0x%08x)\n", hEnum));
+
+ // No need to lock this function.
+ HENUMInternal *pmdEnum = reinterpret_cast<HENUMInternal *> (hEnum);
+
+ if (pmdEnum == NULL)
+ return;
+
+ // This function may be called through RCW. When hosted, we have probed before this call, so the
+ // following contract violation is OK.
+ CONTRACT_VIOLATION(SOToleranceViolation);
+ HENUMInternal::DestroyEnum(pmdEnum);
+ END_CLEANUP_ENTRYPOINT;
+} // RegMeta::CloseEnum
+
+//*****************************************************************************
+// Query the count of items represented by an enumerator.
+//*****************************************************************************
+HRESULT CountEnum(
+ HCORENUM hEnum, // The enumerator.
+ ULONG *pulCount) // Put the count here.
+{
+ HENUMInternal *pmdEnum = reinterpret_cast<HENUMInternal *> (hEnum);
+ HRESULT hr = S_OK;
+
+ // No need to lock this function.
+
+ LOG((LOGMD, "RegMeta::CountEnum(0x%08x, 0x%08x)\n", hEnum, pulCount));
+ START_MD_PERF();
+
+ _ASSERTE( pulCount );
+
+ if (pmdEnum == NULL)
+ {
+ *pulCount = 0;
+ goto ErrExit;
+ }
+
+ if (pmdEnum->m_tkKind == (TBL_MethodImpl << 24))
+ {
+ // Number of tokens must always be a multiple of 2.
+ _ASSERTE(! (pmdEnum->m_ulCount % 2) );
+ // There are two entries in the Enumerator for each MethodImpl.
+ *pulCount = pmdEnum->m_ulCount / 2;
+ }
+ else
+ *pulCount = pmdEnum->m_ulCount;
+ErrExit:
+ STOP_MD_PERF(CountEnum);
+ return hr;
+} // ::CountEnum
+
+STDMETHODIMP RegMeta::CountEnum(
+ HCORENUM hEnum, // The enumerator.
+ ULONG *pulCount) // Put the count here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ hr = ::CountEnum(hEnum, pulCount);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::CountEnum
+
+//*****************************************************************************
+// Reset an enumerator to any position within the enumerator.
+//*****************************************************************************
+STDMETHODIMP RegMeta::ResetEnum(
+ HCORENUM hEnum, // The enumerator.
+ ULONG ulPos) // Seek position.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal *pmdEnum = reinterpret_cast<HENUMInternal *> (hEnum);
+
+ // No need to lock this function.
+
+ LOG((LOGMD, "RegMeta::ResetEnum(0x%08x, 0x%08x)\n", hEnum, ulPos));
+ START_MD_PERF();
+
+ if (pmdEnum == NULL)
+ goto ErrExit;
+
+ pmdEnum->u.m_ulCur = pmdEnum->u.m_ulStart + ulPos;
+
+ErrExit:
+ STOP_MD_PERF(ResetEnum);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::ResetEnum
+
+//*****************************************************************************
+// Enumerate Sym.TypeDef.
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumTypeDefs(
+ HCORENUM *phEnum, // Pointer to the enumerator.
+ mdTypeDef rTypeDefs[], // Put TypeDefs here.
+ ULONG cMax, // Max TypeDefs to put.
+ ULONG *pcTypeDefs) // Put # put here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum;
+
+ LOG((LOGMD, "RegMeta::EnumTypeDefs(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, rTypeDefs, cMax, pcTypeDefs));
+ START_MD_PERF();
+ LOCKREAD();
+
+
+ if ( *ppmdEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ if (pMiniMd->HasDelete() &&
+ ((m_OptionValue.m_ImportOption & MDImportOptionAllTypeDefs) == 0))
+ {
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtTypeDef, &pEnum) );
+
+ // add all Types to the dynamic array if name is not _Delete
+ for (ULONG index = 2; index <= pMiniMd->getCountTypeDefs(); index ++ )
+ {
+ TypeDefRec *pRec;
+ IfFailGo(pMiniMd->GetTypeDefRecord(index, &pRec));
+ LPCSTR szTypeDefName;
+ IfFailGo(pMiniMd->getNameOfTypeDef(pRec, &szTypeDefName));
+ if (IsDeletedName(szTypeDefName))
+ {
+ continue;
+ }
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtTypeDef) ) );
+ }
+ }
+ else
+ {
+ // create the enumerator
+ IfFailGo( HENUMInternal::CreateSimpleEnum(
+ mdtTypeDef,
+ 2,
+ pMiniMd->getCountTypeDefs() + 1,
+ &pEnum) );
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppmdEnum;
+ }
+
+ // we can only fill the minimun of what caller asked for or what we have left
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rTypeDefs, pcTypeDefs);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumTypeDefs);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumTypeDefs
+
+//*****************************************************************************
+// Enumerate Sym.InterfaceImpl where Coclass == td
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumInterfaceImpls(
+ HCORENUM *phEnum, // Pointer to the enum.
+ mdTypeDef td, // TypeDef to scope the enumeration.
+ mdInterfaceImpl rImpls[], // Put InterfaceImpls here.
+ ULONG cMax, // Max InterfaceImpls to put.
+ ULONG *pcImpls) // Put # put here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ HENUMInternal *pEnum;
+ InterfaceImplRec *pRec;
+ ULONG index;
+
+ LOG((LOGMD, "RegMeta::EnumInterfaceImpls(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, td, rImpls, cMax, pcImpls));
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+
+
+ if ( *ppmdEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ if ( pMiniMd->IsSorted( TBL_InterfaceImpl ) )
+ {
+ IfFailGo(pMiniMd->getInterfaceImplsForTypeDef(RidFromToken(td), &ridEnd, &ridStart));
+ IfFailGo( HENUMInternal::CreateSimpleEnum( mdtInterfaceImpl, ridStart, ridEnd, &pEnum) );
+ }
+ else
+ {
+ // table is not sorted so we have to create dynmaic array
+ // create the dynamic enumerator
+ //
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountInterfaceImpls() + 1;
+
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtInterfaceImpl, &pEnum) );
+
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(pMiniMd->GetInterfaceImplRecord(index, &pRec));
+ if ( td == pMiniMd->getClassOfInterfaceImpl(pRec) )
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtInterfaceImpl) ) );
+ }
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppmdEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rImpls, pcImpls);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ STOP_MD_PERF(EnumInterfaceImpls);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumInterfaceImpls
+
+STDMETHODIMP RegMeta::EnumGenericParams(HCORENUM *phEnum, mdToken tkOwner,
+ mdGenericParam rTokens[], ULONG cMaxTokens, ULONG *pcTokens)
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ HENUMInternal *pEnum;
+ GenericParamRec *pRec;
+ ULONG index;
+ CMiniMdRW *pMiniMd = NULL;
+
+
+ LOG((LOGMD, "RegMeta::EnumGenericParams(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, tkOwner, rTokens, cMaxTokens, pcTokens));
+ START_MD_PERF();
+ LOCKREAD();
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+
+ // See if this version of the metadata can do Generics
+ if (!pMiniMd->SupportsGenerics())
+ {
+ if (pcTokens)
+ *pcTokens = 0;
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+
+ _ASSERTE(TypeFromToken(tkOwner) == mdtTypeDef || TypeFromToken(tkOwner) == mdtMethodDef);
+
+
+ if ( *ppmdEnum == 0 )
+ {
+ // instantiating a new ENUM
+
+ //@todo GENERICS: review this. Are we expecting a sorted table or not?
+ if ( pMiniMd->IsSorted( TBL_GenericParam ) )
+ {
+ if (TypeFromToken(tkOwner) == mdtTypeDef)
+ {
+ IfFailGo(pMiniMd->getGenericParamsForTypeDef(RidFromToken(tkOwner), &ridEnd, &ridStart));
+ }
+ else
+ {
+ IfFailGo(pMiniMd->getGenericParamsForMethodDef(RidFromToken(tkOwner), &ridEnd, &ridStart));
+ }
+
+ IfFailGo( HENUMInternal::CreateSimpleEnum(mdtGenericParam, ridStart, ridEnd, &pEnum) );
+ }
+ else
+ {
+ // table is not sorted so we have to create dynamic array
+ // create the dynamic enumerator
+ //
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountGenericParams() + 1;
+
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum(mdtGenericParam, &pEnum) );
+
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(pMiniMd->GetGenericParamRecord(index, &pRec));
+ if ( tkOwner == pMiniMd->getOwnerOfGenericParam(pRec) )
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtGenericParam) ) );
+ }
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppmdEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMaxTokens, rTokens, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+ STOP_MD_PERF(EnumGenericPars);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // RegMeta::EnumGenericParams
+
+STDMETHODIMP RegMeta::EnumMethodSpecs(
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tkOwner, // [IN] MethodDef or MemberRef whose MethodSpecs are requested
+ mdMethodSpec rTokens[], // [OUT] Put MethodSpecs here.
+ ULONG cMaxTokens, // [IN] Max tokens to put.
+ ULONG *pcTokens) // [OUT] Put actual count here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ HENUMInternal *pEnum;
+ MethodSpecRec *pRec;
+ ULONG index;
+ CMiniMdRW *pMiniMd = NULL;
+
+ LOG((LOGMD, "RegMeta::EnumMethodSpecs(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, tkOwner, rTokens, cMaxTokens, pcTokens));
+ START_MD_PERF();
+ LOCKREAD();
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ // See if this version of the metadata can do Generics
+ if (!pMiniMd->SupportsGenerics())
+ {
+ if (pcTokens)
+ *pcTokens = 0;
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+
+ _ASSERTE(RidFromToken(tkOwner)==0 || TypeFromToken(tkOwner) == mdtMethodDef || TypeFromToken(tkOwner) == mdtMemberRef);
+
+
+ if ( *ppmdEnum == 0 )
+ {
+ // instantiating a new ENUM
+
+ if(RidFromToken(tkOwner)==0) // enumerate all MethodSpecs
+ {
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountMethodSpecs() + 1;
+
+ IfFailGo( HENUMInternal::CreateSimpleEnum( mdtMethodSpec, ridStart, ridEnd, &pEnum) );
+ }
+ else
+ {
+ //@todo GENERICS: review this. Are we expecting a sorted table or not?
+ if ( pMiniMd->IsSorted( TBL_MethodSpec ) )
+ {
+ if (TypeFromToken(tkOwner) == mdtMemberRef)
+ {
+ IfFailGo(pMiniMd->getMethodSpecsForMemberRef(RidFromToken(tkOwner), &ridEnd, &ridStart));
+ }
+ else
+ {
+ IfFailGo(pMiniMd->getMethodSpecsForMethodDef(RidFromToken(tkOwner), &ridEnd, &ridStart));
+ }
+
+ IfFailGo( HENUMInternal::CreateSimpleEnum(mdtMethodSpec, ridStart, ridEnd, &pEnum) );
+ }
+ else
+ {
+ // table is not sorted so we have to create dynamic array
+ // create the dynamic enumerator
+ //
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountMethodSpecs() + 1;
+
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum(mdtMethodSpec, &pEnum) );
+
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(pMiniMd->GetMethodSpecRecord(index, &pRec));
+ if ( tkOwner == pMiniMd->getMethodOfMethodSpec(pRec) )
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtMethodSpec) ) );
+ }
+ }
+ }
+ }
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppmdEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMaxTokens, rTokens, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+ STOP_MD_PERF(EnumMethodSpecs);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumMethodSpecs()
+
+STDMETHODIMP RegMeta::EnumGenericParamConstraints(
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdGenericParam tkOwner, // [IN] GenericParam whose constraints are requested
+ mdGenericParamConstraint rTokens[], // [OUT] Put GenericParamConstraints here.
+ ULONG cMaxTokens, // [IN] Max GenericParamConstraints to put.
+ ULONG *pcTokens) // [OUT] Put # of tokens here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG ridStart;
+ ULONG ridEnd;
+ HENUMInternal *pEnum;
+ GenericParamConstraintRec *pRec;
+ ULONG index;
+ CMiniMdRW *pMiniMd = NULL;
+
+ LOG((LOGMD, "RegMeta::EnumGenericParamConstraints(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, tkOwner, rTokens, cMaxTokens, pcTokens));
+ START_MD_PERF();
+ LOCKREAD();
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+
+ if(TypeFromToken(tkOwner) != mdtGenericParam)
+ IfFailGo(META_E_BAD_INPUT_PARAMETER);
+
+ // See if this version of the metadata can do Generics
+ if (!pMiniMd->SupportsGenerics())
+ {
+ if (pcTokens)
+ *pcTokens = 0;
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+ if ( *ppmdEnum == 0 )
+ {
+ // instantiating a new ENUM
+
+ //<TODO> GENERICS: review this. Are we expecting a sorted table or not? </TODO>
+ if ( pMiniMd->IsSorted( TBL_GenericParamConstraint ) )
+ {
+ IfFailGo(pMiniMd->getGenericParamConstraintsForGenericParam(RidFromToken(tkOwner), &ridEnd, &ridStart));
+ IfFailGo( HENUMInternal::CreateSimpleEnum(mdtGenericParamConstraint, ridStart, ridEnd, &pEnum) );
+ }
+ else
+ {
+ // table is not sorted so we have to create dynamic array
+ // create the dynamic enumerator
+ //
+ ridStart = 1;
+ ridEnd = pMiniMd->getCountGenericParamConstraints() + 1;
+
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum(mdtGenericParamConstraint, &pEnum));
+
+ for (index = ridStart; index < ridEnd; index ++ )
+ {
+ IfFailGo(pMiniMd->GetGenericParamConstraintRecord(index, &pRec));
+ if ( tkOwner == pMiniMd->getOwnerOfGenericParamConstraint(pRec))
+ {
+ IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index,
+ mdtGenericParamConstraint)));
+ }
+ }
+ }
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppmdEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMaxTokens, rTokens, pcTokens);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+ STOP_MD_PERF(EnumGenericParamConstraints);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+}
+
+//*****************************************************************************
+// Enumerate Sym.TypeRef
+//*****************************************************************************
+STDMETHODIMP RegMeta::EnumTypeRefs(
+ HCORENUM *phEnum, // Pointer to the enumerator.
+ mdTypeRef rTypeRefs[], // Put TypeRefs here.
+ ULONG cMax, // Max TypeRefs to put.
+ ULONG *pcTypeRefs) // Put # put here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ ULONG cTotal;
+ HENUMInternal *pEnum = *ppmdEnum;
+
+
+
+ LOG((LOGMD, "RegMeta::EnumTypeRefs(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ phEnum, rTypeRefs, cMax, pcTypeRefs));
+ START_MD_PERF();
+ LOCKREAD();
+
+ if ( pEnum == 0 )
+ {
+ // instantiating a new ENUM
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ cTotal = pMiniMd->getCountTypeRefs();
+
+ IfFailGo( HENUMInternal::CreateSimpleEnum( mdtTypeRef, 1, cTotal + 1, &pEnum) );
+
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rTypeRefs, pcTypeRefs);
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+
+ STOP_MD_PERF(EnumTypeRefs);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::EnumTypeRefs()
+
+//*****************************************************************************
+// Given a namespace and a class name, return the typedef
+//*****************************************************************************
+STDMETHODIMP RegMeta::FindTypeDefByName(// S_OK or error.
+ LPCWSTR wzTypeDef, // [IN] Name of the Type.
+ mdToken tkEnclosingClass, // [IN] Enclosing class.
+ mdTypeDef *ptd) // [OUT] Put the TypeDef token here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW
+
+ LOG((LOGMD, "{%08x} RegMeta::FindTypeDefByName(%S, 0x%08x, 0x%08x)\n",
+ this, MDSTR(wzTypeDef), tkEnclosingClass, ptd));
+ START_MD_PERF();
+ LOCKREAD();
+
+
+ if (wzTypeDef == NULL)
+ IfFailGo(E_INVALIDARG);
+ PREFIX_ASSUME(wzTypeDef != NULL);
+ LPSTR szTypeDef;
+ UTF8STR(wzTypeDef, szTypeDef);
+ LPCSTR szNamespace;
+ LPCSTR szName;
+
+ _ASSERTE(ptd);
+ _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef ||
+ TypeFromToken(tkEnclosingClass) == mdtTypeRef ||
+ IsNilToken(tkEnclosingClass));
+
+ // initialize output parameter
+ *ptd = mdTypeDefNil;
+
+ ns::SplitInline(szTypeDef, szNamespace, szName);
+ hr = ImportHelper::FindTypeDefByName(&(m_pStgdb->m_MiniMd),
+ szNamespace,
+ szName,
+ tkEnclosingClass,
+ ptd);
+ErrExit:
+
+ STOP_MD_PERF(FindTypeDefByName);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::FindTypeDefByName()
+
+//*****************************************************************************
+// Get values from Sym.Module
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetScopeProps(
+ __out_ecount_opt (cchName) LPWSTR szName, // Put name here
+ ULONG cchName, // Size in chars of name buffer
+ ULONG *pchName, // Put actual length of name here
+ GUID *pmvid) // Put MVID here
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ ModuleRec *pModuleRec;
+
+
+ LOG((LOGMD, "RegMeta::GetScopeProps(%S, 0x%08x, 0x%08x, 0x%08x)\n",
+ MDSTR(szName), cchName, pchName, pmvid));
+ START_MD_PERF();
+ LOCKREAD();
+
+ // there is only one module record
+ IfFailGo(pMiniMd->GetModuleRecord(1, &pModuleRec));
+
+ if (pmvid != NULL)
+ {
+ IfFailGo(pMiniMd->getMvidOfModule(pModuleRec, pmvid));
+ }
+ // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK
+ if (szName || pchName)
+ IfFailGo( pMiniMd->getNameOfModule(pModuleRec, szName, cchName, pchName) );
+ErrExit:
+
+ STOP_MD_PERF(GetScopeProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetScopeProps()
+
+//*****************************************************************************
+// Get the token for a Scope's (primary) module record.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetModuleFromScope(// S_OK.
+ mdModule *pmd) // [OUT] Put mdModule token here.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LOGMD, "RegMeta::GetModuleFromScope(0x%08x)\n", pmd));
+ START_MD_PERF();
+
+ _ASSERTE(pmd);
+
+ // No need to lock this function.
+
+ *pmd = TokenFromRid(1, mdtModule);
+
+ STOP_MD_PERF(GetModuleFromScope);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetModuleFromScope()
+
+//*****************************************************************************
+// Given a token, is it (or its parent) global?
+//*****************************************************************************
+HRESULT RegMeta::IsGlobal( // S_OK ir error.
+ mdToken tk, // [IN] Type, Field, or Method token.
+ int *pbGlobal) // [OUT] Put 1 if global, 0 otherwise.
+{
+ HRESULT hr=S_OK; // A result.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ mdToken tkParent; // Parent of field or method.
+
+ LOG((LOGMD, "RegMeta::GetTokenForGlobalType(0x%08x, %08x)\n", tk, pbGlobal));
+ //START_MD_PERF();
+
+ // No need to lock this function.
+
+ if (!IsValidToken(tk))
+ IfFailGo(E_INVALIDARG);
+
+ switch (TypeFromToken(tk))
+ {
+ case mdtTypeDef:
+ *pbGlobal = IsGlobalMethodParentToken(tk);
+ break;
+
+ case mdtFieldDef:
+ IfFailGo( pMiniMd->FindParentOfFieldHelper(tk, &tkParent) );
+ *pbGlobal = IsGlobalMethodParentToken(tkParent);
+ break;
+
+ case mdtMethodDef:
+ IfFailGo( pMiniMd->FindParentOfMethodHelper(tk, &tkParent) );
+ *pbGlobal = IsGlobalMethodParentToken(tkParent);
+ break;
+
+ case mdtProperty:
+ IfFailGo( pMiniMd->FindParentOfPropertyHelper(tk, &tkParent) );
+ *pbGlobal = IsGlobalMethodParentToken(tkParent);
+ break;
+
+ case mdtEvent:
+ IfFailGo( pMiniMd->FindParentOfEventHelper(tk, &tkParent) );
+ *pbGlobal = IsGlobalMethodParentToken(tkParent);
+ break;
+
+ // Anything else is NOT global.
+ default:
+ *pbGlobal = FALSE;
+ }
+
+ErrExit:
+ //STOP_MD_PERF(GetModuleFromScope);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // HRESULT RegMeta::IsGlobal()
+
+//*****************************************************************************
+// return flags for a given class
+//*****************************************************************************
+HRESULT
+RegMeta::GetTypeDefProps(
+ mdTypeDef td, // [IN] TypeDef token for inquiry.
+ __out_ecount_opt (cchTypeDef) LPWSTR szTypeDef, // [OUT] Put name here.
+ ULONG cchTypeDef, // [IN] size of name buffer in wide chars.
+ ULONG *pchTypeDef, // [OUT] put size of name (wide chars) here.
+ DWORD *pdwTypeDefFlags, // [OUT] Put flags here.
+ mdToken *ptkExtends) // [OUT] Put base class TypeDef/TypeRef here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+ TypeDefRec *pTypeDefRec;
+ BOOL fTruncation = FALSE; // Was there name truncation?
+
+ LOG((LOGMD, "{%08x} RegMeta::GetTypeDefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ this, td, szTypeDef, cchTypeDef, pchTypeDef,
+ pdwTypeDefFlags, ptkExtends));
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (TypeFromToken(td) != mdtTypeDef)
+ {
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+ if (td == mdTypeDefNil)
+ { // Backward compatibility with CLR 2.0 implementation
+ if (pdwTypeDefFlags != NULL)
+ *pdwTypeDefFlags = 0;
+ if (ptkExtends != NULL)
+ *ptkExtends = mdTypeRefNil;
+ if (pchTypeDef != NULL)
+ *pchTypeDef = 1;
+ if ((szTypeDef != NULL) && (cchTypeDef > 0))
+ szTypeDef[0] = 0;
+
+ hr = S_OK;
+ goto ErrExit;
+ }
+
+ IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+
+ if ((szTypeDef != NULL) || (pchTypeDef != NULL))
+ {
+ LPCSTR szNamespace;
+ LPCSTR szName;
+
+ IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzNamespace, szNamespace);
+ IfNullGo(wzNamespace);
+
+ IfFailGo(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szName));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzName, szName);
+ IfNullGo(wzName);
+
+ if (szTypeDef != NULL)
+ {
+ fTruncation = !(ns::MakePath(szTypeDef, cchTypeDef, wzNamespace, wzName));
+ }
+ if (pchTypeDef != NULL)
+ {
+ if (fTruncation || (szTypeDef == NULL))
+ {
+ *pchTypeDef = ns::GetFullLength(wzNamespace, wzName);
+ }
+ else
+ {
+ *pchTypeDef = (ULONG)(wcslen(szTypeDef) + 1);
+ }
+ }
+ }
+ if (pdwTypeDefFlags != NULL)
+ { // caller wants type flags
+ *pdwTypeDefFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec);
+ }
+ if (ptkExtends != NULL)
+ {
+ *ptkExtends = pMiniMd->getExtendsOfTypeDef(pTypeDefRec);
+
+ // take care of the 0 case
+ if (RidFromToken(*ptkExtends) == 0)
+ {
+ *ptkExtends = mdTypeRefNil;
+ }
+ }
+
+ if (fTruncation && (hr == S_OK))
+ {
+ if ((szTypeDef != NULL) && (cchTypeDef > 0))
+ { // null-terminate the truncated output string
+ szTypeDef[cchTypeDef - 1] = W('\0');
+ }
+ hr = CLDB_S_TRUNCATION;
+ }
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ STOP_MD_PERF(GetTypeDefProps);
+
+ return hr;
+} // RegMeta::GetTypeDefProps
+
+//*****************************************************************************
+// Retrieve information about an implemented interface.
+//*****************************************************************************
+STDMETHODIMP RegMeta::GetInterfaceImplProps( // S_OK or error.
+ mdInterfaceImpl iiImpl, // [IN] InterfaceImpl token.
+ mdTypeDef *pClass, // [OUT] Put implementing class token here.
+ mdToken *ptkIface) // [OUT] Put implemented interface token here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd = NULL;
+ InterfaceImplRec *pIIRec = NULL;
+
+
+
+ LOG((LOGMD, "RegMeta::GetInterfaceImplProps(0x%08x, 0x%08x, 0x%08x)\n",
+ iiImpl, pClass, ptkIface));
+ START_MD_PERF();
+ LOCKREAD();
+
+ _ASSERTE(TypeFromToken(iiImpl) == mdtInterfaceImpl);
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+ IfFailGo(pMiniMd->GetInterfaceImplRecord(RidFromToken(iiImpl), &pIIRec));
+
+ if (pClass)
+ {
+ *pClass = pMiniMd->getClassOfInterfaceImpl(pIIRec);
+ }
+ if (ptkIface)
+ {
+ *ptkIface = pMiniMd->getInterfaceOfInterfaceImpl(pIIRec);
+ }
+
+ErrExit:
+ STOP_MD_PERF(GetInterfaceImplProps);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::GetInterfaceImplProps()
+
+//*****************************************************************************
+// Retrieve information about a TypeRef.
+//*****************************************************************************
+STDMETHODIMP
+RegMeta::GetTypeRefProps(
+ mdTypeRef tr, // The class ref token.
+ mdToken *ptkResolutionScope, // Resolution scope, ModuleRef or AssemblyRef.
+ __out_ecount_opt (cchTypeRef) LPWSTR szTypeRef, // Put the name here.
+ ULONG cchTypeRef, // Size of the name buffer, wide chars.
+ ULONG *pchTypeRef) // Put actual size of name here.
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CMiniMdRW *pMiniMd;
+ TypeRefRec *pTypeRefRec;
+ BOOL fTruncation = FALSE; // Was there name truncation?
+
+ LOG((LOGMD, "RegMeta::GetTypeRefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ tr, ptkResolutionScope, szTypeRef, cchTypeRef, pchTypeRef));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ if (TypeFromToken(tr) != mdtTypeRef)
+ {
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+ if (tr == mdTypeRefNil)
+ { // Backward compatibility with CLR 2.0 implementation
+ if (ptkResolutionScope != NULL)
+ *ptkResolutionScope = mdTokenNil;
+ if (pchTypeRef != NULL)
+ *pchTypeRef = 1;
+ if ((szTypeRef != NULL) && (cchTypeRef > 0))
+ szTypeRef[0] = 0;
+
+ hr = S_OK;
+ goto ErrExit;
+ }
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pTypeRefRec));
+
+ if (ptkResolutionScope != NULL)
+ {
+ *ptkResolutionScope = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec);
+ }
+
+ if ((szTypeRef != NULL) || (pchTypeRef != NULL))
+ {
+ LPCSTR szNamespace;
+ LPCSTR szName;
+
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzNamespace, szNamespace);
+ IfNullGo(wzNamespace);
+
+ IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szName));
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzName, szName);
+ IfNullGo(wzName);
+
+ if (szTypeRef != NULL)
+ {
+ fTruncation = !(ns::MakePath(szTypeRef, cchTypeRef, wzNamespace, wzName));
+ }
+ if (pchTypeRef != NULL)
+ {
+ if (fTruncation || (szTypeRef == NULL))
+ {
+ *pchTypeRef = ns::GetFullLength(wzNamespace, wzName);
+ }
+ else
+ {
+ *pchTypeRef = (ULONG)(wcslen(szTypeRef) + 1);
+ }
+ }
+ }
+ if (fTruncation && (hr == S_OK))
+ {
+ if ((szTypeRef != NULL) && (cchTypeRef > 0))
+ { // null-terminate the truncated output string
+ szTypeRef[cchTypeRef - 1] = W('\0');
+ }
+ hr = CLDB_S_TRUNCATION;
+ }
+
+ErrExit:
+ STOP_MD_PERF(GetTypeRefProps);
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // RegMeta::GetTypeRefProps
+
+//*****************************************************************************
+// Given a TypeRef name, return the typeref
+//*****************************************************************************
+STDMETHODIMP RegMeta::FindTypeRef( // S_OK or error.
+ mdToken tkResolutionScope, // [IN] Resolution Scope.
+ LPCWSTR wzTypeName, // [IN] Name of the TypeRef.
+ mdTypeRef *ptk) // [OUT] Put the TypeRef token here.
+{
+ HRESULT hr = S_OK; // A result.
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LPUTF8 szFullName;
+ LPCUTF8 szNamespace;
+ LPCUTF8 szName;
+ CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ _ASSERTE(wzTypeName && ptk);
+
+
+
+ LOG((LOGMD, "RegMeta::FindTypeRef(0x%8x, %ls, 0x%08x)\n",
+ tkResolutionScope, MDSTR(wzTypeName), ptk));
+ START_MD_PERF();
+ LOCKREAD();
+
+ // Convert the name to UTF8.
+ PREFIX_ASSUME(wzTypeName != NULL); // caller might pass NULL, but they'll AV.
+ UTF8STR(wzTypeName, szFullName);
+ ns::SplitInline(szFullName, szNamespace, szName);
+
+ // Look up the name.
+ hr = ImportHelper::FindTypeRefByName(pMiniMd, tkResolutionScope,
+ szNamespace,
+ szName,
+ ptk);
+ErrExit:
+
+ STOP_MD_PERF(FindTypeRef);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // STDMETHODIMP RegMeta::FindTypeRef()
+
+//*******************************************************************************
+// Find a given param of a Method.
+//*******************************************************************************
+HRESULT RegMeta::_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.
+{
+ HRESULT hr;
+ ParamRec *pParamRec;
+ RID ridStart, ridEnd;
+ RID pmRid;
+
+ _ASSERTE(TypeFromToken(md) == mdtMethodDef && pParamDef);
+
+ // get the methoddef record
+ MethodRec *pMethodRec;
+ IfFailRet(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);
+ IfFailRet(m_pStgdb->m_MiniMd.getEndParamListOfMethod(RidFromToken(md), &ridEnd));
+
+ // loop through each param
+ // <TODO>@consider: parameters are sorted by sequence. Maybe a binary search?
+ //</TODO>
+ for (; ridStart < ridEnd; ridStart++)
+ {
+ IfFailRet(m_pStgdb->m_MiniMd.GetParamRid(ridStart, &pmRid));
+ IfFailRet(m_pStgdb->m_MiniMd.GetParamRecord(pmRid, &pParamRec));
+ if (iSeq == m_pStgdb->m_MiniMd.getSequenceOfParam(pParamRec))
+ {
+ // parameter has the sequence number matches what we are looking for
+ *pParamDef = TokenFromRid(pmRid, mdtParamDef);
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // HRESULT RegMeta::_FindParamOfMethod()
+
+//*******************************************************************************
+// Given the signature, return the token for signature.
+//*******************************************************************************
+HRESULT RegMeta::_GetTokenFromSig( // S_OK or error.
+ PCCOR_SIGNATURE pvSig, // [IN] Signature to define.
+ ULONG cbSig, // [IN] Size of signature data.
+ mdSignature *pmsig) // [OUT] returned signature token.
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(pmsig);
+
+ if (CheckDups(MDDupSignature))
+ {
+ hr = ImportHelper::FindStandAloneSig(&(m_pStgdb->m_MiniMd), pvSig, cbSig, pmsig);
+ if (SUCCEEDED(hr))
+ {
+ if (IsENCOn())
+ return S_OK;
+ else
+ return META_S_DUPLICATE;
+ }
+ else if (hr != CLDB_E_RECORD_NOTFOUND)
+ IfFailGo(hr);
+ }
+
+ // Create a new record.
+ StandAloneSigRec *pSigRec;
+ RID iSigRec;
+
+ IfFailGo(m_pStgdb->m_MiniMd.AddStandAloneSigRecord(&pSigRec, &iSigRec));
+
+ // Set output parameter.
+ *pmsig = TokenFromRid(iSigRec, mdtSignature);
+
+ // Save signature.
+ IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_StandAloneSig, StandAloneSigRec::COL_Signature,
+ pSigRec, pvSig, cbSig));
+ IfFailGo(UpdateENCLog(*pmsig));
+ErrExit:
+ return hr;
+} // RegMeta::_GetTokenFromSig
diff --git a/src/md/compiler/regmeta_vm.cpp b/src/md/compiler/regmeta_vm.cpp
new file mode 100644
index 0000000000..beebb08b5c
--- /dev/null
+++ b/src/md/compiler/regmeta_vm.cpp
@@ -0,0 +1,586 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+//*****************************************************************************
+
+//
+// RegMeta.cpp
+//
+// Implementation for meta data public interface methods for full version.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "regmeta.h"
+#include "metadata.h"
+#include "corerror.h"
+#include "mdutil.h"
+#include "rwutil.h"
+#include "mdlog.h"
+#include "importhelper.h"
+#include "filtermanager.h"
+#include "mdperf.h"
+#include "switches.h"
+#include "posterror.h"
+#include "stgio.h"
+#include "sstring.h"
+
+#include <metamodelrw.h>
+
+
+#ifndef FEATURE_CORECLR
+
+#include <metahost.h>
+
+// Pointer to the activated CLR interface provided by the shim.
+extern ICLRRuntimeInfo *g_pCLRRuntime;
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+
+#include "iappdomainsetup.h"
+
+// {27FFF232-A7A8-40dd-8D4A-734AD59FCD41}
+EXTERN_GUID(IID_IAppDomainSetup, 0x27FFF232, 0xA7A8, 0x40dd, 0x8D, 0x4A, 0x73, 0x4A, 0xD5, 0x9F, 0xCD, 0x41);
+
+#endif //FEATURE_METADATA_EMIT_ALL
+
+#endif // !FEATURE_CORECLR
+
+
+#define DEFINE_CUSTOM_NODUPCHECK 1
+#define DEFINE_CUSTOM_DUPCHECK 2
+#define SET_CUSTOM 3
+
+#if defined(_DEBUG) && defined(_TRACE_REMAPS)
+#define LOGGING
+#endif
+#include <log.h>
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4102)
+#endif
+
+//*****************************************************************************
+// Call this after initialization is complete.
+//*****************************************************************************
+HRESULT RegMeta::AddToCache()
+{
+#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
+ HRESULT hr = S_OK;
+
+ // The ref count must be > 0 before the module is published, else another
+ // thread could find, use, and release the module, causing it to be deleted
+ // before this thread gets a chance to addref.
+ _ASSERTE(GetRefCount() > 0);
+ // add this RegMeta to the loaded module list.
+ m_bCached = true;
+ IfFailGo(LOADEDMODULES::AddModuleToLoadedList(this));
+ErrExit:
+ if (FAILED(hr))
+ {
+ _ASSERTE(!LOADEDMODULES::IsEntryInList(this));
+ m_bCached = false;
+ }
+ return hr;
+#else //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
+ return S_OK;
+#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
+} // RegMeta::AddToCache
+
+
+//*****************************************************************************
+// Search the cached RegMetas for a given scope.
+//*****************************************************************************
+HRESULT RegMeta::FindCachedReadOnlyEntry(
+ LPCWSTR szName, // Name of the desired file.
+ DWORD dwOpenFlags, // Flags the new file is opened with.
+ RegMeta **ppMeta) // Put found RegMeta here.
+{
+#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
+ return LOADEDMODULES::FindCachedReadOnlyEntry(szName, dwOpenFlags, ppMeta);
+#else //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
+ // No cache support in standalone version.
+ *ppMeta = NULL;
+ return S_FALSE;
+#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
+} // RegMeta::FindCachedReadOnlyEntry
+
+
+#ifdef FEATURE_METADATA_EMIT_ALL
+
+//*****************************************************************************
+// Helper function to startup the EE
+//
+// Notes:
+// This is called by code:RegMeta.DefineSecurityAttributeSet.
+//*****************************************************************************
+HRESULT RegMeta::StartupEE()
+{
+#ifdef FEATURE_CORECLR
+ UNREACHABLE_MSG_RET("About to CoCreateInstance! This code should not be "
+ "reachable or needs to be reimplemented for CoreCLR!");
+#else // !FEATURE_CORECLR
+
+ struct Param
+ {
+ RegMeta *pThis;
+ IUnknown *pSetup;
+ IAppDomainSetup *pDomainSetup;
+ bool fDoneStart;
+ HRESULT hr;
+ } param;
+ param.pThis = this;
+ param.pSetup = NULL;
+ param.pDomainSetup = NULL;
+ param.fDoneStart = false;
+ param.hr = S_OK;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ HRESULT hr = S_OK;
+
+ DWORD dwBuffer[1 + (MAX_LONGPATH+1) * sizeof(WCHAR) / sizeof(DWORD) + 1];
+ BSTR bstrDir = NULL;
+
+ // Create a hosting environment.
+ IfFailGo(g_pCLRRuntime->GetInterface(
+ CLSID_CorRuntimeHost,
+ IID_ICorRuntimeHost,
+ (void **)&pParam->pThis->m_pCorHost));
+
+ // Startup the runtime.
+ IfFailGo(pParam->pThis->m_pCorHost->Start());
+ pParam->fDoneStart = true;
+
+ // Create an AppDomain Setup so we can set the AppBase.
+ IfFailGo(pParam->pThis->m_pCorHost->CreateDomainSetup(&pParam->pSetup));
+
+ // Get the current directory (place it in a BSTR).
+ bstrDir = (BSTR)(dwBuffer + 1);
+ if ((dwBuffer[0] = (WszGetCurrentDirectory(MAX_LONGPATH + 1, bstrDir) * sizeof(WCHAR))))
+ {
+ // QI for the IAppDomainSetup interface.
+ IfFailGo(pParam->pSetup->QueryInterface(IID_IAppDomainSetup,
+ (void**)&pParam->pDomainSetup));
+
+ // Set the AppBase.
+ pParam->pDomainSetup->put_ApplicationBase(bstrDir);
+ }
+
+ // Create a new AppDomain.
+ IfFailGo(pParam->pThis->m_pCorHost->CreateDomainEx(W("Compilation Domain"),
+ pParam->pSetup,
+ NULL,
+ &pParam->pThis->m_pAppDomain));
+
+ // That's it, we're all set up.
+ _ASSERTE(pParam->pThis->m_pAppDomain != NULL);
+ pParam->pThis->m_fStartedEE = true;
+
+ ErrExit:
+ pParam->hr = hr;
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ _ASSERTE(!"Unexpected exception setting up hosting environment for security attributes");
+ param.hr = E_FAIL;
+ }
+ PAL_ENDTRY
+
+ // Cleanup temporary resources.
+ if (m_pAppDomain && FAILED(param.hr))
+ m_pAppDomain->Release();
+ if (param.pDomainSetup)
+ param.pDomainSetup->Release();
+ if (param.pSetup)
+ param.pSetup->Release();
+ if (param.fDoneStart && FAILED(param.hr))
+ m_pCorHost->Stop();
+ if (m_pCorHost && FAILED(param.hr))
+ m_pCorHost->Release();
+ return param.hr;
+#endif // FEATURE_CORECLR
+}
+
+#endif //FEATURE_METADATA_EMIT_ALL
+
+#ifdef FEATURE_METADATA_EMIT
+
+//*****************************************************************************
+// Persist a set of security custom attributes into a set of permission set
+// blobs on the same class or method.
+//
+// Notes:
+// Only in the full version because this is an emit operation.
+//*****************************************************************************
+HRESULT RegMeta::DefineSecurityAttributeSet(// Return code.
+ mdToken tkObj, // [IN] Class or method requiring security attributes.
+ COR_SECATTR rSecAttrs[], // [IN] Array of security attribute descriptions.
+ ULONG cSecAttrs, // [IN] Count of elements in above array.
+ ULONG *pulErrorAttr) // [OUT] On error, index of attribute causing problem.
+{
+#ifdef FEATURE_METADATA_EMIT_ALL
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ NewArrayHolder <CORSEC_ATTRSET> rAttrSets;
+ DWORD i;
+ mdPermission ps;
+ DWORD dwAction;
+ bool fProcessDeclarativeSecurityAtRuntime;
+
+ LOG((LOGMD, "RegMeta::DefineSecurityAttributeSet(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ tkObj, rSecAttrs, cSecAttrs, pulErrorAttr));
+ START_MD_PERF();
+ LOCKWRITE();
+
+ IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
+
+ rAttrSets = new (nothrow) CORSEC_ATTRSET[dclMaximumValue + 1];
+ if (rAttrSets == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto ErrExit;
+ }
+
+ memset(rAttrSets, 0, sizeof(CORSEC_ATTRSET) * (dclMaximumValue + 1));
+
+ // Initialize error index to indicate a general error.
+ if (pulErrorAttr)
+ *pulErrorAttr = cSecAttrs;
+
+ fProcessDeclarativeSecurityAtRuntime = true;
+
+ // See if we should default to old v1.0/v1.1 serialization behavior
+ if (m_OptionValue.m_MetadataVersion < MDVersion2)
+ fProcessDeclarativeSecurityAtRuntime = false;
+
+ // Startup the EE just once, no matter how many times we're called (this is
+ // better on performance and the EE falls over if we try a start-stop-start
+ // cycle anyway).
+ if (!m_fStartedEE && !fProcessDeclarativeSecurityAtRuntime)
+ {
+ IfFailGo(StartupEE());
+ }
+
+ // Group the security attributes by SecurityAction (thus creating an array of CORSEC_PERM's)
+ IfFailGo(GroupSecurityAttributesByAction(/*OUT*/rAttrSets, rSecAttrs, cSecAttrs, tkObj, pulErrorAttr, &m_pStgdb->m_MiniMd, NULL));
+
+ // Put appropriate data in the metadata
+ for (i = 0; i <= dclMaximumValue; i++)
+ {
+ NewArrayHolder <BYTE> pbBlob(NULL);
+ NewArrayHolder <BYTE> pbNonCasBlob(NULL);
+ DWORD cbBlob = 0;
+ DWORD cbNonCasBlob = 0;
+
+ rAttrSets[i].pImport = this;
+ rAttrSets[i].pAppDomain = m_pAppDomain;
+ if (rAttrSets[i].dwAttrCount == 0)
+ continue;
+ if (pulErrorAttr)
+ *pulErrorAttr = i;
+
+ if(fProcessDeclarativeSecurityAtRuntime)
+ {
+ // Put a serialized CORSEC_ATTRSET in the metadata
+ SIZE_T cbAttrSet = 0;
+ IfFailGo(AttributeSetToBlob(&rAttrSets[i], NULL, &cbAttrSet, this, i)); // count size required for buffer
+ if (!FitsIn<DWORD>(cbAttrSet))
+ {
+ hr = COR_E_OVERFLOW;
+ goto ErrExit;
+ }
+ cbBlob = static_cast<DWORD>(cbAttrSet);
+
+ pbBlob = new (nothrow) BYTE[cbBlob]; // allocate buffer
+ if (pbBlob == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto ErrExit;
+ }
+
+ IfFailGo(AttributeSetToBlob(&rAttrSets[i], pbBlob, NULL, this, i)); // serialize into the buffer
+ IfFailGo(_DefinePermissionSet(rAttrSets[i].tkObj, rAttrSets[i].dwAction, pbBlob, cbBlob, &ps)); // put it in metadata
+ }
+ else
+ {
+ // Now translate the sets of security attributes into a real permission
+ // set and convert this to a serialized Xml blob. We may possibly end up
+ // with two sets as the result of splitting CAS and non-CAS permissions
+ // into separate sets.
+ hr = TranslateSecurityAttributes(&rAttrSets[i], &pbBlob, &cbBlob, &pbNonCasBlob, &cbNonCasBlob, pulErrorAttr);
+ IfFailGo(hr);
+
+ // Persist the permission set blob into the metadata. For empty CAS
+ // blobs this is only done if the corresponding non-CAS blob is empty
+ if (cbBlob || !cbNonCasBlob)
+ IfFailGo(_DefinePermissionSet(rAttrSets[i].tkObj, rAttrSets[i].dwAction, pbBlob, cbBlob, &ps));
+
+ if (pbNonCasBlob)
+ {
+ // Map the SecurityAction to a special non-CAS action so this
+ // blob will have its own entry in the metadata
+ switch (rAttrSets[i].dwAction)
+ {
+ case dclDemand:
+ dwAction = dclNonCasDemand;
+ break;
+ case dclLinktimeCheck:
+ dwAction = dclNonCasLinkDemand;
+ break;
+ case dclInheritanceCheck:
+ dwAction = dclNonCasInheritance;
+ break;
+ default:
+ PostError(CORSECATTR_E_BAD_NONCAS);
+ IfFailGo(CORSECATTR_E_BAD_NONCAS);
+ }
+
+ // Persist to metadata
+ IfFailGo(_DefinePermissionSet(rAttrSets[i].tkObj,
+ dwAction,
+ pbNonCasBlob,
+ cbNonCasBlob,
+ &ps));
+ }
+ }
+ }
+
+ErrExit:
+ STOP_MD_PERF(DefineSecurityAttributeSet);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return (hr);
+#else //!FEATURE_METADATA_EMIT_ALL
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT_ALL
+} // RegMeta::DefineSecurityAttributeSet
+
+#endif //FEATURE_METADATA_EMIT
+
+
+//*****************************************************************************
+// Implementation of IMetaDataImport::ResolveTypeRef to resolve a typeref across scopes.
+//
+// Arguments:
+// tr - typeref within this scope to resolve
+// riid - interface on ppIScope to support
+// ppIScope - out-parameter to get metadata scope for typedef (*ptd)
+// ptd - out-parameter to get typedef that the ref resolves to.
+//
+// Notes:
+// TypeDefs define a type within a scope. TypeRefs refer to type-defs in other scopes
+// and allow you to import a type from another scope. This function attempts to determine
+// which type-def a type-ref points to.
+//
+// This resolve (type-ref, this cope) --> (type-def=*ptd, other scope=*ppIScope)
+//
+// However, this resolution requires knowing what modules have been loaded, which is not decided
+// until runtime via loader / fusion policy. Thus this interface can't possibly be correct since
+// it doesn't have that knowledge. Furthermore, when inspecting metadata from another process
+// (such as a debugger inspecting the debuggee's metadata), this API can be truly misleading.
+//
+// This API usage should be avoided.
+//
+//*****************************************************************************
+STDMETHODIMP
+RegMeta::ResolveTypeRef(
+ mdTypeRef tr,
+ REFIID riid,
+ IUnknown ** ppIScope,
+ mdTypeDef * ptd)
+{
+#ifdef FEATURE_METADATA_IN_VM
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ TypeRefRec * pTypeRefRec;
+ WCHAR wzNameSpace[_MAX_PATH];
+ CMiniMdRW * pMiniMd = NULL;
+ WCHAR rcModule[_MAX_PATH];
+
+ LOG((LOGMD, "{%08x} RegMeta::ResolveTypeRef(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ this, tr, riid, ppIScope, ptd));
+
+ START_MD_PERF();
+ LOCKREAD();
+
+ pMiniMd = &(m_pStgdb->m_MiniMd);
+
+ _ASSERTE((ppIScope != NULL) && (ptd != NULL));
+
+ // Init the output values.
+ *ppIScope = NULL;
+ *ptd = 0;
+
+ if (IsNilToken(tr))
+ {
+ if (ptd != NULL)
+ {
+ *ptd = mdTypeDefNil;
+ }
+
+ if (ppIScope != NULL)
+ {
+ *ppIScope = NULL;
+ }
+
+ STOP_MD_PERF(ResolveTypeRef);
+ hr = E_INVALIDARG;
+ goto ErrExit;
+ }
+
+ if (TypeFromToken(tr) == mdtTypeDef)
+ {
+ // Shortcut when we receive a TypeDef token
+ *ptd = tr;
+ STOP_MD_PERF(ResolveTypeRef);
+ hr = this->QueryInterface(riid, (void **)ppIScope);
+ goto ErrExit;
+ }
+
+ // Get the class ref row.
+ _ASSERTE(TypeFromToken(tr) == mdtTypeRef);
+
+ IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pTypeRefRec));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, wzNameSpace, lengthof(wzNameSpace), NULL));
+ if (hr != NOERROR)
+ {
+ _ASSERTE(hr == CLDB_S_TRUNCATION);
+ // Truncate the namespace string
+ wzNameSpace[lengthof(wzNameSpace) - 1] = 0;
+ }
+
+ //***********************
+ // before we go off to CORPATH, check the loaded modules!
+ //***********************
+ if (LOADEDMODULES::ResolveTypeRefWithLoadedModules(
+ tr,
+ this,
+ pMiniMd,
+ riid,
+ ppIScope,
+ ptd) == NOERROR)
+ {
+ // Done!! We found one match among the loaded modules.
+ goto ErrExit;
+ }
+
+#ifndef FEATURE_CORECLR
+ wcscpy_s(rcModule, _MAX_PATH, wzNameSpace);
+
+ //******************
+ // Try to find the module on CORPATH
+ //******************
+
+ if ((wcsncmp(rcModule, W("System."), 16) != 0) &&
+ (wcsncmp(rcModule, W("System/"), 16) != 0))
+ {
+ // only go through regular CORPATH lookup by fully qualified class name when
+ // it is not System.*
+ hr = CORPATHService::GetClassFromCORPath(
+ rcModule,
+ tr,
+ pMiniMd,
+ riid,
+ ppIScope,
+ ptd);
+ }
+ else
+ {
+ // force it to look for System.* in mscorlib.dll
+ hr = S_FALSE;
+ }
+
+ if (hr == S_FALSE)
+ {
+ LPWSTR szTmp;
+ WszSearchPath(
+ NULL,
+ W("mscorlib.dll"),
+ NULL,
+ sizeof(rcModule) / sizeof(rcModule[0]),
+ rcModule,
+ &szTmp);
+
+ //*******************
+ // Last desperate try!!
+ //*******************
+
+ // Use the file name "mscorlib:
+ IfFailGo(CORPATHService::FindTypeDef(
+ rcModule,
+ tr,
+ pMiniMd,
+ riid,
+ ppIScope,
+ ptd));
+ if (hr == S_FALSE)
+ {
+ IfFailGo(META_E_CANNOTRESOLVETYPEREF);
+ }
+ }
+#else //FEATURE_CORECLR
+ IfFailGo(META_E_CANNOTRESOLVETYPEREF);
+#endif //FEATURE_CORECLR
+
+ErrExit:
+ STOP_MD_PERF(ResolveTypeRef);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_IN_VM
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_IN_VM
+} // RegMeta::ResolveTypeRef
+
+
+
+// Full version handles metadata caching, which Release() needs to coordinate with.
+// Thus Release() is in a satellite lib.
+ULONG RegMeta::Release()
+{
+ // This is called during cleanup. We can not fail this call by probing.
+ // As long as we make sure the cleanup does not use too much space through
+ // BEGIN_CLEANUP_ENTRYPOINT, we are OK.
+ CONTRACT_VIOLATION (SOToleranceViolation);
+ BEGIN_CLEANUP_ENTRYPOINT;
+
+#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
+ _ASSERTE(!m_bCached || LOADEDMODULES::IsEntryInList(this));
+#else
+ _ASSERTE(!m_bCached);
+#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
+ BOOL bCached = m_bCached;
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ // NOTE: 'this' may be unsafe after this point, if the module is cached, and
+ // another thread finds the module in the cache, releases it, and deletes it
+ // before we get around to deleting it. (That's why we must make a local copy
+ // of m_bCached.)
+ // If no references left...
+ if (cRef == 0)
+ {
+ if (!bCached)
+ { // If the module is not (was not) cached, no other thread can have
+ // discovered the module, so this thread can now safely delete it.
+ delete this;
+ }
+#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
+ else if (LOADEDMODULES::RemoveModuleFromLoadedList(this))
+ { // If the module was cached, RemoveModuleFromLoadedList() will try to
+ // safely un-publish the module, and if it succeeds, no other thread
+ // has (or will) discover the module, so this thread can delete it.
+ m_bCached = false;
+ delete this;
+ }
+#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
+ }
+ END_CLEANUP_ENTRYPOINT
+
+ return cRef;
+} // RegMeta::Release
diff --git a/src/md/compiler/stdafx.cpp b/src/md/compiler/stdafx.cpp
new file mode 100644
index 0000000000..ff341e19a7
--- /dev/null
+++ b/src/md/compiler/stdafx.cpp
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// stdafx.cpp
+//
+
+//
+// Precompiled headers.
+//
+//*****************************************************************************
+#include "stdafx.h"
diff --git a/src/md/compiler/stdafx.h b/src/md/compiler/stdafx.h
new file mode 100644
index 0000000000..520fe44d05
--- /dev/null
+++ b/src/md/compiler/stdafx.h
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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 "nsutilpriv.h"
+
+#include "utsem.h"
+
+#endif // __STDAFX_H_
diff --git a/src/md/compiler/verifylayouts.cpp b/src/md/compiler/verifylayouts.cpp
new file mode 100644
index 0000000000..bbf7b94b14
--- /dev/null
+++ b/src/md/compiler/verifylayouts.cpp
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// VerifyLayouts.cpp
+//
+
+//
+// Make sure that layouts of MD data structures doesn't change accidentally
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "verifylayouts.h"
diff --git a/src/md/compiler/wks/.gitmirror b/src/md/compiler/wks/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/compiler/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/compiler/wks/CMakeLists.txt b/src/md/compiler/wks/CMakeLists.txt
new file mode 100644
index 0000000000..6bf6c80868
--- /dev/null
+++ b/src/md/compiler/wks/CMakeLists.txt
@@ -0,0 +1,4 @@
+include(../../md_wks.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDCOMPILER_SOURCES)
+add_library_clr(mdcompiler_wks ${MDCOMPILER_SOURCES}) \ No newline at end of file
diff --git a/src/md/compiler/wks/MDCompiler_wks.nativeproj b/src/md/compiler/wks/MDCompiler_wks.nativeproj
new file mode 100644
index 0000000000..8457d87c19
--- /dev/null
+++ b/src/md/compiler/wks/MDCompiler_wks.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>
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\Compiler\Compiler.settings.targets" />
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>MDCompiler_wks</OutputName>
+ </PropertyGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/compressedinteger.h b/src/md/compressedinteger.h
new file mode 100644
index 0000000000..29a4f83dba
--- /dev/null
+++ b/src/md/compressedinteger.h
@@ -0,0 +1,87 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: CompressedInteger.h
+//
+
+//
+// Class code:MetaData::CompressedInteger provides secure access to a compressed integer (as defined in CLI
+// ECMA specification). The integer is compressed into 1, 2 or 4 bytes. See code:CompressedInteger#Format
+// for full format description.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// This class provides secure access to a compressed integer (as defined in CLI ECMA specification). The
+// integer is compressed into 1, 2 or 4 bytes. See code:CompressedInteger#Format for full format description.
+//
+class CompressedInteger
+{
+// #Format
+//
+// The format/encoding of compressed integer is (defined in ECMA CLI specification):
+// The encoding is 1, 2 or 4 bytes long and depends on the first byte value. If the first byte is (binary):
+// * 0xxx xxxx ... then it's 1 byte long and the value is 0xxx xxxx.
+// * 10xx xxxx ... then it's 2 bytes long and the value is 00xx xxxx yyyy yyyy, where yyyy yyyy is the
+// second byte. Though values smaller than code:const_Max1Byte are technically invalid
+// when encoded with 2 bytes.
+// * 110x xxxx ... then it's 4 bytes long and the value is 000x xxxx yyyy yyyy zzzz zzzz wwww wwww, where
+// yyyy yyyy is the 2nd byte, zzzz zzzz is the 3rd byte and wwww wwww is the 4th byte.
+// Though values smaller than code:const_Max2Bytes are technically invalid when encoded
+// with 4 bytes.
+// * 111x xxxx ... then it's invalid encoding.
+//
+// Note: Some encodings are invalid, but CLR accepts them (see code:DataBlob::GetCompressedU),
+// e.g. 1000 0000 0000 0000 (0x8000) encodes 0 while correct/valid encoding is 0000 0000 (0x00).
+//
+private:
+ // This class has only static methods and shouldn't be instantiated.
+ CompressedInteger() {}
+
+public:
+ static const UINT32 const_MaxEncodingSize = 4;
+
+ static const UINT32 const_Max1Byte = 0x7f;
+ static const UINT32 const_Max2Bytes = 0x3fff;
+ static const UINT32 const_Max4Bytes = 0x1fffffff;
+
+ static const UINT32 const_Max = const_Max4Bytes;
+
+public:
+ //
+ // Operations
+ //
+
+ // Returns TRUE if the value (nValue) fits into 1-byte, 2-bytes or 4-bytes encoding and fills
+ // *pcbEncodingSize with 1, 2 or 4.
+ // Returns FALSE if the value cannot be encoded as compressed integer, doesn't fill *pcbEncodingSize
+ // then.
+ __checkReturn
+ __success(return)
+ static inline BOOL GetEncodingSize(
+ UINT32 nValue,
+ __out UINT32 *pcbEncodingSize);
+ // Returns TRUE if the value (nValue) fits into 1-byte, 2-bytes or 4-bytes encoding and fills
+ // *pcbEncodingSize with 1, 2 or 4 and *pnEncodedValue with the encoded value.
+ // Returns FALSE if the value cannot be encoded as compressed integer, doesn't fill *pcbEncodingSize
+ // nor *pnEncodedValue then.
+ __success(return)
+ static inline BOOL Encode(
+ UINT32 nValue,
+ __out UINT32 *pnEncodedValue,
+ __out UINT32 *pcbEncodingSize);
+
+}; // class CompressedInteger
+
+}; // namespace MetaData
+
+#include "compressedinteger.inl"
diff --git a/src/md/compressedinteger.inl b/src/md/compressedinteger.inl
new file mode 100644
index 0000000000..94fabfda5c
--- /dev/null
+++ b/src/md/compressedinteger.inl
@@ -0,0 +1,99 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: CompressedInteger.inl
+//
+
+//
+// Class code:MetaData::CompressedInteger provides secure access to a compressed integer (as defined in CLI
+// ECMA specification). The integer is compressed into 1, 2 or 4 bytes. See code:CompressedInteger#Format
+// for full format description.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "compressedinteger.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// Returns TRUE if the value (nValue) fits into 1-byte, 2-bytes or 4-bytes encoding and fills
+// *pcbEncodingSize with 1, 2 or 4.
+// Returns FALSE if the value cannot be encoded as compressed integer, doesn't fill *pcbEncodingSize then.
+//
+__checkReturn
+//static
+inline
+BOOL
+CompressedInteger::GetEncodingSize(
+ UINT32 nValue,
+ __out UINT32 *pcbEncodingSize)
+{
+ // Does it fit into 1-byte encoding?
+ if (nValue <= const_Max1Byte)
+ { // The value fits into 1 byte (binary format 0xxx xxxx)
+ *pcbEncodingSize = 1;
+ return TRUE;
+ }
+ // Does it fit into 2-bytes encoding?
+ if (nValue <= const_Max2Bytes)
+ { // The value fits into 2 bytes (binary format 10xx xxxx yyyy yyyy)
+ *pcbEncodingSize = 2;
+ return TRUE;
+ }
+ // Does it fit into 4-bytes encoding?
+ if (nValue <= const_Max4Bytes)
+ { // The value fits into 4 bytes (binary format 110x xxxx yyyy yyyy zzzz zzzz wwww wwww)
+ *pcbEncodingSize = 4;
+ return TRUE;
+ }
+ // The value cannot be encoded as compressed integer
+ return FALSE;
+} // CompressedInteger::GetEncodingSize
+
+// --------------------------------------------------------------------------------------
+//
+// Returns TRUE if the value (nValue) fits into 1-byte, 2-bytes or 4-bytes encoding and fills
+// *pcbEncodingSize with 1, 2 or 4 and *pnEncodedValue with the encoded value.
+// Returns FALSE if the value cannot be encoded as compressed integer, doesn't fill *pcbEncodingSize
+// nor *pnEncodedValue then.
+//
+__checkReturn
+//static
+inline
+BOOL
+CompressedInteger::Encode(
+ UINT32 nValue,
+ __out UINT32 *pnEncodedValue,
+ __out UINT32 *pcbEncodingSize)
+{
+ // Does it fit into 1-byte encoding?
+ if (nValue <= const_Max1Byte)
+ { // The value fits into 1 byte (binary format 0xxx xxxx)
+ *pnEncodedValue = nValue;
+ *pcbEncodingSize = 1;
+ return TRUE;
+ }
+ // Does it fit into 2-bytes encoding?
+ if (nValue <= const_Max2Bytes)
+ { // The value fits into 2 bytes (binary format 10xx xxxx yyyy yyyy)
+ *pnEncodedValue = 0x8000 | nValue;
+ *pcbEncodingSize = 2;
+ return TRUE;
+ }
+ // Does it fit into 4-bytes encoding?
+ if (nValue <= const_Max4Bytes)
+ { // The value fits into 4 bytes (binary format 110x xxxx yyyy yyyy zzzz zzzz wwww wwww)
+ *pnEncodedValue = 0xC0000000 | nValue;
+ *pcbEncodingSize = 4;
+ return TRUE;
+ }
+ // The value cannot be encoded as compressed integer
+ return FALSE;
+} // CompressedInteger::Encode
+
+}; // namespace MetaData
diff --git a/src/md/datablob.h b/src/md/datablob.h
new file mode 100644
index 0000000000..5fcbc4370d
--- /dev/null
+++ b/src/md/datablob.h
@@ -0,0 +1,231 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: DataBlob.h
+//
+
+//
+// Class code:MetaData::DataBlob provides secure access to a block of memory from MetaData (i.e. with fixed
+// endianess).
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// This class provides secure access to a block of memory.
+//
+class DataBlob
+{
+private:
+ //
+ // Private data
+ //
+
+ // The memory block of size code:m_cbSize. Can be non-NULL even if code:m_cbSize is 0.
+ __field_bcount(m_cbSize)
+ BYTE *m_pbData;
+ // Size of the memory block starting at code:m_pbData. If it is 0, then value of code:m_pbData can be
+ // anything (incl. NULL).
+ UINT32 m_cbSize;
+
+public:
+ //
+ // Initialization
+ //
+
+ // Creates empty memory block.
+ inline DataBlob();
+ // Creates memory block (pbData, of size cbSize).
+ inline DataBlob(
+ __in_bcount_opt(cbSize) BYTE *pbData,
+ UINT32 cbSize);
+ // Creates memory block copy.
+ inline DataBlob(
+ const DataBlob &source);
+ // Initializes memory block to empty data. The object could be already initialzied.
+ inline void Clear();
+ // Initializes memory block to data (pbData, of size cbSize). The object should be empty before.
+ inline void Init(
+ __in_bcount_opt(cbSize) BYTE *pbData,
+ UINT32 cbSize);
+
+ //
+ // Getters
+ //
+
+ //#PeekUx_Functions
+ // Reads the U1/U2/U4/U8 from the data blob without skipping the read data.
+ // Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then.
+ // Returns TRUE otherwise, fills *pnValue, but doesn't move the memory block (doesn't skip the read
+ // data).
+ __checkReturn __success(return) inline BOOL PeekU1(__out BYTE *pnValue) const;
+ __checkReturn __success(return) inline BOOL PeekU2(__out UINT16 *pnValue) const;
+ __checkReturn __success(return) inline BOOL PeekU4(__out UINT32 *pnValue) const;
+ __checkReturn __success(return) inline BOOL PeekU8(__out UINT64 *pnValue) const;
+
+ //#GetUx_Functions
+ // Reads the U1/U2/U4/U8 from the data blob and skips the read data.
+ // Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then.
+ // Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data.
+ __checkReturn __success(return) inline BOOL GetU1(__out BYTE *pnValue);
+ __checkReturn __success(return) inline BOOL GetU2(__out UINT16 *pnValue);
+ __checkReturn __success(return) inline BOOL GetU4(__out UINT32 *pnValue);
+ __checkReturn __success(return) inline BOOL GetU8(__out UINT64 *pnValue);
+
+ // Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size
+ // in *pcbCompressedValueSize) from the data blob without skipping the read data.
+ // Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte
+ // 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value
+ // *pcbCompressedValueSize then.
+ // Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4), but
+ // doesn't move the memory block (doesn't skip the read data).
+ __checkReturn
+ __success(return)
+ inline BOOL PeekCompressedU(
+ __out UINT32 *pnValue,
+ __out UINT32 *pcbCompressedValueSize);
+ // Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) from the data blob
+ // and skips the read data.
+ // Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte
+ // 111? ????), doesn't initialize the value *pnValue then.
+ // Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data.
+ __checkReturn
+ __success(return)
+ inline BOOL GetCompressedU(__out UINT32 *pnValue);
+ // Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size
+ // in *pcbCompressedValueSize) from the data blob and skips the read data.
+ // Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte
+ // 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value
+ // *pcbCompressedValueSize then.
+ // Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4) and moves
+ // the memory block behind the read data.
+ __checkReturn
+ __success(return)
+ inline BOOL GetCompressedU(
+ __out UINT32 *pnValue,
+ __out UINT32 *pcbCompressedValueSize);
+
+ // Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns the data as
+ // *pData).
+ // Returns FALSE if there's not enough data in the blob, clears *pData then.
+ // Returns TRUE otherwise, fills *pData with the "read" data and moves the memory block behind the
+ // "read" data.
+ __checkReturn
+ __success(return)
+ inline BOOL GetDataOfSize(
+ UINT32 cbDataSize,
+ __out DataBlob *pData);
+
+ // Checks if there's at least cbDataSize bytes in the represented memory block.
+ // Returns TRUE if there's >= cbDataSize bytes. Returns FALSE otherwise.
+ inline BOOL ContainsData(UINT32 cbDataSize) const
+ { return cbDataSize <= m_cbSize; }
+/*
+ // Checks if there's at least cbDataSize1 + cbDataSize2 bytes in the represented memory block (and that
+ // the sum doesn't overflow).
+ // Returns TRUE if there's >= cbDataSize1 + cbDataSize2 bytes.
+ // Returns FALSE otherwise and if cbDataSize1 + cbDataSize2 overflows.
+ inline BOOL ContainsData_2Parts(
+ UINT32 cbDataSize1,
+ UINT32 cbDataSize2) const;
+ // Checks if there's valid compressed integer (1, 2 or 4 bytes of format
+ // code:DataBlob#CompressedIntegerFormat) in the data blob.
+ // Returns:
+ // * 0 ... if there's valid compressed integer.
+ // * 1 ... if there's not enough data in the data blob, but the encoding is correct.
+ // * 2 ... if the integer encoding is invalid (starts with byte 111x xxxx byte), but there are at least
+ // 4 bytes in the data blob left.
+ // * 3 ... if there's not enough data in the data blob and the integer encoding is invalid (starts with
+ // 111x xxx byte).
+ inline int ValidateCompressedU() const;
+*/
+
+ // Returns TRUE if the represented memory is empty.
+ inline BOOL IsEmpty() const
+ { return (m_cbSize == 0); }
+ // Gets pointer to the represented data buffer (can be random pointer if size of the data is 0).
+ // Note: Should be used exceptionally. Try to use other operations instead.
+ inline BYTE *GetDataPointer()
+ { return m_pbData; }
+ // Gets pointer to the represented data buffer (can be random pointer if size of the data is 0).
+ // Note: Should be used exceptionally. Try to use other operations instead.
+ inline const BYTE *GetDataPointer() const
+ { return m_pbData; }
+ // Gets pointer right behind the represented data buffer (can be random pointer if size of the data is
+ // 0).
+ inline const BYTE *GetDataPointerBehind() const
+ { return ((m_cbSize == 0) ? NULL : (m_pbData + m_cbSize)); }
+ // Gets the size of represented memory.
+ inline UINT32 GetSize() const
+ { return m_cbSize; }
+ //BOOL SkipBytes(UINT32 cbSize);
+
+public:
+ //
+ // Operations
+ //
+
+ // Truncates the buffer to exact size (cbSize).
+ // Returns FALSE if there's less than cbSize data represented.
+ // Returns TRUE otherwise and truncates the represented data size to cbSize.
+ __checkReturn
+ __success(return)
+ inline BOOL TruncateToExactSize(UINT32 cbSize);
+ // Truncates the buffer by size (cbSize).
+ // Returns FALSE if there's less than cbSize data represented.
+ // Returns TRUE otherwise and truncates the represented data size by cbSize.
+ __checkReturn
+ __success(return)
+ inline BOOL TruncateBySize(UINT32 cbSize);
+
+#ifdef _DEBUG
+ // Returns U1 value at offset (nOffset). Fires an assert if the offset is behind the end of represented
+ // data.
+ inline BYTE Debug_GetByteAtOffset(UINT32 nOffset) const;
+#endif //_DEBUG
+
+public:
+ //
+ // Setters
+ //
+
+ // Writes compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) to the data blob
+ // and skips the written data.
+ // Returns FALSE if there's not enough data in the blob or the value cannot be encoded as compressed
+ // integer (bigger than code:CompressedInteger::const_Max).
+ // Returns TRUE on success and moves the memory block behind the written data.
+ __checkReturn
+ __success(return)
+ inline BOOL StoreCompressedU(UINT32 nValue);
+
+ // Writes data from *pSource to the data blob and skips the written data.
+ // Returns FALSE if there's not enough data in the blob.
+ // Returns TRUE on success and moves memory block behind the written data.
+ __checkReturn
+ __success(return)
+ inline BOOL StoreData(__in const DataBlob *pSource);
+
+private:
+ //
+ // Helpers
+ //
+
+ // Skips cbSize bytes in the represented memory block. The caller is responsible for making sure that
+ // the represented memory block contains at least cbSize bytes, otherwise there will be a security
+ // issue.
+ // Should be used only internally, never call it from outside of this class.
+ inline void SkipBytes_InternalInsecure(UINT32 cbSize);
+
+}; // class DataBlob
+
+}; // namespace MetaData
+
+#include "datablob.inl"
diff --git a/src/md/datablob.inl b/src/md/datablob.inl
new file mode 100644
index 0000000000..5589a083a5
--- /dev/null
+++ b/src/md/datablob.inl
@@ -0,0 +1,581 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: DataBlob.inl
+//
+
+//
+// Class code:MetaData::DataBlob provides secure access to a block of memory from MetaData (i.e. with fixed
+// endianess).
+//
+// ======================================================================================
+
+#pragma once
+
+#include "datablob.h"
+#include "compressedinteger.h"
+
+#include "debug_metadata.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// Creates empty memory block.
+//
+inline
+DataBlob::DataBlob()
+{
+ Clear();
+} // DataBlob::DataBlob
+
+// --------------------------------------------------------------------------------------
+//
+// Creates memory block (pbData, of size cbSize).
+//
+inline
+DataBlob::DataBlob(
+ __in_bcount(cbSize) BYTE *pbData,
+ UINT32 cbSize)
+{
+ m_pbData = pbData;
+ m_cbSize = cbSize;
+} // DataBlob::DataBlob
+
+// --------------------------------------------------------------------------------------
+//
+// Creates memory block copy.
+//
+inline
+DataBlob::DataBlob(
+ const DataBlob &source)
+{
+ m_pbData = source.m_pbData;
+ m_cbSize = source.m_cbSize;
+} // DataBlob::DataBlob
+
+#ifdef _WIN64
+ #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00dbaadf00d)
+#else //!_WIN64
+ #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00d)
+#endif //!_WIN64
+
+// --------------------------------------------------------------------------------------
+//
+// Initializes memory block to empty data. The object could be already initialzied.
+//
+inline
+void
+DataBlob::Clear()
+{
+ m_cbSize = 0;
+ // For debugging purposes let's put invalid non-NULL pointer here
+ INDEBUG_MD(m_pbData = const_pbBadFood);
+} // DataBlob::Clear
+
+#undef const_pbBadFood
+
+// --------------------------------------------------------------------------------------
+//
+// Initializes memory block to data (pbData, of size cbSize). The object should be empty before.
+//
+inline
+void
+DataBlob::Init(
+ __in_bcount(cbSize) BYTE *pbData,
+ UINT32 cbSize)
+{
+ m_pbData = pbData;
+ m_cbSize = cbSize;
+} // DataBlob::Init
+
+// --------------------------------------------------------------------------------------
+//
+// #PeekUx_Functions
+//
+// Reads the U1/U2/U4/U8 from the data blob without skipping the read data.
+// Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then.
+// Returns TRUE otherwise, fills *pnValue, but doesn't move the memory block (doesn't skip the read data).
+//
+
+// --------------------------------------------------------------------------------------
+//
+// See code:#PeekUx_Functions above.
+//
+__checkReturn
+_Success_(return)
+inline
+BOOL
+DataBlob::PeekU1(__out BYTE *pnValue) const
+{
+ if (m_cbSize < sizeof(BYTE))
+ {
+ return FALSE;
+ }
+ *pnValue = *m_pbData;
+ return TRUE;
+} // DataBlob::PeekU1
+
+// --------------------------------------------------------------------------------------
+//
+// See code:#PeekUx_Functions above.
+//
+__checkReturn
+_Success_(return)
+inline
+BOOL
+DataBlob::PeekU2(__out UINT16 *pnValue) const
+{
+ if (m_cbSize < sizeof(UINT16))
+ {
+ return FALSE;
+ }
+ *pnValue = GET_UNALIGNED_VAL16(m_pbData);
+ return TRUE;
+} // DataBlob::PeekU2
+
+// --------------------------------------------------------------------------------------
+//
+// See code:#PeekUx_Functions above.
+//
+__checkReturn
+_Success_(return)
+inline
+BOOL
+DataBlob::PeekU4(__out UINT32 *pnValue) const
+{
+ if (m_cbSize < sizeof(UINT32))
+ {
+ return FALSE;
+ }
+ *pnValue = GET_UNALIGNED_VAL32(m_pbData);
+ return TRUE;
+} // DataBlob::PeekU4
+
+// --------------------------------------------------------------------------------------
+//
+// See code:#PeekUx_Functions above.
+//
+__checkReturn
+_Success_(return)
+inline
+BOOL
+DataBlob::PeekU8(__out UINT64 *pnValue) const
+{
+ if (m_cbSize < sizeof(UINT64))
+ {
+ return FALSE;
+ }
+ *pnValue = GET_UNALIGNED_VAL64(m_pbData);
+ return TRUE;
+} // DataBlob::PeekU8
+
+// --------------------------------------------------------------------------------------
+//
+// #GetUx_Functions
+//
+// Reads the U1/U2/U4/U8 from the data blob and skips the read data.
+// Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then.
+// Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data.
+//
+
+// --------------------------------------------------------------------------------------
+//
+// See code:#GetUx_Functions above.
+//
+__checkReturn
+_Success_(return)
+inline
+BOOL
+DataBlob::GetU1(__out BYTE *pnValue)
+{
+ if (m_cbSize < sizeof(BYTE))
+ {
+ return FALSE;
+ }
+ *pnValue = *m_pbData;
+ SkipBytes_InternalInsecure(sizeof(BYTE));
+ return TRUE;
+} // DataBlob::GetU1
+
+// --------------------------------------------------------------------------------------
+//
+// See code:#GetUx_Functions above.
+//
+__checkReturn
+_Success_(return)
+inline
+BOOL
+DataBlob::GetU2(__out UINT16 *pnValue)
+{
+ if (m_cbSize < sizeof(UINT16))
+ {
+ return FALSE;
+ }
+ *pnValue = GET_UNALIGNED_VAL16(m_pbData);
+ SkipBytes_InternalInsecure(sizeof(UINT16));
+ return TRUE;
+} // DataBlob::GetU2
+
+// --------------------------------------------------------------------------------------
+//
+// See code:#GetUx_Functions above.
+//
+__checkReturn
+_Success_(return)
+inline
+BOOL
+DataBlob::GetU4(__out UINT32 *pnValue)
+{
+ if (m_cbSize < sizeof(UINT32))
+ {
+ return FALSE;
+ }
+ *pnValue = GET_UNALIGNED_VAL32(m_pbData);
+ SkipBytes_InternalInsecure(sizeof(UINT32));
+ return TRUE;
+} // DataBlob::GetU4
+
+// --------------------------------------------------------------------------------------
+//
+// See code:#GetUx_Functions above.
+//
+__checkReturn
+_Success_(return)
+inline
+BOOL
+DataBlob::GetU8(__out UINT64 *pnValue)
+{
+ if (m_cbSize < sizeof(UINT64))
+ {
+ return FALSE;
+ }
+ *pnValue = GET_UNALIGNED_VAL64(m_pbData);
+ SkipBytes_InternalInsecure(sizeof(UINT64));
+ return TRUE;
+} // DataBlob::GetU8
+
+// --------------------------------------------------------------------------------------
+//
+// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) from the data blob
+// and skips the read data.
+// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte
+// 111? ????), doesn't initialize the value *pnValue then.
+// Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data.
+//
+__checkReturn
+inline
+BOOL
+DataBlob::GetCompressedU(__out UINT32 *pnValue)
+{
+ UINT32 cbCompressedValueSize_Ignore;
+ return GetCompressedU(pnValue, &cbCompressedValueSize_Ignore);
+} // DataBlob::GetCompressedU
+
+// --------------------------------------------------------------------------------------
+//
+// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size
+// in *pcbCompressedValueSize) from the data blob without skipping the read data.
+// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte
+// 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value
+// *pcbCompressedValueSize then.
+// Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4), but
+// doesn't move the memory block (doesn't skip the read data).
+//
+__checkReturn
+_Success_(return)
+inline
+BOOL
+DataBlob::PeekCompressedU(
+ __out UINT32 *pnValue,
+ __out UINT32 *pcbCompressedValueSize)
+{
+ // This algorithm has to be in sync with code:CompressedInteger#Format encoding definition.
+ //
+ // Note that this algorithm accepts technically invalid encodings, e.g.
+ // encoding of value 0 is accepted as 0000 0000 (0x00, valid) and 1000 0000 0000 000 (0x8000, invalid).
+
+ // Is there at least 1 byte?
+ if (m_cbSize < 1)
+ { // The data blob is empty, there's not compressed integer stored
+ return FALSE;
+ }
+ if ((*m_pbData & 0x80) == 0x00)
+ { // 0??? ????
+ // The value is compressed into 1 byte
+ *pnValue = (UINT32)(*m_pbData);
+ *pcbCompressedValueSize = 1;
+ return TRUE;
+ }
+ // 1??? ????
+
+ if ((*m_pbData & 0x40) == 0x00)
+ { // 10?? ????
+ // The value is compressed into 2 bytes
+ if (m_cbSize < 2)
+ { // The data blob is too short and doesn't contain 2 bytes needed for storing compressed integer
+ return FALSE;
+ }
+ *pnValue =
+ ((*m_pbData & 0x3f) << 8) |
+ *(m_pbData + 1);
+ *pcbCompressedValueSize = 2;
+ return TRUE;
+ }
+ // 11?? ????
+
+ if ((*m_pbData & 0x20) == 0x00)
+ { // 110? ????
+ // The value is compressed into 4 bytes
+ if (m_cbSize < 4)
+ { // The data blob is too short and doesn't contain 4 bytes needed for storing compressed integer
+ return FALSE;
+ }
+ *pnValue =
+ ((*m_pbData & 0x1f) << 24) |
+ (*(m_pbData + 1) << 16) |
+ (*(m_pbData + 2) << 8) |
+ *(m_pbData + 3);
+ *pcbCompressedValueSize = 4;
+ return TRUE;
+ }
+ // 111? ????
+ // Invalid encoding of the compressed integer
+ return FALSE;
+} // DataBlob::PeekCompressedU
+
+// --------------------------------------------------------------------------------------
+//
+// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size
+// in *pcbCompressedValueSize) from the data blob and skips the read data.
+// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte
+// 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value
+// *pcbCompressedValueSize then.
+// Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4) and moves
+// the memory block behind the read data.
+//
+__checkReturn
+inline
+BOOL
+DataBlob::GetCompressedU(
+ __out UINT32 *pnValue,
+ __out UINT32 *pcbCompressedValueSize)
+{
+ // Read the compressed integer from withou skipping the read data
+ BOOL fReadResult = PeekCompressedU(
+ pnValue,
+ pcbCompressedValueSize);
+ // Was the compressed integer read?
+ if (fReadResult)
+ { // The compressed integer was read
+ // Skip the read data
+ SkipBytes_InternalInsecure(*pcbCompressedValueSize);
+ }
+ // Return the (original) read result
+ return fReadResult;
+} // DataBlob::GetCompressedU
+
+// --------------------------------------------------------------------------------------
+//
+// Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns the data as
+// *pData).
+// Returns FALSE if there's not enough data in the blob, clears *pData then.
+// Returns TRUE otherwise, fills *pData with the "read" data and moves the memory block behind the
+// "read" data.
+//
+__checkReturn
+inline
+BOOL
+DataBlob::GetDataOfSize(
+ UINT32 cbDataSize,
+ __out DataBlob *pData)
+{
+ if (m_cbSize < cbDataSize)
+ { // There's not enough data in the memory block
+ pData->Clear();
+ return FALSE;
+ }
+ // Fill the "read" data
+ pData->Init(m_pbData, cbDataSize);
+ SkipBytes_InternalInsecure(cbDataSize);
+ return TRUE;
+} // DataBlob::GetDataOfSize
+
+/*
+// --------------------------------------------------------------------------------------
+//
+// Checks if there's at least cbDataSize1 + cbDataSize2 bytes in the represented memory block (and that
+// the sum doesn't overflow).
+// Returns TRUE if there's >= cbDataSize1 + cbDataSize2 bytes.
+// Returns FALSE otherwise and if cbDataSize1 + cbDataSize2 overflows.
+//
+inline
+BOOL
+DataBlob::ContainsData_2Parts(
+ UINT32 cbDataSize1,
+ UINT32 cbDataSize2) const
+{
+ S_UINT32 cbDataSize = S_UINT32(cbDataSize1) + S_UITN32(cbDataSize2);
+ if (cbDataSize.IsOverflow())
+ {
+ return FALSE;
+ }
+ return (cbDataSize.Value() <= m_cbSize);
+} // DataBlob::ContainsData
+*/
+
+// --------------------------------------------------------------------------------------
+//
+// Truncates the buffer to exact size (cbSize).
+// Returns FALSE if there's less than cbSize data represented.
+// Returns TRUE otherwise and truncates the represented data size to cbSize.
+//
+__checkReturn
+inline
+BOOL
+DataBlob::TruncateToExactSize(UINT32 cbSize)
+{
+ // Check if there's at least cbSize data present
+ if (m_cbSize < cbSize)
+ { // There's less than cbSize data present
+ // Fail the operation
+ return FALSE;
+ }
+ // Truncate represented data to size cbSize
+ m_cbSize = cbSize;
+ return TRUE;
+} // DataBlob::TruncateToExactSize
+
+// --------------------------------------------------------------------------------------
+//
+// Truncates the buffer by size (cbSize).
+// Returns FALSE if there's less than cbSize data represented.
+// Returns TRUE otherwise and truncates the represented data size by cbSize.
+//
+__checkReturn
+inline
+BOOL
+DataBlob::TruncateBySize(UINT32 cbSize)
+{
+ // Check if there's at least cbSize data present
+ if (m_cbSize < cbSize)
+ { // There's less than cbSize data present
+ // Fail the operation
+ return FALSE;
+ }
+ // Truncate represented data by size cbSize
+ m_cbSize -= cbSize;
+ return TRUE;
+} // DataBlob::TruncateBySize
+
+#ifdef _DEBUG
+// --------------------------------------------------------------------------------------
+//
+// Returns U1 value at offset (nOffset). Fires an assert if the offset is behind the end of represented
+// data.
+//
+inline
+BYTE
+DataBlob::Debug_GetByteAtOffset(UINT32 nOffset) const
+{
+ _ASSERTE(nOffset < m_cbSize);
+ return m_pbData[nOffset];
+} // DataBlob::Debug_GetByteAtOffset
+#endif //_DEBUG
+
+// --------------------------------------------------------------------------------------
+//
+// Writes compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) to the data blob
+// and skips the written data.
+// Returns FALSE if there's not enough data in the blob or the value cannot be encoded as compressed
+// integer (bigger than code:CompressedInteger::const_Max).
+// Returns TRUE on success and moves the memory block behind the written data.
+//
+__checkReturn
+inline
+BOOL
+DataBlob::StoreCompressedU(UINT32 nValue)
+{
+ if (nValue <= CompressedInteger::const_Max1Byte)
+ { // The value fits into 1 byte
+ if (m_cbSize < 1)
+ { // The data blob is empty, we cannot store compressed integer as 1 byte
+ return FALSE;
+ }
+ *m_pbData = (BYTE)nValue;
+ SkipBytes_InternalInsecure(1);
+ return TRUE;
+ }
+ if (nValue <= CompressedInteger::const_Max2Bytes)
+ { // The value fits into 2 bytes
+ if (m_cbSize < 2)
+ { // The data blob is too short, we cannot store compressed integer as 2 bytes
+ return FALSE;
+ }
+ *m_pbData = (BYTE)(nValue >> 8) | 0x80;
+ *(m_pbData + 1) = (BYTE)(nValue & 0xff);
+ SkipBytes_InternalInsecure(2);
+ return TRUE;
+ }
+ if (nValue <= CompressedInteger::const_Max4Bytes)
+ { // The value fits into 4 bytes
+ if (m_cbSize < 4)
+ { // The data blob is too short, we cannot store compressed integer as 4 bytes
+ return FALSE;
+ }
+ *m_pbData = (BYTE)(nValue >> 24) | 0xC0;
+ *(m_pbData + 1) = (BYTE)((nValue >> 16) & 0xff);
+ *(m_pbData + 2) = (BYTE)((nValue >> 8) & 0xff);
+ *(m_pbData + 3) = (BYTE)(nValue & 0xff);
+ SkipBytes_InternalInsecure(4);
+ return TRUE;
+ }
+ // The value cannot be encoded as compressed integer
+ return FALSE;
+} // DataBlob::StoreCompressedU
+
+// --------------------------------------------------------------------------------------
+//
+// Writes data from *pSource to the data blob and skips the written data.
+// Returns FALSE if there's not enough data in the blob.
+// Returns TRUE on success and moves memory block behind the written data.
+//
+__checkReturn
+inline
+BOOL
+DataBlob::StoreData(__in const DataBlob *pSource)
+{
+ // Check that we have enough space to store the *pSource data
+ if (m_cbSize < pSource->m_cbSize)
+ { // There's not enough space to store *pSource data
+ return FALSE;
+ }
+ // Copy the *pSource data to the data blob
+ memcpy(m_pbData, pSource->m_pbData, pSource->m_cbSize);
+ // Move the data blob behind copied/written data *pSource
+ m_pbData += pSource->m_cbSize;
+ m_cbSize -= pSource->m_cbSize;
+
+ return TRUE;
+} // DataBlob::StoreData
+
+// --------------------------------------------------------------------------------------
+//
+// Skips cbSize bytes in the represented memory block. The caller is responsible for making sure that the
+// represented memory block contains at least cbSize bytes, otherwise there will be a security issue.
+// Should be used only internally, never call it from outside of this class.
+//
+inline
+void
+DataBlob::SkipBytes_InternalInsecure(UINT32 cbSize)
+{
+ // The caller is responsible for this check, just double check here
+ _ASSERTE(m_cbSize >= cbSize);
+ // Move the memory block by 'cbSize' bytes
+ m_pbData += cbSize;
+ m_cbSize -= cbSize;
+} // DataBlob::SkipBytes_InternalInsecure
+
+}; // namespace MetaData
diff --git a/src/md/databuffer.h b/src/md/databuffer.h
new file mode 100644
index 0000000000..c1e9410738
--- /dev/null
+++ b/src/md/databuffer.h
@@ -0,0 +1,156 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: DataBuffer.h
+//
+
+//
+// Class code:DataBuffer provides secure access to a block of memory.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+// --------------------------------------------------------------------------------------
+//
+// This class provides secure access to a block of memory.
+//
+class DataBuffer
+{
+private:
+ //
+ // Private data
+ //
+
+ // The memory block of size code:m_cbSize. Can be non-NULL even if code:m_cbSize is 0.
+ __field_bcount(m_cbSize)
+ BYTE *m_pbData;
+ // Size of the memory block starting at code:m_pbData. If it is 0, then value of code:m_pbData can be
+ // anything (incl. NULL).
+ UINT32 m_cbSize;
+
+public:
+ //
+ // Initialization
+ //
+
+ // Creates empty memory block.
+ inline DataBuffer();
+ // Creates memory block (pbData, of size cbSize).
+ inline DataBuffer(
+ __in_bcount(cbSize) BYTE *pbData,
+ UINT32 cbSize);
+ // Creates memory block copy.
+ inline DataBuffer(
+ const DataBuffer &source);
+ // Initializes memory block to empty data. The object could be already initialzied.
+ inline void Clear();
+ // Initializes memory block to data (pbData, of size cbSize). The object should be empty before.
+ inline void Init(
+ __in_bcount(cbSize) BYTE *pbData,
+ UINT32 cbSize);
+
+ //
+ // Getters
+ //
+
+ // Reads data of type T without skipping the read data (returns pointer to the type in *ppTypeData).
+ // Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer
+ // *ppTypeData then.
+ // Returns TRUE otherwise, fills *ppTypeData with the "read" type start, but doesn't move the memory
+ // block (doesn't skip the "read" data).
+ template<class T>
+ __checkReturn
+ inline BOOL PeekData(
+ __deref_out T **ppTypeData);
+ // Reads data of type T at offset nOffset without skipping the read data (returns pointer to the type in
+ // *ppTypeData).
+ // Returns FALSE if there's not enough data (of size T) at offset nOffset in the buffer, doesn't
+ // initialize the pointer *ppTypeData then.
+ // Returns TRUE otherwise, fills *ppTypeData with the type start, but doesn't move the memory block
+ // (doesn't skip any "read" data).
+ template<class T>
+ __checkReturn
+ inline BOOL PeekDataAt(
+ UINT32 nOffset,
+ __deref_out T **ppTypeData);
+ // Reads data of type T and skips the data (instead of reading the bytes, returns pointer to the type in
+ // *ppTypeData).
+ // Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer
+ // *ppTypeData then.
+ // Returns TRUE otherwise, fills *ppTypeData with the "read" type start and moves the memory block
+ // behind the "read" type.
+ template<class T>
+ __checkReturn
+ inline BOOL GetData(
+ __deref_out T **ppTypeData);
+ // Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns pointer to
+ // the bytes in *ppbDataPointer).
+ // Returns FALSE if there's not enough data in the blob, doesn't initialize the pointer *ppbDataPointer
+ // then.
+ // Returns TRUE otherwise, fills *ppbDataPointer with the "read" data start and moves the memory block
+ // behind the "read" data.
+ __checkReturn
+ inline BOOL GetDataOfSize(
+ UINT32 cbDataSize,
+ __out_bcount(cbDataSize) BYTE **ppbDataPointer);
+
+ // Returns TRUE if the represented memory is empty.
+ inline BOOL IsEmpty() const
+ { return (m_cbSize == 0); }
+ // Gets pointer to the represented data buffer (can be random pointer if size of the data is 0).
+ // Note: Should be used exceptionally. Try to use other operations instead.
+ inline BYTE *GetDataPointer()
+ { return m_pbData; }
+ // Gets pointer to the represented data buffer (can be random pointer if size of the data is 0).
+ // Note: Should be used exceptionally. Try to use other operations instead.
+ inline const BYTE *GetDataPointer() const
+ { return m_pbData; }
+ // Gets pointer right behind the represented data buffer (can be random pointer if size of the data is
+ // 0).
+ inline const BYTE *GetDataPointerBehind() const
+ { return m_pbData + m_cbSize; }
+ // Gets the size of represented memory.
+ inline UINT32 GetSize() const
+ { return m_cbSize; }
+ //BOOL SkipBytes(UINT32 cbSize);
+
+public:
+ //
+ // Operations
+ //
+
+ // Truncates the buffer to exact size (cbSize).
+ // Returns FALSE if there's less than cbSize data represented.
+ // Returns TRUE otherwise and truncates the represented data size to cbSize.
+ __checkReturn
+ inline BOOL TruncateToExactSize(UINT32 cbSize);
+ // Truncates the buffer by size (cbSize).
+ // Returns FALSE if there's less than cbSize data represented.
+ // Returns TRUE otherwise and truncates the represented data size by cbSize.
+ __checkReturn
+ inline BOOL TruncateBySize(UINT32 cbSize);
+
+ // Skips the buffer to exact size (cbSize).
+ // Returns FALSE if there's less than cbSize data represented.
+ // Returns TRUE otherwise and skips data at the beggining, so that the result has size cbSize.
+ __checkReturn
+ inline BOOL SkipToExactSize(UINT32 cbSize);
+
+private:
+ //
+ // Helpers
+ //
+
+ // Skips 'cbSize' bytes in the represented memory block. The caller is responsible for making sure that
+ // the represented memory block contains at least 'cbSize' bytes, otherwise there will be a security
+ // issue.
+ // Should be used only internally, never call it from outside of this class.
+ inline void SkipBytes_InternalInsecure(UINT32 cbSize);
+
+}; // class DataBuffer
+
+#include "databuffer.inl"
diff --git a/src/md/databuffer.inl b/src/md/databuffer.inl
new file mode 100644
index 0000000000..e3a226e1f3
--- /dev/null
+++ b/src/md/databuffer.inl
@@ -0,0 +1,274 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: DataBuffer.inl
+//
+
+//
+// Class code:DataBuffer provides secure access to a block of memory.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "databuffer.h"
+
+// --------------------------------------------------------------------------------------
+//
+// Creates empty memory block.
+//
+inline
+DataBuffer::DataBuffer()
+{
+ Clear();
+} // DataBuffer::DataBuffer
+
+// --------------------------------------------------------------------------------------
+//
+// Creates memory block (pbData, of size cbSize).
+//
+inline
+DataBuffer::DataBuffer(
+ __in_bcount(cbSize) BYTE *pbData,
+ UINT32 cbSize)
+{
+ m_pbData = pbData;
+ m_cbSize = cbSize;
+} // DataBuffer::DataBuffer
+
+// --------------------------------------------------------------------------------------
+//
+// Creates memory block copy.
+//
+inline
+DataBuffer::DataBuffer(
+ const DataBuffer &source)
+{
+ m_pbData = source.m_pbData;
+ m_cbSize = source.m_cbSize;
+} // DataBuffer::DataBuffer
+
+#ifdef _WIN64
+ #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00dbaadf00d)
+#else //!_WIN64
+ #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00d)
+#endif //!_WIN64
+
+// --------------------------------------------------------------------------------------
+//
+// Initializes memory block to empty data. The object could be already initialzied.
+//
+inline
+void
+DataBuffer::Clear()
+{
+ m_cbSize = 0;
+ // For debugging purposes let's put invalid non-NULL pointer here
+ INDEBUG_MD(m_pbData = const_pbBadFood);
+} // DataBuffer::Clear
+
+#undef const_pbBadFood
+
+// --------------------------------------------------------------------------------------
+//
+// Initializes memory block to data (pbData, of size cbSize). The object should be empty before.
+//
+inline
+void
+DataBuffer::Init(
+ __in_bcount(cbSize) BYTE *pbData,
+ UINT32 cbSize)
+{
+ _ASSERTE(IsEmpty());
+
+ m_pbData = pbData;
+ m_cbSize = cbSize;
+} // DataBuffer::Init
+
+// --------------------------------------------------------------------------------------
+//
+// Reads data of type T without skipping the read data (returns pointer to the type in *ppTypeData).
+// Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer
+// *ppTypeData then.
+// Returns TRUE otherwise, fills *ppTypeData with the "read" type start, but doesn't move the memory
+// block (doesn't skip the "read" data).
+//
+template<class T>
+__checkReturn
+inline
+BOOL
+DataBuffer::PeekData(
+ __deref_out T **ppTypeData)
+{
+ if (m_cbSize < sizeof(T))
+ { // There's not enough data in the memory block
+ return FALSE;
+ }
+ // Fill the start of the "read" type
+ *ppTypeData = reinterpret_cast<T *>(m_pbData);
+ return TRUE;
+} // DataBuffer::PeekData
+
+// --------------------------------------------------------------------------------------
+//
+// Reads data of type T at offset nOffset without skipping the read data (returns pointer to the type in
+// *ppTypeData).
+// Returns FALSE if there's not enough data (of size T) at offset nOffset in the buffer, doesn't
+// initialize the pointer *ppTypeData then.
+// Returns TRUE otherwise, fills *ppTypeData with the type start, but doesn't move the memory block
+// (doesn't skip any "read" data).
+template<class T>
+__checkReturn
+inline
+BOOL
+DataBuffer::PeekDataAt(
+ UINT32 nOffset,
+ __deref_out T **ppTypeData)
+{
+ if (m_cbSize < nOffset)
+ { // The offset is not in the memory block
+ return FALSE;
+ }
+ if ((m_cbSize - nOffset) < sizeof(T))
+ { // The type is not fully in the memory block
+ return FALSE;
+ }
+ // Fill the start of the "read" type
+ *ppTypeData = reinterpret_cast<T *>(m_pbData + nOffset);
+ return TRUE;
+} // DataBuffer::PeekDataAt
+
+// --------------------------------------------------------------------------------------
+//
+// Reads data of type T and skips the data (instead of reading the bytes, returns pointer to the type in
+// *ppTypeData).
+// Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer
+// *ppTypeData then.
+// Returns TRUE otherwise, fills *ppTypeData with the "read" type start and moves the memory block
+// behind the "read" type.
+//
+template<class T>
+__checkReturn
+inline
+BOOL
+DataBuffer::GetData(
+ __deref_out T **ppTypeData)
+{
+ if (m_cbSize < sizeof(T))
+ { // There's not enough data in the memory block
+ return FALSE;
+ }
+ // Fill the start of the "read" type
+ *ppTypeData = reinterpret_cast<T *>(m_pbData);
+ SkipBytes_InternalInsecure(sizeof(T));
+ return TRUE;
+} // DataBuffer::GetData
+
+// --------------------------------------------------------------------------------------
+//
+// Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns pointer to
+// the bytes in *ppbDataPointer).
+// Returns FALSE if there's not enough data in the blob, doesn't initialize the pointer *ppbDataPointer
+// then.
+// Returns TRUE otherwise, fills *ppbDataPointer with the "read" data start and moves the memory block
+// behind the "read" data.
+//
+__checkReturn
+inline
+BOOL
+DataBuffer::GetDataOfSize(
+ UINT32 cbDataSize,
+ __out_bcount(cbDataSize) BYTE **ppbDataPointer)
+{
+ if (m_cbSize < cbDataSize)
+ { // There's not enough data in the memory block
+ return FALSE;
+ }
+ // Fill the start of the "read" data
+ *ppbDataPointer = m_pbData;
+ SkipBytes_InternalInsecure(cbDataSize);
+ return TRUE;
+} // DataBuffer::GetDataOfSize
+
+// --------------------------------------------------------------------------------------
+//
+// Truncates the buffer to exact size (cbSize).
+// Returns FALSE if there's less than cbSize data represented.
+// Returns TRUE otherwise and truncates the represented data size to cbSize.
+//
+__checkReturn
+inline
+BOOL
+DataBuffer::TruncateToExactSize(UINT32 cbSize)
+{
+ // Check if there's at least cbSize data present
+ if (m_cbSize < cbSize)
+ { // There's less than cbSize data present
+ // Fail the operation
+ return FALSE;
+ }
+ // Truncate represented data to size cbSize
+ m_cbSize = cbSize;
+ return TRUE;
+} // DataBuffer::TruncateToExactSize
+
+// --------------------------------------------------------------------------------------
+//
+// Truncates the buffer by size (cbSize).
+// Returns FALSE if there's less than cbSize data represented.
+// Returns TRUE otherwise and truncates the represented data size by cbSize.
+//
+__checkReturn
+inline
+BOOL
+DataBuffer::TruncateBySize(UINT32 cbSize)
+{
+ // Check if there's at least cbSize data present
+ if (m_cbSize < cbSize)
+ { // There's less than cbSize data present
+ // Fail the operation
+ return FALSE;
+ }
+ // Truncate represented data by size cbSize
+ m_cbSize -= cbSize;
+ return TRUE;
+} // DataBuffer::TruncateBySize
+
+// --------------------------------------------------------------------------------------
+//
+// Skips the buffer to size (cbSize).
+// Returns FALSE if there's less than cbSize data represented.
+// Returns TRUE otherwise and skips data at the beggining, so that the result has size cbSize.
+//
+__checkReturn
+inline
+BOOL
+DataBuffer::SkipToExactSize(UINT32 cbSize)
+{
+ // Check if there's at least cbSize data present
+ if (m_cbSize < cbSize)
+ { // There's less than cbSize data present
+ // Fail the operation
+ return FALSE;
+ }
+ SkipBytes_InternalInsecure(m_cbSize - cbSize);
+ return TRUE;
+} // DataBuffer::SkipToExactSize
+
+// --------------------------------------------------------------------------------------
+//
+// Skips 'cbSize' bytes in the represented memory block. The caller is responsible for making sure that the
+// represented memory block contains at least 'cbSize' bytes, otherwise there will be a security issue.
+// Should be used only internally, never call it from outside of this class.
+//
+inline
+void
+DataBuffer::SkipBytes_InternalInsecure(UINT32 cbSize)
+{
+ // The caller is responsible for this check, just double check here
+ _ASSERTE(m_cbSize >= cbSize);
+ // Move the memory block by 'cbSize' bytes
+ m_pbData += cbSize;
+ m_cbSize -= cbSize;
+} // DataBuffer::SkipBytes_InternalInsecure
diff --git a/src/md/datasource/.gitmirror b/src/md/datasource/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/datasource/.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/datasource/CMakeLists.txt b/src/md/datasource/CMakeLists.txt
new file mode 100644
index 0000000000..6657c0615a
--- /dev/null
+++ b/src/md/datasource/CMakeLists.txt
@@ -0,0 +1,15 @@
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+set(MDDATASOURCE_SOURCES
+ api.cpp
+ datatargetreader.cpp
+ remotemdinternalrwsource.cpp
+ targettypes.cpp
+)
+
+convert_to_absolute_path(MDDATASOURCE_SOURCES ${MDDATASOURCE_SOURCES})
+
+add_subdirectory(dbi)
+
diff --git a/src/md/datasource/DataSource.settings.targets b/src/md/datasource/DataSource.settings.targets
new file mode 100644
index 0000000000..9211b2fc67
--- /dev/null
+++ b/src/md/datasource/DataSource.settings.targets
@@ -0,0 +1,42 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <!--
+ We build MetaData in several flavors:
+ - Full version (wks) - part of clr.dll/coreclr.dll.
+ - dac version - does not need Emit APIs.
+ - Standalone versions for:
+ * mscordbi.dll (hands the interfaces over to debugger client (e.g. VS) - does not need Emit APIs.
+ * CorDbg - does not need Emit APIs.
+ * WinRT
+ - Read-Only version (ships in Windows) - does not need Emit and Internal APIs.
+ - Read-Writer version (ships as private component of MidlRt.exe SDK tool) - does not need Internal APIs.
+ -->
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\MD.props" />
+
+ <PropertyGroup>
+ <MDDataSourceSrcDirectory>$(ClrSrcDirectory)\MD\DataSource\</MDDataSourceSrcDirectory>
+ <UserIncludes>
+ $(UserIncludes);
+ $(ClrSrcDirectory)\MD\inc;
+ </UserIncludes>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE</ClAdditionalOptions>
+ <!--OK to delete NO_NTDLL for devdiv builds.-->
+ <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>$(MDDataSourceSrcDirectory)\stdafx.cpp</PCHCompile>
+ <PCHObject>stdafx_datasource.obj</PCHObject>
+ <LinkUseCMT>false</LinkUseCMT>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <CppCompile Include="$(MDDataSourceSrcDirectory)\api.cpp" />
+ <CppCompile Include="$(MDDataSourceSrcDirectory)\DataTargetReader.cpp" />
+ <CppCompile Include="$(MDDataSourceSrcDirectory)\RemoteMDInternalRWSource.cpp" />
+ <CppCompile Include="$(MDDataSourceSrcDirectory)\TargetTypes.cpp" />
+ </ItemGroup>
+</Project>
diff --git a/src/md/datasource/api.cpp b/src/md/datasource/api.cpp
new file mode 100644
index 0000000000..a3112deaea
--- /dev/null
+++ b/src/md/datasource/api.cpp
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// api.cpp
+//
+
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "remotemdinternalrwsource.h"
+
+HRESULT CreateRemoteMDInternalRWSource(TADDR mdInternalRWRemoteAddress, ICorDebugDataTarget* pDataTarget, DWORD defines, DWORD dataStructureVersion, IMDCustomDataSource** ppDataSource)
+{
+ HRESULT hr = S_OK;
+ RemoteMDInternalRWSource* pSource = new (nothrow) RemoteMDInternalRWSource();
+ if (pSource == NULL)
+ return E_OUTOFMEMORY;
+
+ hr = pSource->InitFromTarget(mdInternalRWRemoteAddress, pDataTarget, defines, dataStructureVersion);
+ if (SUCCEEDED(hr))
+ {
+ hr = pSource->QueryInterface(IID_IMDCustomDataSource, (void**)ppDataSource);
+ }
+ if (FAILED(hr))
+ {
+ delete pSource;
+ }
+ return hr;
+}
diff --git a/src/md/datasource/datatargetreader.cpp b/src/md/datasource/datatargetreader.cpp
new file mode 100644
index 0000000000..9e916e366b
--- /dev/null
+++ b/src/md/datasource/datatargetreader.cpp
@@ -0,0 +1,199 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+#include "stdafx.h"
+#include "datatargetreader.h"
+
+
+DataTargetReader::DataTargetReader(CORDB_ADDRESS remoteAddressCursor, ICorDebugDataTarget* pDataTarget, DWORD targetDefines, DWORD mdStructuresVersion)
+: m_remotePointerSize(0),
+m_currentStructureAlign(1),
+m_targetDefines(targetDefines),
+m_mdStructuresVersion(mdStructuresVersion)
+{
+ m_remoteAddressCursor = remoteAddressCursor;
+ m_pDataTarget = pDataTarget;
+ m_pDataTarget->AddRef();
+}
+DataTargetReader::DataTargetReader(const DataTargetReader & otherReader)
+{
+ m_pDataTarget = otherReader.m_pDataTarget;
+ m_pDataTarget->AddRef();
+ m_remotePointerSize = otherReader.m_remotePointerSize;
+ m_remoteAddressCursor = otherReader.m_remoteAddressCursor;
+ m_targetDefines = otherReader.m_targetDefines;
+ m_mdStructuresVersion = otherReader.m_mdStructuresVersion;
+}
+DataTargetReader & DataTargetReader::operator=(const DataTargetReader & otherReader)
+{
+ if (this != &otherReader)
+ {
+ m_pDataTarget = otherReader.m_pDataTarget;
+ m_pDataTarget->AddRef();
+ m_remotePointerSize = otherReader.m_remotePointerSize;
+ m_remoteAddressCursor = otherReader.m_remoteAddressCursor;
+ m_targetDefines = otherReader.m_targetDefines;
+ m_mdStructuresVersion = otherReader.m_mdStructuresVersion;
+ }
+ return *this;
+}
+DataTargetReader::~DataTargetReader()
+{
+ m_pDataTarget->Release();
+ m_pDataTarget = NULL;
+}
+
+HRESULT DataTargetReader::ReadPointer(CORDB_ADDRESS* pPointerValue)
+{
+ HRESULT hr = S_OK;
+ if (m_remotePointerSize == 0)
+ {
+ IfFailRet(GetRemotePointerSize(&m_remotePointerSize));
+ }
+ _ASSERTE(m_remotePointerSize == 4 || m_remotePointerSize == 8);
+ *pPointerValue = 0;
+ if (m_remotePointerSize == 4)
+ return Read32((ULONG32*)pPointerValue);
+ else
+ return Read64((ULONG64*)pPointerValue);
+}
+HRESULT DataTargetReader::SkipPointer()
+{
+ HRESULT hr = S_OK;
+ if (m_remotePointerSize == 0)
+ {
+ IfFailRet(GetRemotePointerSize(&m_remotePointerSize));
+ }
+ _ASSERTE(m_remotePointerSize == 4 || m_remotePointerSize == 8);
+ Align(m_remotePointerSize);
+ return SkipBytes(m_remotePointerSize);
+}
+HRESULT DataTargetReader::Read8(BYTE* pByteValue)
+{
+ return ReadBytes(pByteValue, 1);
+}
+HRESULT DataTargetReader::Skip8()
+{
+ return SkipBytes(1);
+}
+HRESULT DataTargetReader::Read32(ULONG32* pUlong32Value)
+{
+ Align(4);
+ return ReadBytes((BYTE*)pUlong32Value, sizeof(ULONG32));
+}
+HRESULT DataTargetReader::Skip32()
+{
+ Align(4);
+ return SkipBytes(sizeof(ULONG32));
+}
+HRESULT DataTargetReader::Read64(ULONG64* pUlong64Value)
+{
+ Align(8);
+ return ReadBytes((BYTE*)pUlong64Value, sizeof(ULONG64));
+}
+HRESULT DataTargetReader::Skip64()
+{
+ Align(8);
+ return SkipBytes(sizeof(ULONG64));
+}
+
+HRESULT DataTargetReader::ReadBytes(BYTE* pBuffer, DWORD cbBuffer)
+{
+ HRESULT hr = S_OK;
+ ULONG32 cbTotalRead = 0;
+ CORDB_ADDRESS m_tempRemoteAddressCursor = m_remoteAddressCursor;
+ while (cbTotalRead < cbBuffer)
+ {
+ ULONG32 cbRead = 0;
+ if(FAILED(m_pDataTarget->ReadVirtual(m_remoteAddressCursor + cbTotalRead,
+ pBuffer + cbTotalRead,
+ cbBuffer - cbTotalRead,
+ &cbRead)))
+ return CORDBG_E_READVIRTUAL_FAILURE;
+ if (cbRead == 0)
+ return CORDBG_E_READVIRTUAL_FAILURE;
+ cbTotalRead += cbRead;
+ }
+
+ // on success only, move the cursor
+ m_remoteAddressCursor += cbTotalRead;
+ return S_OK;
+}
+HRESULT DataTargetReader::SkipBytes(DWORD cbRead)
+{
+ m_remoteAddressCursor += cbRead;
+ return S_OK;
+}
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b) ? (a) : (b))
+#endif
+
+HRESULT DataTargetReader::Read(TargetObject* pTargetObject)
+{
+ ULONG32 previousAlign = m_currentStructureAlign;
+ m_currentStructureAlign = 1;
+ HRESULT hr = pTargetObject->ReadFrom(*this);
+ if (SUCCEEDED(hr))
+ {
+ // increase the structure size to a multiple of the maximum alignment of any of its members
+ Align(m_currentStructureAlign);
+ }
+ m_currentStructureAlign = MAX(previousAlign, m_currentStructureAlign);
+ return hr;
+}
+
+HRESULT DataTargetReader::ReadPointer(TargetObject* pTargetObject)
+{
+ HRESULT hr = S_OK;
+ CORDB_ADDRESS pointerValue;
+ IfFailRet(ReadPointer(&pointerValue));
+
+ DataTargetReader reader = CreateReaderAt(pointerValue);
+ return pTargetObject->ReadFrom(reader);
+}
+
+void DataTargetReader::Align(DWORD alignmentBytes)
+{
+ m_remoteAddressCursor = AlignUp(m_remoteAddressCursor, alignmentBytes);
+ m_currentStructureAlign = MAX(m_currentStructureAlign, alignmentBytes);
+}
+
+void DataTargetReader::AlignBase()
+{
+ Align(m_currentStructureAlign);
+}
+
+HRESULT DataTargetReader::GetRemotePointerSize(ULONG32* pPointerSize)
+{
+ HRESULT hr = S_OK;
+ CorDebugPlatform platform;
+ IfFailRet(m_pDataTarget->GetPlatform(&platform));
+ if (platform == CORDB_PLATFORM_WINDOWS_X86)
+ *pPointerSize = 4;
+ else if (platform == CORDB_PLATFORM_WINDOWS_AMD64)
+ *pPointerSize = 8;
+ else if (platform == CORDB_PLATFORM_WINDOWS_ARM)
+ *pPointerSize = 4;
+ else
+ return CORDBG_E_UNSUPPORTED;
+ return S_OK;
+}
+
+DWORD DataTargetReader::GetMDStructuresVersion()
+{
+ return m_mdStructuresVersion;
+}
+
+BOOL DataTargetReader::IsDefined(DWORD define)
+{
+ return (m_targetDefines & define) == define;
+}
+
+DataTargetReader DataTargetReader::CreateReaderAt(CORDB_ADDRESS remoteAddressCursor)
+{
+ DataTargetReader newReader(remoteAddressCursor, m_pDataTarget, m_targetDefines, m_mdStructuresVersion);
+ return newReader;
+}
diff --git a/src/md/datasource/datatargetreader.h b/src/md/datasource/datatargetreader.h
new file mode 100644
index 0000000000..b819813dbf
--- /dev/null
+++ b/src/md/datasource/datatargetreader.h
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef _MD_DATA_TARGET_READER_
+#define _MD_DATA_TARGET_READER_
+
+#include "cor.h"
+#include "cordebug.h"
+
+class DataTargetReader;
+
+class TargetObject
+{
+public:
+ virtual HRESULT ReadFrom(DataTargetReader & reader) = 0;
+};
+
+class DataTargetReader
+{
+public:
+ DataTargetReader(CORDB_ADDRESS remoteAddressCursor, ICorDebugDataTarget* pDataTarget, DWORD targetDefines, DWORD mdStructuresVersion);
+ DataTargetReader(const DataTargetReader & otherReader);
+ DataTargetReader & operator=(const DataTargetReader & rhs);
+ ~DataTargetReader();
+
+ HRESULT Read(TargetObject* pTargetObjectValue);
+ HRESULT ReadPointer(TargetObject* pTargetObjectValue);
+ HRESULT ReadPointer(CORDB_ADDRESS* pPointerValue);
+ HRESULT SkipPointer();
+ HRESULT Read8(BYTE* pByteValue);
+ HRESULT Skip8();
+ HRESULT Read32(ULONG32* pUlong32Value);
+ HRESULT Skip32();
+ HRESULT Read64(ULONG64* pUlong64Value);
+ HRESULT Skip64();
+ HRESULT ReadBytes(BYTE* pBuffer, DWORD cbBuffer);
+ HRESULT SkipBytes(DWORD cbBuffer);
+ void Align(DWORD alignmentBytes);
+ void AlignBase();
+
+ DataTargetReader CreateReaderAt(CORDB_ADDRESS remoteAddressCursor);
+
+ DWORD GetMDStructuresVersion();
+ BOOL IsDefined(DWORD define);
+
+
+private:
+ HRESULT GetRemotePointerSize(ULONG32* pPointerSize);
+ ICorDebugDataTarget* m_pDataTarget;
+ ULONG32 m_remotePointerSize;
+ CORDB_ADDRESS m_remoteAddressCursor;
+ ULONG32 m_currentStructureAlign;
+ DWORD m_targetDefines;
+ DWORD m_mdStructuresVersion;
+};
+
+#endif // _MD_DATA_TARGET_READER_
diff --git a/src/md/datasource/dbi/.gitmirror b/src/md/datasource/dbi/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/datasource/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/datasource/dbi/CMakeLists.txt b/src/md/datasource/dbi/CMakeLists.txt
new file mode 100644
index 0000000000..c30c62e33c
--- /dev/null
+++ b/src/md/datasource/dbi/CMakeLists.txt
@@ -0,0 +1,4 @@
+include(../../md_dbi.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDDATASOURCE_SOURCES)
+add_library_clr(mddatasource_dbi STATIC ${MDDATASOURCE_SOURCES}) \ No newline at end of file
diff --git a/src/md/datasource/dbi/DataSource-dbi.props b/src/md/datasource/dbi/DataSource-dbi.props
new file mode 100644
index 0000000000..e2a980f790
--- /dev/null
+++ b/src/md/datasource/dbi/DataSource-dbi.props
@@ -0,0 +1,9 @@
+<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>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\DataSource\DataSource.settings.targets" />
+
+</Project>
diff --git a/src/md/datasource/dbi/dirs.proj b/src/md/datasource/dbi/dirs.proj
new file mode 100644
index 0000000000..2a3f4e0252
--- /dev/null
+++ b/src/md/datasource/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 Condition="'$(FeatureDbiDebugging)'=='true'" Include="HostLocal\DataSource-dbi.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/datasource/dirs.proj b/src/md/datasource/dirs.proj
new file mode 100644
index 0000000000..1d397301eb
--- /dev/null
+++ b/src/md/datasource/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="dbi\dirs.proj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/datasource/remotemdinternalrwsource.cpp b/src/md/datasource/remotemdinternalrwsource.cpp
new file mode 100644
index 0000000000..02eb45ba2f
--- /dev/null
+++ b/src/md/datasource/remotemdinternalrwsource.cpp
@@ -0,0 +1,241 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// RemoteMDInternalRWSource.cpp
+//
+
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "remotemdinternalrwsource.h"
+
+RemoteMDInternalRWSource::RemoteMDInternalRWSource() :
+m_cRef(0)
+{
+ memset(&m_TableDefs, 0, sizeof(CMiniTableDef)*TBL_COUNT);
+ memset(&m_bSortable, 0, sizeof(BOOL)*TBL_COUNT);
+}
+
+RemoteMDInternalRWSource::~RemoteMDInternalRWSource()
+{
+ for (int i = 0; i < TBL_COUNT; i++)
+ {
+ delete[] m_TableDefs[i].m_pColDefs;
+ }
+}
+
+ULONG RemoteMDInternalRWSource::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+ULONG RemoteMDInternalRWSource::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ return cRef;
+}
+
+HRESULT RemoteMDInternalRWSource::QueryInterface(REFIID riid, void ** ppUnk)
+{
+ *ppUnk = 0;
+ if (riid == IID_IUnknown)
+ {
+ *ppUnk = static_cast<IUnknown*>(this);
+ }
+ else if (riid == IID_IMDCustomDataSource)
+ {
+ *ppUnk = static_cast<IMDCustomDataSource*>(this);
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+HRESULT _MarshalDataFromTargetStgPool(DataTargetReader & reader, const Target_StgPool & pool, MetaData::DataBlob* pBlob)
+{
+ HRESULT hr = S_OK;
+ ULONG32 dataSize = 0;
+ ULONG32 segmentCount = 0;
+ Target_StgPoolSeg curSeg = (Target_StgPoolSeg)pool;
+
+ // The storage pool grows exponentially, and should reach 2GB in at most 64 segments. Allowing for 1000
+ // adds a sizable risk mitigation factor in case my analysis was inaccurate or the algorithm changes in the future
+ // without corresponding changes here.
+ CORDB_ADDRESS segmentData[1000];
+ ULONG32 segmentSize[1000];
+ for (; segmentCount < 1000; segmentCount++)
+ {
+ // sanity check that each segment and the sum of all segments is less than 100M bytes
+ if (curSeg.m_cbSegNext > 100000000)
+ return CLDB_E_FILE_CORRUPT;
+ dataSize += curSeg.m_cbSegNext;
+ if (dataSize > 100000000)
+ return CLDB_E_FILE_CORRUPT;
+ segmentData[segmentCount] = curSeg.m_pSegData;
+ segmentSize[segmentCount] = curSeg.m_cbSegNext;
+ if (curSeg.m_pNextSeg == 0)
+ break;
+
+ DataTargetReader segReader = reader.CreateReaderAt(curSeg.m_pNextSeg);
+ IfFailRet(segReader.Read(&curSeg));
+ }
+
+ //we exited the loop with a break, count should be one more than the last index
+ segmentCount++;
+
+ // sanity check, no more than 1000 segments allowed
+ if (segmentCount > 1000)
+ return CLDB_E_FILE_CORRUPT;
+
+ // things looked reasonable enough, marshal over the data
+ NewArrayHolder<BYTE> pData = new (nothrow) BYTE[dataSize];
+ if (pData == NULL)
+ return E_OUTOFMEMORY;
+ BYTE* pCursor = pData;
+ for (ULONG32 i = 0; i < segmentCount; i++)
+ {
+ DataTargetReader segDataReader = reader.CreateReaderAt(segmentData[i]);
+ hr = segDataReader.ReadBytes(pCursor, segmentSize[i]);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ else
+ {
+ pCursor += segmentSize[i];
+ }
+ }
+ pBlob->Init(pData, dataSize);
+ pData.SuppressRelease(); // our caller owns the buffer now
+ return S_OK;
+
+}
+
+HRESULT RemoteMDInternalRWSource::InitFromTarget(TADDR remoteMDInternalRWAddr, ICorDebugDataTarget* pDataTarget, DWORD defines, DWORD dataStructureVersion)
+{
+ HRESULT hr = S_OK;
+ DataTargetReader reader(remoteMDInternalRWAddr, pDataTarget, defines, dataStructureVersion);
+ IfFailRet(reader.Read(&m_targetData));
+
+ Target_CMiniMdSchema targetSchema = m_targetData.m_pStgdb.m_MiniMd.m_Schema;
+ m_Schema.m_ulReserved = targetSchema.m_ulReserved;
+ m_Schema.m_major = targetSchema.m_major;
+ m_Schema.m_minor = targetSchema.m_minor;
+ m_Schema.m_heaps = targetSchema.m_heaps;
+ m_Schema.m_rid = targetSchema.m_rid;
+ m_Schema.m_maskvalid = targetSchema.m_maskvalid;
+ m_Schema.m_sorted = targetSchema.m_sorted;
+ memcpy(m_Schema.m_cRecs, targetSchema.m_cRecs, sizeof(ULONG32)*TBL_COUNT);
+ m_Schema.m_ulExtra = targetSchema.m_ulExtra;
+
+ for (int i = 0; i < TBL_COUNT; i++)
+ {
+ Target_CMiniTableDef* pTargetTableDef = &(m_targetData.m_pStgdb.m_MiniMd.m_TableDefs[i]);
+ m_TableDefs[i].m_cCols = pTargetTableDef->m_cCols;
+ m_TableDefs[i].m_iKey = pTargetTableDef->m_iKey;
+ m_TableDefs[i].m_cbRec = pTargetTableDef->m_cbRec;
+ m_TableDefs[i].m_pColDefs = new (nothrow) CMiniColDef[m_TableDefs[i].m_cCols];
+ if (m_TableDefs[i].m_pColDefs == NULL)
+ return E_OUTOFMEMORY;
+ for (int j = 0; j < m_TableDefs[i].m_cCols; j++)
+ {
+ m_TableDefs[i].m_pColDefs[j].m_Type = pTargetTableDef->m_pColDefs[j].m_Type;
+ m_TableDefs[i].m_pColDefs[j].m_oColumn = pTargetTableDef->m_pColDefs[j].m_oColumn;
+ m_TableDefs[i].m_pColDefs[j].m_cbColumn = pTargetTableDef->m_pColDefs[j].m_cbColumn;
+ }
+ }
+
+ IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_StringHeap, &m_StringHeap));
+ m_StringHeapStorage = m_StringHeap.GetDataPointer();
+ IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_BlobHeap, &m_BlobHeap));
+ m_BlobHeapStorage = m_BlobHeap.GetDataPointer();
+ IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_UserStringHeap, &m_UserStringHeap));
+ m_UserStringHeapStorage = m_UserStringHeap.GetDataPointer();
+ IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_GuidHeap, &m_GuidHeap));
+ m_GuidHeapStorage = m_GuidHeap.GetDataPointer();
+
+ for (int i = 0; i < TBL_COUNT; i++)
+ {
+ IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_Tables[i], &m_TableRecords[i]));
+ m_TableRecordsStorage[i] = m_TableRecords[i].GetDataPointer();
+ m_bSortable[i] = m_targetData.m_pStgdb.m_MiniMd.m_bSortable[i];
+ }
+
+ if (m_targetData.m_pStgdb.m_pvMd != 0)
+ {
+ STORAGESIGNATURE sig = { 0 };
+ DataTargetReader storageSigReader = reader.CreateReaderAt(m_targetData.m_pStgdb.m_pvMd);
+ storageSigReader.ReadBytes((BYTE*)&sig, sizeof(sig));
+ if (sig.GetVersionStringLength() > 1000)
+ return CLDB_E_FILE_CORRUPT;
+ ULONG32 totalSigSize = offsetof(STORAGESIGNATURE, pVersion) + sig.GetVersionStringLength();
+ m_SigStorage = new (nothrow)BYTE[totalSigSize];
+ if (m_SigStorage == NULL)
+ return E_OUTOFMEMORY;
+ memcpy_s(m_SigStorage, totalSigSize, &sig, sizeof(sig));
+ storageSigReader.ReadBytes(m_SigStorage + sizeof(sig), totalSigSize - sizeof(sig));
+ m_Sig.Init(m_SigStorage, totalSigSize);
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP RemoteMDInternalRWSource::GetSchema(CMiniMdSchema* pSchema)
+{
+ *pSchema = m_Schema;
+ return S_OK;
+}
+STDMETHODIMP RemoteMDInternalRWSource::GetTableDef(ULONG32 tableIndex, CMiniTableDef* pTableDef)
+{
+ *pTableDef = m_TableDefs[tableIndex];
+ return S_OK;
+}
+
+STDMETHODIMP RemoteMDInternalRWSource::GetBlobHeap(MetaData::DataBlob* pBlobHeapData)
+{
+ *pBlobHeapData = m_BlobHeap;
+ return S_OK;
+}
+
+STDMETHODIMP RemoteMDInternalRWSource::GetGuidHeap(MetaData::DataBlob* pGuidHeapData)
+{
+ *pGuidHeapData = m_GuidHeap;
+ return S_OK;
+}
+
+STDMETHODIMP RemoteMDInternalRWSource::GetStringHeap(MetaData::DataBlob* pStringHeapData)
+{
+ *pStringHeapData = m_StringHeap;
+ return S_OK;
+}
+
+STDMETHODIMP RemoteMDInternalRWSource::GetUserStringHeap(MetaData::DataBlob* pUserStringHeapData)
+{
+ *pUserStringHeapData = m_UserStringHeap;
+ return S_OK;
+}
+
+STDMETHODIMP RemoteMDInternalRWSource::GetTableRecords(ULONG32 tableIndex, MetaData::DataBlob* pTableRecordData)
+{
+ *pTableRecordData = m_TableRecords[tableIndex];
+ return S_OK;
+}
+
+STDMETHODIMP RemoteMDInternalRWSource::GetTableSortable(ULONG32 tableIndex, BOOL* pSortable)
+{
+ *pSortable = TRUE;
+ return S_OK;
+}
+
+STDMETHODIMP RemoteMDInternalRWSource::GetStorageSignature(MetaData::DataBlob* pStorageSignature)
+{
+ *pStorageSignature = m_Sig;
+ return S_OK;
+}
diff --git a/src/md/datasource/remotemdinternalrwsource.h b/src/md/datasource/remotemdinternalrwsource.h
new file mode 100644
index 0000000000..a44f50dc64
--- /dev/null
+++ b/src/md/datasource/remotemdinternalrwsource.h
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// RemoteMDInternalRWSource.h
+//
+
+//
+//*****************************************************************************
+
+#ifndef _REMOTE_MDINTERNALRW_SOURCE_
+#define _REMOTE_MDINTERNALRW_SOURCE_
+
+#include "targettypes.h"
+
+class RemoteMDInternalRWSource : IMDCustomDataSource
+{
+public:
+ RemoteMDInternalRWSource();
+ virtual ~RemoteMDInternalRWSource();
+
+ //*****************************************************************************
+ // IUnknown methods
+ //*****************************************************************************
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
+ STDMETHODIMP_(ULONG) AddRef(void);
+ STDMETHODIMP_(ULONG) Release(void);
+
+ //*****************************************************************************
+ // IMDCustomDataSource methods
+ //*****************************************************************************
+ STDMETHODIMP GetSchema(CMiniMdSchema* pSchema);
+ STDMETHODIMP GetTableDef(ULONG32 tableIndex, CMiniTableDef* pTableDef);
+ STDMETHODIMP GetBlobHeap(MetaData::DataBlob* pBlobHeapData);
+ STDMETHODIMP GetGuidHeap(MetaData::DataBlob* pGuidHeapData);
+ STDMETHODIMP GetStringHeap(MetaData::DataBlob* pStringHeapData);
+ STDMETHODIMP GetUserStringHeap(MetaData::DataBlob* pUserStringHeapData);
+ STDMETHODIMP GetTableRecords(ULONG32 tableIndex, MetaData::DataBlob* pTableRecordData);
+ STDMETHODIMP GetTableSortable(ULONG32 tableIndex, BOOL* pSortable);
+ STDMETHODIMP GetStorageSignature(MetaData::DataBlob* pStorageSignature);
+
+ //*****************************************************************************
+ // public non-COM methods
+ //*****************************************************************************
+ HRESULT InitFromTarget(TADDR remoteMDInternalRWAddress, ICorDebugDataTarget* pDataTarget, DWORD defines, DWORD dataStructureVersion);
+
+private:
+ Target_MDInternalRW m_targetData;
+ CMiniMdSchema m_Schema;
+ CMiniTableDef m_TableDefs[TBL_COUNT];
+ MetaData::DataBlob m_StringHeap;
+ MetaData::DataBlob m_UserStringHeap;
+ MetaData::DataBlob m_BlobHeap;
+ MetaData::DataBlob m_GuidHeap;
+ MetaData::DataBlob m_TableRecords[TBL_COUNT];
+ BOOL m_bSortable[TBL_COUNT];
+ MetaData::DataBlob m_Sig;
+
+ NewArrayHolder<BYTE> m_StringHeapStorage;
+ NewArrayHolder<BYTE> m_UserStringHeapStorage;
+ NewArrayHolder<BYTE> m_BlobHeapStorage;
+ NewArrayHolder<BYTE> m_GuidHeapStorage;
+ NewArrayHolder<BYTE> m_TableRecordsStorage[TBL_COUNT];
+ NewArrayHolder<BYTE> m_SigStorage;
+
+ volatile LONG m_cRef;
+};
+
+
+#endif
diff --git a/src/md/datasource/stdafx.cpp b/src/md/datasource/stdafx.cpp
new file mode 100644
index 0000000000..ff341e19a7
--- /dev/null
+++ b/src/md/datasource/stdafx.cpp
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// stdafx.cpp
+//
+
+//
+// Precompiled headers.
+//
+//*****************************************************************************
+#include "stdafx.h"
diff --git a/src/md/datasource/stdafx.h b/src/md/datasource/stdafx.h
new file mode 100644
index 0000000000..2e5e62a0bb
--- /dev/null
+++ b/src/md/datasource/stdafx.h
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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>
+
+
+#endif // __STDAFX_H_
diff --git a/src/md/datasource/targettypes.cpp b/src/md/datasource/targettypes.cpp
new file mode 100644
index 0000000000..9af7dd15da
--- /dev/null
+++ b/src/md/datasource/targettypes.cpp
@@ -0,0 +1,535 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// TargetTypes.cpp
+//
+
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "targettypes.h"
+
+
+Target_CMiniMdSchemaBase::Target_CMiniMdSchemaBase() :
+m_ulReserved(0),
+m_major(0),
+m_minor(0),
+m_heaps(0),
+m_rid(0),
+m_maskvalid(0),
+m_sorted(0)
+{}
+
+HRESULT Target_CMiniMdSchemaBase::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ reader.Align(8); // this type needs 8 byte alignment from m_maskvalid
+ IfFailRet(reader.Read32(&m_ulReserved));
+ IfFailRet(reader.Read8(&m_major));
+ IfFailRet(reader.Read8(&m_minor));
+ IfFailRet(reader.Read8(&m_heaps));
+ IfFailRet(reader.Read8(&m_rid));
+ IfFailRet(reader.Read64(&m_maskvalid));
+ IfFailRet(reader.Read64(&m_sorted));
+ return S_OK;
+}
+
+Target_CMiniMdSchema::Target_CMiniMdSchema() :
+m_ulExtra(0)
+{
+ memset(&m_cRecs, 0, TBL_COUNT*sizeof(ULONG32));
+}
+
+HRESULT Target_CMiniMdSchema::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_CMiniMdSchemaBase::ReadFrom(reader));
+ reader.AlignBase();
+ for (int i = 0; i < TBL_COUNT; i++)
+ IfFailRet(reader.Read32(&(m_cRecs[i])));
+ IfFailRet(reader.Read32(&m_ulExtra));
+ return S_OK;
+}
+
+Target_CMiniColDef::Target_CMiniColDef() :
+m_Type(0),
+m_oColumn(0),
+m_cbColumn(0)
+{
+}
+
+HRESULT Target_CMiniColDef::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.Read8(&m_Type));
+ IfFailRet(reader.Read8(&m_oColumn));
+ IfFailRet(reader.Read8(&m_cbColumn));
+ return S_OK;
+}
+
+Target_CMiniTableDef::Target_CMiniTableDef() :
+m_pColDefs(NULL),
+m_cCols(0),
+m_iKey(0),
+m_cbRec(0)
+{}
+
+HRESULT Target_CMiniTableDef::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ CORDB_ADDRESS pColDefs = NULL;
+ IfFailRet(reader.ReadPointer(&pColDefs));
+ IfFailRet(reader.Read8(&m_cCols));
+ IfFailRet(reader.Read8(&m_iKey));
+ IfFailRet(reader.Read8(&m_cbRec));
+
+ // sanity check before allocating
+ if (m_cCols > 100)
+ return CLDB_E_FILE_CORRUPT;
+ m_pColDefs = new (nothrow) Target_CMiniColDef[m_cCols];
+ if (m_pColDefs == NULL)
+ return E_OUTOFMEMORY;
+ DataTargetReader colsReader = reader.CreateReaderAt(pColDefs);
+ for (int i = 0; i < m_cCols; i++)
+ {
+ IfFailRet(colsReader.Read(&m_pColDefs[i]));
+ }
+
+ return S_OK;
+}
+
+Target_CMiniMdBase::Target_CMiniMdBase() :
+m_TblCount(0),
+m_fVerifiedByTrustedSource(FALSE),
+m_iStringsMask(0),
+m_iGuidsMask(0),
+m_iBlobsMask(0)
+{}
+
+HRESULT Target_CMiniMdBase::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.SkipPointer()); // vtable
+ IfFailRet(reader.Read(&m_Schema));
+ IfFailRet(reader.Read32(&m_TblCount));
+ IfFailRet(reader.Read32((ULONG32*)&m_fVerifiedByTrustedSource));
+ for (int i = 0; i < TBL_COUNT; i++)
+ IfFailRet(reader.Read(&(m_TableDefs[i])));
+ IfFailRet(reader.Read32(&m_iStringsMask));
+ IfFailRet(reader.Read32(&m_iGuidsMask));
+ IfFailRet(reader.Read32(&m_iBlobsMask));
+ return S_OK;
+}
+
+Target_MapSHash::Target_MapSHash() :
+m_table(0),
+m_tableSize(0),
+m_tableCount(0),
+m_tableOccupied(0),
+m_tableMax(0)
+{}
+
+HRESULT Target_MapSHash::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.Skip8()); // this byte gets used by the base class even though it has no members
+ // I'm guessing this is so the 2nd base class (noncopyable) doesn't start at the same
+ // location, but I can't be sure. Whatever the cause, the layout skips a byte.
+ IfFailRet(reader.ReadPointer(&m_table));
+ IfFailRet(reader.Read32(&m_tableSize));
+ IfFailRet(reader.Read32(&m_tableCount));
+ IfFailRet(reader.Read32(&m_tableOccupied));
+ IfFailRet(reader.Read32(&m_tableMax));
+ return S_OK;
+}
+
+
+
+Target_StgPoolSeg::Target_StgPoolSeg() :
+m_pSegData(0),
+m_pNextSeg(0),
+m_cbSegSize(0),
+m_cbSegNext(0)
+{}
+
+HRESULT Target_StgPoolSeg::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.ReadPointer(&m_pSegData));
+ IfFailRet(reader.ReadPointer(&m_pNextSeg));
+ IfFailRet(reader.Read32(&m_cbSegSize));
+ IfFailRet(reader.Read32(&m_cbSegNext));
+ return S_OK;
+}
+
+
+Target_CChainedHash::Target_CChainedHash() :
+m_rgData(0),
+m_iBuckets(0),
+m_iSize(0),
+m_iCount(0),
+m_iMaxChain(0),
+m_iFree(0)
+{}
+
+HRESULT Target_CChainedHash::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.SkipPointer()); // __vfptr
+ IfFailRet(reader.ReadPointer(&m_rgData));
+ IfFailRet(reader.Read32(&m_iBuckets));
+ IfFailRet(reader.Read32(&m_iSize));
+ IfFailRet(reader.Read32(&m_iCount));
+ IfFailRet(reader.Read32(&m_iMaxChain));
+ IfFailRet(reader.Read32(&m_iFree));
+ return S_OK;
+}
+
+Target_CStringPoolHash::Target_CStringPoolHash() :
+m_Pool(0)
+{}
+
+HRESULT Target_CStringPoolHash::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_CChainedHash::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.ReadPointer(&m_Pool));
+ return S_OK;
+}
+
+Target_CBlobPoolHash::Target_CBlobPoolHash() :
+m_Pool(0)
+{}
+
+HRESULT Target_CBlobPoolHash::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_CChainedHash::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.ReadPointer(&m_Pool));
+ return S_OK;
+}
+
+
+Target_CGuidPoolHash::Target_CGuidPoolHash() :
+m_Pool(0)
+{ }
+
+HRESULT Target_CGuidPoolHash::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_CChainedHash::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.ReadPointer(&m_Pool));
+ return S_OK;
+}
+
+Target_HotHeap::Target_HotHeap() :
+m_pHotHeapHeader(0)
+{}
+
+HRESULT Target_HotHeap::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.ReadPointer(&m_pHotHeapHeader));
+ return S_OK;
+}
+
+HRESULT Target_StgPoolReadOnly::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.SkipPointer()); // __vfptr
+ IfFailRet(Target_StgPoolSeg::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.Read(&m_HotHeap));
+ return S_OK;
+}
+
+Target_StgPool::Target_StgPool() :
+m_ulGrowInc(0),
+m_pCurSeg(0),
+m_cbCurSegOffset(0),
+m_bFree(FALSE),
+m_bReadOnly(FALSE),
+m_nVariableAlignmentMask(0),
+m_cbStartOffsetOfEdit(0),
+m_fValidOffsetOfEdit(FALSE)
+{}
+
+HRESULT Target_StgPool::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_StgPoolReadOnly::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.Read32(&m_ulGrowInc));
+ IfFailRet(reader.ReadPointer(&m_pCurSeg));
+ IfFailRet(reader.Read32(&m_cbCurSegOffset));
+ ULONG32 bitField;
+ IfFailRet(reader.Read32(&bitField));
+ m_bFree = (bitField & 0x1) != 0;
+ m_bReadOnly = (bitField & 0x2) != 0;
+ IfFailRet(reader.Read32(&m_nVariableAlignmentMask));
+ IfFailRet(reader.Read32(&m_cbStartOffsetOfEdit));
+ IfFailRet(reader.Read8((BYTE*)&m_fValidOffsetOfEdit));
+ return S_OK;
+}
+
+HRESULT Target_StgBlobPool::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_StgPool::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.Read(&m_Hash));
+ return S_OK;
+}
+
+Target_StgStringPool::Target_StgStringPool() :
+m_bHash(FALSE)
+{
+}
+
+HRESULT Target_StgStringPool::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_StgPool::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.Read(&m_Hash));
+ IfFailRet(reader.Read8((BYTE*)&m_bHash));
+ return S_OK;
+}
+
+Target_StgGuidPool::Target_StgGuidPool() :
+m_bHash(FALSE)
+{}
+
+HRESULT Target_StgGuidPool::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_StgPool::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.Read(&m_Hash));
+ IfFailRet(reader.Read8((BYTE*)&m_bHash));
+ return S_OK;
+}
+
+Target_RecordPool::Target_RecordPool() :
+m_cbRec(0)
+{}
+
+HRESULT Target_RecordPool::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_StgPool::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.Read32(&m_cbRec));
+ return S_OK;
+}
+
+Target_OptionValue::Target_OptionValue() :
+m_DupCheck(0),
+m_RefToDefCheck(0),
+m_NotifyRemap(0),
+m_UpdateMode(0),
+m_ErrorIfEmitOutOfOrder(0),
+m_ThreadSafetyOptions(0),
+m_ImportOption(0),
+m_LinkerOption(0),
+m_GenerateTCEAdapters(0),
+m_RuntimeVersion(0),
+m_MetadataVersion(0),
+m_MergeOptions(0),
+m_InitialSize(0),
+m_LocalRefPreservation(0)
+{}
+
+HRESULT Target_OptionValue::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.Read32(&m_DupCheck));
+ IfFailRet(reader.Read32(&m_RefToDefCheck));
+ IfFailRet(reader.Read32(&m_NotifyRemap));
+ IfFailRet(reader.Read32(&m_UpdateMode));
+ IfFailRet(reader.Read32(&m_ErrorIfEmitOutOfOrder));
+ IfFailRet(reader.Read32(&m_ThreadSafetyOptions));
+ IfFailRet(reader.Read32(&m_ImportOption));
+ IfFailRet(reader.Read32(&m_LinkerOption));
+ IfFailRet(reader.Read32(&m_GenerateTCEAdapters));
+ IfFailRet(reader.ReadPointer(&m_RuntimeVersion));
+ IfFailRet(reader.Read32(&m_MetadataVersion));
+ IfFailRet(reader.Read32(&m_MergeOptions));
+ IfFailRet(reader.Read32(&m_InitialSize));
+ IfFailRet(reader.Read32(&m_LocalRefPreservation));
+ return S_OK;
+}
+
+Target_CMiniMdRW::Target_CMiniMdRW() :
+m_pMemberRefHash(0),
+m_pMemberDefHash(0),
+m_pNamedItemHash(0),
+m_maxRid(0),
+m_limRid(0),
+m_maxIx(0),
+m_limIx(0),
+m_eGrow(0),
+m_pHandler(0),
+m_cbSaveSize(0),
+m_fIsReadOnly(FALSE),
+m_bPreSaveDone(FALSE),
+m_bSaveCompressed(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),
+dbg_m_pLock(0),
+m_fMinimalDelta(FALSE),
+m_rENCRecs(0)
+{
+ memset(&m_pLookUpHashs, 0, TBL_COUNT*sizeof(CORDB_ADDRESS));
+ memset(&m_pVS, 0, TBL_COUNT*sizeof(CORDB_ADDRESS));
+ memset(&m_bSortable, 0, TBL_COUNT*sizeof(BOOL));
+}
+
+HRESULT Target_CMiniMdRW::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_CMiniMdTemplate_CMiniMdRW::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.ReadPointer(&m_pMemberRefHash));
+ IfFailRet(reader.ReadPointer(&m_pMemberDefHash));
+ for (int i = 0; i < TBL_COUNT; i++)
+ IfFailRet(reader.ReadPointer(&m_pLookUpHashs[i]));
+ IfFailRet(reader.Read(&m_StringPoolOffsetHash));
+ IfFailRet(reader.ReadPointer(&m_pNamedItemHash));
+ IfFailRet(reader.Read32(&m_maxRid));
+ IfFailRet(reader.Read32(&m_limRid));
+ IfFailRet(reader.Read32(&m_maxIx));
+ IfFailRet(reader.Read32(&m_limIx));
+ IfFailRet(reader.Read32(&m_eGrow));
+ for (int i = 0; i < TBL_COUNT; i++)
+ IfFailRet(reader.Read(&m_Tables[i]));
+ for (int i = 0; i < TBL_COUNT; i++)
+ IfFailRet(reader.ReadPointer(&m_pVS[i]));
+ IfFailRet(reader.Read(&m_StringHeap));
+ IfFailRet(reader.Read(&m_BlobHeap));
+ IfFailRet(reader.Read(&m_UserStringHeap));
+ IfFailRet(reader.Read(&m_GuidHeap));
+ IfFailRet(reader.ReadPointer(&m_pHandler));
+ IfFailRet(reader.Read32(&m_cbSaveSize));
+ ULONG32 bitField;
+ IfFailRet(reader.Read32(&bitField));
+ m_fIsReadOnly = (bitField & 0x1) != 0;
+ m_bPreSaveDone = (bitField & 0x2) != 0;
+ m_bSaveCompressed = (bitField & 0x4) != 0;
+ m_bPostGSSMod = (bitField & 0x8) != 0;
+ IfFailRet(reader.ReadPointer(&m_pMethodMap));
+ IfFailRet(reader.ReadPointer(&m_pFieldMap));
+ IfFailRet(reader.ReadPointer(&m_pPropertyMap));
+ IfFailRet(reader.ReadPointer(&m_pEventMap));
+ IfFailRet(reader.ReadPointer(&m_pParamMap));
+ IfFailRet(reader.ReadPointer(&m_pFilterTable));
+ IfFailRet(reader.ReadPointer(&m_pHostFilter));
+ IfFailRet(reader.ReadPointer(&m_pTokenRemapManager));
+ IfFailRet(reader.Read(&m_OptionValue));
+ IfFailRet(reader.Read(&m_StartupSchema));
+ for (int i = 0; i < TBL_COUNT; i++)
+ IfFailRet(reader.Read8((BYTE*)&m_bSortable[i]));
+ if (reader.IsDefined(1)) // replace this with DEFINE__DEBUG
+ {
+ IfFailRet(reader.ReadPointer(&dbg_m_pLock));
+ }
+ IfFailRet(reader.Read8((BYTE*)&m_fMinimalDelta));
+ IfFailRet(reader.ReadPointer(&m_rENCRecs));
+ return S_OK;
+}
+
+
+
+Target_CLiteWeightStgdb_CMiniMdRW::Target_CLiteWeightStgdb_CMiniMdRW() :
+m_pvMd(0),
+m_cbMd(0)
+{
+}
+
+HRESULT Target_CLiteWeightStgdb_CMiniMdRW::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.Read(&m_MiniMd));
+ IfFailRet(reader.ReadPointer(&m_pvMd));
+ IfFailRet(reader.Read32(&m_cbMd));
+ return S_OK;
+}
+
+Target_CLiteWeightStgdbRW::Target_CLiteWeightStgdbRW() :
+m_cbSaveSize(0),
+m_bSaveCompressed(FALSE),
+m_pImage(0),
+m_dwImageSize(0),
+m_dwPEKind(0),
+m_dwMachine(0),
+m_pStreamList(0),
+m_pNextStgdb(0),
+m_eFileType(0),
+m_wszFileName(0),
+m_dwDatabaseLFT(0),
+m_dwDatabaseLFS(0)
+{}
+
+HRESULT Target_CLiteWeightStgdbRW::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(Target_CLiteWeightStgdb_CMiniMdRW::ReadFrom(reader));
+ reader.AlignBase();
+ IfFailRet(reader.Read32(&m_cbSaveSize));
+ IfFailRet(reader.Read8((BYTE*)&m_bSaveCompressed));
+ IfFailRet(reader.ReadPointer(&m_pImage));
+ IfFailRet(reader.Read32(&m_dwImageSize));
+ IfFailRet(reader.Read32(&m_dwPEKind));
+ IfFailRet(reader.Read32(&m_dwMachine));
+ IfFailRet(reader.ReadPointer(&m_pStreamList));
+ IfFailRet(reader.ReadPointer(&m_pNextStgdb));
+ IfFailRet(reader.Read32(&m_eFileType));
+ IfFailRet(reader.ReadPointer(&m_wszFileName));
+ IfFailRet(reader.Read32(&m_dwDatabaseLFT));
+ IfFailRet(reader.Read32(&m_dwDatabaseLFS));
+ IfFailRet(reader.ReadPointer(&m_pStgIO));
+ return S_OK;
+}
+
+
+
+Target_MDInternalRW::Target_MDInternalRW() :
+m_tdModule(0),
+m_cRefs(0),
+m_fOwnStgdb(FALSE),
+m_pUnk(0),
+m_pUserUnk(0),
+m_pIMetaDataHelper(0),
+m_pSemReadWrite(0),
+m_fOwnSem(FALSE)
+{
+}
+
+HRESULT Target_MDInternalRW::ReadFrom(DataTargetReader & reader)
+{
+ HRESULT hr = S_OK;
+ IfFailRet(reader.SkipPointer()); // IMDInternalImportENC vtable
+ IfFailRet(reader.SkipPointer()); // IMDCommon vtable
+ IfFailRet(reader.ReadPointer(&m_pStgdb));
+ IfFailRet(reader.Read32(&m_tdModule));
+ IfFailRet(reader.Read32(&m_cRefs));
+ IfFailRet(reader.Read8((BYTE*)&m_fOwnStgdb));
+ IfFailRet(reader.ReadPointer(&m_pUnk));
+ IfFailRet(reader.ReadPointer(&m_pUserUnk));
+ IfFailRet(reader.ReadPointer(&m_pIMetaDataHelper));
+ IfFailRet(reader.ReadPointer(&m_pSemReadWrite));
+ IfFailRet(reader.Read8((BYTE*)&m_fOwnSem));
+ return S_OK;
+}
diff --git a/src/md/datasource/targettypes.h b/src/md/datasource/targettypes.h
new file mode 100644
index 0000000000..6f7acbc595
--- /dev/null
+++ b/src/md/datasource/targettypes.h
@@ -0,0 +1,360 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// TargetTypes.h
+//
+
+//
+//*****************************************************************************
+
+#ifndef _MD_TARGET_TYPES_
+#define _MD_TARGET_TYPES_
+
+#include "datatargetreader.h"
+
+class Target_CMiniMdSchemaBase : public TargetObject
+{
+public:
+ Target_CMiniMdSchemaBase();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ ULONG32 m_ulReserved;
+ BYTE m_major;
+ BYTE m_minor;
+ BYTE m_heaps;
+ BYTE m_rid;
+ ULONG64 m_maskvalid;
+ ULONG64 m_sorted;
+};
+
+class Target_CMiniMdSchema : public Target_CMiniMdSchemaBase
+{
+public:
+ Target_CMiniMdSchema();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ ULONG32 m_cRecs[TBL_COUNT];
+ ULONG32 m_ulExtra;
+};
+
+class Target_CMiniColDef : public TargetObject
+{
+public:
+ Target_CMiniColDef();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ BYTE m_Type;
+ BYTE m_oColumn;
+ BYTE m_cbColumn;
+};
+
+class Target_CMiniTableDef : public TargetObject
+{
+public:
+ Target_CMiniTableDef();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ NewArrayHolder<Target_CMiniColDef> m_pColDefs;
+ BYTE m_cCols;
+ BYTE m_iKey;
+ BYTE m_cbRec;
+
+private:
+ // don't copy this type - avoids needing to deep copy m_pColDefs
+ Target_CMiniTableDef(const Target_CMiniTableDef & rhs) { _ASSERTE(!"Don't copy"); }
+ Target_CMiniTableDef & operator=(const Target_CMiniTableDef &)
+ {
+ _ASSERTE(!"Don't copy");
+ return *this;
+ }
+};
+
+class Target_CMiniMdBase : public TargetObject
+{
+public:
+ Target_CMiniMdBase();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ Target_CMiniMdSchema m_Schema;
+ ULONG32 m_TblCount;
+ BOOL m_fVerifiedByTrustedSource;
+ Target_CMiniTableDef m_TableDefs[TBL_COUNT];
+
+ ULONG32 m_iStringsMask;
+ ULONG32 m_iGuidsMask;
+ ULONG32 m_iBlobsMask;
+};
+
+class Target_CMiniMdTemplate_CMiniMdRW : public Target_CMiniMdBase
+{
+};
+
+class Target_MapSHash : public TargetObject
+{
+public:
+ Target_MapSHash();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ CORDB_ADDRESS m_table;
+ ULONG32 m_tableSize;
+ ULONG32 m_tableCount;
+ ULONG32 m_tableOccupied;
+ ULONG32 m_tableMax;
+};
+
+class Target_CChainedHash : public TargetObject
+{
+public:
+ Target_CChainedHash();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ CORDB_ADDRESS m_rgData;
+ ULONG32 m_iBuckets;
+ ULONG32 m_iSize;
+ ULONG32 m_iCount;
+ ULONG32 m_iMaxChain;
+ ULONG32 m_iFree;
+};
+
+class Target_CStringPoolHash : public Target_CChainedHash
+{
+public:
+ Target_CStringPoolHash();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ CORDB_ADDRESS m_Pool;
+};
+
+class Target_StgPoolSeg : public TargetObject
+{
+public:
+ Target_StgPoolSeg();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ CORDB_ADDRESS m_pSegData;
+ CORDB_ADDRESS m_pNextSeg;
+ ULONG32 m_cbSegSize;
+ ULONG32 m_cbSegNext;
+};
+
+class Target_HotHeap : public TargetObject
+{
+public:
+ Target_HotHeap();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ CORDB_ADDRESS m_pHotHeapHeader;
+};
+
+class Target_StgPoolReadOnly : public Target_StgPoolSeg
+{
+public:
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ Target_HotHeap m_HotHeap;
+};
+
+class Target_StgPool : public Target_StgPoolReadOnly
+{
+public:
+ Target_StgPool();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ ULONG32 m_ulGrowInc;
+ CORDB_ADDRESS m_pCurSeg;
+ ULONG32 m_cbCurSegOffset;
+ BOOL m_bFree;
+ BOOL m_bReadOnly;
+ ULONG32 m_nVariableAlignmentMask;
+ ULONG32 m_cbStartOffsetOfEdit;
+ BOOL m_fValidOffsetOfEdit;
+};
+
+class Target_StgStringPool : public Target_StgPool
+{
+public:
+ Target_StgStringPool();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ Target_CStringPoolHash m_Hash;
+ BOOL m_bHash;
+};
+
+class Target_StringHeapRW : public Target_StgStringPool
+{
+};
+
+class Target_CBlobPoolHash : public Target_CChainedHash
+{
+public:
+ Target_CBlobPoolHash();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ CORDB_ADDRESS m_Pool;
+};
+
+class Target_StgBlobPool : public Target_StgPool
+{
+public:
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ Target_CBlobPoolHash m_Hash;
+};
+
+class Target_BlobHeapRW : public Target_StgBlobPool
+{
+};
+
+class Target_CGuidPoolHash : public Target_CChainedHash
+{
+public:
+ Target_CGuidPoolHash();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ CORDB_ADDRESS m_Pool;
+};
+
+class Target_StgGuidPool : public Target_StgPool
+{
+public:
+ Target_StgGuidPool();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ Target_CGuidPoolHash m_Hash;
+ BOOL m_bHash;
+};
+
+class Target_GuidHeapRW : public Target_StgGuidPool
+{
+};
+
+class Target_RecordPool : public Target_StgPool
+{
+public:
+ Target_RecordPool();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ ULONG32 m_cbRec;
+};
+
+class Target_TableRW : public Target_RecordPool
+{
+};
+
+class Target_OptionValue : public TargetObject
+{
+public:
+ Target_OptionValue();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ ULONG32 m_DupCheck;
+ ULONG32 m_RefToDefCheck;
+ ULONG32 m_NotifyRemap;
+ ULONG32 m_UpdateMode;
+ ULONG32 m_ErrorIfEmitOutOfOrder;
+ ULONG32 m_ThreadSafetyOptions;
+ ULONG32 m_ImportOption;
+ ULONG32 m_LinkerOption;
+ ULONG32 m_GenerateTCEAdapters;
+ CORDB_ADDRESS m_RuntimeVersion;
+ ULONG32 m_MetadataVersion;
+ ULONG32 m_MergeOptions;
+ ULONG32 m_InitialSize;
+ ULONG32 m_LocalRefPreservation;
+};
+
+class Target_CMiniMdRW : public Target_CMiniMdTemplate_CMiniMdRW
+{
+public:
+ Target_CMiniMdRW();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ CORDB_ADDRESS m_pMemberRefHash;
+ CORDB_ADDRESS m_pMemberDefHash;
+ CORDB_ADDRESS m_pLookUpHashs[TBL_COUNT];
+ Target_MapSHash m_StringPoolOffsetHash;
+ CORDB_ADDRESS m_pNamedItemHash;
+ ULONG32 m_maxRid;
+ ULONG32 m_limRid;
+ ULONG32 m_maxIx;
+ ULONG32 m_limIx;
+ ULONG32 m_eGrow;
+ Target_TableRW m_Tables[TBL_COUNT];
+ CORDB_ADDRESS m_pVS[TBL_COUNT];
+ Target_StringHeapRW m_StringHeap;
+ Target_BlobHeapRW m_BlobHeap;
+ Target_BlobHeapRW m_UserStringHeap;
+ Target_GuidHeapRW m_GuidHeap;
+ CORDB_ADDRESS m_pHandler;
+ ULONG32 m_cbSaveSize;
+ BOOL m_fIsReadOnly;
+ BOOL m_bPreSaveDone;
+ BOOL m_bSaveCompressed;
+ BOOL m_bPostGSSMod;
+ CORDB_ADDRESS m_pMethodMap;
+ CORDB_ADDRESS m_pFieldMap;
+ CORDB_ADDRESS m_pPropertyMap;
+ CORDB_ADDRESS m_pEventMap;
+ CORDB_ADDRESS m_pParamMap;
+ CORDB_ADDRESS m_pFilterTable;
+ CORDB_ADDRESS m_pHostFilter;
+ CORDB_ADDRESS m_pTokenRemapManager;
+ Target_OptionValue m_OptionValue;
+ Target_CMiniMdSchema m_StartupSchema;
+ BYTE m_bSortable[TBL_COUNT];
+ CORDB_ADDRESS dbg_m_pLock;
+ BOOL m_fMinimalDelta;
+ CORDB_ADDRESS m_rENCRecs;
+};
+
+class Target_CLiteWeightStgdb_CMiniMdRW : public TargetObject
+{
+public:
+ Target_CLiteWeightStgdb_CMiniMdRW();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ Target_CMiniMdRW m_MiniMd;
+ CORDB_ADDRESS m_pvMd;
+ ULONG32 m_cbMd;
+};
+
+class Target_CLiteWeightStgdbRW : public Target_CLiteWeightStgdb_CMiniMdRW
+{
+public:
+ Target_CLiteWeightStgdbRW();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ ULONG32 m_cbSaveSize;
+ BOOL m_bSaveCompressed;
+ CORDB_ADDRESS m_pImage;
+ ULONG32 m_dwImageSize;
+ ULONG32 m_dwPEKind;
+ ULONG32 m_dwMachine;
+ CORDB_ADDRESS m_pStreamList;
+ CORDB_ADDRESS m_pNextStgdb;
+ ULONG32 m_eFileType;
+ CORDB_ADDRESS m_wszFileName;
+ ULONG32 m_dwDatabaseLFT;
+ ULONG32 m_dwDatabaseLFS;
+ CORDB_ADDRESS m_pStgIO;
+};
+
+class Target_MDInternalRW : public TargetObject
+{
+public:
+ Target_MDInternalRW();
+ virtual HRESULT ReadFrom(DataTargetReader & reader);
+
+ Target_CLiteWeightStgdbRW m_pStgdb;
+ ULONG32 m_tdModule;
+ ULONG32 m_cRefs;
+ BOOL m_fOwnStgdb;
+ CORDB_ADDRESS m_pUnk;
+ CORDB_ADDRESS m_pUserUnk;
+ CORDB_ADDRESS m_pIMetaDataHelper;
+ CORDB_ADDRESS m_pSemReadWrite;
+ BOOL m_fOwnSem;
+};
+
+#endif
diff --git a/src/md/debug_metadata.h b/src/md/debug_metadata.h
new file mode 100644
index 0000000000..cf1d312e5e
--- /dev/null
+++ b/src/md/debug_metadata.h
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: Debug_MetaData.h
+//
+
+//
+// This file defines special macros for debugging MetaData (even in special retail builds).
+// The level of debugging is set by these (input) macros:
+// * code:#_DEBUG_METADATA
+// * code:#_DEBUG_MDSCHEMA
+//
+//
+// #_DEBUG_METADATA
+// _DEBUG_METADATA ... Enables debugging information in MetaData implementation. It's useful for debugging
+// retail builds in MetaData (when using CHK build is too slow).
+// Note: Enabled by default if _DEBUG is defined (see code:#DefaultSetting_DEBUG_METADATA), can be
+// enabled externally/explicitly also in retail builds (without _DEBUG defined).
+//
+// Defines macros (see code:#Macros_DEBUG_METADATA):
+// * code:#INDEBUG_MD
+// * code:#COMMA_INDEBUG_MD
+// * code:#INDEBUG_MD_COMMA
+//
+// #_DEBUG_MDSCHEMA
+// _DEBUG_MDSCHEMA ... Enables additional debugging of MetaData schema.
+// Note: Allowed to be enabled only if _DEBUG is defined (see code:#Check_DEBUG_MDSCHEMA).
+//
+// Defines macros (see code:#Macros_DEBUG_MDSCHEMA):
+// * code:#_ASSERTE_MDSCHEMA
+//
+// ======================================================================================
+
+#pragma once
+
+// Include for REGUTIL class used in Debug_ReportError
+#include <utilcode.h>
+
+// --------------------------------------------------------------------------------------
+//#DefaultSetting_DEBUG_METADATA
+//
+// Enable _DEBUG_METADATA by default if _DEBUG is defined (code:#_DEBUG_METADATA).
+//
+#ifdef _DEBUG
+ #define _DEBUG_METADATA
+#endif //_DEBUG
+
+// --------------------------------------------------------------------------------------
+//#Macros_DEBUG_METADATA
+//
+// Define macros for MetaData implementation debugging (see code:#_DEBUG_METADATA).
+//
+#ifdef _DEBUG_METADATA
+ //#INDEBUG_MD
+ #define INDEBUG_MD(expr) expr
+ //#COMMA_INDEBUG_MD
+ #define COMMA_INDEBUG_MD(expr) , expr
+ //#INDEBUG_MD_COMMA
+ #define INDEBUG_MD_COMMA(expr) expr,
+
+ #define Debug_ReportError(strMessage) \
+ do { \
+ if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 0)) \
+ { _ASSERTE_MSG(FALSE, (strMessage)); } \
+ } while(0)
+ #define Debug_ReportInternalError(strMessage) _ASSERTE_MSG(FALSE, (strMessage))
+#else //!_DEBUG_METADATA
+ #define INDEBUG_MD(expr)
+ #define COMMA_INDEBUG_MD(expr)
+ #define INDEBUG_MD_COMMA(expr)
+
+ #define Debug_ReportError(strMessage)
+ #define Debug_ReportInternalError(strMessage) _ASSERTE(!strMessage)
+#endif //!_DEBUG_METADATA
+
+// --------------------------------------------------------------------------------------
+//#Check_DEBUG_MDSCHEMA
+//
+// Check that _DEBUG_MDSCHEMA is defined only if _DEBUG is defined (see code:#_DEBUG_MDSCHEMA).
+//
+#ifdef _DEBUG_MDSCHEMA
+ #ifndef _DEBUG
+ #error _DEBUG_MDSCHEMA is defined while _DEBUG is not defined.
+ #endif //!_DEBUG
+#endif //_DEBUG_MDSCHEMA
+
+// --------------------------------------------------------------------------------------
+//#Macros_DEBUG_MDSCHEMA
+//
+// Define macros for MetaData schema debugging (see code:#_DEBUG_MDSCHEMA).
+//
+#ifdef _DEBUG_MDSCHEMA
+ //#_ASSERTE_MDSCHEMA
+ // This assert is useful only to catch errors in schema (tables and columns) definitions. It is useful e.g.
+ // for verifying consistency between table record classes (e.g. code:MethodDefRecord) and columns'
+ // offsets/sizes as defined in code:ColumnDefinition.
+ #define _ASSERTE_MDSCHEMA(expr) _ASSERTE(expr)
+#else //!_DEBUG_MDSCHEMA
+ #define _ASSERTE_MDSCHEMA(expr)
+#endif //!_DEBUG_MDSCHEMA
diff --git a/src/md/dirs.proj b/src/md/dirs.proj
new file mode 100644
index 0000000000..31626b9906
--- /dev/null
+++ b/src/md/dirs.proj
@@ -0,0 +1,25 @@
+<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="hotdata\dirs.proj" />
+ <ProjectFile Include="enc\dirs.proj" />
+ <ProjectFile Include="runtime\dirs.proj" />
+ <ProjectFile Include="compiler\dirs.proj" />
+ <ProjectFile Include="winmd\dirs.proj" />
+ <ProjectFile Include="datasource\dirs.proj" />
+ <ProjectFile Include="ceefilegen\ceefgen.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
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..8e388f526e
--- /dev/null
+++ b/src/md/enc/CMakeLists.txt
@@ -0,0 +1,22 @@
+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)
+add_subdirectory(dbi)
+add_subdirectory(crossgen)
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/CMakeLists.txt b/src/md/enc/crossgen/CMakeLists.txt
new file mode 100644
index 0000000000..f01451a337
--- /dev/null
+++ b/src/md/enc/crossgen/CMakeLists.txt
@@ -0,0 +1,5 @@
+include(${CLR_DIR}/crossgen.cmake)
+include(../../md_wks.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDRUNTIMERW_SOURCES)
+add_library_clr(mdruntimerw_crossgen ${MDRUNTIMERW_SOURCES})
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..9bef4636f7
--- /dev/null
+++ b/src/md/enc/dac/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+include(${CLR_DIR}/dac.cmake)
+include(../../md_dac.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDRUNTIMERW_SOURCES)
+add_library_clr(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..5be16bc28c
--- /dev/null
+++ b/src/md/enc/dbi/CMakeLists.txt
@@ -0,0 +1,4 @@
+include(../../md_dbi.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDRUNTIMERW_SOURCES)
+add_library_clr(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..1c92bb5086
--- /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</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..faeb0d9882
--- /dev/null
+++ b/src/md/enc/imptlb.cpp
@@ -0,0 +1,8057 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ===========================================================================
+// 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 occurred.
+ 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..12779f59c0
--- /dev/null
+++ b/src/md/enc/liteweightstgdbrw.cpp
@@ -0,0 +1,1278 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+
+//
+// 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.
+ void *pData;
+ ULONG cbData;
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ if (m_pStgIO == NULL)
+ IfFailGo(COR_E_NOTSUPPORTED);
+#endif
+
+ pData = m_pStgIO->m_pData;
+ 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)
+{
+#ifdef FEATURE_CORECLR
+ return TRUE;
+#else
+ 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;
+#endif
+} // CLiteWeightStgdbRW::IsValidFileNameLength
diff --git a/src/md/enc/mdinternalrw.cpp b/src/md/enc/mdinternalrw.cpp
new file mode 100644
index 0000000000..02fb407358
--- /dev/null
+++ b/src/md/enc/mdinternalrw.cpp
@@ -0,0 +1,4355 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ===========================================================================
+// 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..4d972827ca
--- /dev/null
+++ b/src/md/enc/metamodelenc.cpp
@@ -0,0 +1,471 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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;
+ }
+
+#ifndef FEATURE_CORECLR
+ // 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) &&
+ CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_UseMinimalDeltas) &&
+ (GuidDelta != GuidBase))
+ {
+ _ASSERTE(!"The Delta MetaData is based on a different generation than the current MetaData.");
+ return E_INVALIDARG;
+ }
+#endif //!FEATURE_CORECLR
+
+ // 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..f9002b6fa9
--- /dev/null
+++ b/src/md/enc/metamodelrw.cpp
@@ -0,0 +1,8893 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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] = ~0U;
+
+ 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..3bb6c413cd
--- /dev/null
+++ b/src/md/enc/peparse.cpp
@@ -0,0 +1,148 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#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..874d972716
--- /dev/null
+++ b/src/md/enc/rwutil.cpp
@@ -0,0 +1,1440 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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)
+{
+ if (ppUnk == NULL)
+ return E_INVALIDARG;
+
+ if (IsEqualIID(riid, IID_IMapToken))
+ {
+ //*ppUnk = (IUnknown *) (IMapToken *) this;
+ // it should return the accurate type requested,
+ // if IUnknown is returned, it will finally converted to IMapToken*
+ *ppUnk = (IMapToken *) this;
+ }
+ else if (IsEqualIID(riid, IID_IUnknown))
+ {
+ // add query handling for IUnknown
+ // this upcasting (converting a derived-class
+ // reference or pointer to a base-class) is safe
+ *ppUnk = (IUnknown *) this;
+ }
+ else
+ {
+ *ppUnk = NULL;
+ 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)
+{
+ if (ppUnk == NULL)
+ return E_INVALIDARG;
+
+ if (IsEqualIID(riid, IID_IMapToken))
+ {
+ *ppUnk = (IMapToken *) this;
+ }
+ else if (IsEqualIID(riid, IID_IUnknown))
+ {
+ *ppUnk = (IUnknown *) this;
+ }
+ else
+ {
+ *ppUnk = NULL;
+ 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..ff341e19a7
--- /dev/null
+++ b/src/md/enc/stdafx.cpp
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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..31ab3861be
--- /dev/null
+++ b/src/md/enc/stdafx.h
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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..27d5ff2c5d
--- /dev/null
+++ b/src/md/enc/stgio.cpp
@@ -0,0 +1,1437 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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_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.
+ // Also, open for read/write needs a full backing store.
+ if ((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)
+ {
+ // 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, nullptr)) == 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..436b3d72e3
--- /dev/null
+++ b/src/md/enc/stgtiggerstorage.cpp
@@ -0,0 +1,1025 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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..383e969636
--- /dev/null
+++ b/src/md/enc/stgtiggerstream.cpp
@@ -0,0 +1,137 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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..df7664187d
--- /dev/null
+++ b/src/md/enc/wks/CMakeLists.txt
@@ -0,0 +1,4 @@
+include(../../md_wks.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDRUNTIMERW_SOURCES)
+add_library_clr(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>
diff --git a/src/md/errors_metadata.h b/src/md/errors_metadata.h
new file mode 100644
index 0000000000..cc1edf617a
--- /dev/null
+++ b/src/md/errors_metadata.h
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <corerror.h>
+#include <winerror.h>
+
+// Index into heap/table is too large.
+#define METADATA_E_INDEX_NOTFOUND CLDB_E_INDEX_NOTFOUND
+ // Options:
+ // * CLDB_E_INDEX_NOTFOUND
+ // * VLDTR_E_BLOB_INVALID
+ // * VLDTR_E_GUID_INVALID
+ // * VLDTR_E_STRING_INVALID
+ // * VLDTR_E_RID_OUTOFRANGE
+
+// Internal error, it's a runtime assert check to avoid security errors. If this is returned, then there's
+// something wrong with MetaData code.
+#define METADATA_E_INTERNAL_ERROR CLDB_E_INTERNALERROR
+ // Options:
+ // * CLDB_E_INTERNALERROR
+ // * COR_E_EXECUTIONENGINE
+
+// MetaData space (heap/table) is full, cannot store more items.
+#define METADATA_E_HEAP_FULL META_E_STRINGSPACE_FULL
+ // Options:
+ // * META_E_STRINGSPACE_FULL
+ // * CLDB_E_TOO_BIG
+
+// Invalid heap (blob, user string) data encoding.
+#define METADATA_E_INVALID_HEAP_DATA META_E_BADMETADATA
+ // Options:
+ // * META_E_BADMETADATA
+ // * META_E_CA_INVALID_BLOB
+ // * META_E_BAD_SIGNATURE
+ // * CLDB_E_FILE_CORRUPT
+ // * COR_E_BADIMAGEFORMAT
+
+// The data is too big to encode (the string/blob is larger than possible heap size).
+#define METADATA_E_DATA_TOO_BIG CLDB_E_TOO_BIG
+ // Options:
+ // * CLDB_E_TOO_BIG
+
+// Invalid MetaData format (headers, etc.).
+#define METADATA_E_INVALID_FORMAT COR_E_BADIMAGEFORMAT
+ // Options:
+ // * META_E_BADMETADATA
+ // * META_E_CA_INVALID_BLOB
+ // * META_E_BAD_SIGNATURE
+ // * CLDB_E_FILE_CORRUPT
+ // * COR_E_BADIMAGEFORMAT
+
+//
+// Other used error codes:
+// * COR_E_OUTOFMEMORY ... defined as E_OUTOFMEMORY
+// Alternatives:
+// * E_OUTOFMEMORY (from IfNullGo/IfNullRet macros)
+// * COR_E_OVERFLOW
+// Alternatives:
+// * COR_E_ARITHMETIC
+//
diff --git a/src/md/export.h b/src/md/export.h
new file mode 100644
index 0000000000..a2a228f131
--- /dev/null
+++ b/src/md/export.h
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: export.h
+//
+
+//
+// Popular types/macros defined in MetaData directory (no subdirectories).
+// It's supposed to be included from other subcomponents (i.e. subdirectories), not from this directory.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "debug_metadata.h"
+#include "errors_metadata.h"
+#include "datablob.h"
diff --git a/src/md/external.h b/src/md/external.h
new file mode 100644
index 0000000000..7a93aa51df
--- /dev/null
+++ b/src/md/external.h
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: external.h
+//
+
+//
+// External types used in MetaData\* subcomponent classes.
+//
+// ======================================================================================
+
+#pragma once
+
+#include <clrtypes.h>
+#include <safemath.h>
+#include <new.hpp>
diff --git a/src/md/heaps/.gitmirror b/src/md/heaps/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/heaps/.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/heaps/blobheap.h b/src/md/heaps/blobheap.h
new file mode 100644
index 0000000000..8765537bd8
--- /dev/null
+++ b/src/md/heaps/blobheap.h
@@ -0,0 +1,324 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: BlobHeap.h
+//
+
+//
+// Classes code:MetaData::BlobHeapRO and code:MetaData::BlobHeapRW represent #Blob heap.
+// The #Blob heap stores size-prefixed data chunks (as defined in CLI ECMA specification). Elements are
+// indexed by code:#BlobHeapIndex.
+//
+//#BlobHeapIndex
+// Blob heap indexes are 0-based. They are stored the same way in the table columns (i.e. there is no
+// 0-based vs. 1-based index difference as in table record indexes code:TableRecordStorage).
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents read-only #Blob heap with all utility methods.
+//
+class BlobHeapRO
+{
+ friend class BlobHeapRW;
+
+private:
+ //
+ // Private data
+ //
+
+ // The storage of blobs.
+ StgBlobPoolReadOnly m_BlobPool;
+
+public:
+ //
+ // Initialization
+ //
+
+ __checkReturn
+ HRESULT Initialize(
+ DataBlob sourceData,
+ BOOL fCopyData)
+ {
+ _ASSERTE(!fCopyData);
+ return m_BlobPool.InitOnMemReadOnly((void *)sourceData.GetDataPointer(), sourceData.GetSize());
+ }
+
+#ifdef FEATURE_PREJIT
+ // Can be called multiple times.
+ inline void InitializeHotData(
+ HotHeap hotHeap)
+ {
+ m_BlobPool.InitHotData(hotHeap);
+ }
+#endif //FEATURE_PREJIT
+
+ inline void Delete()
+ {
+ return m_BlobPool.Uninit();
+ }
+
+public:
+ //
+ // Getters
+ //
+
+ __checkReturn
+ inline HRESULT GetBlob(
+ UINT32 nIndex,
+ __out DataBlob *pData)
+ {
+ return m_BlobPool.GetBlob(nIndex, pData);
+ }
+
+ __checkReturn
+ inline HRESULT GetAllData(
+ __inout DataBlob *pData)
+ {
+ return m_BlobPool.GetDataReadOnly(0, pData);
+ }
+
+ // Gets raw size (in bytes) of the represented blob data.
+ inline UINT32 GetUnalignedSize() const
+ {
+ return m_BlobPool.GetPoolSize();
+ }
+
+ // Returns TRUE if the blob index (nIndex, see code:#BlobHeapIndex) is valid (i.e. in the blob
+ // heap).
+ inline BOOL IsValidIndex(UINT32 nIndex) const
+ {
+ return const_cast<StgBlobPoolReadOnly &>(m_BlobPool).IsValidCookie(nIndex);
+ }
+
+}; // class BlobHeapRO
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents read-write #Blob heap with all utility methods.
+//
+class BlobHeapRW
+{
+private:
+ //
+ // Private data
+ //
+
+ // The storage of blobs.
+ StgBlobPool m_BlobPool;
+
+public:
+ //
+ // Initialization
+ //
+
+ __checkReturn
+ HRESULT InitializeEmpty(
+ UINT32 cbAllocationSize
+ COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite))
+ {
+ return m_BlobPool.InitNew(cbAllocationSize, 0, TRUE);
+ }
+ __checkReturn
+ HRESULT InitializeEmpty_WithItemsCount(
+ UINT32 cbAllocationSize,
+ UINT32 cItemsCount
+ COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite))
+ {
+ return m_BlobPool.InitNew(cbAllocationSize, cItemsCount, TRUE);
+ }
+ __checkReturn
+ HRESULT InitializeEmpty_WithoutDefaultEmptyBlob(
+ UINT32 cbAllocationSize
+ COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite))
+ {
+ return m_BlobPool.InitNew(cbAllocationSize, 0, FALSE);
+ }
+
+ __checkReturn
+ HRESULT Initialize(
+ DataBlob sourceData,
+ BOOL fCopyData)
+ {
+ return m_BlobPool.InitOnMem((void *)sourceData.GetDataPointer(), sourceData.GetSize(), !fCopyData);
+ }
+ __checkReturn
+ HRESULT InitializeFromBlobHeap(
+ const BlobHeapRO *pSourceBlobHeap,
+ BOOL fCopyData)
+ {
+ return m_BlobPool.InitOnMem(
+ (void *)pSourceBlobHeap->m_BlobPool.GetSegData(),
+ pSourceBlobHeap->m_BlobPool.GetDataSize(),
+ !fCopyData);
+ }
+ __checkReturn
+ HRESULT InitializeFromBlobHeap(
+ const BlobHeapRW *pSourceBlobHeap,
+ BOOL fCopyData)
+ {
+ return m_BlobPool.InitOnMem(
+ (void *)pSourceBlobHeap->m_BlobPool.GetSegData(),
+ pSourceBlobHeap->m_BlobPool.GetDataSize(),
+ !fCopyData);
+ }
+
+ // Destroys the blob heap and all its allocated data. Can run on uninitialized blob heap.
+ inline void Delete()
+ {
+ return m_BlobPool.Uninit();
+ }
+
+public:
+ //
+ // Getters
+ //
+
+ __checkReturn
+ inline HRESULT GetBlob(
+ UINT32 nIndex,
+ __out DataBlob *pData)
+ {
+ return m_BlobPool.GetBlob(nIndex, pData);
+ }
+
+ // Gets the blob with its size-prefix at index (nIndex, see code:#BlobHeapIndex), or returns error.
+ //
+ // Returns S_OK and the data (*pData) at index (nIndex). The end of the data marks the end of the blob.
+ // Returns error code otherwise (and clears *pData).
+ //
+ // User of this API shouldn't access memory behind the data buffer (*pData).
+ __checkReturn
+ inline HRESULT GetBlobWithSizePrefix(
+ UINT32 nIndex,
+ __out DataBlob *pData)
+ {
+ return m_BlobPool.GetBlobWithSizePrefix(nIndex, pData);
+ }
+
+ // Gets raw size (in bytes) of the represented blob data. Doesn't align the size as code:GetAlignedSize.
+ inline UINT32 GetUnalignedSize() const
+ {
+ return m_BlobPool.GetRawSize();
+ }
+ // Gets size (in bytes) aligned up to 4-bytes of the represented blob data.
+ // Fills *pcbSize with 0 on error.
+ __checkReturn
+ inline HRESULT GetAlignedSize(
+ __out UINT32 *pcbSize) const
+ {
+ return m_BlobPool.GetSaveSize(pcbSize);
+ }
+ // Returns TRUE if the blob heap is empty (even if it contains only default empty blob).
+ inline BOOL IsEmpty() const
+ {
+ return const_cast<StgBlobPool &>(m_BlobPool).IsEmpty();
+ }
+
+ // Returns TRUE if the blob index (nIndex, see code:#BlobHeapIndex) is valid (i.e. in the blob
+ // heap).
+ inline BOOL IsValidIndex(UINT32 nIndex) const
+ {
+ return const_cast<StgBlobPool &>(m_BlobPool).IsValidCookie(nIndex);
+ }
+
+ __checkReturn
+ HRESULT SaveToStream_Aligned(
+ UINT32 nStartIndex,
+ __in IStream *pStream) const
+ {
+ if (nStartIndex == 0)
+ {
+ return const_cast<StgBlobPool &>(m_BlobPool).PersistToStream(pStream);
+ }
+
+ if (nStartIndex == m_BlobPool.GetRawSize())
+ {
+ _ASSERTE(!m_BlobPool.HaveEdits());
+ return S_OK;
+ }
+ _ASSERTE(m_BlobPool.HaveEdits());
+ _ASSERTE(nStartIndex == m_BlobPool.GetOffsetOfEdit());
+ return const_cast<StgBlobPool &>(m_BlobPool).PersistPartialToStream(pStream, nStartIndex);
+ }
+
+public:
+ //
+ // Heap modifications
+ //
+
+ __checkReturn
+ inline HRESULT AddBlob(
+ DataBlob data,
+ __out UINT32 *pnIndex)
+ {
+ return m_BlobPool.AddBlob(&data, pnIndex);
+ }
+
+ __checkReturn
+ HRESULT AddBlobHeap(
+ const BlobHeapRW *pSourceBlobHeap,
+ UINT32 nStartSourceIndex)
+ {
+ return m_BlobPool.CopyPool(
+ nStartSourceIndex,
+ &pSourceBlobHeap->m_BlobPool);
+ } // BlobHeapRW::AddBlobHeap
+
+ __checkReturn
+ inline HRESULT MakeWritable()
+ {
+ return m_BlobPool.ConvertToRW();
+ }
+
+public:
+ //
+ // Tracking of heap modifications for EnC
+ //
+
+ //#EnCSessionTracking
+ // EnC session starts automatically with initialization (code:Initialize or code:InitializeEmpty) or by
+ // user's explicit call to code:StartNewEnCSession. The heap stores its actual data size, so we can find
+ // out if some data were added later.
+
+ // Gets heap size (in bytes) from the beginning of the last EnC session (code:#EnCSessionTracking).
+ inline UINT32 GetEnCSessionStartHeapSize() const
+ {
+ if (m_BlobPool.HaveEdits())
+ {
+ return m_BlobPool.GetOffsetOfEdit();
+ }
+
+ return m_BlobPool.GetRawSize();
+ }
+ // Starts new EnC session (code:#EnCSessionTracking).
+ inline void StartNewEnCSession()
+ {
+ m_BlobPool.ResetOffsetOfEdit();
+ }
+ // Gets size (in bytes) aligned to 4-bytes of adds made from the beginning of the last EnC session.
+ __checkReturn
+ inline HRESULT GetEnCSessionAddedHeapSize_Aligned(
+ __out UINT32 *pcbSize) const
+ {
+ if (m_BlobPool.HaveEdits())
+ {
+ return m_BlobPool.GetEditSaveSize(pcbSize);
+ }
+
+ *pcbSize = 0;
+ return S_OK;
+ }
+
+}; // class BlobHeapRW
+
+}; // namespace MetaData
diff --git a/src/md/heaps/export.h b/src/md/heaps/export.h
new file mode 100644
index 0000000000..b14023706d
--- /dev/null
+++ b/src/md/heaps/export.h
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: export.h
+//
+
+//
+// Popular types defined in MetaData\Heaps directory.
+// It's supposed to be included from other (MetaData) subcomponents, not from this directory.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "stringheap.h"
+#include "blobheap.h"
+#include "guidheap.h"
diff --git a/src/md/heaps/external.h b/src/md/heaps/external.h
new file mode 100644
index 0000000000..9b5362c761
--- /dev/null
+++ b/src/md/heaps/external.h
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: external.h
+//
+
+//
+// External types used in MetaData\Heaps subcomponent classes.
+// This file is used for precompiled headers, so it has to be included at the beginning of every .cpp in
+// this directory.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "../external.h"
+#include "../export.h"
+
+#include <stgpool.h>
+#include <metamodelpub.h>
+#include <utilcode.h>
diff --git a/src/md/heaps/guidheap.h b/src/md/heaps/guidheap.h
new file mode 100644
index 0000000000..57f0147871
--- /dev/null
+++ b/src/md/heaps/guidheap.h
@@ -0,0 +1,259 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: GuidHeap.h
+//
+
+//
+// Classes code:MetaData::GuidHeapRO and code:MetaData::GuidHeapRW represent #GUID heap.
+// The #GUID heap stores size-prefixed data chunks (as defined in CLI ECMA specification). Elements are
+// indexed by code:#GuidHeapIndex.
+//
+//#GuidHeapIndex
+// Guid heap indexes are 1-based and they are really indexes, not offsets (as in string heap).
+// The indexes correspond to:
+// * 0 ... invalid index,
+// * 1 ... data offset 0,
+// * 2 ... data offset sizeof(GUID),
+// * n ... data offset (n-1)*sizeof(GUID).
+// Note that this class provides only translation from 1-based index to 0-based index. The translation of
+// 0-based index to data offset is done in code:GuidHeapStorage::GetGuid.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents read-only #GUID heap with all utility methods.
+//
+class GuidHeapRO
+{
+ friend class GuidHeapRW;
+
+private:
+ //
+ // Private data
+ //
+
+ // The storage of guids.
+ StgPoolReadOnly m_GuidPool;
+
+public:
+ //
+ // Initialization
+ //
+
+ __checkReturn
+ inline HRESULT Initialize(
+ DataBlob sourceData,
+ BOOL fCopyData)
+ {
+ _ASSERTE(!fCopyData);
+ return m_GuidPool.InitOnMemReadOnly((void *)sourceData.GetDataPointer(), sourceData.GetSize());
+ }
+
+#ifdef FEATURE_PREJIT
+ // Can be called multiple times.
+ inline void InitializeHotData(
+ HotHeap hotHeap)
+ {
+ m_GuidPool.InitHotData(hotHeap);
+ }
+#endif //FEATURE_PREJIT
+
+ // Destroys the guid heap and all its allocated data. Can run on uninitialized guid heap.
+ inline void Delete()
+ {
+ return m_GuidPool.Uninit();
+ }
+
+public:
+ //
+ // Getters
+ //
+
+ // Gets pointer to guid (*ppGuid) at index (nIndex, see code:#GuidHeapIndex).
+ // Returns error code for invalid index (0, or too large index) and sets *ppGuid to NULL.
+ __checkReturn
+ inline HRESULT GetGuid(
+ UINT32 nIndex,
+ __deref_out GUID UNALIGNED **ppGuid)
+ {
+ return m_GuidPool.GetGuid(nIndex, ppGuid);
+ }
+ __checkReturn
+ inline HRESULT GetGuid(
+ UINT32 nIndex,
+ __deref_out const GUID UNALIGNED **ppGuid) const
+ {
+ return const_cast<StgPoolReadOnly &>(m_GuidPool).GetGuid(nIndex, const_cast<GUID UNALIGNED **>(ppGuid));
+ }
+
+ inline UINT32 GetSize() const
+ {
+ return const_cast<StgPoolReadOnly &>(m_GuidPool).GetPoolSize();
+ }
+
+}; // class GuidHeapRO
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents read-write #GUID heap with all utility methods.
+//
+class GuidHeapRW
+{
+private:
+ //
+ // Private data
+ //
+
+ // The storage of guids.
+ StgGuidPool m_GuidPool;
+
+public:
+ //
+ // Initialization
+ //
+
+ __checkReturn
+ inline HRESULT InitializeEmpty(
+ UINT32 cbAllocationSize
+ COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite))
+ {
+ return m_GuidPool.InitNew(cbAllocationSize, 0);
+ }
+ __checkReturn
+ inline HRESULT InitializeEmpty_WithItemsCount(
+ UINT32 cbAllocationSize,
+ UINT32 cItemsCount
+ COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite))
+ {
+ return m_GuidPool.InitNew(cbAllocationSize, cItemsCount);
+ }
+ __checkReturn
+ inline HRESULT Initialize(
+ DataBlob sourceData,
+ BOOL fCopyData)
+ {
+ return m_GuidPool.InitOnMem((void *)sourceData.GetDataPointer(), sourceData.GetSize(), !fCopyData);
+ }
+
+ __checkReturn
+ inline HRESULT InitializeFromGuidHeap(
+ const GuidHeapRO *pSourceGuidHeap,
+ BOOL fCopyData)
+ {
+ return m_GuidPool.InitOnMem(
+ (void *)pSourceGuidHeap->m_GuidPool.GetSegData(),
+ pSourceGuidHeap->m_GuidPool.GetDataSize(),
+ !fCopyData);
+ }
+ __checkReturn
+ inline HRESULT InitializeFromGuidHeap(
+ const GuidHeapRW *pSourceGuidHeap,
+ BOOL fCopyData)
+ {
+ return m_GuidPool.InitOnMem(
+ (void *)pSourceGuidHeap->m_GuidPool.GetSegData(),
+ pSourceGuidHeap->m_GuidPool.GetDataSize(),
+ !fCopyData);
+ }
+
+ // Destroys the guid heap and all its allocated data. Can run on uninitialized guid heap.
+ inline void Delete()
+ {
+ return m_GuidPool.Uninit();
+ }
+
+public:
+ //
+ // Getters
+ //
+
+ __checkReturn
+ inline HRESULT GetGuid(
+ UINT32 nIndex,
+ __deref_out GUID UNALIGNED **ppGuid)
+ {
+ return m_GuidPool.GetGuid(nIndex, ppGuid);
+ }
+ __checkReturn
+ inline HRESULT GetGuid(
+ UINT32 nIndex,
+ __deref_out const GUID UNALIGNED **ppGuid) const
+ {
+ return const_cast<StgGuidPool &>(m_GuidPool).GetGuid(nIndex, const_cast<GUID UNALIGNED **>(ppGuid));
+ }
+
+ // Gets size (in bytes) of the represented guid data. Note: the size is everytime aligned.
+ inline UINT32 GetSize() const
+ {
+ _ASSERTE(m_GuidPool.GetRawSize() % sizeof(GUID) == 0);
+ return m_GuidPool.GetRawSize();
+ }
+
+ // Returns TRUE if the guid heap is empty.
+ inline BOOL IsEmpty() const
+ {
+ return const_cast<StgGuidPool &>(m_GuidPool).IsEmpty();
+ }
+
+ // Returns TRUE if the guid index (nIndex, see code:#GuidHeapIndex) is valid (i.e. is in the guid
+ // heap).
+ // Note: index 0 is considered invalid.
+ inline BOOL IsValidIndex(UINT32 nIndex) const
+ {
+ return const_cast<StgGuidPool &>(m_GuidPool).IsValidCookie(nIndex);
+ }
+
+ __checkReturn
+ inline HRESULT SaveToStream(
+ __in IStream *pStream) const
+ {
+ return const_cast<StgGuidPool &>(m_GuidPool).PersistToStream(pStream);
+ }
+
+public:
+ //
+ // Heap modifications
+ //
+
+ // Adds guid (*pGuid) to the end of the heap.
+ // Returns S_OK and index (*pnIndex, see code:#GuidHeapIndex) of added GUID.
+ // Returns error code otherwise (and fills *pnIndex with 0 - an invalid GUID index).
+ __checkReturn
+ inline HRESULT AddGuid(
+ __in const GUID *pGuid,
+ __out UINT32 *pnIndex)
+ {
+ return m_GuidPool.AddGuid(pGuid, pnIndex);
+ }
+
+ // Adds data from *pSourceGuidHeap starting at index (nStartSourceIndex) to the guid heap.
+ // Returns S_OK (even if the source is empty) or error code.
+ __checkReturn
+ HRESULT AddGuidHeap(
+ const GuidHeapRW *pSourceGuidHeap,
+ UINT32 nStartSourceIndex)
+ {
+ return m_GuidPool.CopyPool(
+ nStartSourceIndex,
+ &pSourceGuidHeap->m_GuidPool);
+ } // GuidHeapRW::AddGuidHeap
+
+ __checkReturn
+ inline HRESULT MakeWritable()
+ {
+ return m_GuidPool.ConvertToRW();
+ }
+
+}; // class GuidHeapRW
+
+}; // namespace MetaData
diff --git a/src/md/heaps/stringheap.h b/src/md/heaps/stringheap.h
new file mode 100644
index 0000000000..b5353a12fd
--- /dev/null
+++ b/src/md/heaps/stringheap.h
@@ -0,0 +1,307 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: StringHeap.h
+//
+
+//
+// Classes code:MetaData::StringHeapRO and code:MetaData::StringHeapRW represent #String heap.
+// The #String heap stores null-terminated UTF-8 strings (as defined in CLI ECMA specification). Elements
+// are indexed by code:#StringHeapIndex.
+//
+//#StringHeapIndex
+// String heap indexes are 0-based. They are stored the same way in the table columns (i.e. there is no
+// 0-based vs. 1-based index difference as in table record indexes code:TableRecordStorage).
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents read-only #String heap with all utility methods.
+//
+class StringHeapRO
+{
+ friend class StringHeapRW;
+
+private:
+ //
+ // Private data
+ //
+
+ // The storage of strings.
+ StgPoolReadOnly m_StringPool;
+
+public:
+ //
+ // Initialization
+ //
+
+ __checkReturn
+ inline HRESULT Initialize(
+ DataBlob sourceData,
+ BOOL fCopyData)
+ {
+ _ASSERTE(!fCopyData);
+ return m_StringPool.InitOnMemReadOnly((void *)sourceData.GetDataPointer(), sourceData.GetSize());
+ }
+
+#ifdef FEATURE_PREJIT
+ // Can be called multiple times.
+ inline void InitializeHotData(
+ HotHeap hotHeap)
+ {
+ m_StringPool.InitHotData(hotHeap);
+ }
+#endif //FEATURE_PREJIT
+
+ inline void Delete()
+ {
+ return m_StringPool.Uninit();
+ }
+
+public:
+ //
+ // Getters
+ //
+
+ __checkReturn
+ inline HRESULT GetString(
+ UINT32 nIndex,
+ __deref_out_z LPCSTR *pszString) const
+ {
+ return const_cast<StgPoolReadOnly &>(m_StringPool).GetString(
+ nIndex,
+ pszString);
+ }
+
+ // Gets raw size (in bytes) of the represented strings.
+ inline UINT32 GetUnalignedSize() const
+ {
+ return m_StringPool.GetPoolSize();
+ }
+
+}; // class StringHeapRO
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents read-write #String heap with all utility methods.
+//
+class StringHeapRW
+{
+private:
+ //
+ // Private data
+ //
+
+ // The storage of strings.
+ StgStringPool m_StringPool;
+
+public:
+ //
+ // Initialization
+ //
+
+ __checkReturn
+ inline HRESULT InitializeEmpty(
+ UINT32 cbAllocationSize
+ COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite))
+ {
+ return m_StringPool.InitNew(cbAllocationSize, 0);
+ }
+ __checkReturn
+ inline HRESULT InitializeEmpty_WithItemsCount(
+ UINT32 cbAllocationSize,
+ UINT32 cItemsCount
+ COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite))
+ {
+ return m_StringPool.InitNew(cbAllocationSize, cItemsCount);
+ }
+ __checkReturn
+ inline HRESULT Initialize(
+ DataBlob sourceData,
+ BOOL fCopyData)
+ {
+ return m_StringPool.InitOnMem((void *)sourceData.GetDataPointer(), sourceData.GetSize(), !fCopyData);
+ }
+ __checkReturn
+ inline HRESULT InitializeFromStringHeap(
+ const StringHeapRO *pSourceStringHeap,
+ BOOL fCopyData)
+ {
+ return m_StringPool.InitOnMem(
+ (void *)pSourceStringHeap->m_StringPool.GetSegData(),
+ pSourceStringHeap->m_StringPool.GetDataSize(),
+ !fCopyData);
+ }
+ __checkReturn
+ inline HRESULT InitializeFromStringHeap(
+ const StringHeapRW *pSourceStringHeap,
+ BOOL fCopyData)
+ {
+ return m_StringPool.InitOnMem(
+ (void *)pSourceStringHeap->m_StringPool.GetSegData(),
+ pSourceStringHeap->m_StringPool.GetDataSize(),
+ !fCopyData);
+ }
+
+ // Destroys the string heap and all its allocated data. Can run on uninitialized string heap.
+ inline void Delete()
+ {
+ return m_StringPool.Uninit();
+ }
+
+public:
+ //
+ // Getters
+ //
+
+ __checkReturn
+ inline HRESULT GetString(
+ UINT32 nIndex,
+ __deref_out_z LPCSTR *pszString) const
+ {
+ return const_cast<StgStringPool &>(m_StringPool).GetString(
+ nIndex,
+ pszString);
+ }
+
+ // Gets raw size (in bytes) of the represented strings. Doesn't align the size as code:GetAlignedSize.
+ inline UINT32 GetUnalignedSize() const
+ {
+ return m_StringPool.GetRawSize();
+ }
+ // Gets size (in bytes) aligned up to 4-bytes of the represented strings.
+ // Fills *pcbSize with 0 on error.
+ __checkReturn
+ inline HRESULT GetAlignedSize(
+ __out UINT32 *pcbSize) const
+ {
+ return m_StringPool.GetSaveSize(pcbSize);
+ }
+ // Returns TRUE if the string heap is empty (even if it contains only default empty string).
+ inline BOOL IsEmpty() const
+ {
+ return const_cast<StgStringPool &>(m_StringPool).IsEmpty();
+ }
+
+ // Returns TRUE if the string index (nIndex, see code:#StringHeapIndex) is valid (i.e. in the string
+ // heap).
+ inline BOOL IsValidIndex(UINT32 nIndex) const
+ {
+ return const_cast<StgStringPool &>(m_StringPool).IsValidCookie(nIndex);
+ }
+
+ __checkReturn
+ inline HRESULT SaveToStream_Aligned(
+ UINT32 nStartIndex,
+ __in IStream *pStream) const
+ {
+ if (nStartIndex == 0)
+ {
+ return const_cast<StgStringPool &>(m_StringPool).PersistToStream(pStream);
+ }
+
+ if (nStartIndex == m_StringPool.GetRawSize())
+ {
+ _ASSERTE(!m_StringPool.HaveEdits());
+ return S_OK;
+ }
+ _ASSERTE(m_StringPool.HaveEdits());
+ _ASSERTE(nStartIndex == m_StringPool.GetOffsetOfEdit());
+ return const_cast<StgStringPool &>(m_StringPool).PersistPartialToStream(pStream, nStartIndex);
+ }
+
+public:
+ //
+ // Heap modifications
+ //
+
+ // Adds null-terminated UTF-8 string (szString) to the end of the heap (incl. its null-terminator).
+ // Returns S_OK and index of added string (*pnIndex).
+ // Returns error code otherwise (and fills *pnIndex with 0).
+ __checkReturn
+ inline HRESULT AddString(
+ __in_z LPCSTR szString,
+ __out UINT32 *pnIndex)
+ {
+ return m_StringPool.AddString(szString, pnIndex);
+ }
+ // Adds null-terminated UTF-16 string (wszString) to the end of the heap (incl. its null-terminator).
+ // Returns S_OK and index of added string (*pnIndex).
+ // Returns error code otherwise (and fills *pnIndex with 0).
+ __checkReturn
+ inline HRESULT AddStringW(
+ __in_z LPCWSTR wszString,
+ __out UINT32 *pnIndex)
+ {
+ return m_StringPool.AddStringW(wszString, pnIndex);
+ }
+
+ // Adds data from *pSourceStringHeap starting at index (nStartSourceIndex) to the string heap.
+ // Returns S_OK (even if the source is empty) or error code.
+ __checkReturn
+ inline HRESULT AddStringHeap(
+ const StringHeapRW *pSourceStringHeap,
+ UINT32 nStartSourceIndex)
+ {
+ return m_StringPool.CopyPool(
+ nStartSourceIndex,
+ &pSourceStringHeap->m_StringPool);
+ } // StringHeapRW::AddStringHeap
+
+ __checkReturn
+ inline HRESULT MakeWritable()
+ {
+ return m_StringPool.ConvertToRW();
+ }
+
+public:
+ //
+ // Tracking of heap modifications for EnC
+ //
+
+ //#EnCSessionTracking
+ // EnC session starts automatically with initialization (code:Initialize or code:InitializeEmpty) or by
+ // user's explicit call to code:StartNewEnCSession. The heap stores its actual data size, so we can find
+ // out if some data were added later.
+
+ // Gets heap size (in bytes) from the beginning of the last EnC session (code:#EnCSessionTracking).
+ inline UINT32 GetEnCSessionStartHeapSize() const
+ {
+ if (m_StringPool.HaveEdits())
+ {
+ return m_StringPool.GetOffsetOfEdit();
+ }
+
+ return m_StringPool.GetRawSize();
+ }
+ // Starts new EnC session (code:#EnCSessionTracking).
+ inline void StartNewEnCSession()
+ {
+ m_StringPool.ResetOffsetOfEdit();
+ }
+ // Gets size (in bytes) aligned to 4-bytes of adds made from the beginning of the last EnC session.
+ __checkReturn
+ inline HRESULT GetEnCSessionAddedHeapSize_Aligned(
+ __out UINT32 *pcbSize) const
+ {
+ if (m_StringPool.HaveEdits())
+ {
+ return m_StringPool.GetEditSaveSize(pcbSize);
+ }
+
+ *pcbSize = 0;
+ return S_OK;
+ }
+
+}; // class StringHeapRW
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/.gitmirror b/src/md/hotdata/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/hotdata/.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/hotdata/CMakeLists.txt b/src/md/hotdata/CMakeLists.txt
new file mode 100644
index 0000000000..199edaa40e
--- /dev/null
+++ b/src/md/hotdata/CMakeLists.txt
@@ -0,0 +1,21 @@
+
+set(MDHOTDATA_SOURCES
+ hotmetadata.cpp
+ hottable.cpp
+ hotheapsdirectoryiterator.cpp
+ hotheap.cpp
+ hotheapwriter.cpp
+)
+
+convert_to_absolute_path(MDHOTDATA_SOURCES ${MDHOTDATA_SOURCES})
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+add_subdirectory(dac)
+add_subdirectory(full)
+add_subdirectory(crossgen)
+if(WIN32)
+ add_subdirectory(full-staticcrt)
+endif(WIN32)
diff --git a/src/md/hotdata/HotData.settings.targets b/src/md/hotdata/HotData.settings.targets
new file mode 100644
index 0000000000..c8bde7d372
--- /dev/null
+++ b/src/md/hotdata/HotData.settings.targets
@@ -0,0 +1,28 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <!--OK to delete NO_NTDLL for devdiv builds.-->
+ <MDHotDataSrcDirectory>$(ClrSrcDirectory)\MD\HotData\</MDHotDataSrcDirectory>
+ <CDefines>$(CDefines);UNICODE;_UNICODE</CDefines>
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <PCHHeader>external.h</PCHHeader>
+ <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders>
+ <!--PCH: Both precompiled header and cpp are on the same ..\ path this is likely to be wrong.-->
+ <PCHCompile>$(MDHotDataSrcDirectory)\external.cpp</PCHCompile>
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <CppCompile Include="$(MDHotDataSrcDirectory)\HotMetaData.cpp" />
+ <CppCompile Include="$(MDHotDataSrcDirectory)\HotTable.cpp" />
+ <CppCompile Include="$(MDHotDataSrcDirectory)\HotHeapsDirectoryIterator.cpp" />
+ <CppCompile Include="$(MDHotDataSrcDirectory)\HotHeap.cpp" />
+ <CppCompile Include="$(MDHotDataSrcDirectory)\HotHeapWriter.cpp" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/md/hotdata/crossgen/.gitmirror b/src/md/hotdata/crossgen/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/hotdata/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/hotdata/crossgen/CMakeLists.txt b/src/md/hotdata/crossgen/CMakeLists.txt
new file mode 100644
index 0000000000..7d7738c5c1
--- /dev/null
+++ b/src/md/hotdata/crossgen/CMakeLists.txt
@@ -0,0 +1,5 @@
+include(${CLR_DIR}/crossgen.cmake)
+include(../../md_wks.cmake)
+
+add_precompiled_header(external.h ../external.cpp MDHOTDATA_SOURCES)
+add_library_clr(mdhotdata_crossgen ${MDHOTDATA_SOURCES})
diff --git a/src/md/hotdata/crossgen/MDHotData_crossgen.nativeproj b/src/md/hotdata/crossgen/MDHotData_crossgen.nativeproj
new file mode 100644
index 0000000000..1e6087e594
--- /dev/null
+++ b/src/md/hotdata/crossgen/MDHotData_crossgen.nativeproj
@@ -0,0 +1,18 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>mdhotdata_crossgen</OutputName>
+ </PropertyGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\HotData\HotData.settings.targets" />
+
+ <!--Leaf Project Items-->
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/hotdata/dac/.gitmirror b/src/md/hotdata/dac/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/hotdata/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/hotdata/dac/CMakeLists.txt b/src/md/hotdata/dac/CMakeLists.txt
new file mode 100644
index 0000000000..99a3f1d00d
--- /dev/null
+++ b/src/md/hotdata/dac/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+include(${CLR_DIR}/dac.cmake)
+
+add_precompiled_header(external.h ../external.cpp MDHOTDATA_SOURCES)
+
+add_library_clr(mdhotdata_dac ${MDHOTDATA_SOURCES})
diff --git a/src/md/hotdata/dac/dirs.proj b/src/md/hotdata/dac/dirs.proj
new file mode 100644
index 0000000000..730c0c89d6
--- /dev/null
+++ b/src/md/hotdata/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\mdhotdata_dac.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/hotdata/dirs.proj b/src/md/hotdata/dirs.proj
new file mode 100644
index 0000000000..1ad2cf969f
--- /dev/null
+++ b/src/md/hotdata/dirs.proj
@@ -0,0 +1,21 @@
+<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="full\mdhotdata.nativeproj" />
+ <ProjectFile Include="full-staticcrt\dirs.proj" />
+ <ProjectFile Include="dac\dirs.proj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/hotdata/export.h b/src/md/hotdata/export.h
new file mode 100644
index 0000000000..a0a9305626
--- /dev/null
+++ b/src/md/hotdata/export.h
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: export.h
+//
+
+//
+// Popular types defined in MetaData\HotData directory.
+// It's supposed to be included from other (MetaData) subcomponents, not from this directory.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "hotmetadata.h"
+
+#include "hottable.h"
+
+#include "hotheapsdirectoryiterator.h"
+#include "hotheap.h"
+
+#include "heapindex.h"
+
+#include "hotheapwriter.h"
diff --git a/src/md/hotdata/external.cpp b/src/md/hotdata/external.cpp
new file mode 100644
index 0000000000..26ec26d9a4
--- /dev/null
+++ b/src/md/hotdata/external.cpp
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: external.cpp
+//
+
+//
+// Precompiled headers.
+//
+// ======================================================================================
+
+#include "external.h"
diff --git a/src/md/hotdata/external.h b/src/md/hotdata/external.h
new file mode 100644
index 0000000000..eb14b17167
--- /dev/null
+++ b/src/md/hotdata/external.h
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: external.h
+//
+
+//
+// External types used in MetaData\HotData subcomponent classes.
+// This file is used for precompiled headers, so it has to be included at the beginning of every .cpp in
+// this directory.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "../external.h"
+#include "../export.h"
+
+#include "../databuffer.h"
diff --git a/src/md/hotdata/full-staticcrt/.gitmirror b/src/md/hotdata/full-staticcrt/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/hotdata/full-staticcrt/.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/hotdata/full-staticcrt/CMakeLists.txt b/src/md/hotdata/full-staticcrt/CMakeLists.txt
new file mode 100644
index 0000000000..8570c4a6f6
--- /dev/null
+++ b/src/md/hotdata/full-staticcrt/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_definitions(-D_CRTIMP=) # static link of crt
+
+add_precompiled_header(external.h ../external.cpp MDHOTDATA_SOURCES)
+add_library_clr(mdhotdata-staticcrt ${MDHOTDATA_SOURCES})
diff --git a/src/md/hotdata/full-staticcrt/MDHotData-staticcrt.props b/src/md/hotdata/full-staticcrt/MDHotData-staticcrt.props
new file mode 100644
index 0000000000..a9ac42bf5f
--- /dev/null
+++ b/src/md/hotdata/full-staticcrt/MDHotData-staticcrt.props
@@ -0,0 +1,11 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\HotData\HotData.settings.targets" />
+
+ <PropertyGroup>
+ <LinkNoLibraries>true</LinkNoLibraries>
+ <LinkUseCMT>true</LinkUseCMT>
+ <UseMsvcrt>false</UseMsvcrt>
+ </PropertyGroup>
+
+</Project>
diff --git a/src/md/hotdata/full-staticcrt/dirs.proj b/src/md/hotdata/full-staticcrt/dirs.proj
new file mode 100644
index 0000000000..cf4651a468
--- /dev/null
+++ b/src/md/hotdata/full-staticcrt/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 Condition="'$(FeatureDbiDebugging)'=='true'" Include="HostLocal\mdhotdata-staticcrt.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/hotdata/full/.gitmirror b/src/md/hotdata/full/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/hotdata/full/.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/hotdata/full/CMakeLists.txt b/src/md/hotdata/full/CMakeLists.txt
new file mode 100644
index 0000000000..26fc6a0721
--- /dev/null
+++ b/src/md/hotdata/full/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_precompiled_header(external.h ../external.cpp MDHOTDATA_SOURCES)
+
+add_library_clr(mdhotdata_full ${MDHOTDATA_SOURCES})
diff --git a/src/md/hotdata/full/MDHotData.nativeproj b/src/md/hotdata/full/MDHotData.nativeproj
new file mode 100644
index 0000000000..b85a064343
--- /dev/null
+++ b/src/md/hotdata/full/MDHotData.nativeproj
@@ -0,0 +1,19 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Leaf project Properties-->
+ <!--Leaf project Properties-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\HotData\HotData.settings.targets" />
+
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>MDHotData</OutputName>
+ </PropertyGroup>
+
+ <!--Leaf Project Items-->
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/hotdata/heapindex.h b/src/md/hotdata/heapindex.h
new file mode 100644
index 0000000000..5d63f97063
--- /dev/null
+++ b/src/md/hotdata/heapindex.h
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotHeapWriter.h
+//
+
+//
+// Class code:HeapIndex represents type of MetaData heap (#String, #GUID, #Blob, or #US).
+//
+// ======================================================================================
+
+#pragma once
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents type of MetaData heap (#String, #GUID, #Blob, or #US).
+//
+class HeapIndex
+{
+private:
+ UINT32 m_Index;
+public:
+ enum
+ {
+ StringHeapIndex = 0,
+ GuidHeapIndex = 1,
+ BlobHeapIndex = 2,
+ UserStringHeapIndex = 3,
+
+ CountHeapIndex,
+ InvalidHeapIndex
+ };
+ HeapIndex()
+ {
+ m_Index = InvalidHeapIndex;
+ }
+ HeapIndex(UINT32 index)
+ {
+ _ASSERTE(IsValid(index));
+ m_Index = index;
+ }
+ void Set(UINT32 index)
+ {
+ _ASSERTE(IsValid(index));
+ m_Index = index;
+ }
+ void SetInvalid()
+ {
+ m_Index = InvalidHeapIndex;
+ }
+ BOOL IsValid() const
+ {
+ return m_Index < CountHeapIndex;
+ }
+ static BOOL IsValid(UINT32 index)
+ {
+ return index < CountHeapIndex;
+ }
+ UINT32 Get() const
+ { return m_Index; }
+
+}; // class HeapIndex
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hotdataformat.h b/src/md/hotdata/hotdataformat.h
new file mode 100644
index 0000000000..0823010611
--- /dev/null
+++ b/src/md/hotdata/hotdataformat.h
@@ -0,0 +1,155 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotDataFormat.h
+//
+
+//
+// Format of the hot data stored in the hot stream. The format consists of several structures:
+// * code:MetaData::HotMetaDataHeader, which contains reference to:
+// * code:MetaData::HotTablesDirectory, which contains array of references to:
+// * code:HotTableHeader
+// * code:MetaData::HotHeapsDirectory, which contains array of code:MetaData::HotHeapsDirectoryEntry,
+// each containig:
+// * index of the heap code:HeapIndex and a reference to:
+// * code:MetaData::HotHeapHeader, which contains reference to:
+// * index table, which contains sorted array of represented hot indexes in the heap
+// * value offsets table, which contains offsets of values for corresponding hot indexes in
+// previous table
+// * value heap, which contains the values (copied out) from the original cold heap
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+// To avoid weird .h cycles, we have to include stgpool.h to get include of metamodelpub.h
+#include <stgpool.h>
+#include <metamodelpub.h>
+
+namespace MetaData
+{
+
+// #HotMetaData
+// To help with startup time, we create a section of metadata that is only that meta-data that was touched
+// durring IBC profiling. Given an offset into a pool this checks if we have any hot data associated with
+// it. If we do we return a poitner to it, otherwse we return NULL.
+
+#include <pshpack1.h>
+
+// --------------------------------------------------------------------------------------
+//
+// Top level hot data header.
+// Ends at the end of MetaData hot stream (i.e. starts at offset end-8).
+//
+struct HotMetaDataHeader
+{
+ // Negative offset relative to the beginning of this structure.
+ // Points to the END (!!!) of code:HotTablesDirectory structure.
+ UINT32 m_nTablesDirectoryEnd_NegativeOffset;
+ // Negative offset relative to the beginning of this structure.
+ // Points to the (start of) code:HotHeapsDirectory structure.
+ UINT32 m_nHeapsDirectoryStart_NegativeOffset;
+
+}; // struct HotMetaDataHeader
+
+// --------------------------------------------------------------------------------------
+//
+// This is the starting structure for hot data of tables.
+// It's referenced (via reference to the end) from
+// code:HotMetaDataHeader::m_nTablesDirectoryEnd_NegativeOffset.
+//
+struct HotTablesDirectory
+{
+ // Magic number (code:#const_nMagic) for format verification.
+ UINT32 m_nMagic;
+ // Array of signed offsets (should have negative or 0 value) relative to the beginning of this structute
+ // for each MetaData table.
+ // Points to the (start of) code:HotTableHeader structure.
+ INT32 m_rgTableHeader_SignedOffset[TBL_COUNT];
+
+ //#const_nMagic
+ // Magic value "HOMD" in code:m_nMagic field.
+ static const UINT32 const_nMagic = 0x484f4e44;
+
+}; // struct HotTablesDirectory
+
+// --------------------------------------------------------------------------------------
+//
+// Describes hot data in a table.
+// Entry referenced (via reference to the start) from code:HotTablesDirectory::m_rgTableHeader_SignedOffset.
+//
+struct HotTableHeader
+{
+ UINT32 m_cTableRecordCount;
+ // Can be 0 or sizeof(struct HotTableHeader)
+ UINT32 m_nFirstLevelTable_PositiveOffset;
+ // Can be 0
+ UINT32 m_nSecondLevelTable_PositiveOffset;
+ UINT32 m_offsIndexMappingTable;
+ UINT32 m_offsHotData;
+ UINT16 m_shiftCount;
+
+}; // struct HotTableHeader
+
+// --------------------------------------------------------------------------------------
+//
+// This is the starting structure for hot data of heaps (string, blob, guid and user string heap).
+// The directory is an array of code:HotHeapsDirectoryEntry structures.
+// It's referenced from code:HotMetaDataHeader::m_nHeapsDirectoryStart_NegativeOffset.
+//
+struct HotHeapsDirectory
+{
+ //code:HotHeapsDirectoryEntry m_rgEntries[*];
+
+}; // struct HotHeapsDirectory
+
+// --------------------------------------------------------------------------------------
+//
+// Describes one heap and its hot data.
+// Entry in the hot heaps directory (code:HotHeapsDirectory).
+//
+struct HotHeapsDirectoryEntry
+{
+ // Index of the represented heap code:HeapIndex.
+ UINT32 m_nHeapIndex;
+ // Negative offset relative to the beginning of this structure.
+ // Points to the (start of) code:HotHeapHeader structure.
+ UINT32 m_nHeapHeaderStart_NegativeOffset;
+
+}; // struct HotHeapsDirectoryEntry
+
+// --------------------------------------------------------------------------------------
+//
+// Describes hot data in a heap.
+// It's referenced from code:HotHeapsDirectoryEntry::m_nHeapHeaderStart_NegativeOffset.
+//
+struct HotHeapHeader
+{
+ // Negative offset relative to the beginning of this structure.
+ // Points to a (start of) table of indexes (UINT32). This table is sorted, so binary search can be
+ // performed. If an index is cached in hot data of this heap, then the index is present in this table
+ // of indexes.
+ UINT32 m_nIndexTableStart_NegativeOffset;
+ // Negative offset relative to the beginning of this structure.
+ // Points to a (start of) table of value offsets (UINT32). This table contains value for each iteam in
+ // previous table of indexes. When an index is found in the previous table, then the value offset is
+ // stored in this table at the same index.
+ // The value offset is positive (!!!) offset relative to the start of heap values (see next member -
+ // code:m_nValueHeapStart_NegativeOffset)
+ UINT32 m_nValueOffsetTableStart_NegativeOffset;
+ // Negative offset relative to the beginning of this structure.
+ // Points to a (start of) values in the hot heap. This heap contains copies of values from the "normal"
+ // (cold) heap. The values in this heap have therefore the same encoding as the values in corresponding
+ // normal/cold heap.
+ // Offsets into this heap are stored in value offset table (code:m_nValueOffsetTableStart_NegativeOffset)
+ // as positive (!!!) offsets relative to the start of this hot value heap.
+ UINT32 m_nValueHeapStart_NegativeOffset;
+
+}; // struct HotHeapHeader
+
+#include <poppack.h>
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hotheap.cpp b/src/md/hotdata/hotheap.cpp
new file mode 100644
index 0000000000..ac53d23aff
--- /dev/null
+++ b/src/md/hotdata/hotheap.cpp
@@ -0,0 +1,185 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotHeap.cpp
+//
+
+//
+// Class code:MetaData::HotHeap represents a hot heap in MetaData hot stream.
+//
+// ======================================================================================
+
+#include "external.h"
+
+#include "hotheap.h"
+#include "hotdataformat.h"
+#include <utilcode.h>
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// Initializes hot heap from its header and data.
+// Provides limited debug-only validation of the structure.
+//
+__checkReturn
+HRESULT
+HotHeap::Initialize(
+ struct HotHeapHeader *pHotHeapHeader,
+ DataBuffer hotHeapData)
+{
+ _ASSERTE(hotHeapData.GetDataPointerBehind() == reinterpret_cast<BYTE *>(pHotHeapHeader));
+
+ UINT32 nMaximumNegativeOffset = hotHeapData.GetSize();
+ if (pHotHeapHeader->m_nIndexTableStart_NegativeOffset > nMaximumNegativeOffset)
+ {
+ m_pHotHeapHeader = NULL;
+ Debug_ReportError("Invalid hot heap header format - invalid index table offset.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ if ((pHotHeapHeader->m_nIndexTableStart_NegativeOffset % 4) != 0)
+ {
+ m_pHotHeapHeader = NULL;
+ Debug_ReportError("Invalid hot heap header format - index table offset is not aligned.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ if (pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset > nMaximumNegativeOffset)
+ {
+ m_pHotHeapHeader = NULL;
+ Debug_ReportError("Invalid hot heap header format - invalid value offset table offset.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ if ((pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset % 4) != 0)
+ {
+ m_pHotHeapHeader = NULL;
+ Debug_ReportError("Invalid hot heap header format - value offset table offset is not aligned.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ // Index table has to be behind value offset table
+ if (pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset < pHotHeapHeader->m_nIndexTableStart_NegativeOffset)
+ {
+ m_pHotHeapHeader = NULL;
+ Debug_ReportError("Invalid hot heap header format - value offset table doesn't start before index table.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ if (pHotHeapHeader->m_nValueHeapStart_NegativeOffset > nMaximumNegativeOffset)
+ {
+ m_pHotHeapHeader = NULL;
+ Debug_ReportError("Invalid hot heap header format - invalid value heap offset.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ m_pHotHeapHeader = pHotHeapHeader;
+ return S_OK;
+} // HotHeap::Initialize
+
+#ifdef _DEBUG_METADATA
+// --------------------------------------------------------------------------------------
+//
+// Validates hot heap structure (extension of code:Initialize checks).
+//
+__checkReturn
+HRESULT
+HotHeap::Debug_Validate()
+{
+ // Additional verification, more strict checks than in code:Initialize
+ S_UINT32 nValueOffsetTableStart =
+ S_UINT32(2) *
+ S_UINT32(m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset);
+ if (nValueOffsetTableStart.IsOverflow() ||
+ (nValueOffsetTableStart.Value() != m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset))
+ {
+ Debug_ReportError("Invalid hot heap header format.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ if (m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset <= m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset)
+ {
+ Debug_ReportError("Invalid hot heap header format.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+
+ // Already validated against underflow in code:Initialize
+ BYTE *pIndexTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset;
+ UINT32 *rgIndexTable = reinterpret_cast<UINT32 *>(pIndexTableStart);
+ // Already validated against underflow in code:Initialize
+ BYTE *pValueOffsetTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset;
+ UINT32 *rgValueOffsetTable = reinterpret_cast<UINT32 *>(pValueOffsetTableStart);
+ // Already validated against underflow in code:Initialize
+ BYTE *pValueHeapStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset;
+ DataBuffer valueHeap(
+ pValueHeapStart,
+ m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset - m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset);
+
+ // Already validated for % 4 == 0 in code:Initialize
+ UINT32 cIndexTableCount = m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset / 4;
+ UINT32 nPreviousValue = 0;
+ for (UINT32 nIndex = 0; nIndex < cIndexTableCount; nIndex++)
+ {
+ if (nPreviousValue >= rgIndexTable[nIndex])
+ {
+ Debug_ReportError("Invalid hot heap header format.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ UINT32 nValueOffset = rgValueOffsetTable[nIndex];
+ if (nValueOffset >= valueHeap.GetSize())
+ {
+ Debug_ReportError("Invalid hot heap header format.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ // TODO: Verify item (depends if it is string, blob, guid or user string)
+ }
+ return S_OK;
+} // HotHeap::Debug_Validate
+#endif //_DEBUG_METADATA
+
+// --------------------------------------------------------------------------------------
+//
+// Gets stored data at index.
+// Returns S_FALSE if data index is not stored in hot heap.
+//
+__checkReturn
+HRESULT
+HotHeap::GetData(
+ UINT32 nDataIndex,
+ __in DataBlob *pData)
+{
+ // Already validated against underflow in code:Initialize
+ BYTE *pIndexTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset;
+ // Already validated against underflow in code:Initialize
+ BYTE *pValueOffsetTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset;
+ // Already validated against underflow in code:Initialize
+ BYTE *pValueHeapStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset;
+
+ const UINT32 *pnFoundDataIndex = BinarySearch<UINT32>(
+ reinterpret_cast<UINT32 *>(pIndexTableStart),
+ m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset / sizeof(UINT32),
+ nDataIndex);
+
+ if (pnFoundDataIndex == NULL)
+ { // Index is not stored in hot data
+ return S_FALSE;
+ }
+ _ASSERTE(((UINT32 *)pIndexTableStart <= pnFoundDataIndex) &&
+ (pnFoundDataIndex + 1 <= (UINT32 *)m_pHotHeapHeader));
+
+ // Index of found data index in the index table (note: it is not offset, but really index)
+ UINT32 nIndexOfFoundDataIndex = (UINT32)(pnFoundDataIndex - (UINT32 *)pIndexTableStart);
+
+ // Value offset contains positive offset to the ValueHeap start
+ // Already validated against overflow in code:Initialize
+ UINT32 nValueOffset_PositiveOffset = reinterpret_cast<UINT32 *>(pValueOffsetTableStart)[nIndexOfFoundDataIndex];
+ if (nValueOffset_PositiveOffset >= m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset)
+ {
+ pData->Clear();
+ Debug_ReportError("Invalid hot data format - value offset reaches behind the hot heap data.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ pData->Init(
+ pValueHeapStart + nValueOffset_PositiveOffset,
+ m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset - nValueOffset_PositiveOffset);
+
+ return S_OK;
+} // HotHeap::GetData
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hotheap.h b/src/md/hotdata/hotheap.h
new file mode 100644
index 0000000000..bad1ed2d0d
--- /dev/null
+++ b/src/md/hotdata/hotheap.h
@@ -0,0 +1,67 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotHeap.h
+//
+
+//
+// Class code:MetaData::HotHeap represents a hot heap in MetaData hot stream.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+namespace MetaData
+{
+
+// Forward declarations
+struct HotHeapHeader;
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents a hot heap in MetaData hot stream.
+//
+class HotHeap
+{
+ friend class VerifyLayoutsMD;
+private:
+ struct HotHeapHeader *m_pHotHeapHeader;
+
+private:
+ friend class HotHeapsDirectoryIterator;
+
+ // Initializes hot heap from its header and data.
+ __checkReturn
+ HRESULT Initialize(struct HotHeapHeader *pHotHeapHeader, DataBuffer hotHeapData);
+
+public:
+ HotHeap()
+ { m_pHotHeapHeader = NULL; }
+ HotHeap(const HotHeap &source)
+ { m_pHotHeapHeader = source.m_pHotHeapHeader; }
+
+ void Clear()
+ { m_pHotHeapHeader = NULL; }
+
+ // Gets stored data at index.
+ // Returns S_FALSE if data index is not stored in hot heap.
+ __checkReturn
+ HRESULT GetData(
+ UINT32 nDataIndex,
+ __out DataBlob *pData);
+
+ inline BOOL IsEmpty() const
+ { return m_pHotHeapHeader == NULL; }
+
+#ifdef _DEBUG_METADATA
+ // Validates hot heap structure (extension of code:Initialize checks).
+ __checkReturn
+ HRESULT Debug_Validate();
+#endif //_DEBUG_METADATA
+
+}; // class HotHeap
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hotheapsdirectoryiterator.cpp b/src/md/hotdata/hotheapsdirectoryiterator.cpp
new file mode 100644
index 0000000000..e0cdb1ca8e
--- /dev/null
+++ b/src/md/hotdata/hotheapsdirectoryiterator.cpp
@@ -0,0 +1,110 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotHeapsDirectoryIterator.h
+//
+
+//
+// Class code:MetaData::HotHeapsDirectoryIterator represents an iterator through hot heaps directory
+// (code:HotHeapsDirectory).
+//
+// ======================================================================================
+
+#include "external.h"
+
+#include "hotheapsdirectoryiterator.h"
+#include "hotdataformat.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// Creates empty iterator.
+//
+HotHeapsDirectoryIterator::HotHeapsDirectoryIterator()
+{
+ m_RemainingHeapsDirectoryData.Clear();
+ m_HotHeapsData.Clear();
+} // HotHeapsDirectoryIterator::HotHeapsDirectoryIterator
+
+// --------------------------------------------------------------------------------------
+//
+// Initialize iteration on heaps directory (hotHeapsDirectoryData) with heap hot data (hotHeapsData).
+// The caller guarantees that the heap hot data end where heaps directory beggins.
+//
+void
+HotHeapsDirectoryIterator::Initialize(
+ DataBuffer hotHeapsDirectoryData,
+ DataBuffer hotHeapsData)
+{
+ _ASSERTE(hotHeapsData.GetDataPointerBehind() == hotHeapsDirectoryData.GetDataPointer());
+ m_RemainingHeapsDirectoryData = hotHeapsDirectoryData;
+ m_HotHeapsData = hotHeapsData;
+} // HotHeapsDirectoryIterator::Initialize
+
+// --------------------------------------------------------------------------------------
+//
+// Gets next hot heap (*pHotHeap, of index *pHotHeapIndex) from the heaps directory.
+// Returns S_OK and fills *pHotHeap and *pHotHeapIndex with the next code:HotHeap information.
+// Returns S_FALSE, if the last hot heap was already returned. Clears *pHotHeap and *pHotHeapIndex in this
+// case.
+// Returns error code if the format is invalid. Clears *pHotHeap and *pHotHeapIndex in this case.
+//
+__checkReturn
+HRESULT
+HotHeapsDirectoryIterator::GetNext(
+ HotHeap *pHotHeap,
+ HeapIndex *pHotHeapIndex)
+{
+ HRESULT hr;
+ DataBuffer hotHeapHeaderData;
+ DataBuffer hotHeapData;
+
+ struct HotHeapsDirectoryEntry *pEntry;
+ if (!m_RemainingHeapsDirectoryData.GetData<struct HotHeapsDirectoryEntry>(
+ &pEntry))
+ {
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+ if (!HeapIndex::IsValid(pEntry->m_nHeapIndex))
+ {
+ Debug_ReportError("Invalid hot heaps directory format - invalid heap index.");
+ IfFailGo(METADATA_E_INVALID_FORMAT);
+ }
+ pHotHeapIndex->Set(pEntry->m_nHeapIndex);
+
+ hotHeapHeaderData = m_HotHeapsData;
+ if (!hotHeapHeaderData.SkipToExactSize(pEntry->m_nHeapHeaderStart_NegativeOffset))
+ {
+ Debug_ReportError("Invalid hot heaps directory format - heap header offset reaches in front of of hot heaps data.");
+ IfFailGo(METADATA_E_INVALID_FORMAT);
+ }
+
+ struct HotHeapHeader *pHeader;
+ if (!hotHeapHeaderData.PeekData<struct HotHeapHeader>(&pHeader))
+ {
+ Debug_ReportError("Invalid hot heaps directory format - heap header reaches behind hot heaps data.");
+ IfFailGo(METADATA_E_INVALID_FORMAT);
+ }
+
+ hotHeapData = m_HotHeapsData;
+ if (!hotHeapData.TruncateBySize(pEntry->m_nHeapHeaderStart_NegativeOffset))
+ {
+ Debug_ReportInternalError("There's a bug because previous call to SkipToExactSize succeeded.");
+ IfFailGo(METADATA_E_INVALID_FORMAT);
+ }
+
+ IfFailGo(pHotHeap->Initialize(pHeader, hotHeapData));
+ _ASSERTE(hr == S_OK);
+ return hr;
+ErrExit:
+ pHotHeap->Clear();
+ pHotHeapIndex->SetInvalid();
+ return hr;
+} // HotHeapsDirectoryIterator::GetNext
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hotheapsdirectoryiterator.h b/src/md/hotdata/hotheapsdirectoryiterator.h
new file mode 100644
index 0000000000..fc5acb9a53
--- /dev/null
+++ b/src/md/hotdata/hotheapsdirectoryiterator.h
@@ -0,0 +1,71 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotHeapsDirectoryIterator.h
+//
+
+//
+// Class code:MetaData::HotHeapsDirectoryIterator represents an iterator through hot heaps directory
+// (code:HotHeapsDirectory).
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+#include "heapindex.h"
+#include "hotheap.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents an iterator through hot heaps directory (code:HotHeapsDirectory), i.e. through an
+// array of code:HotHeapsDirectoryEntry.
+//
+class HotHeapsDirectoryIterator
+{
+private:
+ //
+ // Private data
+ //
+
+ // Remaining data from the heaps directory. On each iteration this will be shrinked (the
+ // code:HotHeapsDirectoryEntry will be skipped).
+ DataBuffer m_RemainingHeapsDirectoryData;
+ // Data for the hot heaps. It has to end exactly where heaps directory starts.
+ DataBuffer m_HotHeapsData;
+
+private:
+ //
+ // Operations with restricted access
+ //
+
+ // code:HotMetaData is the only class allowed to create this iteration.
+ friend class HotMetaData;
+
+ // Initialize iteration on heaps directory (hotHeapsDirectoryData) with heap hot data (hotHeapsData).
+ // The caller guarantees that the heap hot data end where heaps directory beggins.
+ void Initialize(
+ DataBuffer hotHeapsDirectoryData,
+ DataBuffer hotHeapsData);
+
+public:
+ //
+ // Operations
+ //
+
+ // Creates empty iterator.
+ HotHeapsDirectoryIterator();
+
+ // S_OK, S_FALSE, error code (clears the HotHeap if not S_OK)
+ __checkReturn
+ HRESULT GetNext(
+ HotHeap *pHotHeap,
+ HeapIndex *pHotHeapIndex);
+
+}; // class HotHeapsDirectoryIterator
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hotheapwriter.cpp b/src/md/hotdata/hotheapwriter.cpp
new file mode 100644
index 0000000000..7f4edf4cff
--- /dev/null
+++ b/src/md/hotdata/hotheapwriter.cpp
@@ -0,0 +1,305 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotHeapWriter.cpp
+//
+
+//
+// Class code:HotHeapWriter represents a writer of hot heap into MetaData hot stream (collected by IBC).
+//
+// ======================================================================================
+
+#include "external.h"
+
+#include "hotheapwriter.h"
+#include "../heaps/export.h"
+
+#include <stgpool.h>
+#include <metamodelpub.h>
+#include <utilcode.h>
+#include "../inc/streamutil.h"
+
+#include "hotdataformat.h"
+
+#ifdef FEATURE_PREJIT
+// Cannot be included without FEATURE_PREJIT:
+#include <corcompile.h>
+#endif //FEATURE_PREJIT
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// Creates writer for #String heap.
+//
+HotHeapWriter::HotHeapWriter(
+ const StringHeapRW *pStringHeap)
+{
+ m_HeapIndex = HeapIndex::StringHeapIndex;
+ m_pStringHeap = pStringHeap;
+} // HotHeapWriter::HotHeapWriter
+
+// --------------------------------------------------------------------------------------
+//
+// Creates writer for #Blob or #US heap (if fUserStringHeap is TRUE).
+//
+HotHeapWriter::HotHeapWriter(
+ const BlobHeapRW *pBlobHeap,
+ BOOL fUserStringHeap)
+{
+ m_HeapIndex = fUserStringHeap ? HeapIndex::UserStringHeapIndex : HeapIndex::BlobHeapIndex;
+ m_pBlobHeap = pBlobHeap;
+} // HotHeapWriter::HotHeapWriter
+
+// --------------------------------------------------------------------------------------
+//
+// Creates writer for #GUID heap.
+//
+HotHeapWriter::HotHeapWriter(
+ const GuidHeapRW *pGuidHeap)
+{
+ m_HeapIndex = HeapIndex::GuidHeapIndex;
+ m_pGuidHeap = pGuidHeap;
+} // HotHeapWriter::HotHeapWriter
+
+// --------------------------------------------------------------------------------------
+//
+// Destroys the writer of hot heap.
+//
+void
+HotHeapWriter::Delete()
+{
+} // HotHeapWriter::Delete
+
+typedef struct _RidOffsetPair
+{
+ ULONG rid;
+ ULONG offset;
+ // compare function for qsort
+ static int __cdecl Compare(void const *_x, void const *_y);
+} RidOffsetPair;
+
+// static
+int __cdecl
+RidOffsetPair::Compare(void const *_x, void const *_y)
+{
+ RidOffsetPair const *x = reinterpret_cast<RidOffsetPair const *>(_x);
+ RidOffsetPair const *y = reinterpret_cast<RidOffsetPair const *>(_y);
+
+ return x->rid - y->rid;
+}
+
+// --------------------------------------------------------------------------------------
+//
+// Stores hot data reported by IBC in profile data (code:CorProfileData) to a stream.
+// Aligns output stream to 4-bytes.
+//
+__checkReturn
+HRESULT
+HotHeapWriter::SaveToStream(
+ IStream *pStream,
+ CorProfileData *pProfileData,
+ UINT32 *pnSavedSize) const
+{
+ _ASSERTE(pStream != NULL);
+ _ASSERTE(pProfileData != NULL);
+ _ASSERTE(pnSavedSize != NULL);
+
+#ifdef FEATURE_PREJIT
+ HRESULT hr = S_OK;
+ UINT32 nOffset = 0;
+ UINT32 nValueHeapStart_PositiveOffset;
+ UINT32 nValueOffsetTableStart_PositiveOffset;
+ UINT32 nIndexTableStart_PositiveOffset;
+
+ // data
+ //
+
+ // number of hot tokens
+ UINT32 nHotItemsCount = pProfileData->GetHotTokens(
+ GetTableIndex(),
+ 1 << ProfilingFlags_MetaData,
+ 1 << ProfilingFlags_MetaData,
+ NULL,
+ 0);
+ CONSISTENCY_CHECK(nHotItemsCount != 0);
+
+ NewArrayHolder<UINT32> hotItemArr = new (nothrow) UINT32[nHotItemsCount];
+ IfNullRet(hotItemArr);
+
+ // get hot tokens
+ static_assert_no_msg(sizeof(UINT32) == sizeof(mdToken));
+ pProfileData->GetHotTokens(
+ GetTableIndex(),
+ 1 << ProfilingFlags_MetaData,
+ 1 << ProfilingFlags_MetaData,
+ reinterpret_cast<mdToken *>(&hotItemArr[0]),
+ nHotItemsCount);
+
+ // convert tokens to rids
+ for (UINT32 i = 0; i < nHotItemsCount; i++)
+ {
+ hotItemArr[i] = RidFromToken(hotItemArr[i]);
+ }
+
+ NewArrayHolder<RidOffsetPair> offsetMapping = new (nothrow) RidOffsetPair[nHotItemsCount];
+ IfNullRet(offsetMapping);
+
+ // write data
+ nValueHeapStart_PositiveOffset = nOffset;
+
+ // note that we write hot items in the order they appear in pProfileData->GetHotTokens
+ // this is so that we preserve the ordering optimizations done by IbcMerge
+ for (UINT32 i = 0; i < nHotItemsCount; i++)
+ {
+ DataBlob data;
+ IfFailRet(GetData(
+ hotItemArr[i],
+ &data));
+
+ // keep track of the offset at which each hot item is written
+ offsetMapping[i].rid = hotItemArr[i];
+ offsetMapping[i].offset = nOffset;
+
+ IfFailRet(StreamUtil::WriteToStream(
+ pStream,
+ data.GetDataPointer(),
+ data.GetSize(),
+ &nOffset));
+ }
+
+ IfFailRet(StreamUtil::AlignDWORD(pStream, &nOffset));
+
+ // sort by rid so that a hot rid can be looked up by binary search
+ qsort(offsetMapping, nHotItemsCount, sizeof(RidOffsetPair), RidOffsetPair::Compare);
+
+ // initialize table of offsets to data
+ NewArrayHolder<UINT32> dataIndices = new (nothrow) UINT32[nHotItemsCount];
+ IfNullRet(dataIndices);
+
+ // fill in the hotItemArr (now sorted by rid) and dataIndices array with each offset
+ for (UINT32 i = 0; i < nHotItemsCount; i++)
+ {
+ hotItemArr[i] = offsetMapping[i].rid;
+ dataIndices[i] = offsetMapping[i].offset;
+ }
+
+ // table of offsets to data
+ //
+
+ nValueOffsetTableStart_PositiveOffset = nOffset;
+ IfFailRet(StreamUtil::WriteToStream(pStream, &dataIndices[0], sizeof(UINT32) * nHotItemsCount, &nOffset));
+
+ // rid table (sorted)
+ //
+
+ nIndexTableStart_PositiveOffset = nOffset;
+
+ IfFailRet(StreamUtil::WriteToStream(pStream, &hotItemArr[0], nHotItemsCount * sizeof(UINT32), &nOffset));
+ IfFailRet(StreamUtil::AlignDWORD(pStream, &nOffset));
+
+ {
+ // hot pool header
+ struct HotHeapHeader header;
+
+ // fix offsets
+ header.m_nIndexTableStart_NegativeOffset = nOffset - nIndexTableStart_PositiveOffset;
+ header.m_nValueOffsetTableStart_NegativeOffset = nOffset - nValueOffsetTableStart_PositiveOffset;
+ header.m_nValueHeapStart_NegativeOffset = nOffset - nValueHeapStart_PositiveOffset;
+
+ // write header
+ IfFailRet(StreamUtil::WriteToStream(pStream, &header, sizeof(header), &nOffset));
+ }
+
+ *pnSavedSize = nOffset;
+
+#endif //FEATURE_PREJIT
+
+ return S_OK;
+} // HotHeapWriter::PersistHotToStream
+
+// --------------------------------------------------------------------------------------
+//
+// Returns index of the heap as table index used by IBC (code:CorProfileData).
+//
+UINT32
+HotHeapWriter::GetTableIndex() const
+{
+ return TBL_COUNT + m_HeapIndex.Get();
+} // HotHeapWriter::GetTableIndex
+
+// --------------------------------------------------------------------------------------
+//
+// Returns heap data at index (nIndex).
+//
+__checkReturn
+HRESULT
+HotHeapWriter::GetData(
+ UINT32 nIndex,
+ DataBlob *pData) const
+{
+ HRESULT hr;
+
+ switch (m_HeapIndex.Get())
+ {
+ case HeapIndex::StringHeapIndex:
+ {
+ LPCSTR szString;
+ IfFailGo(m_pStringHeap->GetString(
+ nIndex,
+ &szString));
+ _ASSERTE(hr == S_OK);
+
+ // This should not overflow, because we checked it before, but it doesn't hurt
+ S_UINT32 cbStringSize = S_UINT32(strlen(szString)) + S_UINT32(1);
+ if (cbStringSize.IsOverflow())
+ {
+ Debug_ReportInternalError("There's a bug in the string heap consistency - string is too long.");
+ IfFailGo(METADATA_E_INTERNAL_ERROR);
+ }
+
+ pData->Init((BYTE *)szString, cbStringSize.Value());
+ return S_OK;
+ }
+ case HeapIndex::GuidHeapIndex:
+ {
+ // The nIndex is in fact 0-based offset into GUID heap (0, 16, 32, ...), convert it to 1-based element index (1, 2, 3, ...) for GetGuid method
+ if ((nIndex % sizeof(GUID)) != 0)
+ {
+ Debug_ReportInternalError("There's a bug in the caller/IBC - this should be GUID offset aligned to 16-B.");
+ IfFailGo(METADATA_E_INTERNAL_ERROR);
+ }
+ nIndex = (nIndex / sizeof(GUID)) + 1;
+
+ GUID UNALIGNED *pGuid;
+ IfFailGo(const_cast<GuidHeapRW *>(m_pGuidHeap)->GetGuid(
+ nIndex,
+ &pGuid));
+ _ASSERTE(hr == S_OK);
+ pData->Init((BYTE *)pGuid, sizeof(GUID));
+ return S_OK;
+ }
+ case HeapIndex::BlobHeapIndex:
+ case HeapIndex::UserStringHeapIndex:
+ {
+ IfFailGo(const_cast<BlobHeapRW *>(m_pBlobHeap)->GetBlobWithSizePrefix(
+ nIndex,
+ pData));
+ _ASSERTE(hr == S_OK);
+
+ return S_OK;
+ }
+ default:
+ Debug_ReportInternalError("There's a bug in the caller - this is wrong heap index.");
+ IfFailGo(METADATA_E_INTERNAL_ERROR);
+ }
+ return S_OK;
+
+ErrExit:
+ pData->Clear();
+ return hr;
+} // HotHeapWriter::GetData
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hotheapwriter.h b/src/md/hotdata/hotheapwriter.h
new file mode 100644
index 0000000000..c62b50277a
--- /dev/null
+++ b/src/md/hotdata/hotheapwriter.h
@@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotHeapWriter.h
+//
+
+//
+// Class code:HotHeapWriter represents a writer of hot heap into MetaData hot stream (collected by IBC).
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+#include "heapindex.h"
+
+// Forward declarations
+class CorProfileData;
+struct IStream;
+
+namespace MetaData
+{
+
+// Forward declarations
+class StringHeapRW;
+class BlobHeapRW;
+class GuidHeapRW;
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents a writer of hot heap into MetaData hot stream (collected by IBC).
+//
+class HotHeapWriter
+{
+private:
+ // Index of the represented heap (type of the heap).
+ HeapIndex m_HeapIndex;
+ union
+ {
+ const StringHeapRW *m_pStringHeap;
+ // Both #Blob and #US heaps are represented as code:BlobHeapRW.
+ const BlobHeapRW *m_pBlobHeap;
+ const GuidHeapRW *m_pGuidHeap;
+ };
+
+public:
+ // Creates writer for #String heap.
+ HotHeapWriter(const StringHeapRW *pStringHeap);
+ // Creates writer for #Blob or #US heap (if fUserStringHeap is TRUE).
+ HotHeapWriter(
+ const BlobHeapRW *pBlobHeap,
+ BOOL fUserStringHeap);
+ // Creates writer for #GUID heap.
+ HotHeapWriter(const GuidHeapRW *pGuidHeap);
+
+ // Destroys the writer of hot heap.
+ void Delete();
+
+ // Stores hot data reported by IBC in profile data (code:CorProfileData) to a stream.
+ // Aligns output stream to 4-bytes.
+ __checkReturn
+ HRESULT SaveToStream(
+ IStream *pStream,
+ CorProfileData *pProfileData,
+ UINT32 *pnSavedSize) const;
+
+ // Returns index of the heap as table index used by IBC (code:CorProfileData).
+ UINT32 GetTableIndex() const;
+
+private:
+ //
+ // Helpers
+ //
+
+ // Returns heap data at index (nIndex).
+ __checkReturn
+ HRESULT GetData(
+ UINT32 nIndex,
+ DataBlob *pData) const;
+
+}; // class HotHeapWriter
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hotmetadata.cpp b/src/md/hotdata/hotmetadata.cpp
new file mode 100644
index 0000000000..2810eecd15
--- /dev/null
+++ b/src/md/hotdata/hotmetadata.cpp
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotMetaData.cpp
+//
+
+//
+// Class code:MetaData::HotMetaData represents a reader of hot data in MetaData hot stream.
+//
+// ======================================================================================
+
+#include "external.h"
+
+#include "hotmetadata.h"
+#include "hotdataformat.h"
+#include "hotheapsdirectoryiterator.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// Class code:MetaData::HotMetaData represents a reader of hot data in MetaData hot stream.
+//
+__checkReturn
+HRESULT
+HotMetaData::Initialize(
+ DataBuffer data)
+{
+ m_Data = data;
+
+ return S_OK;
+} // HotMetaData::Initialize
+
+// --------------------------------------------------------------------------------------
+//
+// Returns iterator of stored hot heaps (code:HotHeap).
+//
+__checkReturn
+HRESULT
+HotMetaData::GetHeapsDirectoryIterator(
+ HotHeapsDirectoryIterator *pHeapsDirectoryIterator)
+{
+ if (m_Data.GetSize() < sizeof(struct HotMetaDataHeader))
+ {
+ Debug_ReportError("Invalid hot MetaData format - header doesn't fit into the hot data.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+
+ struct HotMetaDataHeader *pHeader;
+ if (!m_Data.PeekDataAt<struct HotMetaDataHeader>(
+ m_Data.GetSize() - sizeof(struct HotMetaDataHeader),
+ &pHeader))
+ {
+ Debug_ReportInternalError("There's a bug, because previous size check succeeded.");
+ return METADATA_E_INTERNAL_ERROR;
+ }
+ // Get rid of the read header
+ DataBuffer heapsData = m_Data;
+ if (!heapsData.TruncateBySize(sizeof(struct HotMetaDataHeader)))
+ {
+ Debug_ReportInternalError("There's a bug, because previous size check succeeded.");
+ return METADATA_E_INTERNAL_ERROR;
+ }
+ DataBuffer heapsDirectoryData = heapsData;
+ if (!heapsDirectoryData.SkipToExactSize(pHeader->m_nHeapsDirectoryStart_NegativeOffset))
+ {
+ Debug_ReportError("Invalid hot MetaData format - heaps directory offset reaches in front of hot data.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+ if (!heapsData.TruncateBySize(pHeader->m_nHeapsDirectoryStart_NegativeOffset))
+ {
+ Debug_ReportInternalError("There's a bug, because previous call to SkipToExactSize succeeded.");
+ return METADATA_E_INVALID_FORMAT;
+ }
+
+ pHeapsDirectoryIterator->Initialize(
+ heapsDirectoryData,
+ heapsData);
+
+ return S_OK;
+} // HotMetaData::GetHeapsDirectoryIterator
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hotmetadata.h b/src/md/hotdata/hotmetadata.h
new file mode 100644
index 0000000000..cba75fe46a
--- /dev/null
+++ b/src/md/hotdata/hotmetadata.h
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotMetaData.h
+//
+
+//
+// Class code:MetaData::HotMetaData represents a reader of hot data in MetaData hot stream.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+namespace MetaData
+{
+
+// Forward declaration
+class HotHeapsDirectoryIterator;
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents a reader of hot data in MetaData hot stream.
+//
+class HotMetaData
+{
+private:
+ DataBuffer m_Data;
+
+public:
+ // Creates reader for MetaData hot stream of format file:HotDataFormat.h.
+ __checkReturn
+ HRESULT Initialize(DataBuffer data);
+
+ // Returns iterator of stored hot heaps (code:HotHeap).
+ __checkReturn
+ HRESULT GetHeapsDirectoryIterator(HotHeapsDirectoryIterator *pHeapsDirectoryIterator);
+
+}; // class HotMetaData
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hottable.cpp b/src/md/hotdata/hottable.cpp
new file mode 100644
index 0000000000..51efe1bf94
--- /dev/null
+++ b/src/md/hotdata/hottable.cpp
@@ -0,0 +1,138 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotTable.cpp
+//
+
+//
+// Class code:MetaData::HotTable stores hot table records cache, a cache of often-accessed
+// table records stored only in NGEN images.
+// The cache is created using IBC logging.
+//
+// ======================================================================================
+
+#include "external.h"
+
+#include "hottable.h"
+#include "hotdataformat.h"
+
+#include <metamodelpub.h>
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// Returns S_OK if index (nIndex) is present in the hot table record cache and returns its value from
+// cache (*ppRecord).
+// Returns S_FALSE if offset is not in the hot table record cache, DOES NOT initialize *ppRecord in this
+// case!
+// Returns error code otherwise (and sets *pRecord to NULL).
+//
+
+//static
+__checkReturn
+HRESULT
+HotTable::GetData(
+ UINT32 nRowIndex,
+ __deref_out_opt BYTE **ppRecord,
+ UINT32 cbRecordSize,
+ struct HotTableHeader *pHotTableHeader)
+{
+ BYTE *pHotTableHeaderData = (BYTE *)pHotTableHeader;
+
+ if (pHotTableHeader->m_nFirstLevelTable_PositiveOffset != 0)
+ { // Has first level table
+ // fetch the first level table
+ WORD *pFirstLevelTable = (WORD *)(pHotTableHeaderData + pHotTableHeader->m_nFirstLevelTable_PositiveOffset);
+
+ // find the high order bits of the rid
+ BYTE bRid = (BYTE)(nRowIndex >> pHotTableHeader->m_shiftCount);
+
+ // use the low order bits of the rid to index into
+ // the first level table.
+ UINT32 nMask = (1 << pHotTableHeader->m_shiftCount) - 1;
+ int i = pFirstLevelTable[nRowIndex & nMask];
+ int end = pFirstLevelTable[(nRowIndex & nMask) + 1];
+
+ // the generation logic should make sure either all tables are present
+ // or all absent.
+ _ASSERTE(pHotTableHeader->m_nSecondLevelTable_PositiveOffset != 0);
+ _ASSERTE(pHotTableHeader->m_offsIndexMappingTable != 0);
+
+ // fetch second level and index mapping tables
+ BYTE *pSecondLevelTable = pHotTableHeaderData + pHotTableHeader->m_nSecondLevelTable_PositiveOffset;
+ WORD *pIndexMappingTable = (WORD *)(pHotTableHeaderData + pHotTableHeader->m_offsIndexMappingTable);
+
+ // look for the high order bits of the rid in the second level table.
+ // search is linear, but should be short on average.
+ for ( ; i < end; i++)
+ {
+ if (pSecondLevelTable[i] == bRid)
+ {
+ // we have found the hot rid we are looking for
+
+ // now consult the index mapping table to find where the hot data is stored
+ int index = pIndexMappingTable[i];
+
+ *ppRecord = pHotTableHeaderData + pHotTableHeader->m_offsHotData + (index * cbRecordSize);
+ return S_OK;
+ }
+ }
+ // Not found in hot data
+ return S_FALSE;
+ }
+ // no first level table - this implies the whole table is replicated
+ // in the hot section. simply multiply and fetch the right record.
+ // hot indices are 0-based, rids are 1-base, so need to subtract 1 from rid.
+
+ *ppRecord = pHotTableHeaderData + pHotTableHeader->m_offsHotData + ((nRowIndex - 1) * cbRecordSize);
+ return S_OK;
+} // HotTable::GetData
+
+// static
+void
+HotTable::CheckTables(struct HotTablesDirectory *pHotTablesDirectory)
+{
+#ifdef _DEBUG
+ _ASSERTE(pHotTablesDirectory->m_nMagic == HotTablesDirectory::const_nMagic);
+
+ for (UINT32 nTableIndex = 0; nTableIndex < TBL_COUNT; nTableIndex++)
+ {
+ if (pHotTablesDirectory->m_rgTableHeader_SignedOffset[nTableIndex] != 0)
+ {
+ struct HotTableHeader *pHotTableHeader = GetTableHeader(pHotTablesDirectory, nTableIndex);
+
+ _ASSERTE((pHotTableHeader->m_cTableRecordCount > 0) && (pHotTableHeader->m_cTableRecordCount <= USHRT_MAX));
+ if (pHotTableHeader->m_nFirstLevelTable_PositiveOffset == 0)
+ {
+ _ASSERTE(pHotTableHeader->m_nSecondLevelTable_PositiveOffset == 0);
+ _ASSERTE(pHotTableHeader->m_offsIndexMappingTable == 0);
+ _ASSERTE(pHotTableHeader->m_offsHotData == Align4(sizeof(struct HotTableHeader)));
+ }
+ else
+ {
+ UINT32 nFirstLevelTableOffset = sizeof(struct HotTableHeader);
+ _ASSERTE(pHotTableHeader->m_nFirstLevelTable_PositiveOffset == nFirstLevelTableOffset);
+ UINT32 cbFirstLevelTableSize = sizeof(WORD) * ((1 << pHotTableHeader->m_shiftCount) + 1);
+
+ _ASSERTE(pHotTableHeader->m_nSecondLevelTable_PositiveOffset != 0);
+ UINT32 nSecondLevelTableOffset = nFirstLevelTableOffset + cbFirstLevelTableSize;
+ _ASSERTE(pHotTableHeader->m_nSecondLevelTable_PositiveOffset == nSecondLevelTableOffset);
+ UINT32 cbSecondLevelTableSize = sizeof(BYTE) * pHotTableHeader->m_cTableRecordCount;
+
+ _ASSERTE(pHotTableHeader->m_offsIndexMappingTable != 0);
+ UINT32 nIndexMappingTableOffset = nSecondLevelTableOffset + cbSecondLevelTableSize;
+ _ASSERTE(pHotTableHeader->m_offsIndexMappingTable == nIndexMappingTableOffset);
+ UINT32 cbIndexMappingTableSize = sizeof(WORD) * pHotTableHeader->m_cTableRecordCount;
+
+ UINT32 nHotDataOffset = nIndexMappingTableOffset + cbIndexMappingTableSize;
+ _ASSERTE(pHotTableHeader->m_offsHotData == Align4(nHotDataOffset));
+ }
+ }
+ }
+#endif //_DEBUG
+} // HotTable::CheckTables
+
+}; // namespace MetaData
diff --git a/src/md/hotdata/hottable.h b/src/md/hotdata/hottable.h
new file mode 100644
index 0000000000..361638e3f5
--- /dev/null
+++ b/src/md/hotdata/hottable.h
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: HotTable.h
+//
+
+//
+// Class code:MetaData::HotTable stores hot table records cache, a cache of often-accessed
+// table records stored only in NGEN images.
+// The cache is created using IBC logging.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+#include "hotdataformat.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// This class stores hot table records cache, a cache of often-accessed table records stored only in NGEN
+// images.
+// The cache is created using IBC logging.
+//
+class HotTable
+{
+public:
+ __checkReturn
+ static HRESULT GetData(
+ UINT32 nRowIndex,
+ __deref_out_opt BYTE **ppRecord,
+ UINT32 cbRecordSize,
+ struct HotTableHeader *pHotTableHeader);
+
+ inline static struct HotTableHeader *GetTableHeader(
+ struct HotTablesDirectory *pHotTablesDirectory,
+ UINT32 nTableIndex)
+ {
+ _ASSERTE(pHotTablesDirectory != NULL);
+
+ INT32 nTableOffset = pHotTablesDirectory->m_rgTableHeader_SignedOffset[nTableIndex];
+ _ASSERTE(nTableOffset != 0);
+
+ BYTE *pHotTableHeaderData = ((BYTE *)pHotTablesDirectory) + nTableOffset;
+ return (struct HotTableHeader *)pHotTableHeaderData;
+ }
+
+ static void CheckTables(struct HotTablesDirectory *pHotTablesDirectory);
+
+}; // class HotTable
+
+}; // namespace MetaData
diff --git a/src/md/inc/.gitmirror b/src/md/inc/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/inc/.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/inc/VerifyLayouts.inc b/src/md/inc/VerifyLayouts.inc
new file mode 100644
index 0000000000..4c0bd65b30
--- /dev/null
+++ b/src/md/inc/VerifyLayouts.inc
@@ -0,0 +1,351 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+// This file provides an explicit check of field layouts using some macro magic
+// in VerifyLayouts.h. The goal is that if any field changes offset or type changes
+// size then a build time assert should trigger. DO NOT change these definitions without
+// reading the comments in VerifyLayouts.h and understanding what other code you need
+// to change at the same time.
+//
+//
+// AN EXAMPLE:
+// You want to validate the fields in type CDerived
+//
+// class CFoo
+// {
+// void* m_ptrField;
+// }
+//
+// struct BigStruct
+// {
+// DWORD m_one;
+// DWORD m_two;
+// DWORD m_three;
+// }
+//
+// CDerived : CFoo
+// {
+// DWORD m_cRef;
+// SomeOtherType* m_otherType;
+// #ifdef _SOME_DEFINE
+// BigStruct m_struct;
+// #endif //_SOME_DEFINE
+// }
+//
+//
+// and the layout validation is written as:
+//
+// BEGIN_TYPE(CDerived, sizeof(CFoo)) // a) The first field starts at sizeof(CFoo) to account for base class data
+// // b) Beware of vtable pointers, they take up space before the data fields too
+// // c) Beware of using sizeof(some_other_type) unless you also explicitly verify
+// // the layout of that type here. Changing the base type would change the derived types
+// // layout and won't be caught unless the base type is explicitly verified.
+// // d) sizeof(<primitive_type>) is fine - they never change over time and we know how
+// // to deal with platform pointer size differences
+// FIELD(CDerived, m_cRef, 4)
+// FIELD(CDerived, m_otherType, sizeof(void*))
+// #ifdef _SOME_DEFINE_
+// ALIGN_FIELD(CDerived, m_struct, sizeof(BigStruct), 4) // We need to use the ALIGN_FIELD macro because the alignment isn't the same as
+// // the field size. The alignment of a structure is typically the max alignment
+// // of any member. The alignment of a primitive type is typically the size of the type.
+// #endif _SOME_DEFINE_
+// END_TYPE(CDerived, sizeof(void*)
+//
+//
+// BEGIN_TYPE(CFoo, 0) // We must verify this type because we used it to define the starting offset of CDerived
+// FIELD(CFoo, m_ptrField, sizeof(void*))
+// END_TYPE(CFoo, sizeof(void*))
+//
+//
+// BEGIN_TYPE(BigStruct, 0) // We must verify this type because we used sizeof(BigStruct) to define the size of
+// // CDerived::m_struct field
+// FIELD(BigStruct, m_one, 4)
+// FIELD(BigStruct, m_two, 4)
+// FIELD(BigStruct, m_thee, 4)
+// END_TYPE(BigStruct, 4)
+//
+//
+//
+//
+// OTHER CONSIDERATIONS:
+//
+// 1) if the type layout is conditional on a define, just include the same define here in the layout verification
+// Make sure that the define is registered in the list of defines and the debugger reading code knows how to dynamically
+// adjust for it (See VerifyLayouts.h comment item (b) and (c))
+//
+// 2) If your type names use characters that aren't valid identifiers (such as the '<' and '>' chars in templates)
+// then you need to provide an escaped name. There are variations of the macros above to do that:
+// BEGIN_TYPE_ESCAPED(typeName, escaptedTypeName, firstFieldOffset)
+// FIELD_ESCAPED(typeName, escapedTypeName, fieldName, fieldSize)
+// ALIGN_FIELD_ESCAPED(typeName, escapedTypeName, fieldName, fieldSize, fieldAlign)
+// END_TYPE_ESCAPED(typeName, escapedTypeName, typeAlignment)
+//
+// If CFoo above had instead been CFoo<ULONG> we would write:
+// BEGIN_TYPE_ESCAPED(CFoo<ULONG>, CFoo__ULONG__, 0)
+// FIELD_ESCAPED(CFoo<ULONG>, CFoo__ULONG__, m_ptrField, sizeof(void*))
+// END_TYPE_ESCAPED(CFoo<ULONG>, CFoo__ULONG__, sizeof(void*))
+//
+// The escapedTypeName is relatively arbitrary, but convention is to replace the illegal characters with double underscore
+// The name does show up in build error messages, so it should close to the real name for people to understand
+//
+// 3) If your type name has commas in it (such as in a list of template arguments) the macros could interpret it as seperate
+// arguments (no good). To fix this use the IGNORE_COMMAS() macro to escape any typeNames with commas. If CFoo above had
+// been CFoo<ULONG,INT> you could rewrite the defines as:
+// BEGIN_TYPE_ESCAPED(IGNORE_COMMAS(CFoo<ULONG,INT>), CFoo__ULONG__INT__, 0)
+// FIELD_ESCAPED(IGNORE_COMMAS(CFoo<ULONG,INT>), CFoo__ULONG__INT__, m_ptrField, sizeof(void*))
+// END_TYPE_ESCAPED(IGNORE_COMMAS(CFoo<ULONG,INT>), CFoo__ULONG__INT__, sizeof(void*))
+//
+// 4) If you have a bitfield in your type, the offsetof macro can't be used which will break the static asserts.
+// There is a special BITFIELD macro that can work around it:
+// BITFIELD(typeName, fieldName, expectedFieldOffset, fieldSize, fieldAlign)
+//
+// The macro is just like FIELD execpt you must provide the offset yourself. Since you can't use offsetof on the field directly
+// the convention is to use the offset of the previous field and then add the size and alignment requirements. For example if your
+// type had this:
+// CMiniMdRW
+// {
+// ULONG m_cbSaveSize;
+// int m_fIsReadOnly : 1;
+// int m_bPreSaveDone : 1;
+// int m_bSaveCompressed : 1;
+// int m_bPostGSSMod : 1;
+// }
+//
+// You could write
+// FIELD(CMiniMdRW, m_cbSaveSize, 4)
+// BITFIELD(CMiniMdRW, m_fIsReadOnly, offsetof(CMiniMdRW, m_cbSaveSize)+4, 4)
+//
+// Don't include al the fields in the bitfield, just pick one as the canonical field name
+//
+//
+//
+// HOW DO I DEBUG THIS STUFF WHEN THE BUILD DOESN'T WORK?
+//
+// One way that has been effective for me is to write a few static_assert_no_msg entries manually in VerifyLayouts.h
+// You can use those to verify your assumptions such as:
+// static_assert_no_msg(sizeof(Foo) == 24)
+// static_assert_no_msg(offset(Foo, m_lastField) == 20)
+// static_assert_no_msg(offset_of_field_affter_Foo_m_lastField == 24)
+// Then rebuild and find out where the compiler disagress with you.
+//
+// Another option is to run the source through the preprocessor
+//
+//
+//
+
+
+
+
+
+
+BEGIN_TYPE(MDInternalRW, 2*sizeof(void*))
+FIELD(MDInternalRW, m_pStgdb, sizeof(void*))
+FIELD(MDInternalRW, m_tdModule, 4)
+FIELD(MDInternalRW, m_cRefs, 4)
+FIELD(MDInternalRW, m_fOwnStgdb, 4)
+FIELD(MDInternalRW, m_pUnk, sizeof(void*))
+FIELD(MDInternalRW, m_pUserUnk, sizeof(void*))
+FIELD(MDInternalRW, m_pIMetaDataHelper, sizeof(void*))
+FIELD(MDInternalRW, m_pSemReadWrite, sizeof(void*))
+FIELD(MDInternalRW, m_fOwnSem, 4)
+END_TYPE(MDInternalRW, sizeof(void*))
+
+BEGIN_TYPE(CLiteWeightStgdbRW, sizeof(CLiteWeightStgdb<CMiniMdRW>))
+FIELD(CLiteWeightStgdbRW, m_cbSaveSize, 4)
+FIELD(CLiteWeightStgdbRW, m_bSaveCompressed, 4)
+FIELD(CLiteWeightStgdbRW, m_pImage, sizeof(void*))
+FIELD(CLiteWeightStgdbRW, m_dwImageSize, 4)
+FIELD(CLiteWeightStgdbRW, m_dwPEKind, 4)
+FIELD(CLiteWeightStgdbRW, m_dwMachine, 4)
+FIELD(CLiteWeightStgdbRW, m_pStreamList, sizeof(void*))
+FIELD(CLiteWeightStgdbRW, m_pNextStgdb, sizeof(void*))
+FIELD(CLiteWeightStgdbRW, m_eFileType, 4)
+FIELD(CLiteWeightStgdbRW, m_wszFileName, sizeof(void*))
+FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFT, 4)
+FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFS, 4)
+FIELD(CLiteWeightStgdbRW, m_pStgIO, sizeof(void*))
+END_TYPE(CLiteWeightStgdbRW, 8)
+
+BEGIN_TYPE_ESCAPED(CLiteWeightStgdb<CMiniMdRW>, CLiteWeightStgdb__CMiniMdRW__, 0)
+ALIGN_FIELD_ESCAPED(CLiteWeightStgdb<CMiniMdRW>, CLiteWeightStgdb__CMiniMdRW__, m_MiniMd, sizeof(CMiniMdRW), sizeof(void*))
+FIELD_ESCAPED(CLiteWeightStgdb<CMiniMdRW>, CLiteWeightStgdb__CMiniMdRW__, m_pvMd, sizeof(void*))
+FIELD_ESCAPED(CLiteWeightStgdb<CMiniMdRW>, CLiteWeightStgdb__CMiniMdRW__, m_cbMd, 4)
+END_TYPE_ESCAPED(CLiteWeightStgdb<CMiniMdRW>, CLiteWeightStgdb__CMiniMdRW__, sizeof(void*))
+
+BEGIN_TYPE(CMiniMdRW, sizeof(CMiniMdTemplate<CMiniMdRW>))
+FIELD(CMiniMdRW, m_pMemberRefHash, sizeof(void*))
+FIELD(CMiniMdRW, m_pMemberDefHash, sizeof(void*))
+ALIGN_FIELD(CMiniMdRW, m_pLookUpHashs, sizeof(void*)*TBL_COUNT, sizeof(void*))
+ALIGN_FIELD(CMiniMdRW, m_StringPoolOffsetHash, sizeof(MapSHash<UINT32, UINT32>), 4)
+FIELD(CMiniMdRW, m_pNamedItemHash, sizeof(void*))
+FIELD(CMiniMdRW, m_maxRid, 4)
+FIELD(CMiniMdRW, m_limRid, 4)
+FIELD(CMiniMdRW, m_maxIx, 4)
+FIELD(CMiniMdRW, m_limIx, 4)
+FIELD(CMiniMdRW, m_eGrow, 4)
+ALIGN_FIELD(CMiniMdRW, m_Tables, sizeof(RecordPool)*TBL_COUNT, sizeof(void*))
+ALIGN_FIELD(CMiniMdRW, m_pVS, sizeof(void*)*TBL_COUNT, sizeof(void*))
+ALIGN_FIELD(CMiniMdRW, m_StringHeap, sizeof(StgStringPool), sizeof(void*))
+ALIGN_FIELD(CMiniMdRW, m_BlobHeap, sizeof(StgBlobPool), sizeof(void*))
+ALIGN_FIELD(CMiniMdRW, m_UserStringHeap, sizeof(StgBlobPool), sizeof(void*))
+ALIGN_FIELD(CMiniMdRW, m_GuidHeap, sizeof(StgGuidPool), sizeof(void*))
+FIELD(CMiniMdRW, m_pHandler, sizeof(void*))
+FIELD(CMiniMdRW, m_cbSaveSize, 4)
+BITFIELD(CMiniMdRW, m_fIsReadOnly, offsetof(CMiniMdRW, m_cbSaveSize)+4, 4)
+FIELD(CMiniMdRW, m_pMethodMap, sizeof(void*))
+FIELD(CMiniMdRW, m_pFieldMap, sizeof(void*))
+FIELD(CMiniMdRW, m_pPropertyMap, sizeof(void*))
+FIELD(CMiniMdRW, m_pEventMap, sizeof(void*))
+FIELD(CMiniMdRW, m_pParamMap, sizeof(void*))
+FIELD(CMiniMdRW, m_pFilterTable, sizeof(void*))
+FIELD(CMiniMdRW, m_pHostFilter, sizeof(void*))
+FIELD(CMiniMdRW, m_pTokenRemapManager, sizeof(void*))
+ALIGN_FIELD(CMiniMdRW, m_OptionValue, sizeof(OptionValue), 4)
+ALIGN_FIELD(CMiniMdRW, m_StartupSchema, sizeof(CMiniMdSchema), 8)
+ALIGN_FIELD(CMiniMdRW, m_bSortable, sizeof(BYTE)*TBL_COUNT, sizeof(BYTE))
+#ifdef _DEBUG
+FIELD(CMiniMdRW, dbg_m_pLock, sizeof(void*))
+#endif
+FIELD(CMiniMdRW, m_fMinimalDelta, 4)
+FIELD(CMiniMdRW, m_rENCRecs, sizeof(void*))
+END_TYPE(CMiniMdRW, 8)
+
+BEGIN_TYPE(OptionValue, 0)
+FIELD(OptionValue, m_DupCheck, 4)
+FIELD(OptionValue, m_RefToDefCheck, 4)
+FIELD(OptionValue, m_NotifyRemap, 4)
+FIELD(OptionValue, m_UpdateMode, 4)
+FIELD(OptionValue, m_ErrorIfEmitOutOfOrder, 4)
+FIELD(OptionValue, m_ThreadSafetyOptions, 4)
+FIELD(OptionValue, m_ImportOption, 4)
+FIELD(OptionValue, m_LinkerOption, 4)
+FIELD(OptionValue, m_GenerateTCEAdapters, 4)
+FIELD(OptionValue, m_RuntimeVersion, sizeof(void*))
+FIELD(OptionValue, m_MetadataVersion, 4)
+FIELD(OptionValue, m_MergeOptions, 4)
+FIELD(OptionValue, m_InitialSize, 4)
+FIELD(OptionValue, m_LocalRefPreservation, 4)
+END_TYPE(OptionValue, sizeof(void*))
+
+
+BEGIN_TYPE(StgBlobPool, sizeof(StgPool))
+ALIGN_FIELD(StgBlobPool, m_Hash, sizeof(CBlobPoolHash), sizeof(void*))
+END_TYPE(StgBlobPool, sizeof(void*))
+
+BEGIN_TYPE(StgStringPool, sizeof(StgPool))
+ALIGN_FIELD(StgStringPool, m_Hash, sizeof(CStringPoolHash), sizeof(void*))
+FIELD(StgStringPool, m_bHash, sizeof(BOOL))
+END_TYPE(StgStringPool, sizeof(void*))
+
+BEGIN_TYPE(StgGuidPool, sizeof(StgPool))
+ALIGN_FIELD(StgGuidPool, m_Hash, sizeof(CGuidPoolHash), sizeof(void*))
+FIELD(StgGuidPool, m_bHash, sizeof(BOOL))
+END_TYPE(StgGuidPool, sizeof(void*))
+
+BEGIN_TYPE(RecordPool, sizeof(StgPool))
+FIELD(RecordPool, m_cbRec, 4)
+END_TYPE(RecordPool, sizeof(void*))
+
+BEGIN_TYPE(StgPool, sizeof(StgPoolReadOnly))
+FIELD(StgPool, m_ulGrowInc, 4)
+FIELD(StgPool, m_pCurSeg, sizeof(void*))
+FIELD(StgPool, m_cbCurSegOffset, 4)
+BITFIELD(StgPool, m_bFree, offsetof(StgPool, m_cbCurSegOffset)+4, 4) // can't take offsetof on a bitfield so we have to provide the offset another way
+FIELD(StgPool, m_nVariableAlignmentMask, 4)
+FIELD(StgPool, m_cbStartOffsetOfEdit, 4)
+FIELD(StgPool, m_fValidOffsetOfEdit, 4)
+END_TYPE(StgPool, sizeof(void*))
+
+
+BEGIN_TYPE(CStringPoolHash, sizeof(CChainedHash<STRINGHASH>))
+FIELD(CStringPoolHash, m_Pool, sizeof(void*))
+END_TYPE(CStringPoolHash, sizeof(void*))
+
+
+BEGIN_TYPE(CBlobPoolHash, sizeof(CChainedHash<STRINGHASH>))
+FIELD(CBlobPoolHash, m_Pool, sizeof(void*))
+END_TYPE(CStringPoolHash, sizeof(void*))
+
+
+BEGIN_TYPE(CGuidPoolHash, sizeof(CChainedHash<GUIDHASH>))
+FIELD(CGuidPoolHash, m_Pool, sizeof(void*))
+END_TYPE(CGuidPoolHash, sizeof(void*))
+
+
+BEGIN_TYPE_ESCAPED(MetaData::HotHeap, MetaData__HotHeap, 0)
+FIELD_ESCAPED(MetaData::HotHeap, MetaData__HotHeap, m_pHotHeapHeader, sizeof(void*))
+END_TYPE_ESCAPED(MetaData::HotHeap, MetaData__HotHeap, sizeof(void*))
+
+
+BEGIN_TYPE(StgPoolReadOnly, sizeof(StgPoolSeg) + sizeof(void*)) //vtable pointer
+ALIGN_FIELD(StgPoolReadOnly, m_HotHeap, sizeof(MetaData::HotHeap), sizeof(void*))
+END_TYPE(StgPoolReadOnly, sizeof(void*))
+
+BEGIN_TYPE_ESCAPED(IGNORE_COMMAS(MapSHash<ULONG, ULONG>), MapSHash__ULONG__ULONG, sizeof(void*))
+FIELD_ESCAPED(IGNORE_COMMAS(MapSHash<ULONG, ULONG>), MapSHash__ULONG__ULONG, m_table, sizeof(void*))
+FIELD_ESCAPED(IGNORE_COMMAS(MapSHash<ULONG, ULONG>), MapSHash__ULONG__ULONG, m_tableSize, 4)
+FIELD_ESCAPED(IGNORE_COMMAS(MapSHash<ULONG, ULONG>), MapSHash__ULONG__ULONG, m_tableCount, 4)
+FIELD_ESCAPED(IGNORE_COMMAS(MapSHash<ULONG, ULONG>), MapSHash__ULONG__ULONG, m_tableOccupied, 4)
+FIELD_ESCAPED(IGNORE_COMMAS(MapSHash<ULONG, ULONG>), MapSHash__ULONG__ULONG, m_tableMax, 4)
+END_TYPE_ESCAPED(IGNORE_COMMAS(MapSHash<ULONG, ULONG>), MapSHash__ULONG__ULONG, sizeof(void*))
+
+
+BEGIN_TYPE(StgPoolSeg, 0)
+FIELD(StgPoolSeg, m_pSegData, sizeof(void*))
+FIELD(StgPoolSeg, m_pNextSeg, sizeof(void*))
+FIELD(StgPoolSeg, m_cbSegSize, 4)
+FIELD(StgPoolSeg, m_cbSegNext, 4)
+END_TYPE(StgPoolSeg, sizeof(void*))
+
+
+BEGIN_TYPE_ESCAPED(CChainedHash<STRINGHASH>, CCHainedHash_STRINGHASH, sizeof(void*)) // vtable pointer
+FIELD_ESCAPED(CChainedHash<STRINGHASH>, CCHainedHash_STRINGHASH, m_rgData, sizeof(void*))
+FIELD_ESCAPED(CChainedHash<STRINGHASH>, CCHainedHash_STRINGHASH, m_iBuckets, 4)
+FIELD_ESCAPED(CChainedHash<STRINGHASH>, CCHainedHash_STRINGHASH, m_iSize, 4)
+FIELD_ESCAPED(CChainedHash<STRINGHASH>, CCHainedHash_STRINGHASH, m_iCount, 4)
+FIELD_ESCAPED(CChainedHash<STRINGHASH>, CCHainedHash_STRINGHASH, m_iMaxChain, 4)
+FIELD_ESCAPED(CChainedHash<STRINGHASH>, CCHainedHash_STRINGHASH, m_iFree, 4)
+END_TYPE_ESCAPED(CChainedHash<STRINGHASH>, CCHainedHash_STRINGHASH, sizeof(void*))
+
+
+BEGIN_TYPE(CMiniColDef, 0)
+FIELD(CMiniColDef, m_Type, 1)
+FIELD(CMiniColDef, m_oColumn, 1)
+FIELD(CMiniColDef, m_cbColumn, 1)
+END_TYPE(CMiniColDef, 1)
+
+
+BEGIN_TYPE(CMiniTableDef, 0)
+FIELD(CMiniTableDef, m_pColDefs, sizeof(void*))
+FIELD(CMiniTableDef, m_cCols, 1)
+FIELD(CMiniTableDef, m_iKey, 1)
+FIELD(CMiniTableDef, m_cbRec, 1)
+END_TYPE(CMiniTableDef, sizeof(void*))
+
+BEGIN_TYPE(CMiniMdBase, 8) //vtable ptr and first field 8-byte alignment
+ALIGN_FIELD(CMiniMdBase, m_Schema, sizeof(CMiniMdSchema), 8)
+FIELD(CMiniMdBase, m_TblCount, 4)
+FIELD(CMiniMdBase, m_fVerifiedByTrustedSource, 4)
+ALIGN_FIELD(CMiniMdBase, m_TableDefs, sizeof(CMiniTableDef)*TBL_COUNT, sizeof(void*))
+FIELD(CMiniMdBase, m_iStringsMask, 4)
+FIELD(CMiniMdBase, m_iGuidsMask, 4)
+FIELD(CMiniMdBase, m_iBlobsMask, 4)
+END_TYPE(CMiniMdBase, 8)
+
+
+BEGIN_TYPE(CMiniMdSchemaBase, 0)
+FIELD(CMiniMdSchemaBase, m_ulReserved, 4)
+FIELD(CMiniMdSchemaBase, m_major, 1)
+FIELD(CMiniMdSchemaBase, m_minor, 1)
+FIELD(CMiniMdSchemaBase, m_heaps, 1)
+FIELD(CMiniMdSchemaBase, m_rid, 1)
+FIELD(CMiniMdSchemaBase, m_maskvalid, 8)
+FIELD(CMiniMdSchemaBase, m_sorted, 8)
+END_TYPE(CMiniMdSchemaBase, 8)
+
+BEGIN_TYPE(CMiniMdSchema, sizeof(CMiniMdSchemaBase))
+ALIGN_FIELD(CMiniMdSchema, m_cRecs, 4*TBL_COUNT, 4)
+FIELD(CMiniMdSchema, m_ulExtra, 4)
+END_TYPE(CMiniMdSchema, 8)
diff --git a/src/md/inc/assemblymdinternaldisp.h b/src/md/inc/assemblymdinternaldisp.h
new file mode 100644
index 0000000000..91b7d2cc29
--- /dev/null
+++ b/src/md/inc/assemblymdinternaldisp.h
@@ -0,0 +1,723 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// AssemblyMDInternalDispenser.h
+//
+
+//
+// Contains utility code for MD directory
+//
+//*****************************************************************************
+#ifndef __AssemblyMDInternalDispenser__h__
+#define __AssemblyMDInternalDispenser__h__
+
+#include "../runtime/mdinternalro.h"
+
+#ifdef FEATURE_FUSION
+
+#include "fusionpriv.h"
+
+struct CORCOMPILE_VERSION_INFO;
+struct CORCOMPILE_DEPENDENCY;
+
+//*****************************************************************************
+// This class can support the IMetaDataAssemblyImport and some funcationalities
+// of IMetaDataImport on the internal import interface (IMDInternalImport).
+//*****************************************************************************
+class AssemblyMDInternalImport :
+ public IMetaDataAssemblyImport,
+ public IMetaDataImport2,
+#ifdef FEATURE_PREJIT
+ public IGetIMDInternalImport,
+#endif //FEATURE_PREJIT
+ public ISNAssemblySignature
+#ifdef FEATURE_PREJIT
+ , public INativeImageInstallInfo
+#endif // FEATURE_PREJIT
+{
+public:
+ AssemblyMDInternalImport(IMDInternalImport *pMDInternalImport);
+ ~AssemblyMDInternalImport();
+
+ // *** IUnknown methods ***
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk);
+ STDMETHODIMP_(ULONG) AddRef(void);
+ STDMETHODIMP_(ULONG) Release(void);
+
+ // *** IMetaDataAssemblyImport methods ***
+ STDMETHODIMP GetAssemblyProps ( // S_OK or error.
+ 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.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ DWORD *pdwAssemblyFlags); // [OUT] Flags.
+
+ STDMETHODIMP GetAssemblyRefProps ( // S_OK or error.
+ 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.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ const void **ppbHashValue, // [OUT] Hash blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ DWORD *pdwAssemblyRefFlags); // [OUT] Flags.
+
+ STDMETHODIMP GetFileProps ( // S_OK or error.
+ mdFile mdf, // [IN] The File for which to get the properties.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in 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.
+
+ STDMETHODIMP GetExportedTypeProps ( // S_OK or error.
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef or mdExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags); // [OUT] Flags.
+
+ STDMETHODIMP GetManifestResourceProps ( // S_OK or error.
+ mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ManifestResource.
+ DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file.
+ DWORD *pdwResourceFlags); // [OUT] Flags.
+
+ STDMETHODIMP EnumAssemblyRefs ( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdAssemblyRef rAssemblyRefs[], // [OUT] Put AssemblyRefs here.
+ ULONG cMax, // [IN] Max AssemblyRefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumFiles ( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdFile rFiles[], // [OUT] Put Files here.
+ ULONG cMax, // [IN] Max Files to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumExportedTypes ( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdExportedType rExportedTypes[], // [OUT] Put ExportedTypes here.
+ ULONG cMax, // [IN] Max ExportedTypes to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP EnumManifestResources ( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdManifestResource rManifestResources[], // [OUT] Put ManifestResources here.
+ ULONG cMax, // [IN] Max Resources to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHODIMP GetAssemblyFromScope ( // S_OK or error
+ mdAssembly *ptkAssembly); // [OUT] Put token here.
+
+ STDMETHODIMP FindExportedTypeByName ( // S_OK or error
+ LPCWSTR szName, // [IN] Name of the ExportedType.
+ mdToken mdtExportedType, // [IN] ExportedType for the enclosing class.
+ mdExportedType *ptkExportedType); // [OUT] Put the ExportedType token here.
+
+ STDMETHODIMP FindManifestResourceByName ( // S_OK or error
+ LPCWSTR szName, // [IN] Name of the ManifestResource.
+ mdManifestResource *ptkManifestResource); // [OUT] Put the ManifestResource token here.
+
+ STDMETHOD_(void, CloseEnum)(
+ HCORENUM hEnum); // Enum to be closed.
+
+ STDMETHODIMP FindAssembliesByName ( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
+ IUnknown *ppIUnk[], // [OUT] put IMetaDataAssemblyImport pointers here
+ ULONG cMax, // [IN] The max number to put
+ ULONG *pcAssemblies); // [OUT] The number of assemblies returned.
+
+ // *** IMetaDataImport methods ***
+ STDMETHOD(CountEnum)(HCORENUM hEnum, ULONG *pulCount);
+ STDMETHOD(ResetEnum)(HCORENUM hEnum, ULONG ulPos);
+ STDMETHOD(EnumTypeDefs)(HCORENUM *phEnum, mdTypeDef rTypeDefs[],
+ ULONG cMax, ULONG *pcTypeDefs);
+ STDMETHOD(EnumInterfaceImpls)(HCORENUM *phEnum, mdTypeDef td,
+ mdInterfaceImpl rImpls[], ULONG cMax,
+ ULONG* pcImpls);
+ STDMETHOD(EnumTypeRefs)(HCORENUM *phEnum, mdTypeRef rTypeRefs[],
+ ULONG cMax, ULONG* pcTypeRefs);
+
+ STDMETHOD(FindTypeDefByName)( // S_OK or error.
+ LPCWSTR szTypeDef, // [IN] Name of the Type.
+ mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef for Enclosing class.
+ mdTypeDef *ptd); // [OUT] Put the TypeDef token here.
+
+ STDMETHOD(GetScopeProps)(
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR wszName, // [OUT] Put the name here.
+ ULONG cchName, // [IN] Size of name buffer in wide chars.
+ ULONG * pchName, // [OUT] Put size of name (wide chars) here.
+ GUID * pMvid); // [OUT, OPTIONAL] Put MVID here.
+
+ STDMETHOD(GetModuleFromScope)( // S_OK.
+ mdModule *pmd); // [OUT] Put mdModule token here.
+
+ STDMETHOD(GetTypeDefProps)(
+ mdTypeDef td, // [IN] TypeDef token for inquiry.
+ __out_ecount_part_opt(cchTypeDef, *pchTypeDef)
+ LPWSTR wszTypeDef, // [OUT] Put name here.
+ ULONG cchTypeDef, // [IN] size of name buffer in wide chars.
+ ULONG * pchTypeDef, // [OUT] put size of name (wide chars) here.
+ DWORD * pdwTypeDefFlags, // [OUT] Put flags here.
+ mdToken * ptkExtends); // [OUT] Put base class TypeDef/TypeRef here.
+
+ STDMETHOD(GetInterfaceImplProps)( // S_OK or error.
+ mdInterfaceImpl iiImpl, // [IN] InterfaceImpl token.
+ mdTypeDef *pClass, // [OUT] Put implementing class token here.
+ mdToken *ptkIface); // [OUT] Put implemented interface token here.
+
+ STDMETHOD(GetTypeRefProps)(
+ mdTypeRef tr, // [IN] TypeRef token.
+ mdToken * ptkResolutionScope, // [OUT] Resolution scope, ModuleRef or AssemblyRef.
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR wszName, // [OUT] Name of the TypeRef.
+ ULONG cchName, // [IN] Size of buffer.
+ ULONG * pchName); // [OUT] Size of Name.
+
+ STDMETHOD(ResolveTypeRef)(mdTypeRef tr, REFIID riid, IUnknown **ppIScope, mdTypeDef *ptd);
+
+ STDMETHOD(EnumMembers)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(EnumMembersWithName)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(EnumMethods)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdMethodDef rMethods[], // [OUT] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(EnumMethodsWithName)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdMethodDef rMethods[], // [OU] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(EnumFields)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdFieldDef rFields[], // [OUT] Put FieldDefs here.
+ ULONG cMax, // [IN] Max FieldDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(EnumFieldsWithName)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdFieldDef rFields[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+
+ STDMETHOD(EnumParams)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdParamDef rParams[], // [OUT] Put ParamDefs here.
+ ULONG cMax, // [IN] Max ParamDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(EnumMemberRefs)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tkParent, // [IN] Parent token to scope the enumeration.
+ mdMemberRef rMemberRefs[], // [OUT] Put MemberRefs here.
+ ULONG cMax, // [IN] Max MemberRefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(EnumMethodImpls)( // S_OK, S_FALSE, or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdToken rMethodBody[], // [OUT] Put Method Body tokens here.
+ mdToken rMethodDecl[], // [OUT] Put Method Declaration tokens here.
+ ULONG cMax, // [IN] Max tokens to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(EnumPermissionSets)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] if !NIL, token to scope the enumeration.
+ DWORD dwActions, // [IN] if !0, return only these actions.
+ mdPermission rPermission[], // [OUT] Put Permissions here.
+ ULONG cMax, // [IN] Max Permissions to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(FindMember)(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdToken *pmb); // [OUT] matching memberdef
+
+ STDMETHOD(FindMethod)(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmb); // [OUT] matching memberdef
+
+ STDMETHOD(FindField)(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdFieldDef *pmb); // [OUT] matching memberdef
+
+ STDMETHOD(FindMemberRef)(
+ mdTypeRef td, // [IN] given typeRef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMemberRef *pmr); // [OUT] matching memberref
+
+ STDMETHOD (GetMethodProps)(
+ mdMethodDef mb, // The method for which to get props.
+ mdTypeDef * pClass, // Put method's class here.
+ __out_ecount_part_opt(cchMethod, *pchMethod)
+ LPWSTR wszMethod, // Put method's name here.
+ ULONG cchMethod, // Size of szMethod buffer in wide chars.
+ ULONG * pchMethod, // Put actual size here.
+ DWORD * pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE * ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG * pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG * pulCodeRVA, // [OUT] codeRVA
+ DWORD * pdwImplFlags); // [OUT] Impl. Flags
+
+ STDMETHOD(GetMemberRefProps)(
+ mdMemberRef mr, // [IN] given memberref
+ mdToken * ptk, // [OUT] Put classref or classdef here.
+ __out_ecount_part_opt(cchMember, *pchMember)
+ LPWSTR wszMember, // [OUT] buffer to fill for member's name
+ ULONG cchMember, // [IN] the count of char of szMember
+ ULONG * pchMember, // [OUT] actual count of char in member name
+ PCCOR_SIGNATURE * ppvSigBlob, // [OUT] point to meta data blob value
+ ULONG * pbSig); // [OUT] actual size of signature blob
+
+ STDMETHOD(EnumProperties)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdProperty rProperties[], // [OUT] Put Properties here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcProperties); // [OUT] Put # put here.
+
+ STDMETHOD(EnumEvents)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdEvent rEvents[], // [OUT] Put events here.
+ ULONG cMax, // [IN] Max events to put.
+ ULONG *pcEvents); // [OUT] Put # put here.
+
+ STDMETHOD(GetEventProps)( // S_OK, S_FALSE, or error.
+ mdEvent ev, // [IN] event token
+ mdTypeDef *pClass, // [OUT] typedef containing the event declarion.
+ LPCWSTR szEvent, // [OUT] Event name
+ ULONG cchEvent, // [IN] the count of wchar of szEvent
+ ULONG *pchEvent, // [OUT] actual count of wchar for event's name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType, // [OUT] EventType class
+ mdMethodDef *pmdAddOn, // [OUT] AddOn method of the event
+ mdMethodDef *pmdRemoveOn, // [OUT] RemoveOn method of the event
+ mdMethodDef *pmdFire, // [OUT] Fire method of the event
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the event
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod); // [OUT] total number of other method of this event
+
+ STDMETHOD(EnumMethodSemantics)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdToken rEventProp[], // [OUT] Put Event/Property here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcEventProp); // [OUT] Put # put here.
+
+ STDMETHOD(GetMethodSemantics)( // S_OK, S_FALSE, or error.
+ mdMethodDef mb, // [IN] method token
+ mdToken tkEventProp, // [IN] event/property token.
+ DWORD *pdwSemanticsFlags); // [OUT] the role flags for the method/propevent pair
+
+ STDMETHOD(GetClassLayout) (
+ mdTypeDef td, // [IN] give typedef
+ DWORD *pdwPackSize, // [OUT] 1, 2, 4, 8, or 16
+ COR_FIELD_OFFSET rFieldOffset[], // [OUT] field offset array
+ ULONG cMax, // [IN] size of the array
+ ULONG *pcFieldOffset, // [OUT] needed array size
+ ULONG *pulClassSize); // [OUT] the size of the class
+
+ STDMETHOD(GetFieldMarshal) (
+ mdToken tk, // [IN] given a field's memberdef
+ PCCOR_SIGNATURE *ppvNativeType, // [OUT] native type of this field
+ ULONG *pcbNativeType); // [OUT] the count of bytes of *ppvNativeType
+
+ STDMETHOD(GetRVA)( // S_OK or error.
+ mdToken tk, // Member for which to set offset
+ ULONG *pulCodeRVA, // The offset
+ DWORD *pdwImplFlags); // the implementation flags
+
+ STDMETHOD(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.
+
+ STDMETHOD(GetSigFromToken)( // S_OK or error.
+ mdSignature mdSig, // [IN] Signature token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token.
+ ULONG *pcbSig); // [OUT] return size of signature.
+
+ STDMETHOD(GetModuleRefProps)(
+ mdModuleRef mur, // [IN] moduleref token.
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR wszName, // [OUT] buffer to fill with the moduleref name.
+ ULONG cchName, // [IN] size of szName in wide characters.
+ ULONG * pchName); // [OUT] actual count of characters in the name.
+
+ STDMETHOD(EnumModuleRefs)( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdModuleRef rModuleRefs[], // [OUT] put modulerefs here.
+ ULONG cmax, // [IN] max memberrefs to put.
+ ULONG *pcModuleRefs); // [OUT] put # put here.
+
+ STDMETHOD(GetTypeSpecFromToken)( // S_OK or error.
+ mdTypeSpec typespec, // [IN] TypeSpec token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to TypeSpec signature
+ ULONG *pcbSig); // [OUT] return size of signature.
+
+ STDMETHOD(GetNameFromToken)( // <TODO>Not Recommended! May be removed!</TODO>
+ mdToken tk, // [IN] Token to get name from. Must have a name.
+ MDUTF8CSTR *pszUtf8NamePtr); // [OUT] Return pointer to UTF8 name in heap.
+
+ STDMETHOD(EnumUnresolvedMethods)( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken rMethods[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens); // [OUT] Put # put here.
+
+ STDMETHOD(GetUserString)(
+ mdString stk, // [IN] String token.
+ __out_ecount_part_opt(cchString, *pchString)
+ LPWSTR wszString, // [OUT] Copy of string.
+ ULONG cchString, // [IN] Max chars of room in szString.
+ ULONG * pchString); // [OUT] How many chars in actual string.
+
+ STDMETHOD(GetPinvokeMap)(
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD * pdwMappingFlags, // [OUT] Flags used for mapping.
+ __out_ecount_part_opt(cchImportName, *pchImportName)
+ LPWSTR wszImportName, // [OUT] Import name.
+ ULONG cchImportName, // [IN] Size of the name buffer.
+ ULONG * pchImportName, // [OUT] Actual number of characters stored.
+ mdModuleRef * pmrImportDLL); // [OUT] ModuleRef token for the target DLL.
+
+ STDMETHOD(EnumSignatures)( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdSignature rSignatures[], // [OUT] put signatures here.
+ ULONG cmax, // [IN] max signatures to put.
+ ULONG *pcSignatures); // [OUT] put # put here.
+
+ STDMETHOD(EnumTypeSpecs)( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdTypeSpec rTypeSpecs[], // [OUT] put TypeSpecs here.
+ ULONG cmax, // [IN] max TypeSpecs to put.
+ ULONG *pcTypeSpecs); // [OUT] put # put here.
+
+ STDMETHOD(EnumUserStrings)( // S_OK or error.
+ HCORENUM *phEnum, // [IN/OUT] pointer to the enum.
+ mdString rStrings[], // [OUT] put Strings here.
+ ULONG cmax, // [IN] max Strings to put.
+ ULONG *pcStrings); // [OUT] put # put here.
+
+ STDMETHOD(GetParamForMethodIndex)( // S_OK or error.
+ mdMethodDef md, // [IN] Method token.
+ ULONG ulParamSeq, // [IN] Parameter sequence.
+ mdParamDef *ppd); // [IN] Put Param token here.
+
+ STDMETHOD(EnumCustomAttributes)( // S_OK or error.
+ HCORENUM *phEnum, // [IN, OUT] COR enumerator.
+ mdToken tk, // [IN] Token to scope the enumeration, 0 for all.
+ mdToken tkType, // [IN] Type of interest, 0 for all.
+ mdCustomAttribute rCustomAttributes[], // [OUT] Put custom attribute tokens here.
+ ULONG cMax, // [IN] Size of rCustomAttributes.
+ ULONG *pcCustomAttributes); // [OUT, OPTIONAL] Put count of token values here.
+
+ STDMETHOD(GetCustomAttributeProps)( // S_OK or error.
+ mdCustomAttribute cv, // [IN] CustomAttribute token.
+ mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here.
+ mdToken *ptkType, // [OUT, OPTIONAL] Put AttrType token here.
+ void const **ppBlob, // [OUT, OPTIONAL] Put pointer to data here.
+ ULONG *pcbSize); // [OUT, OPTIONAL] Put size of date here.
+
+ STDMETHOD(FindTypeRef)(
+ mdToken tkResolutionScope, // [IN] ModuleRef, AssemblyRef or TypeRef.
+ LPCWSTR szName, // [IN] TypeRef Name.
+ mdTypeRef *ptr); // [OUT] matching TypeRef.
+
+ STDMETHOD(GetMemberProps)(
+ mdToken mb, // The member for which to get props.
+ mdTypeDef * pClass, // Put member's class here.
+ __out_ecount_part_opt(cchMember, *pchMember)
+ LPWSTR wszMember, // Put member's name here.
+ ULONG cchMember, // Size of szMember buffer in wide chars.
+ ULONG * pchMember, // Put actual size here
+ DWORD * pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE * ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG * pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG * pulCodeRVA, // [OUT] codeRVA
+ DWORD * pdwImplFlags, // [OUT] Impl. Flags
+ DWORD * pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT * ppValue, // [OUT] constant value
+ ULONG * pcchValue); // [OUT] size of constant string in chars, 0 for non-strings.
+
+ STDMETHOD(GetFieldProps)(
+ mdFieldDef mb, // The field for which to get props.
+ mdTypeDef * pClass, // Put field's class here.
+ __out_ecount_part_opt(cchField, *pchField)
+ LPWSTR szField, // Put field's name here.
+ ULONG cchField, // Size of szField buffer in wide chars.
+ ULONG * pchField, // Put actual size here.
+ DWORD * pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE * ppvSigBlob, // [OUT] point to the blob value of meta data.
+ ULONG * pcbSigBlob, // [OUT] actual size of signature blob.
+ DWORD * pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*.
+ UVCP_CONSTANT * ppValue, // [OUT] constant value.
+ ULONG * pcchValue); // [OUT] size of constant string in chars, 0 for non-strings.
+
+ STDMETHOD(GetPropertyProps)( // S_OK, S_FALSE, or error.
+ mdProperty prop, // [IN] property token
+ mdTypeDef *pClass, // [OUT] typedef containing the property declarion.
+ LPCWSTR szProperty, // [OUT] Property name
+ ULONG cchProperty, // [IN] the count of wchar of szProperty
+ ULONG *pchProperty, // [OUT] actual count of wchar for property name
+ DWORD *pdwPropFlags, // [OUT] property flags.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob
+ ULONG *pbSig, // [OUT] count of bytes in *ppvSig
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppDefaultValue, // [OUT] constant value
+ ULONG *pcchDefaultValue, // [OUT] size of constant string in chars, 0 for non-strings.
+ mdMethodDef *pmdSetter, // [OUT] setter method of the property
+ mdMethodDef *pmdGetter, // [OUT] getter method of the property
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the property
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod); // [OUT] total number of other method of this property
+
+ STDMETHOD(GetParamProps)(
+ mdParamDef tk, // [IN]The Parameter.
+ mdMethodDef * pmd, // [OUT] Parent Method token.
+ ULONG * pulSequence, // [OUT] Parameter sequence.
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR wszName, // [OUT] Put name here.
+ ULONG cchName, // [OUT] Size of name buffer.
+ ULONG * pchName, // [OUT] Put actual size of name here.
+ DWORD * pdwAttr, // [OUT] Put flags here.
+ DWORD * pdwCPlusTypeFlag, // [OUT] Flag for value type. selected ELEMENT_TYPE_*.
+ UVCP_CONSTANT * ppValue, // [OUT] Constant value.
+ ULONG * pcchValue); // [OUT] size of constant string in chars, 0 for non-strings.
+
+ STDMETHOD(GetCustomAttributeByName)( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCWSTR szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData); // [OUT] Put size of data here.
+
+ STDMETHOD_(BOOL, IsValidToken)( // True or False.
+ mdToken tk); // [IN] Given token.
+
+ STDMETHOD(GetNestedClassProps)( // S_OK or error.
+ mdTypeDef tdNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptdEnclosingClass); // [OUT] EnclosingClass token.
+
+ STDMETHOD(GetNativeCallConvFromSig)( // S_OK or error.
+ void const *pvSig, // [IN] Pointer to signature.
+ ULONG cbSig, // [IN] Count of signature bytes.
+ ULONG *pCallConv); // [OUT] Put calling conv here (see CorPinvokemap).
+
+ STDMETHOD(IsGlobal)( // S_OK or error.
+ mdToken pd, // [IN] Type, Field, or Method token.
+ int *pbGlobal); // [OUT] Put 1 if global, 0 otherwise.
+
+//*****************************************************************************
+// IMetaDataImport2 methods
+//*****************************************************************************
+ STDMETHOD(GetMethodSpecProps)(
+ 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
+
+ STDMETHOD(GetGenericParamProps)(
+ mdGenericParam gp, // [IN] GenericParam
+ ULONG * pulParamSeq, // [OUT] Index of the type parameter
+ DWORD * pdwParamFlags, // [OUT] Flags, for future use (e.g. variance)
+ mdToken * ptOwner, // [OUT] Owner (TypeDef or MethodDef)
+ DWORD * pdwReserved, // [OUT] For future use (e.g. non-type parameters)
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR wszName, // [OUT] Put name here
+ ULONG cchName, // [IN] Size of buffer
+ ULONG * pchName); // [OUT] Put size of name (wide chars) here.
+
+ STDMETHOD(GetGenericParamConstraintProps)( // S_OK or error.
+ mdGenericParamConstraint gpc, // [IN] GenericParamConstraint
+ mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained
+ mdToken *ptkConstraintType); // [OUT] TypeDef/Ref/Spec constraint
+
+ STDMETHOD(EnumGenericParams)( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] TypeDef or MethodDef whose generic parameters are requested
+ mdGenericParam rGenericParams[], // [OUT] Put GenericParams here.
+ ULONG cMax, // [IN] Max GenericParams to put.
+ ULONG *pcGenericParams); // [OUT] Put # put here.
+
+ STDMETHOD(EnumGenericParamConstraints)( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdGenericParam tk, // [IN] GenericParam whose constraints are requested
+ mdGenericParamConstraint rGenericParamConstraints[], // [OUT] Put GenericParamConstraints here.
+ ULONG cMax, // [IN] Max GenericParamConstraints to put.
+ ULONG *pcGenericParamConstraints); // [OUT] Put # put here.
+
+ STDMETHOD(EnumMethodSpecs)(
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] MethodDef or MemberRef whose MethodSpecs are requested
+ mdMethodSpec rMethodSpecs[], // [OUT] Put MethodSpecs here.
+ ULONG cMax, // [IN] Max tokens to put.
+ ULONG *pcMethodSpecs); // [OUT] Put actual count here.
+
+ STDMETHOD(GetPEKind)( // S_OK or error.
+ DWORD* pdwPEKind, // [OUT] The kind of PE (0 - not a PE)
+ DWORD* pdwMachine); // [OUT] Machine as defined in NT header
+
+ STDMETHOD(GetVersionString)(
+ __out_ecount_part_opt(ccBufSize, *pccBufSize)
+ LPWSTR pwzBuf, // Put version string here.
+ DWORD ccBufSize, // [in] Size of the buffer, in wide chars.
+ DWORD * pccBufSize); // [out] Size of the version string, wide chars, including terminating nul.
+
+
+ // *** ISNAssemblySignature methods ***
+
+ STDMETHOD(GetSNAssemblySignature)( // S_OK or error.
+ BYTE *pbSig, // [IN, OUT] Buffer to write signature
+ DWORD *pcbSig); // [IN, OUT] Size of buffer, bytes written
+
+
+#ifdef FEATURE_PREJIT
+ // *** IGetIMDInternalImport methods ***
+
+ STDMETHOD(GetIMDInternalImport) (
+ IMDInternalImport ** ppIMDInternalImport);
+
+ // *** INativeImageInstallInfo ***
+
+ STDMETHOD (GetSignature) (
+ CORCOMPILE_NGEN_SIGNATURE * pNgenSign
+ );
+
+ STDMETHOD (GetVersionInfo) (
+ CORCOMPILE_VERSION_INFO * pVersionInfo
+ );
+
+
+ STDMETHOD (GetILSignature) (
+ CORCOMPILE_ASSEMBLY_SIGNATURE * pILSign
+ );
+
+ STDMETHOD (GetConfigMask) (
+ DWORD * pConfigMask
+ );
+
+ STDMETHOD (EnumDependencies) (
+ HCORENUM * phEnum,
+ INativeImageDependency *rDeps[],
+ ULONG cMax,
+ DWORD * pdwCount
+ );
+
+ STDMETHOD (GetDependency) (
+ const CORCOMPILE_NGEN_SIGNATURE *pcngenSign,
+ CORCOMPILE_DEPENDENCY *pDep
+ );
+
+
+#endif // FEATURE_PREJIT
+
+ //------------ setters for privates -----------
+ void SetHandle(HCORMODULE hHandle)
+ {
+ RuntimeAddRefHandle(hHandle);
+ m_pHandle = hHandle;
+ }
+
+ void SetPEKind(DWORD dwPEKind)
+ {
+ m_dwPEKind = dwPEKind;
+ }
+
+ void SetMachine(DWORD dwMachine)
+ {
+ m_dwMachine = dwMachine;
+ }
+
+ void SetVersionString(const char* szVersionString)
+ {
+ m_szVersionString = szVersionString;
+ }
+
+ void SetBase(LPVOID base)
+ {
+ m_pBase = base;
+ }
+
+#ifdef FEATURE_PREJIT
+ void SetZapVersionInfo(CORCOMPILE_VERSION_INFO * info, CORCOMPILE_DEPENDENCY * pDeps, COUNT_T cDeps)
+ {
+ m_pZapVersionInfo = info;
+ m_pZapDependencies = pDeps;
+ m_cZapDependencies = cDeps;
+ }
+#endif // FEATURE_PREJIT
+
+private:
+ LONG m_cRef;
+ HCORMODULE m_pHandle; // Handle to a cached PE image
+ LPVOID m_pBase; // File mapping (if runtime is not inited)
+#ifdef FEATURE_PREJIT
+ struct CORCOMPILE_VERSION_INFO * m_pZapVersionInfo; // Zap image information
+ struct CORCOMPILE_DEPENDENCY * m_pZapDependencies; // Zap Dependancies directory
+ COUNT_T m_cZapDependencies;
+#endif // FEATURE_PREJIT
+ IMDInternalImport * m_pMDInternalImport;
+ DWORD m_dwPEKind;
+ DWORD m_dwMachine;
+ const char * m_szVersionString;
+#ifdef _DEBUG
+ IMetaDataAssemblyImport * m_pDebugMDImport;
+#endif //_DEBUG
+};
+
+#endif // FEATURE_FUSION
+
+#endif // __AssemblyMDInternalDispenser__h__
diff --git a/src/md/inc/cahlprinternal.h b/src/md/inc/cahlprinternal.h
new file mode 100644
index 0000000000..c4ae18b0f9
--- /dev/null
+++ b/src/md/inc/cahlprinternal.h
@@ -0,0 +1,94 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+#ifndef __CAHLPR_H__
+#define __CAHLPR_H__
+
+#include "sarray.h"
+#include "caparser.h"
+
+//*****************************************************************************
+// This class assists in the parsing of CustomAttribute blobs.
+//*****************************************************************************
+struct CaType
+{
+ void Init(CorSerializationType _type)
+ {
+ _ASSERTE(_type != SERIALIZATION_TYPE_SZARRAY && _type != SERIALIZATION_TYPE_ENUM);
+ tag = _type;
+ arrayType = SERIALIZATION_TYPE_UNDEFINED;
+ enumType = SERIALIZATION_TYPE_UNDEFINED;
+ szEnumName = NULL;
+ cEnumName = 0;
+ }
+
+ void Init(CorSerializationType _type, CorSerializationType _arrayType, CorSerializationType _enumType, LPCSTR _szEnumName, ULONG _cEnumName)
+ {
+ tag = _type;
+ arrayType = _arrayType;
+ enumType = _enumType;
+ szEnumName = _szEnumName;
+ cEnumName = _cEnumName;
+ }
+
+ CorSerializationType tag;
+ CorSerializationType arrayType;
+ CorSerializationType enumType;
+ LPCSTR szEnumName;
+ ULONG cEnumName;
+};
+
+struct CaTypeCtor : public CaType
+{
+ CaTypeCtor(CorSerializationType _type)
+ {
+ Init(_type);
+ }
+
+ CaTypeCtor(CorSerializationType _type, CorSerializationType _arrayType, CorSerializationType _enumType, LPCSTR _szEnumName, ULONG _cEnumName)
+ {
+ Init(_type, _arrayType, _enumType, _szEnumName, _cEnumName);
+ }
+};
+
+typedef struct CaValue
+{
+ union
+ {
+ unsigned __int8 boolean;
+ signed __int8 i1;
+ unsigned __int8 u1;
+ signed __int16 i2;
+ unsigned __int16 u2;
+ signed __int32 i4;
+ unsigned __int32 u4;
+ signed __int64 i8;
+ unsigned __int64 u8;
+ float r4;
+ double r8;
+
+ struct
+ {
+ CorSerializationType tag;
+ SArray<CaValue>* pSArray;
+ ULONG length;
+ inline CaValue &operator[](int index) { return (*pSArray)[index]; }
+ } arr;
+
+ struct
+ {
+ LPCUTF8 pStr;
+ ULONG cbStr;
+ } str;
+ };
+
+ CaType type;
+
+} CaValue;
+
+
+
+#endif // __CAHLPR_H__
diff --git a/src/md/inc/imptlb.h b/src/md/inc/imptlb.h
new file mode 100644
index 0000000000..ba2981a415
--- /dev/null
+++ b/src/md/inc/imptlb.h
@@ -0,0 +1,777 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// File: imptlb.h
+//
+
+//
+// TypeLib importer.
+//*****************************************************************************
+#ifndef __imptlb_h__
+#define __imptlb_h__
+
+#ifndef FEATURE_COMINTEROP
+#error FEATURE_COMINTEROP is required for this file
+#endif // FEATURE_COMINTEROP
+#ifndef FEATURE_COMINTEROP_TLB_SUPPORT
+#error FEATURE_COMINTEROP_TLB_SUPPORT is required for this file
+#endif // FEATURE_COMINTEROP_TLB_SUPPORT
+
+//#define TLB_STATS
+
+#define MAX_TLB_VT VT_LPWSTR + 1
+#define MAX_INIT_SIG 3
+#define MAX_COM_GUID_SIG 6
+#define MAX_COM_ADDLISTENER_SIG 8
+#define MAX_COM_REMOVELISTENER_SIG 8
+#define CB_MAX_ELEMENT_TYPE 4
+
+// Forward declarations.
+struct ITypeLibImporterNotifySink;
+class Assembly;
+class Module;
+class CImportTlb;
+
+//*****************************************************************************
+// Class to perform memory management. Memory is not moved as the heap is
+// expanded, and all of the allocations are cleaned up in the destructor.
+//*****************************************************************************
+class CWCHARPool : public StgPool
+{
+public:
+ CWCHARPool() : StgPool()
+ {
+ HRESULT hr = InitNew();
+ _ASSERTE(hr == S_OK);
+ }
+
+ // Allocate some bytes from the pool.
+ WCHAR * Alloc(ULONG nChars)
+ {
+ BYTE *pRslt;
+ // Convert from characters to bytes.
+ nChars *= sizeof(WCHAR);
+ if (nChars > GetCbSegAvailable())
+ if (!Grow(nChars))
+ return 0;
+ pRslt = GetNextLocation();
+ SegAllocate(nChars);
+ return (WCHAR*)pRslt;
+ }
+}; // class CDescPool : public StgPool
+
+
+//*****************************************************************************
+// This helper method is used to track an url to typeref token. This makes
+// defining new typerefs faster.
+//*****************************************************************************
+class CImpTlbTypeRef
+{
+public:
+ CImpTlbTypeRef() { }
+ ~CImpTlbTypeRef() { m_Map.Clear(); }
+
+ //*****************************************************************************
+ // Look for an existing typeref in the map and return if found. If not found,
+ // then create a new one and add it to the map for later.
+ //*****************************************************************************
+ HRESULT DefineTypeRef( // S_OK or error.
+ IMetaDataEmit *pEmit, // Emit interface.
+ mdAssemblyRef ar, // Containing assembly.
+ const LPCWSTR szURL, // URL of the TypeDef, wide chars.
+ mdTypeRef *ptr); // Put mdTypeRef here
+
+ class TokenOfTypeRefHashKey
+ {
+ public:
+ mdToken tkResolutionScope; // TypeRef's resolution scope.
+ LPCWSTR szName; // TypeRef's name.
+ mdTypeRef tr; // The TypeRef's token.
+ };
+
+private:
+
+ class CTokenOfTypeRefHash : public CClosedHash<class TokenOfTypeRefHashKey>
+ {
+ public:
+ typedef CClosedHash<class TokenOfTypeRefHashKey> Super;
+ typedef TokenOfTypeRefHashKey T;
+
+ CTokenOfTypeRefHash() : CClosedHash<class TokenOfTypeRefHashKey>(101) {}
+ ~CTokenOfTypeRefHash() { Clear(); }
+
+ virtual void Clear();
+
+ unsigned int Hash(const void *pData) {return Hash((const T*)pData);}
+ unsigned int Hash(const T *pData);
+
+ unsigned int Compare(const void *p1, BYTE *p2) {return Compare((const T*)p1, (T*)p2);}
+ unsigned int Compare(const T *p1, T *p2);
+
+ ELEMENTSTATUS Status(BYTE *p) {return Status((T*)p);}
+ ELEMENTSTATUS Status(T *p);
+
+ void SetStatus(BYTE *p, ELEMENTSTATUS s) {SetStatus((T*)p, s);}
+ void SetStatus(T *p, ELEMENTSTATUS s);
+
+ void* GetKey(BYTE *p) {return GetKey((T*)p);}
+ void *GetKey(T *p);
+
+ T* Add(const T *pData);
+
+ CWCHARPool m_Names; // Heap of names.
+ };
+
+ CTokenOfTypeRefHash m_Map; // Map of namespace to token.
+};
+
+
+//*****************************************************************************
+// This helper class is used to track source interface ITypeInfo*'s to event
+// information.
+//*****************************************************************************
+class ImpTlbEventInfo
+{
+public:
+ LPCWSTR szSrcItfName; // The source interface name (the key).
+ mdTypeRef trEventItf; // The event interface typedef.
+ LPCWSTR szEventItfName; // The event interface name.
+ LPCWSTR szEventProviderName; // The event provider name.
+ Assembly* SrcItfAssembly; // The assembly where source interface resides.
+};
+
+class CImpTlbEventInfoMap : protected CClosedHash<class ImpTlbEventInfo>
+{
+public:
+ typedef CClosedHash<class ImpTlbEventInfo> Super;
+ typedef ImpTlbEventInfo T;
+
+ CImpTlbEventInfoMap() : CClosedHash<class ImpTlbEventInfo>(101) {}
+ ~CImpTlbEventInfoMap() { Clear(); }
+
+ HRESULT AddEventInfo(LPCWSTR szSrcItfName, mdTypeRef trEventItf, LPCWSTR szEventItfName, LPCWSTR szEventProviderName, Assembly* SrcItfAssembly);
+ ImpTlbEventInfo *FindEventInfo(LPCWSTR szSrcItfName);
+
+ HRESULT GetEventInfoList(CQuickArray<ImpTlbEventInfo*> &qbEvInfoList);
+
+private:
+ unsigned int Hash(const void *pData) {return Hash((const T*)pData);}
+ unsigned int Hash(const T *pData);
+
+ unsigned int Compare(const void *p1, BYTE *p2) {return Compare((const T*)p1, (T*)p2);}
+ unsigned int Compare(const T *p1, T *p2);
+
+ ELEMENTSTATUS Status(BYTE *p) {return Status((T*)p);}
+ ELEMENTSTATUS Status(T *p);
+
+ void SetStatus(BYTE *p, ELEMENTSTATUS s) {SetStatus((T*)p, s);}
+ void SetStatus(T *p, ELEMENTSTATUS s);
+
+ void* GetKey(BYTE *p) {return GetKey((T*)p);}
+ void *GetKey(T *p);
+
+ T* Add(const T *pData);
+
+ CWCHARPool m_Names; // Heap of names.
+};
+
+
+#if defined(_UNICODE) || defined(UNICODE)
+#define _tHashString(szStr) HashString(szStr)
+#else
+#define _tHashString(szStr) HashStringA(szStr)
+#endif
+
+
+
+//*****************************************************************************
+// This helper template is used by the TStringMap to track an item by its
+// character name.
+//*****************************************************************************
+template <class T> class TStringMapItem : HASHENTRY
+{
+public:
+ TStringMapItem() :
+ m_szString(0)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+ ~TStringMapItem()
+ {
+ LIMITED_METHOD_CONTRACT;
+ delete [] m_szString;
+ }
+
+ HRESULT SetString(LPCTSTR szValue)
+ {
+ WRAPPER_NO_CONTRACT;
+ int iLen = (int)(::_tcslen(szValue) + 1);
+ if ((m_szString = new TCHAR[iLen]) == 0)
+ return (OutOfMemory());
+ ::_tcscpy_s((TCHAR*)m_szString, iLen, szValue);
+ return (S_OK);
+ }
+
+public:
+ LPTSTR m_szString; // Key data.
+ T m_value; // Value for this key.
+};
+
+
+//*****************************************************************************
+// IMPORTANT: This data structure is deprecated, please do not add any new uses.
+// The hashtable implementation that should be used instead is code:SHash.
+// If code:SHash does not work for you, talk to mailto:clrdeag.
+//*****************************************************************************
+// This template provides a map from string to item, determined by the template
+// type passed in.
+//*****************************************************************************
+template <class T, int iBuckets=17, class TAllocator=CNewData, int iMaxSize=4096>
+class TStringMap :
+ protected CHashTableAndData<TAllocator>
+{
+ typedef CHashTableAndData<TAllocator> Super;
+
+public:
+ typedef TStringMapItem<T> TItemType;
+ typedef TStringMapItem<long> TOffsetType;
+
+#ifndef DACCESS_COMPILE
+
+ TStringMap() :
+ CHashTableAndData<TAllocator>(iBuckets)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+//*****************************************************************************
+// This is the second part of construction where we do all of the work that
+// can fail. We also take the array of structs here because the calling class
+// presumably needs to allocate it in its NewInit.
+//*****************************************************************************
+ HRESULT NewInit() // Return status.
+ {
+ WRAPPER_NO_CONTRACT;
+ return (CHashTableAndData<TAllocator>::NewInit(
+ CNewData::GrowSize(0)/sizeof(TItemType),
+ sizeof(TItemType),
+ iMaxSize));
+ }
+
+//*****************************************************************************
+// For each item still allocated, invoke its dtor so it frees up anything it
+// holds onto.
+//*****************************************************************************
+ void Clear()
+ {
+ WRAPPER_NO_CONTRACT;
+ HASHFIND sSrch;
+ TItemType *p = (TItemType *) FindFirstEntry(&sSrch);
+
+ while (p != 0)
+ {
+ // Call dtor on the item, since m_value is contained the scalar
+ // dtor will get called.
+ p->~TStringMapItem<T>();
+ p = (TItemType *) FindNextEntry(&sSrch);
+ }
+ CHashTableAndData<TAllocator>::Clear();
+ }
+
+#endif // #ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+// Retrieve an item by name.
+//*****************************************************************************
+ T *GetItem( // Null or object.
+ LPCTSTR szKey) // What to do the lookup on.
+ {
+ WRAPPER_NO_CONTRACT;
+ TItemType sInfo;
+ TItemType *ptr; // Working pointer.
+
+ // Create a key.
+ sInfo.m_szString = (LPTSTR) szKey;
+
+ // Look it up in the hash table.
+ ptr = (TItemType *) Super::Find( _tHashString(szKey), (SIZE_T) &sInfo);
+
+ // Don't let dtor free our string.
+ sInfo.m_szString = 0;
+
+ // If pointer found, return to caller. To handle T's that have
+ // an operator &(), find raw address without going through &m_value.
+ if (ptr)
+ return ((T *) ((BYTE *) ptr + offsetof(TOffsetType, m_value)));
+ else
+ return (0);
+ }
+
+//*****************************************************************************
+// Initialize an iterator and return the first item.
+//*****************************************************************************
+ TItemType *FindFirstEntry(
+ HASHFIND *psSrch)
+ {
+ WRAPPER_NO_CONTRACT;
+ TItemType *ptr = (TItemType *) Super::FindFirstEntry(psSrch);
+
+ return (ptr);
+ }
+
+//*****************************************************************************
+// Return the next item, via an iterator.
+//*****************************************************************************
+ TItemType *FindNextEntry(
+ HASHFIND *psSrch)
+ {
+ WRAPPER_NO_CONTRACT;
+ TItemType *ptr = (TItemType *) Super::FindNextEntry(psSrch);
+
+ return (ptr);
+ }
+
+#ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+// Add an item to the list.
+//*****************************************************************************
+ HRESULT AddItem( // S_OK, or S_FALSE.
+ LPCTSTR szKey, // The key value for the item.
+ T &item) // Thing to add.
+ {
+ WRAPPER_NO_CONTRACT;
+ TItemType *ptr; // Working pointer.
+
+ // Allocate an entry in the hash table.
+ if ((ptr = (TItemType *) this->Add( _tHashString(szKey))) == 0)
+ return (OutOfMemory());
+
+ // Fill the record.
+ if (ptr->SetString(szKey) < 0)
+ {
+ DelItem(ptr);
+ return (OutOfMemory());
+ }
+
+ // Call the placement new operator on the item so it can init itself.
+ // To handle T's that have an operator &(), find raw address without
+ // going through &m_value.
+ T *p = new ((void *) ((BYTE *) ptr + offsetof(TOffsetType, m_value))) T;
+ *p = item;
+ return (S_OK);
+ }
+
+//*****************************************************************************
+// Delete an item.
+//*****************************************************************************
+ void DelItem(
+ LPCTSTR szKey) // What to delete.
+ {
+ WRAPPER_NO_CONTRACT;
+ TItemType sInfo;
+ TItemType *ptr; // Working pointer.
+
+ // Create a key.
+ sInfo.m_szString = (LPTSTR) szKey;
+
+ // Look it up in the hash table.
+ ptr = (TItemType *) this->Find( _tHashString(szKey), (BYTE *) &sInfo);
+
+ // Don't let dtor free our string.
+ sInfo.m_szString = 0;
+
+ // If found, delete.
+ if (ptr)
+ DelItem(ptr);
+ }
+
+#endif // #ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+// Compare the keys for two collections.
+//*****************************************************************************
+ BOOL Cmp( // 0 or != 0.
+ SIZE_T data, // Raw key data on lookup.
+ const HASHENTRY *pElement) // The element to compare data against.
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ TItemType *p = (TItemType *) (size_t) pElement;
+ return (::_tcscmp(((TItemType *) data)->m_szString, p->m_szString));
+ }
+
+private:
+ void DelItem(
+ TItemType *pItem) // Entry to delete.
+ {
+ WRAPPER_NO_CONTRACT;
+ // Need to destruct this item.
+ pItem->~TStringMapItem<T>();
+ CHashTableAndData<TAllocator>::Delete( HashString(pItem->m_szString), (HASHENTRY *)(void *)pItem);
+ }
+};
+
+class CImpTlbReservedNames
+{
+public:
+ CImpTlbReservedNames() {}
+ ~CImpTlbReservedNames() {/*m_StringMap.Clear();*/}
+
+#ifndef DACCESS_COMPILE
+ HRESULT Init() {return m_StringMap.NewInit();}
+
+ void AddReservedName(LPCWSTR szName) {BOOL flag = TRUE; m_StringMap.AddItem(szName, flag);}
+#endif
+ BOOL IsReservedName(LPCWSTR szName) {return m_StringMap.GetItem(szName) != 0;}
+
+private:
+ TStringMap<BOOL> m_StringMap;
+};
+
+
+//*****************************************************************************
+// Helper class to keep track of the mappings from default interfaces to
+// class interfaces.
+//*****************************************************************************
+class ImpTlbClassItfInfo
+{
+public:
+ IID ItfIID; // The IID of the interface.
+ LPCWSTR szClassItfName; // The class interface name.
+};
+
+class CImpTlbDefItfToClassItfMap : protected CClosedHash<class ImpTlbClassItfInfo>
+{
+public:
+ typedef CClosedHash<class ImpTlbClassItfInfo> Super;
+ typedef ImpTlbClassItfInfo T;
+
+ CImpTlbDefItfToClassItfMap();
+ ~CImpTlbDefItfToClassItfMap();
+
+ HRESULT Init(ITypeLib *pTlb, BSTR bstrNameSpace);
+
+ LPCWSTR GetClassItfName(IID &rItfIID);
+
+private:
+ HRESULT AddCoClassInterfaces(ITypeInfo *pCoClassITI, TYPEATTR *pCoClassTypeAttr);
+
+ unsigned int Hash(const void *pData) {return Hash((const T*)pData);}
+ unsigned int Hash(const T *pData);
+
+ unsigned int Compare(const void *p1, BYTE *p2) {return Compare((const T*)p1, (T*)p2);}
+ unsigned int Compare(const T *p1, T *p2);
+
+ ELEMENTSTATUS Status(BYTE *p) {return Status((T*)p);}
+ ELEMENTSTATUS Status(T *p);
+
+ void SetStatus(BYTE *p, ELEMENTSTATUS s) {SetStatus((T*)p, s);}
+ void SetStatus(T *p, ELEMENTSTATUS s);
+
+ void* GetKey(BYTE *p) {return GetKey((T*)p);}
+ void *GetKey(T *p);
+
+ T* Add(const T *pData);
+
+ CWCHARPool m_Names; // Heap of names.
+ BSTR m_bstrNameSpace; // Namespace of the typelib.
+};
+
+
+//*****************************************************************************
+// Helper class to keep track of imported typelibs. Typically, a typelib
+// imports only 2 or 3 other typelibs, so a simple array is used.
+//*****************************************************************************
+struct CTlbRef
+{
+ GUID guid; // GUID of referenced typelib.
+ mdAssemblyRef ar; // AssemblyRef for the module containing reference.
+ BSTR szNameSpace; // The namespace of the types contained in the assembly.
+ BSTR szAsmName; // The assembly name.
+ Assembly* Asm; // The assembly.
+ CImpTlbDefItfToClassItfMap *pDefItfToClassItfMap; // The default interface to class interface map.
+
+ ~CTlbRef()
+ {
+ SysFreeString(szNameSpace);
+ SysFreeString(szAsmName);
+ delete pDefItfToClassItfMap;
+ }
+};
+
+class CImpTlbLibRef : public CQuickArray<CTlbRef>
+{
+ typedef CQuickArray<CTlbRef> base;
+public:
+ CImpTlbLibRef() {base::Shrink(0);}
+ ~CImpTlbLibRef();
+
+ HRESULT Add(ITypeLib *pITLB, CImportTlb *pImporter, mdAssemblyRef ar, BSTR wzNamespace, BSTR wzAsmName, Assembly* assm, CImpTlbDefItfToClassItfMap **ppMap);
+ int Find(ITypeLib *pITLB, mdAssemblyRef *par, BSTR *pwzNamespace, BSTR *pwzAsmName, Assembly** assm, CImpTlbDefItfToClassItfMap **ppDefItfToClassItfMap);
+};
+
+
+class CImportTlb
+{
+public:
+ static CImportTlb* CreateImporter(LPCWSTR szLibrary, ITypeLib *pitlb, BOOL bGenerateTCEAdapters, BOOL bUnsafeInterfaces, BOOL bSafeArrayAsSystemArray, BOOL bTransformDispRetVals, BOOL bPreventClassMembers, BOOL bSerializableValueClasses);
+
+ CImportTlb();
+ CImportTlb(LPCWSTR szLibrary, ITypeLib *pitlb, BOOL bGenerateTCEAdapters, BOOL bUnsafeInterfaces, BOOL bSafeArrayAsSystemArray, BOOL bTransformDispRetVals, BOOL bPreventClassMembers, BOOL bSerializableValueClasses);
+ ~CImportTlb();
+
+ HRESULT Import();
+ HRESULT SetNamespace(WCHAR const *pNamespace);
+ WCHAR *GetNamespace() {return m_wzNamespace;}
+ HRESULT SetNotification(ITypeLibImporterNotifySink *pINotify);
+ HRESULT SetMetaData(IUnknown *pIUnk);
+ void SetAssembly(Assembly *pAssembly) {m_pAssembly = pAssembly;}
+ void SetModule(Module *pModule) {m_pModule = pModule;}
+ HRESULT GetNamespaceOfRefTlb(ITypeLib *pITLB, BSTR *pwzNamespace, CImpTlbDefItfToClassItfMap **ppDefItfToClassItfMap);
+ HRESULT GetEventInfoList(CQuickArray<ImpTlbEventInfo*> &qbEvInfoList) {return m_EventInfoMap.GetEventInfoList(qbEvInfoList);}
+
+ static HRESULT GetDefaultInterface(ITypeInfo *pCoClassTI, ITypeInfo **pDefaultItfTI);
+
+protected:
+
+ struct MemberInfo
+ {
+ union
+ {
+ FUNCDESC *m_psFunc; // Pointer to FuncDesc.
+ VARDESC *m_psVar; // Pointer to VarDesc.
+ };
+ LPWSTR m_pName; // Function/Prop's name, possibly decorated.
+ int m_iMember; // The index of the member in the ITypeInfo.
+ union
+ {
+ LPWSTR m_pName2; // Prop's second name, if any.
+ mdToken m_mdFunc; // Function's token & semantics, if not property.
+ USHORT m_msSemantics; // Semantics only.
+ };
+ void SetFuncInfo(mdMethodDef mdFunc, USHORT msSemantics) {m_mdFunc = RidFromToken(mdFunc) | (msSemantics<<24);}
+ void GetFuncInfo(mdMethodDef &mdFunc, USHORT &msSemantics) {mdFunc = m_mdFunc&0xffffff | mdtMethodDef; msSemantics = m_mdFunc>>24;}
+ };
+
+
+ HRESULT ConvertTypeLib();
+ HRESULT ConvertTypeInfo();
+
+ HRESULT ExplicitlyImplementsIEnumerable(ITypeInfo *pITI, TYPEATTR *psAttr, BOOL fLookupPartner = TRUE);
+
+ HRESULT _NewLibraryObject();
+ HRESULT ConvCoclass(ITypeInfo *pITI, TYPEATTR *psAttr);
+ HRESULT ConvEnum(ITypeInfo *pITI, TYPEATTR *psAttr);
+ HRESULT ConvRecord(ITypeInfo *pITI, TYPEATTR *psAttr, BOOL bUnion);
+ HRESULT ConvIface(ITypeInfo *pITI, TYPEATTR *psAttr, BOOL bVtblGaps=true);
+ HRESULT ConvDispatch(ITypeInfo *pITI, TYPEATTR *psAttr, BOOL bVtblGaps=true);
+ HRESULT ConvModule(ITypeInfo *pITI, TYPEATTR *psAttr);
+
+ HRESULT IsIUnknownDerived(ITypeInfo *pITI, TYPEATTR *psAttr);
+ HRESULT IsIDispatchDerived(ITypeInfo *pITI, TYPEATTR *psAttr);
+ HRESULT HasNewEnumMember(ITypeInfo *pItfTI);
+ HRESULT FuncIsNewEnum(ITypeInfo *pITI, FUNCDESC *pFuncDesc, DWORD index);
+ HRESULT PropertyIsNewEnum(ITypeInfo *pITI, VARDESC *pVarDesc, DWORD index);
+
+ HRESULT HasObjectFields(ITypeInfo *pITI, TYPEATTR *psAttr);
+ HRESULT IsObjectType(ITypeInfo *pITI, const TYPEDESC *pType);
+ HRESULT CompareSigsIgnoringRetType(PCCOR_SIGNATURE pbSig1, ULONG cbSig1, PCCOR_SIGNATURE pbSig2, ULONG cbSig2);
+
+ HRESULT FindMethod(mdTypeDef td, LPCWSTR szName, PCCOR_SIGNATURE pbSig, ULONG cbSig, mdMethodDef *pmb);
+ HRESULT FindProperty(mdTypeDef td, LPCWSTR szName, PCCOR_SIGNATURE pSig, ULONG cbSig, mdProperty *pPr);
+ HRESULT FindEvent(mdTypeDef td, LPCWSTR szName, mdProperty *pEv);
+
+ HRESULT ReportEvent(int ev, int hr, ...);
+
+ HRESULT _DefineSysRefs();
+ HRESULT _GetNamespaceName(ITypeLib *pITLB, BSTR *pwzNamespace);
+ HRESULT _GetTokenForTypeInfo(ITypeInfo *pITI, BOOL bConvDefItfToClassItf, mdToken *pToken, __out_ecount (chTypeRef) __out_opt LPWSTR pszTypeRef=0, int chTypeRef=0, int *pchTypeRef=0, BOOL bAsmQualifiedName = FALSE);
+
+ HRESULT _FindFirstUserMethod(ITypeInfo *pITI, TYPEATTR *psAttr, int *pIx);
+ HRESULT _ResolveTypeDescAliasTypeKind(ITypeInfo *pITIAlias, TYPEDESC *ptdesc, TYPEKIND *ptkind);
+ HRESULT _ResolveTypeDescAlias(ITypeInfo *pITIAlias, const TYPEDESC *ptdesc, ITypeInfo **ppTIResolved, TYPEATTR **ppsAttrResolved, GUID *pGuid=0);
+
+ HRESULT _SetHiddenCA(mdTypeDef token);
+ HRESULT _ForceIEnumerableCVExists(ITypeInfo* pITI, BOOL* CVExists);
+ HRESULT _SetDispIDCA(ITypeInfo* pITI, int iMember, long lDispId, mdToken func, BOOL fAlwaysAdd, long* lDispSet, BOOL bFunc);
+ HRESULT _GetDispIDCA(ITypeInfo* pITI, int iMember, long* lDispSet, BOOL bFunc);
+ HRESULT _CheckForPropertyCustomAttributes(ITypeInfo* pITI, int index, INVOKEKIND* ikind);
+
+ HRESULT _ConvIfaceMembers(ITypeInfo *pITI, TYPEATTR *psAttr, BOOL bVtblGaps, BOOL bAddDispIds, BOOL bInheritsIEnum);
+ HRESULT _ConvSrcIfaceMembers(ITypeInfo *pITI, TYPEATTR* psAttr, BOOL fInheritsIEnum);
+ HRESULT _ConvDispatchMembers(ITypeInfo *pITI, TYPEATTR *psAttr, BOOL fInheritsIEnum);
+ HRESULT _GetFunctionPropertyInfo(FUNCDESC *psFunc, USHORT *pSemantics, FUNCDESC **ppSig, TYPEDESC **ppProperty, BOOL *pbRetval, BOOL fUseLastParam, BSTR strName);
+ HRESULT _ConvFunction(ITypeInfo *pITI, MemberInfo *pMember, int bVtblGapFuncs, BOOL bAddDispIds, BOOL bDelegateInvokeMeth, BOOL* bAllowIEnum);
+ HRESULT _GenerateEvent(ITypeInfo *pITI, MemberInfo *pMember, BOOL fInheritsIEnum);
+ HRESULT _GenerateEventDelegate(ITypeInfo *pITI, MemberInfo *pMember, mdTypeDef *ptd, BOOL fInheritsIEnum);
+ HRESULT _AddSrcItfMembersToClass(mdTypeRef trSrcItf);
+
+ HRESULT _ConvPropertiesForFunctions(ITypeInfo *pITI, TYPEATTR *psAttr);
+ enum ParamOpts{ParamNormal=0, ParamOptional, ParamVarArg};
+ HRESULT _ConvParam(ITypeInfo *pITI, mdMethodDef mbFunc, int iSequence, const ELEMDESC *pdesc, ParamOpts paramOpts, const WCHAR *pszName, BYTE *pbNative, ULONG cbNative);
+ HRESULT _ConvConstant(ITypeInfo *pITI, VARDESC *psVar, BOOL bEnumMember=false);
+ HRESULT _ConvField(ITypeInfo *pITI, VARDESC *psVar, mdFieldDef *pmdField, BOOL bUnion);
+ HRESULT _ConvProperty(ITypeInfo *pITI, MemberInfo *pMember);
+ HRESULT _ConvNewEnumProperty(ITypeInfo *pITI, VARDESC *psVar, MemberInfo *pMember);
+
+ HRESULT _HandleAliasInfo(ITypeInfo *pITI, TYPEDESC *pTypeDesc, mdToken tk);
+
+ HRESULT _AddTlbRef(ITypeLib *pITLB, mdAssemblyRef *par, BSTR *pwzNamespace, BSTR *pwzAsmName, CImpTlbDefItfToClassItfMap **ppDefItfToClassItfMap);
+
+ HRESULT _AddGuidCa(mdToken tkObj, REFGUID guid);
+ HRESULT _AddDefaultMemberCa(mdToken tkObj, LPCWSTR szName);
+
+ HRESULT _AddStringCa(int attr, mdToken tk, LPCWSTR wzString);
+
+ HRESULT GetKnownTypeToken(VARTYPE vt, mdTypeRef *ptr);
+
+ HRESULT _GetTokenForEventItf(ITypeInfo *pSrcItfITI, mdTypeRef *ptr);
+ HRESULT _CreateClassInterface(ITypeInfo *pCoClassITI, ITypeInfo *pDefItfITI, mdTypeRef trDefItf, mdTypeRef rtDefEvItf, mdToken *ptr);
+
+ HRESULT GetManagedNameForCoClass(ITypeInfo *pITI, CQuickArray<WCHAR> &qbClassName);
+ HRESULT GenerateUniqueTypeName(CQuickArray<WCHAR> &qbTypeName);
+ HRESULT GenerateUniqueMemberName(CQuickArray<WCHAR> &qbMemberName, PCCOR_SIGNATURE pSig, ULONG SigSize, LPCWSTR szPrefix, mdToken type);
+ HRESULT _IsAlias(ITypeInfo *pITI, TYPEDESC *pTypeDesc);
+
+ HRESULT GetDefMemberName(ITypeInfo *pITI, BOOL bItfQualified, CQuickArray<WCHAR> &qbDefMemberName);
+
+ enum SigFlags {
+ // These match the typelib values
+ SIG_IN = 0x0001, // Input param.
+ SIG_OUT = 0x0002, // Output param.
+ SIG_RET = 0x0008, // Retval. Currently unused.
+ SIG_OPT = 0x0010, // Optional param. Currently unused.
+ SIG_FLAGS_MASK = 0x001b, // Mask of flags from TypeLib PARAMFLAGs
+
+ SIG_FUNC = 0x0100, // Convert signature for function.
+ SIG_FIELD = 0x0200, // Convert signature for field.
+ SIG_ELEM = 0x0300, // Convert signature for sub element (eg, array of X).
+ SIG_TYPE_MASK = 0x0300,
+
+ SIG_USE_BYREF = 0x1000, // If set convert one ptr as E_T_BYREF.
+ SIG_VARARG = 0x4000, // If set, this is a paramarray type. Use szArray, not System.Array.
+
+ SIG_FLAGS_NONE = 0 // '0' of this enum type.
+ };
+
+ #define IsSigIn(flags) ((flags & SIG_IN) == SIG_IN)
+ #define IsSigOut(flags) ((flags & SIG_OUT) == SIG_OUT)
+ #define IsSigRet(flags) ((flags & SIG_RET) == SIG_RET)
+ #define IsSigOpt(flags) ((flags & SIG_OPT) == SIG_OPT)
+ #define IsSigOutRet(flags) ((flags & (SIG_OUT|SIG_RET)) == (SIG_OUT|SIG_RET))
+
+ #define IsSigFunc(flags) ((flags & SIG_TYPE_MASK) == SIG_FUNC)
+ #define IsSigField(flags) ((flags & SIG_TYPE_MASK) == SIG_FIELD)
+ #define IsSigElem(flags) ((flags & SIG_TYPE_MASK) == SIG_ELEM)
+
+ #define IsSigUseByref(flags) ((flags & SIG_USE_BYREF) == SIG_USE_BYREF)
+ #define IsSigVarArg(flags) ((flags & SIG_VARARG) == SIG_VARARG)
+
+ HRESULT _ConvSignature(ITypeInfo *pITI, const TYPEDESC *pType, ULONG Flags, CQuickBytes &qbSigBuf, ULONG cbSig, ULONG *pcbSig, CQuickArray<BYTE> &qbNativeTypeBuf, ULONG cbNativeType, ULONG *pcbNativeType, BOOL bNewEnumMember, int iByRef=0);
+
+ // For handling out-of-order vtables.
+ CQuickArray<MemberInfo> m_MemberList;
+ CWCHARPool *m_pMemberNames;
+ int m_cMemberProps; // Count of props in memberlist.
+ HRESULT BuildMemberList(ITypeInfo *pITI, int iStart, int iEnd, BOOL bInheritsIEnum);
+ HRESULT FreeMemberList(ITypeInfo *pITI);
+
+ // List of predefined token types for custom attributes.
+#define INTEROP_ATTRIBUTES() \
+ INTEROP_ATTRIBUTE(DISPID) \
+ INTEROP_ATTRIBUTE(CLASSINTERFACE) \
+ INTEROP_ATTRIBUTE(INTERFACETYPE) \
+ INTEROP_ATTRIBUTE(TYPELIBTYPE) \
+ INTEROP_ATTRIBUTE(TYPELIBVAR) \
+ INTEROP_ATTRIBUTE(TYPELIBFUNC) \
+ INTEROP_ATTRIBUTE(COMSOURCEINTERFACES) \
+ INTEROP_ATTRIBUTE(COMCONVERSIONLOSS) \
+ INTEROP_ATTRIBUTE(GUID) \
+ INTEROP_ATTRIBUTE(DEFAULTMEMBER) \
+ INTEROP_ATTRIBUTE(COMALIASNAME) \
+ INTEROP_ATTRIBUTE(PARAMARRAY) \
+ INTEROP_ATTRIBUTE(LCIDCONVERSION) \
+ INTEROP_ATTRIBUTE(DECIMALVALUE) \
+ INTEROP_ATTRIBUTE(DATETIMEVALUE) \
+ INTEROP_ATTRIBUTE(IUNKNOWNVALUE) \
+ INTEROP_ATTRIBUTE(IDISPATCHVALUE) \
+ INTEROP_ATTRIBUTE(COMVISIBLE) \
+ INTEROP_ATTRIBUTE(SERIALIZABLE) \
+ INTEROP_ATTRIBUTE_SPECIAL(COMEVENTINTERFACE) \
+ INTEROP_ATTRIBUTE_SPECIAL(COCLASS) \
+
+#define INTEROP_ATTRIBUTE(x) ATTR_##x,
+#define INTEROP_ATTRIBUTE_SPECIAL(x) ATTR_##x,
+ enum {INTEROP_ATTRIBUTES()
+ // Last value gives array size.
+ ATTR_COUNT};
+#undef INTEROP_ATTRIBUTE
+#undef INTEROP_ATTRIBUTE_SPECIAL
+
+ mdToken m_tkAttr[ATTR_COUNT];
+ HRESULT GetAttrType(int attr, mdToken *ptk);
+
+ // look up table for known type
+ mdTypeRef m_tkKnownTypes[MAX_TLB_VT];
+
+ LPCWSTR m_szLibrary; // Name of typelib being imported.
+ BOOL m_bGenerateTCEAdapters; // A flag indicating if the TCE adapters are being generated or not.
+ BOOL m_bUnsafeInterfaces; // A flag indicating whether runtime security checks should be disabled on an interface
+ BOOL m_bSafeArrayAsSystemArray; // A flag indicating whether to import SAFEARRAY's as System.Array's.
+ BOOL m_bTransformDispRetVals; // A flag indicating if we should do [out,retval] transformation on disp only itfs.
+ BOOL m_bPreventClassMembers; // A flag indicating if we should add members to CoClasses.
+ BOOL m_bSerializableValueClasses; // A flag indicating if we should mark value classes as serializable.
+ mdMemberRef m_tkSuppressCheckAttr; // Cached ctor for security check custom attribute
+ ITypeLib *m_pITLB; // Typelib being imported.
+ IMetaDataEmit2 *m_pEmit; // Emit API Interface pointer.
+ IMetaDataImport2 *m_pImport; // Import API Interface pointer.
+
+ BSTR m_wzNamespace; // Namespace of the created TypeDefs.
+ mdTypeRef m_trObject; // Token of System.Object.
+ mdTypeRef m_trValueType; // Token of System.ValueType.
+ mdTypeRef m_trEnum; // Token of System.Enum.
+ mdAssemblyRef m_arSystem; // AssemblyRef for classlib.
+
+ ITypeInfo *m_pITI; // "Current" ITypeInfo being converted.
+ ITypeInfo *m_pOrigITI; // Original "Current" ITypeInfo being converted,
+ // represents TKIND_ALIAS or is equal to m_pITI.
+
+ TYPEATTR *m_psAttr; // "TYPEATTR" of current ITypeInfo.
+
+ BSTR m_szName; // Name of original current ITypeInfo.
+ BSTR m_szMember; // Name of current Member (method or field).
+ LPWSTR m_szMngName; // Full name of the managed type.
+
+ ULONG m_cbVtableSlot; // Size of a vtable slot.
+ ULONG m_Slot; // "Current" vtbl index within an interface.
+
+ void *m_psClass; // "Current" class record.
+ mdTypeDef m_tdTypeDef; // Current TypeDef.
+ mdTypeDef m_tdHasDefault; // Most recent TypeDef with a default.
+ enum {eImplIfaceNone, eImplIfaceDefault, eImplIface} m_ImplIface;
+ mdToken m_tkInterface; // Interface being added to a coclass.
+ BSTR m_szInterface; // Interface name for decoration.
+
+ CImpTlbTypeRef m_TRMap; // Typeref map.
+ CImpTlbLibRef m_LibRefs; // Referenced typelibs.
+ CImpTlbDefItfToClassItfMap m_DefItfToClassItfMap; // The default interface to class interface map.
+
+ CImpTlbReservedNames m_ReservedNames; // Reserved names.
+ CImpTlbEventInfoMap m_EventInfoMap; // Map of event info's.
+
+ ITypeLibImporterNotifySink *m_Notify; // Notification object.
+ Assembly *m_pAssembly; // Containing assembly.
+ Module *m_pModule; // Module we are emiting into.
+
+#if defined(TLB_STATS)
+ LARGE_INTEGER m_freqVal; // Frequency of perf counter.
+ BOOL m_bStats; // If true, collect timings.
+#endif // TLB_STATS
+};
+
+
+
+#endif
+
+//-eof-************************************************************************
diff --git a/src/md/inc/liteweightstgdb.h b/src/md/inc/liteweightstgdb.h
new file mode 100644
index 0000000000..1234524731
--- /dev/null
+++ b/src/md/inc/liteweightstgdb.h
@@ -0,0 +1,257 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// LiteWeightStgdb.h
+//
+
+//
+// This contains definition of class CLiteWeightStgDB. This is light weight
+// read-only implementation for accessing compressed meta data format.
+//
+//*****************************************************************************
+#ifndef __LiteWeightStgdb_h__
+#define __LiteWeightStgdb_h__
+
+#include "metamodelro.h"
+#include "metamodelrw.h"
+
+#include "stgtiggerstorage.h"
+
+class StgIO;
+
+#include "mdcommon.h"
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:28718) // public header missing SAL annotations
+#endif // _PREFAST_
+class TiggerStorage;
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif // _PREFAST_
+
+//*****************************************************************************
+// This class provides common definitions for heap segments. It is both the
+// base class for the heap, and the class for heap extensions (additional
+// memory that must be allocated to grow the heap).
+//*****************************************************************************
+template <class MiniMd>
+class CLiteWeightStgdb
+{
+ friend class VerifyLayoutsMD;
+public:
+ CLiteWeightStgdb() : m_pvMd(NULL), m_cbMd(0)
+ {}
+
+ ~CLiteWeightStgdb()
+ { Uninit(); }
+
+ // open an in-memory metadata section for read.
+ __checkReturn
+ HRESULT InitOnMem(
+ ULONG cbData,
+ LPCVOID pbData);
+
+ __checkReturn
+ HRESULT InitHotPools(DataBuffer hotMetaData);
+
+ void Uninit();
+
+protected:
+ MiniMd m_MiniMd; // embedded compress meta data schemas definition
+ const void *m_pvMd; // Pointer to meta data.
+ ULONG m_cbMd; // Size of the meta data.
+
+ friend class CorMetaDataScope;
+ friend class COR;
+ friend class RegMeta;
+ friend class MERGER;
+ friend class NEWMERGER;
+ friend class MDInternalRO;
+ friend class MDInternalRW;
+};
+
+//*****************************************************************************
+// Open an in-memory metadata section for read
+//*****************************************************************************
+template <class MiniMd>
+void CLiteWeightStgdb<MiniMd>::Uninit()
+{
+ m_MiniMd.m_StringHeap.Delete();
+ m_MiniMd.m_UserStringHeap.Delete();
+ m_MiniMd.m_GuidHeap.Delete();
+ m_MiniMd.m_BlobHeap.Delete();
+ m_pvMd = NULL;
+ m_cbMd = 0;
+}
+
+class CLiteWeightStgdbRW : public CLiteWeightStgdb<CMiniMdRW>
+{
+ friend class CImportTlb;
+ friend class RegMeta;
+ friend class VerifyLayoutsMD;
+ friend HRESULT TranslateSigHelper(
+ IMDInternalImport* pImport,
+ IMDInternalImport* pAssemImport,
+ const void* pbHashValue,
+ ULONG cbHashValue,
+ PCCOR_SIGNATURE pbSigBlob,
+ ULONG cbSigBlob,
+ IMetaDataAssemblyEmit* pAssemEmit,
+ IMetaDataEmit* emit,
+ CQuickBytes* pqkSigEmit,
+ ULONG* pcbSig);
+public:
+ CLiteWeightStgdbRW() : m_cbSaveSize(0), m_pStreamList(0), m_pNextStgdb(NULL), m_pStgIO(NULL)
+ {
+ m_wszFileName = NULL;
+ m_pImage = NULL;
+ m_dwImageSize = 0;
+ m_dwPEKind = (DWORD)(-1);
+ m_dwDatabaseLFS = 0;
+ m_dwDatabaseLFT = 0;
+ }
+ ~CLiteWeightStgdbRW();
+
+ __checkReturn
+ HRESULT InitNew();
+
+ // open an in-memory metadata section for read.
+ __checkReturn
+ HRESULT InitOnMem(
+ ULONG cbData,
+ LPCVOID pbData,
+ int bReadOnly);
+
+ __checkReturn
+ HRESULT GetSaveSize(
+ CorSaveSize fSize,
+ UINT32 *pcbSaveSize,
+ MetaDataReorderingOptions reorderingOptions = NoReordering,
+ CorProfileData *pProfileData = NULL); // optional IBC profile data for working set optimization
+
+ __checkReturn
+ HRESULT SaveToStream(
+ IStream *pIStream, // Stream to which to write
+ MetaDataReorderingOptions reorderingOptions = NoReordering,
+ CorProfileData *pProfileData = NULL); // optional IBC profile data for working set optimization
+
+ __checkReturn
+ HRESULT Save(
+ LPCWSTR szFile,
+ DWORD dwSaveFlags);
+
+ // Open a metadata section for read/write
+ __checkReturn
+ HRESULT 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.
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ // Open a metadata section for read/write
+ __checkReturn
+ HRESULT OpenForRead(
+ IMDCustomDataSource *pDataSource, // data to open on top of
+ DWORD dwFlags); // Flags for the open.
+#endif
+
+ __checkReturn
+ HRESULT FindImageMetaData(
+ PVOID pImage, // Pointer to head of a file
+ DWORD dwFileLength, // length of a flat file
+ BOOL bMappedImage, // Is the file mapped
+ PVOID *ppMetaData, // [out] pointer to the metadata
+ ULONG *pcbMetaData); // [out] size of the metadata
+
+ __checkReturn
+ HRESULT FindObjMetaData(
+ PVOID pImage, // Pointer to an OBJ file
+ DWORD dwFileLength, // Length of the file
+ PVOID *ppMetaData, // [out] pointer to the metadata
+ ULONG *pcbMetaData); // [out] size of the metadata
+
+ __checkReturn
+ HRESULT 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
+
+ // Low level data access; not useful for most clients.
+ __checkReturn
+ HRESULT GetRawData(
+ const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB').
+ ULONG *pcbMd); // [OUT] put size of the stream here.
+
+ __checkReturn
+ STDMETHODIMP GetRawStreamInfo( // Get info about the MD stream.
+ 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.
+
+ UINT32 m_cbSaveSize; // Size of the saved streams.
+ int m_bSaveCompressed; // If true, save as compressed stream (#-, not #~)
+ VOID* m_pImage; // Set in OpenForRead, NULL for anything but PE files
+ DWORD m_dwImageSize; // On-disk size of image
+
+protected:
+ DWORD m_dwPEKind; // The kind of PE - 0: not a PE.
+ DWORD m_dwMachine; // Machine as defined in NT header.
+
+ __checkReturn
+ HRESULT GetPoolSaveSize(
+ LPCWSTR szHeap, // Name of the heap stream.
+ int iPool, // The pool whose size to get.
+ UINT32 *pcbSaveSize); // Add pool data to this value.
+
+ __checkReturn
+ HRESULT GetTablesSaveSize(
+ CorSaveSize fSave,
+ UINT32 *pcbSaveSize,
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData = NULL); // Add pool data to this value.
+
+ __checkReturn
+ HRESULT AddStreamToList(
+ UINT32 cbSize, // Size of the stream data.
+ LPCWSTR szName); // Name of the stream.
+
+ __checkReturn
+ HRESULT SaveToStorage(
+ TiggerStorage *pStorage,
+ MetaDataReorderingOptions reorderingOptions = NoReordering,
+ CorProfileData *pProfileData = NULL);
+
+ __checkReturn
+ HRESULT SavePool(LPCWSTR szName, TiggerStorage *pStorage, int iPool);
+
+ STORAGESTREAMLST *m_pStreamList;
+
+ __checkReturn
+ HRESULT InitFileForRead(
+ StgIO *pStgIO, // For file i/o.
+ int bReadOnly=true); // If read-only.
+
+ // Set file name of this database (makes copy of the file name).
+ __checkReturn HRESULT SetFileName(const WCHAR * wszFileName);
+ // Returns TRUE if wszFileName has valid file name length.
+ static BOOL IsValidFileNameLength(const WCHAR * wszFileName);
+
+ CLiteWeightStgdbRW *m_pNextStgdb;
+
+public:
+ FORCEINLINE FILETYPE GetFileType() { return m_eFileType; }
+
+private:
+ FILETYPE m_eFileType;
+ WCHAR * m_wszFileName; // Database file name (NULL or non-empty string)
+ DWORD m_dwDatabaseLFT; // Low bytes of the database file's last write time
+ DWORD m_dwDatabaseLFS; // Low bytes of the database file's size
+ StgIO * m_pStgIO; // For file i/o.
+
+}; // class CLiteWeightStgdbRW
+
+#endif // __LiteWeightStgdb_h__
diff --git a/src/md/inc/mdcolumndescriptors.h b/src/md/inc/mdcolumndescriptors.h
new file mode 100644
index 0000000000..ac2565cdb5
--- /dev/null
+++ b/src/md/inc/mdcolumndescriptors.h
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+static const BYTE s_ModuleCol[];
+static const BYTE s_TypeRefCol[];
+static const BYTE s_TypeDefCol[];
+static const BYTE s_FieldPtrCol[];
+static const BYTE s_FieldCol[];
+static const BYTE s_MethodPtrCol[];
+static const BYTE s_MethodCol[];
+static const BYTE s_ParamPtrCol[];
+static const BYTE s_ParamCol[];
+static const BYTE s_InterfaceImplCol[];
+static const BYTE s_MemberRefCol[];
+static const BYTE s_ConstantCol[];
+static const BYTE s_CustomAttributeCol[];
+static const BYTE s_FieldMarshalCol[];
+static const BYTE s_DeclSecurityCol[];
+static const BYTE s_ClassLayoutCol[];
+static const BYTE s_FieldLayoutCol[];
+static const BYTE s_StandAloneSigCol[];
+static const BYTE s_EventMapCol[];
+static const BYTE s_EventPtrCol[];
+static const BYTE s_EventCol[];
+static const BYTE s_PropertyMapCol[];
+static const BYTE s_PropertyPtrCol[];
+static const BYTE* s_PropertyCol;
+static const BYTE s_MethodSemanticsCol[];
+static const BYTE s_MethodImplCol[];
+static const BYTE s_ModuleRefCol[];
+static const BYTE* s_TypeSpecCol;
+static const BYTE s_ImplMapCol[];
+static const BYTE* s_FieldRVACol;
+static const BYTE s_ENCLogCol[];
+static const BYTE s_ENCMapCol[];
+static const BYTE s_AssemblyCol[];
+static const BYTE* s_AssemblyProcessorCol;
+static const BYTE s_AssemblyOSCol[];
+static const BYTE s_AssemblyRefCol[];
+static const BYTE s_AssemblyRefProcessorCol[];
+static const BYTE s_AssemblyRefOSCol[];
+static const BYTE s_FileCol[];
+static const BYTE s_ExportedTypeCol[];
+static const BYTE s_ManifestResourceCol[];
+static const BYTE s_NestedClassCol[];
+static const BYTE s_GenericParamCol[];
+static const BYTE s_MethodSpecCol[];
+static const BYTE s_GenericParamConstraintCol[];
diff --git a/src/md/inc/mdfileformat.h b/src/md/inc/mdfileformat.h
new file mode 100644
index 0000000000..a86549323e
--- /dev/null
+++ b/src/md/inc/mdfileformat.h
@@ -0,0 +1,269 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDFileFormat.h
+//
+
+//
+// This file contains a set of helpers to verify and read the file format.
+// This code does not handle the paging of the data, or different types of
+// I/O. See the StgTiggerStorage and StgIO code for this level of support.
+//
+//*****************************************************************************
+#ifndef __MDFileFormat_h__
+#define __MDFileFormat_h__
+
+//*****************************************************************************
+// The signature ULONG is the first 4 bytes of the file format. The second
+// signature string starts the header containing the stream list. It is used
+// for an integrity check when reading the header in lieu of a more complicated
+// system.
+//*****************************************************************************
+#define STORAGE_MAGIC_SIG 0x424A5342 // BSJB
+
+
+
+//*****************************************************************************
+// These values get written to the signature at the front of the file. Changing
+// these values should not be done lightly because all old files will no longer
+// be supported. In a future revision if a format change is required, a
+// backwards compatible migration path must be provided.
+//*****************************************************************************
+
+#define FILE_VER_MAJOR 1
+#define FILE_VER_MINOR 1
+
+// These are the last legitimate 0.x version macros. The file format has
+// sinced move up to 1.x (see macros above). After COM+ 1.0/NT 5 RTM's, these
+// macros should no longer be required or ever seen.
+#define FILE_VER_MAJOR_v0 0
+
+#define FILE_VER_MINOR_v0 19
+
+
+#define MAXSTREAMNAME 32
+
+enum
+{
+ STGHDR_NORMAL = 0x00, // Normal default flags.
+ STGHDR_EXTRADATA = 0x01, // Additional data exists after header.
+};
+
+
+//*****************************************************************************
+// This is the formal signature area at the front of the file. This structure
+// is not allowed to change, the shim depends on it staying the same size.
+// Use the reserved pointer if it must extended.
+//*****************************************************************************
+struct STORAGESIGNATURE;
+typedef STORAGESIGNATURE UNALIGNED * PSTORAGESIGNATURE;
+
+#include "pshpack1.h"
+struct STORAGESIGNATURE
+{
+METADATA_FIELDS_PROTECTION:
+ ULONG lSignature; // "Magic" signature.
+ USHORT iMajorVer; // Major file version.
+ USHORT iMinorVer; // Minor file version.
+ ULONG iExtraData; // Offset to next structure of information
+ ULONG iVersionString; // Length of version string
+public:
+ BYTE pVersion[0]; // Version string
+ ULONG GetSignature()
+ {
+ return VAL32(lSignature);
+ }
+ void SetSignature(ULONG Signature)
+ {
+ lSignature = VAL32(Signature);
+ }
+
+ USHORT GetMajorVer()
+ {
+ return VAL16(iMajorVer);
+ }
+ void SetMajorVer(USHORT MajorVer)
+ {
+ iMajorVer = VAL16(MajorVer);
+ }
+
+ USHORT GetMinorVer()
+ {
+ return VAL16(iMinorVer);
+ }
+ void SetMinorVer(USHORT MinorVer)
+ {
+ iMinorVer = VAL16(MinorVer);
+ }
+
+ ULONG GetExtraDataOffset()
+ {
+ return VAL32(iExtraData);
+ }
+ void SetExtraDataOffset(ULONG ExtraDataOffset)
+ {
+ iExtraData = VAL32(ExtraDataOffset);
+ }
+
+ ULONG GetVersionStringLength()
+ {
+ return VAL32(iVersionString);
+ }
+ void SetVersionStringLength(ULONG VersionStringLength)
+ {
+ iVersionString = VAL32(VersionStringLength);
+ }
+};
+#include "poppack.h"
+
+
+//*****************************************************************************
+// The header of the storage format.
+//*****************************************************************************
+struct STORAGEHEADER;
+typedef STORAGEHEADER UNALIGNED * PSTORAGEHEADER;
+
+#include "pshpack1.h"
+struct STORAGEHEADER
+{
+METADATA_FIELDS_PROTECTION:
+ BYTE fFlags; // STGHDR_xxx flags.
+ BYTE pad;
+ USHORT iStreams; // How many streams are there.
+public:
+ BYTE GetFlags()
+ {
+ return fFlags;
+ }
+ void SetFlags(BYTE flags)
+ {
+ fFlags = flags;
+ }
+ void AddFlags(BYTE flags)
+ {
+ fFlags |= flags;
+ }
+
+
+ USHORT GetiStreams()
+ {
+ return VAL16(iStreams);
+ }
+ void SetiStreams(USHORT iStreamsCount)
+ {
+ iStreams = VAL16(iStreamsCount);
+ }
+};
+#include "poppack.h"
+
+
+//*****************************************************************************
+// Each stream is described by this struct, which includes the offset and size
+// of the data. The name is stored in ANSI null terminated.
+//*****************************************************************************
+struct STORAGESTREAM;
+typedef STORAGESTREAM UNALIGNED * PSTORAGESTREAM;
+
+#include "pshpack1.h"
+struct STORAGESTREAM
+{
+METADATA_FIELDS_PROTECTION:
+ ULONG iOffset; // Offset in file for this stream.
+ ULONG iSize; // Size of the file.
+ char rcName[MAXSTREAMNAME]; // Start of name, null terminated.
+public:
+ // Returns pointer to the next stream. Doesn't validate the structure.
+ inline PSTORAGESTREAM NextStream()
+ {
+ int iLen = (int)(strlen(rcName) + 1);
+ iLen = ALIGN4BYTE(iLen);
+ return ((PSTORAGESTREAM) (((BYTE*)this) + (sizeof(ULONG) * 2) + iLen));
+ }
+ // Returns pointer to the next stream.
+ // Returns NULL if the structure has invalid format.
+ inline PSTORAGESTREAM NextStream_Verify()
+ {
+ // Check existence of null-terminator in the name
+ if (memchr(rcName, 0, MAXSTREAMNAME) == NULL)
+ {
+ return NULL;
+ }
+ return NextStream();
+ }
+
+ inline ULONG GetStreamSize()
+ {
+ return (ULONG)(strlen(rcName) + 1 + (sizeof(STORAGESTREAM) - sizeof(rcName)));
+ }
+
+ inline char* GetName()
+ {
+ return rcName;
+ }
+ inline LPCWSTR GetName(__inout_ecount (iMaxSize) LPWSTR szName, int iMaxSize)
+ {
+ VERIFY(::WszMultiByteToWideChar(CP_ACP, 0, rcName, -1, szName, iMaxSize));
+ return (szName);
+ }
+ inline void SetName(LPCWSTR szName)
+ {
+ int size;
+ size = WszWideCharToMultiByte(CP_ACP, 0, szName, -1, rcName, MAXSTREAMNAME, 0, 0);
+ _ASSERTE(size > 0);
+ }
+
+ ULONG GetSize()
+ {
+ return VAL32(iSize);
+ }
+ void SetSize(ULONG Size)
+ {
+ iSize = VAL32(Size);
+ }
+
+ ULONG GetOffset()
+ {
+ return VAL32(iOffset);
+ }
+ void SetOffset(ULONG Offset)
+ {
+ iOffset = VAL32(Offset);
+ }
+};
+#include "poppack.h"
+
+
+class MDFormat
+{
+public:
+//*****************************************************************************
+// Verify the signature at the front of the file to see what type it is.
+//*****************************************************************************
+ static HRESULT VerifySignature(
+ PSTORAGESIGNATURE pSig, // The signature to check.
+ ULONG cbData); // Size of metadata.
+
+//*****************************************************************************
+// Skip over the header and find the actual stream data.
+// It doesn't perform any checks for buffer overflow - use GetFirstStream_Verify
+// instead.
+//*****************************************************************************
+ static PSTORAGESTREAM GetFirstStream(// Return pointer to the first stream.
+ PSTORAGEHEADER pHeader, // Return copy of header struct.
+ const void *pvMd); // Pointer to the full file.
+//*****************************************************************************
+// Skip over the header and find the actual stream data. Secure version of
+// GetFirstStream method.
+// The header is supposed to be verified by VerifySignature.
+//
+// Caller has to check available buffer size before using the first stream.
+//*****************************************************************************
+ static PSTORAGESTREAM GetFirstStream_Verify(// Return pointer to the first stream.
+ PSTORAGEHEADER pHeader, // Return copy of header struct.
+ const void *pvMd, // Pointer to the full file.
+ ULONG *pcbMd); // [in, out] Size of pvMd buffer (we don't want to read behind it)
+
+};
+
+#endif // __MDFileFormat_h__
diff --git a/src/md/inc/mdinternalrw.h b/src/md/inc/mdinternalrw.h
new file mode 100644
index 0000000000..a0cc4816a4
--- /dev/null
+++ b/src/md/inc/mdinternalrw.h
@@ -0,0 +1,862 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDInternalRW.h
+//
+
+//
+// Contains utility code for MD directory
+//
+//*****************************************************************************
+#ifndef __MDInternalRW__h__
+#define __MDInternalRW__h__
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+#include "../inc/mdlog.h"
+
+class UTSemReadWrite;
+
+class MDInternalRW : public IMDInternalImportENC, public IMDCommon
+{
+ friend class VerifyLayoutsMD;
+public:
+
+
+ MDInternalRW();
+ virtual ~MDInternalRW();
+ __checkReturn
+ HRESULT Init(LPVOID pData, ULONG cbData, int bReadOnly);
+ __checkReturn
+ HRESULT InitWithStgdb(IUnknown *pUnk, CLiteWeightStgdbRW *pStgdb);
+ __checkReturn
+ HRESULT InitWithRO(MDInternalRO *pRO, int bReadOnly);
+
+ // *** IUnknown methods ***
+ __checkReturn
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
+ STDMETHODIMP_(ULONG) AddRef(void);
+ STDMETHODIMP_(ULONG) Release(void);
+
+ __checkReturn
+ STDMETHODIMP 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
+ DAC_UNEXPECTED();
+
+ __checkReturn
+ STDMETHODIMP 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
+ DAC_UNEXPECTED();
+
+ STDMETHODIMP_(IMetaModelCommon*) GetMetaModelCommon()
+ {
+ return static_cast<IMetaModelCommon*>(&m_pStgdb->m_MiniMd);
+ }
+
+ STDMETHODIMP_(IMetaModelCommonRO*) GetMetaModelCommonRO()
+ {
+ if (m_pStgdb->m_MiniMd.IsWritable())
+ {
+ _ASSERTE(!"IMetaModelCommonRO methods cannot be used because this importer is writable.");
+ return NULL;
+ }
+
+ return static_cast<IMetaModelCommonRO*>(&m_pStgdb->m_MiniMd);
+ }
+
+ __checkReturn
+ STDMETHODIMP SetOptimizeAccessForSpeed(// return hresult
+ BOOL fOptSpeed)
+ {
+ // If there is any optional work we can avoid (for example, because we have
+ // traded space for speed) this is the place to turn it off or on.
+
+ return S_OK;
+ }
+
+ //*****************************************************************************
+ // return the count of entries of a given kind in a scope
+ // For example, pass in mdtMethodDef will tell you how many MethodDef
+ // contained in a scope
+ //*****************************************************************************
+ STDMETHODIMP_(ULONG) GetCountWithTokenKind(// return hresult
+ DWORD tkKind) // [IN] pass in the kind of token.
+ DAC_UNEXPECTED();
+
+ //*****************************************************************************
+ // enumerator for typedef
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP EnumTypeDefInit( // return hresult
+ HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data
+
+ STDMETHODIMP_(ULONG) EnumTypeDefGetCount(
+ HENUMInternal *phEnum); // [IN] the enumerator to retrieve information
+
+ STDMETHODIMP_(void) EnumTypeDefReset(
+ HENUMInternal *phEnum); // [IN] the enumerator to retrieve information
+
+ STDMETHODIMP_(bool) EnumTypeDefNext( // return hresult
+ HENUMInternal *phEnum, // [IN] input enum
+ mdTypeDef *ptd); // [OUT] return token
+
+ STDMETHODIMP_(void) EnumTypeDefClose(
+ HENUMInternal *phEnum); // [IN] the enumerator to retrieve information
+
+ //*****************************************************************************
+ // enumerator for MethodImpl
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP 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.
+ DAC_UNEXPECTED();
+
+ STDMETHODIMP_(ULONG) EnumMethodImplGetCount(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+ DAC_UNEXPECTED();
+
+ STDMETHODIMP_(void) EnumMethodImplReset(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+ DAC_UNEXPECTED();
+
+ __checkReturn
+ STDMETHODIMP EnumMethodImplNext( // return hresult (S_OK = TRUE, S_FALSE = FALSE or error code)
+ 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
+ DAC_UNEXPECTED();
+
+ STDMETHODIMP_(void) EnumMethodImplClose(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+ DAC_UNEXPECTED();
+
+ //*****************************************
+ // Enumerator helpers for memberdef, memberref, interfaceimp,
+ // event, property, param, methodimpl
+ //*****************************************
+
+ __checkReturn
+ STDMETHODIMP EnumGlobalFunctionsInit( // return hresult
+ HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data
+
+ __checkReturn
+ STDMETHODIMP EnumGlobalFieldsInit( // return hresult
+ HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data
+
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP EnumAllInit( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ HENUMInternal *phEnum); // [OUT] the enumerator to fill
+
+ STDMETHODIMP_(bool) EnumNext(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk); // [OUT] token to scope the search
+
+ STDMETHODIMP_(ULONG) EnumGetCount(
+ HENUMInternal *phEnum); // [IN] the enumerator to retrieve information
+
+ STDMETHODIMP_(void) EnumReset(
+ HENUMInternal *phEnum); // [IN] the enumerator to be reset
+
+ STDMETHODIMP_(void) EnumClose(
+ HENUMInternal *phEnum); // [IN] the enumerator to be closed
+
+ __checkReturn
+ STDMETHODIMP EnumPermissionSetsInit( // return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ CorDeclSecurity Action, // [IN] Action to scope the search
+ HENUMInternal *phEnum) // [OUT] the enumerator to fill
+ DAC_UNEXPECTED();
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP GetParentToken(
+ mdToken tkChild, // [IN] given child token
+ mdToken *ptkParent); // [OUT] returning parent
+
+ __checkReturn
+ STDMETHODIMP GetCustomAttributeProps(
+ mdCustomAttribute at, // The attribute.
+ mdToken *ptkType); // Put attribute type here.
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP GetCustomAttributeByName( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData); // [OUT] Put size of data here.
+
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP GetScopeProps(
+ LPCSTR *pszName, // [OUT] scope name
+ GUID *pmvid); // [OUT] version id
+
+ // finding a particular method
+ __checkReturn
+ STDMETHODIMP FindMethodDef(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmd); // [OUT] matching memberdef
+
+ // return a iSeq's param given a MethodDef
+ __checkReturn
+ STDMETHODIMP 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.
+
+ //*****************************************
+ //
+ // GetName* functions
+ //
+ //*****************************************
+
+ // return the name and namespace of typedef
+ __checkReturn
+ STDMETHODIMP GetNameOfTypeDef(
+ mdTypeDef classdef, // given classdef
+ LPCSTR *pszname, // return class name(unqualified)
+ LPCSTR *psznamespace); // return the name space name
+
+ __checkReturn
+ STDMETHODIMP GetIsDualOfTypeDef(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pDual); // [OUT] return dual flag here.
+
+ __checkReturn
+ STDMETHODIMP GetIfaceTypeOfTypeDef(
+ mdTypeDef tkTypeDef,
+ ULONG * pIface); // [OUT] 0=dual, 1=vtable, 2=dispinterface
+
+ __checkReturn
+ STDMETHODIMP GetNameOfMethodDef(
+ mdMethodDef tkMethodDef,
+ LPCSTR * pszName);
+
+ __checkReturn
+ STDMETHODIMP 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 *pszName);
+
+ // return the name of a FieldDef
+ __checkReturn
+ STDMETHODIMP GetNameOfFieldDef(
+ mdFieldDef fd, // given memberdef
+ LPCSTR *pszName);
+
+ // return the name of typeref
+ __checkReturn
+ STDMETHODIMP GetNameOfTypeRef(
+ mdTypeRef classref, // [IN] given typeref
+ LPCSTR *psznamespace, // [OUT] return typeref name
+ LPCSTR *pszname); // [OUT] return typeref namespace
+
+ // return the resolutionscope of typeref
+ __checkReturn
+ STDMETHODIMP GetResolutionScopeOfTypeRef(
+ mdTypeRef classref, // given classref
+ mdToken *ptkResolutionScope);
+
+ // return the typeref token given the name.
+ __checkReturn
+ STDMETHODIMP FindTypeRefByName(
+ 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.
+
+ // return the TypeDef properties
+ __checkReturn
+ STDMETHODIMP GetTypeDefProps( // return hresult
+ mdTypeDef classdef, // given classdef
+ DWORD *pdwAttr, // return flags on class, tdPublic, tdAbstract
+ mdToken *ptkExtends); // [OUT] Put base class TypeDef/TypeRef here.
+
+ // return the item's guid
+ __checkReturn
+ STDMETHODIMP GetItemGuid( // return hresult
+ mdToken tkObj, // [IN] given item.
+ CLSID *pGuid); // [OUT] Put guid here.
+
+ // get enclosing class of NestedClass.
+ __checkReturn
+ STDMETHODIMP GetNestedClassProps( // S_OK or error
+ mdTypeDef tkNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptkEnclosingClass); // [OUT] EnclosingClass token.
+
+ // Get count of Nested classes given the enclosing class.
+ __checkReturn
+ STDMETHODIMP GetCountNestedClasses( // return count of Nested classes.
+ mdTypeDef tkEnclosingClass, // [IN]Enclosing class.
+ ULONG *pcNestedClassesCount);
+
+ // Return array of Nested classes given the enclosing class.
+ __checkReturn
+ STDMETHODIMP 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);
+
+ // return the ModuleRef properties
+ __checkReturn
+ STDMETHODIMP GetModuleRefProps(
+ mdModuleRef mur, // [IN] moduleref token
+ LPCSTR *pszName); // [OUT] buffer to fill with the moduleref name
+
+
+ //*****************************************
+ //
+ // GetSig* functions
+ //
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetSigOfMethodDef(
+ mdMethodDef methoddef, // [IN] given memberdef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig);
+
+ __checkReturn
+ STDMETHODIMP GetSigOfFieldDef(
+ mdMethodDef methoddef, // [IN] given memberdef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig);
+
+ __checkReturn
+ STDMETHODIMP GetSigFromToken(
+ mdToken tk, // FieldDef, MethodDef, Signature or TypeSpec token
+ ULONG * pcbSig,
+ PCCOR_SIGNATURE * ppSig);
+
+
+
+ //*****************************************
+ // get method property
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetMethodDefProps(
+ mdMethodDef md, // The method for which to get props.
+ DWORD *pdwFlags);
+
+ STDMETHODIMP_(ULONG) GetMethodDefSlot(
+ mdMethodDef mb); // The method for which to get props.
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetMethodImplProps(
+ mdToken tk, // [IN] MethodDef or MethodImpl
+ DWORD *pulCodeRVA, // [OUT] CodeRVA
+ DWORD *pdwImplFlags); // [OUT] Impl. Flags
+
+ //*****************************************************************************
+ // return the field RVA
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetFieldRVA(
+ mdToken fd, // [IN] FieldDef
+ ULONG *pulCodeRVA); // [OUT] CodeRVA
+
+ //*****************************************************************************
+ // return the field offset for a given field
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetFieldOffset(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulOffset); // [OUT] FieldOffset
+
+ //*****************************************
+ // get field property
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetFieldDefProps(
+ mdFieldDef fd, // [IN] given fielddef
+ DWORD *pdwFlags); // [OUT] return fdPublic, fdPrive, etc flags
+
+ //*****************************************************************************
+ // return default value of a token(could be paramdef, fielddef, or property
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetDefaultValue(
+ mdToken tk, // [IN] given FieldDef, ParamDef, or Property
+ MDDefaultValue *pDefaultValue); // [OUT] default value to fill
+
+
+ //*****************************************
+ // get dispid of a MethodDef or a FieldDef
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetDispIdOfMemberDef( // return hresult
+ mdToken tk, // [IN] given methoddef or fielddef
+ ULONG *pDispid); // [OUT] Put the dispid here.
+
+ //*****************************************
+ // return TypeRef/TypeDef given an InterfaceImpl token
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetTypeOfInterfaceImpl( // return the TypeRef/typedef token for the interfaceimpl
+ mdInterfaceImpl iiImpl, // given a interfaceimpl
+ mdToken *ptkType);
+
+ __checkReturn
+ STDMETHODIMP GetMethodSpecProps(
+ 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
+
+ //*****************************************
+ // look up function for TypeDef
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP FindTypeDef(
+ 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
+
+ __checkReturn
+ STDMETHODIMP FindTypeDefByGUID(
+ REFGUID guid, // guid to look up
+ mdTypeDef *ptypedef); // return typedef
+
+
+
+ //*****************************************
+ // return name and sig of a memberref
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetNameAndSigOfMemberRef( // return name here
+ mdMemberRef memberref, // given memberref
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName);
+
+ //*****************************************************************************
+ // Given memberref, return the parent. It can be TypeRef, ModuleRef, MethodDef
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetParentOfMemberRef(
+ mdMemberRef memberref, // given memberref
+ mdToken *ptkParent); // return the parent token
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ //******************************************
+ // property info for method.
+ //******************************************
+ __checkReturn
+ STDMETHODIMP 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
+
+ //*****************************************
+ // class layout/sequence information
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetClassPackSize( // [OUT] return error if a class doesn't have packsize info
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwPackSize); // [OUT] return the pack size of the class. 1, 2, 4, 8 or 16
+
+ __checkReturn
+ STDMETHODIMP GetClassTotalSize( // [OUT] return error if a class doesn't have total size info
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwClassSize); // [OUT] return the total size of the class
+
+ __checkReturn
+ STDMETHODIMP GetClassLayoutInit(
+ mdTypeDef td, // [IN] give typedef
+ MD_CLASS_LAYOUT *pLayout); // [OUT] set up the status of query here
+
+ __checkReturn
+ STDMETHODIMP GetClassLayoutNext(
+ MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here
+ mdFieldDef *pfd, // [OUT] return the fielddef
+ ULONG *pulOffset); // [OUT] return the offset/ulSequence associate with it
+
+ //*****************************************
+ // marshal information of a field
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetFieldMarshal( // return error if no native type associate with the token
+ mdFieldDef fd, // [IN] given fielddef
+ PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature
+ ULONG *pcbNativeType); // [OUT] the count of bytes of *ppvNativeType
+
+
+ //*****************************************
+ // property APIs
+ //*****************************************
+ // find a property by name
+ __checkReturn
+ STDMETHODIMP FindProperty(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szPropName, // [IN] property name
+ mdProperty *pProp); // [OUT] return property token
+
+ __checkReturn
+ STDMETHODIMP GetPropertyProps(
+ mdProperty prop, // [IN] property token
+ LPCSTR *szProperty, // [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
+
+ //**********************************
+ // Event APIs
+ //**********************************
+ __checkReturn
+ STDMETHODIMP FindEvent(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szEventName, // [IN] event name
+ mdEvent *pEvent); // [OUT] return event token
+
+ __checkReturn
+ STDMETHODIMP 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
+
+
+ //**********************************
+ // Generics APIs
+ //**********************************
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ //**********************************
+ // find a particular associate of a property or an event
+ //**********************************
+ __checkReturn
+ STDMETHODIMP FindAssociate(
+ mdToken evprop, // [IN] given a property or event token
+ DWORD associate, // [IN] given a associate semantics(setter, getter, testdefault, reset, AddOn, RemoveOn, Fire)
+ mdMethodDef *pmd); // [OUT] return method def token
+
+ __checkReturn
+ STDMETHODIMP EnumAssociateInit(
+ mdToken evprop, // [IN] given a property or an event token
+ HENUMInternal *phEnum); // [OUT] cursor to hold the query result
+
+ __checkReturn
+ STDMETHODIMP GetAllAssociates(
+ HENUMInternal *phEnum, // [IN] query result form GetPropertyAssociateCounts
+ ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output
+ ULONG cAssociateRec); // [IN] size of the buffer
+
+
+ //**********************************
+ // Get info about a PermissionSet.
+ //**********************************
+ __checkReturn
+ STDMETHODIMP 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.
+
+ //****************************************
+ // Get the String given the String token.
+ // Returns a pointer to the string, or NULL in case of error.
+ //****************************************
+ __checkReturn
+ STDMETHODIMP GetUserString(
+ mdString stk, // [IN] the string token.
+ ULONG *pchString, // [OUT] count of characters in the string.
+ BOOL *pbIs80Plus, // [OUT] specifies where there are extended characters >= 0x80.
+ LPCWSTR *pwszUserString);
+
+ //*****************************************************************************
+ // p-invoke APIs.
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetPinvokeMap(
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ LPCSTR *pszImportName, // [OUT] Import name.
+ mdModuleRef *pmrImportDLL); // [OUT] ModuleRef token for the target DLL.
+
+ //*****************************************************************************
+ // Assembly MetaData APIs.
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP GetExportedTypeProps(
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ LPCSTR *pszNamespace, // [OUT] Buffer to fill with namespace.
+ 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.
+
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP FindExportedTypeByName( // S_OK or error
+ LPCSTR szNamespace, // [IN] Namespace of the ExportedType.
+ LPCSTR szName, // [IN] Name of the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] Token for the enclosing Type.
+ mdExportedType *pmct); // [OUT] Put ExportedType token here.
+
+ __checkReturn
+ STDMETHODIMP FindManifestResourceByName(// S_OK or error
+ LPCSTR szName, // [IN] Name of the resource.
+ mdManifestResource *pmmr); // [OUT] Put ManifestResource token here.
+
+ __checkReturn
+ STDMETHODIMP GetAssemblyFromScope( // S_OK or error
+ mdAssembly *ptkAssembly); // [OUT] Put token here.
+
+ //***************************************************************************
+ // return properties regarding a TypeSpec
+ //***************************************************************************
+ __checkReturn
+ STDMETHODIMP 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.
+
+
+ //*****************************************************************************
+ // 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
+ STDMETHODIMP GetVersionString( // S_OK or error.
+ LPCSTR *pVer); // [OUT] Put version string here.
+
+
+ //*****************************************************************************
+ // helpers to convert a text signature to a com format
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP ConvertTextSigToComSig( // Return hresult.
+ BOOL fCreateTrIfNotFound, // [IN] create typeref if not found
+ LPCSTR pSignature, // [IN] class file format signature
+ CQuickBytes *pqbNewSig, // [OUT] place holder for COM+ signature
+ ULONG *pcbCount); // [OUT] the result size of signature
+
+ __checkReturn
+ STDMETHODIMP SetUserContextData( // S_OK or E_NOTIMPL
+ IUnknown *pIUnk); // The user context.
+
+ STDMETHODIMP_(BOOL) IsValidToken( // True or False.
+ mdToken tk); // [IN] Given token.
+
+ STDMETHODIMP_(IUnknown *) GetCachedPublicInterface(BOOL fWithLock); // return the cached public interface
+ __checkReturn
+ STDMETHODIMP SetCachedPublicInterface(IUnknown *pUnk); // return hresult
+ STDMETHODIMP_(UTSemReadWrite*) GetReaderWriterLock(); // return the reader writer lock
+ __checkReturn
+ STDMETHODIMP SetReaderWriterLock(UTSemReadWrite *pSem)
+ {
+ _ASSERTE(m_pSemReadWrite == NULL);
+ m_pSemReadWrite = pSem;
+ INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
+ return NOERROR;
+ }
+
+ // *** IMDInternalImportENC methods ***
+ __checkReturn
+ STDMETHODIMP ApplyEditAndContinue( // S_OK or error.
+ MDInternalRW *pDelta); // MD with the ENC delta.
+
+ __checkReturn
+ STDMETHODIMP EnumDeltaTokensInit( // return hresult
+ HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data
+
+ STDMETHODIMP_(mdModule) GetModuleFromScope(void);
+
+ // finding a particular method
+ __checkReturn
+ STDMETHODIMP FindMethodDefUsingCompare(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ 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 *pmd); // [OUT] matching memberdef
+
+ //*****************************************************************************
+ // return the table pointer and size for a given table index
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetTableInfoWithIndex(
+ ULONG index, // [IN] pass in the index
+ void **pTable, // [OUT] pointer to table at index
+ void **pTableSize); // [OUT] size of table at index
+
+ __checkReturn
+ STDMETHODIMP ApplyEditAndContinue(
+ void *pData, // [IN] the delta metadata
+ ULONG cbData, // [IN] length of pData
+ IMDInternalImport **ppv); // [OUT] the resulting metadata interface
+
+
+ FORCEINLINE CLiteWeightStgdbRW* GetMiniStgdb() { return m_pStgdb; }
+ FORCEINLINE UTSemReadWrite *getReaderWriterLock() { return m_pSemReadWrite; }
+
+
+ CLiteWeightStgdbRW *m_pStgdb;
+
+private:
+ mdTypeDef m_tdModule; // <Module> typedef value.
+ LONG m_cRefs; // Ref count.
+ bool m_fOwnStgdb;
+ IUnknown *m_pUnk;
+ IUnknown *m_pUserUnk; // Release at shutdown.
+ IMetaDataHelper *m_pIMetaDataHelper;// pointer to cached public interface
+ UTSemReadWrite *m_pSemReadWrite; // read write lock for multi-threading.
+ bool m_fOwnSem; // Does MDInternalRW own this read write lock object?
+
+public:
+ STDMETHODIMP_(DWORD) GetMetadataStreamVersion()
+ {
+ return (DWORD)m_pStgdb->m_MiniMd.m_Schema.m_minor |
+ ((DWORD)m_pStgdb->m_MiniMd.m_Schema.m_major << 16);
+ };
+
+ __checkReturn
+ STDMETHODIMP SetVerifiedByTrustedSource(// return hresult
+ BOOL fVerified)
+ {
+ m_pStgdb->m_MiniMd.SetVerifiedByTrustedSource(fVerified);
+ return S_OK;
+ }
+
+ STDMETHODIMP GetRvaOffsetData(// S_OK or error
+ DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table.
+ DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table.
+ DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table.
+ DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table.
+ DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table.
+ DWORD *pFieldRvaCount) // [OUT] Number of records in FieldRVA table.
+ {
+ return m_pStgdb->m_MiniMd.GetRvaOffsetData(
+ pFirstMethodRvaOffset,
+ pMethodDefRecordSize,
+ pMethodDefCount,
+ pFirstFieldRvaOffset,
+ pFieldRvaRecordSize,
+ pFieldRvaCount);
+ }
+}; // class MDInternalRW
+
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+#endif // __MDInternalRW__h__
diff --git a/src/md/inc/mdlog.h b/src/md/inc/mdlog.h
new file mode 100644
index 0000000000..7eb792f9cc
--- /dev/null
+++ b/src/md/inc/mdlog.h
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDLog.h - Meta data logging helper.
+//
+
+//
+//*****************************************************************************
+#ifndef __MDLog_h__
+#define __MDLog_h__
+
+#if defined(_DEBUG) && !defined(DACCESS_COMPILE)
+#define LOGGING
+#endif
+
+#include <log.h>
+
+#define LOGMD LF_METADATA, LL_INFO10000
+#define LOG_MDCALL(func) LOG((LF_METADATA, LL_INFO10000, "MD: %s\n", #func))
+
+#define MDSTR(str) ((str) ? str : W("<null>"))
+#define MDSTRA(str) ((str) ? str : "<null>")
+
+#endif // __MDLog_h__
diff --git a/src/md/inc/metadatahash.h b/src/md/inc/metadatahash.h
new file mode 100644
index 0000000000..6fa43dbb99
--- /dev/null
+++ b/src/md/inc/metadatahash.h
@@ -0,0 +1,207 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MetaDataHash.h -- Meta data hash data structures.
+//
+
+//
+// Used by Emitters and by E&C.
+//
+//*****************************************************************************
+#ifndef _MetaDataHash_h_
+#define _MetaDataHash_h_
+
+#if _MSC_VER >= 1100
+ # pragma once
+#endif
+
+#include "utilcode.h"
+
+
+#define REHASH_THREADSHOLD 3
+
+
+//*****************************************************************************
+// A hash entry list item.
+//*****************************************************************************
+struct TOKENHASHENTRY
+{
+ mdToken tok;
+ ULONG ulHash;
+ ULONG iNext;
+};
+
+//*****************************************************************************
+// The following is a hash class definition used for hashing MemberDef. The difference
+// from the hash table above is because it is expansive to retrieve the parent for MemberDef.
+//
+//*****************************************************************************
+struct MEMBERDEFHASHENTRY
+{
+ mdToken tok;
+ mdToken tkParent;
+ ULONG ulHash;
+ ULONG iNext;
+};
+
+
+//*****************************************************************************
+// This class is used to create transient indexes for meta data structures.
+// This class is generic; one must override it to provide hashing and
+// accessor methods for your specific record type. It can start out on top
+// of malloc with a small memory footprint, and as you get larger, it must
+// be capable of rehashing.
+//*****************************************************************************
+template <class Entry> class CMetaDataHashTemplate
+{
+public:
+ CMetaDataHashTemplate()
+ {
+ m_rgBuckets = 0;
+ m_cItems = 0;
+ m_iBuckets = 0;
+ }
+
+ ~CMetaDataHashTemplate()
+ {
+ // Free the bucket list.
+ if (m_rgBuckets)
+ {
+ delete [] m_rgBuckets;
+ m_rgBuckets = 0;
+ m_cItems = 0;
+ m_iBuckets = 0;
+ }
+ }
+
+//*****************************************************************************
+// Called to allocate the hash table entries so that new data may be added.
+//*****************************************************************************
+ HRESULT NewInit( // Return status.
+ int iBuckets=17) // How many buckets you want.
+ {
+ m_rgBuckets = new (nothrow) int[iBuckets];
+ if (!m_rgBuckets)
+ return (OutOfMemory());
+ m_iBuckets = iBuckets;
+ memset(m_rgBuckets, ~0, sizeof(int) * iBuckets);
+ return (S_OK);
+ }
+
+//*****************************************************************************
+// Add new items to the hash list.
+//*****************************************************************************
+ Entry *Add( // Pointer to element to write to.
+ ULONG iHash) // Hash value of entry to add.
+ {
+ Entry *p = 0;
+ HRESULT hr;
+
+ int iBucket = iHash % m_iBuckets;
+
+ if (m_cItems > REHASH_THREADSHOLD * m_iBuckets)
+ {
+ hr = ReHash();
+ if (FAILED(hr))
+ return (0);
+ iBucket = iHash % m_iBuckets;
+ }
+
+ // Add a new item pointer.
+ p = m_Heap.Append();
+ if (!p)
+ return (0);
+
+ // Chain the new item to the front of the heap.
+ p->iNext = m_rgBuckets[iBucket];
+ p->ulHash = iHash;
+ m_cItems++;
+ m_rgBuckets[iBucket] = m_Heap.ItemIndex(p);
+ return (p);
+ }
+
+
+//*****************************************************************************
+// Grow the hash table
+//*****************************************************************************
+ HRESULT ReHash()
+ {
+ int *rgBuckets;
+ int iBuckets;
+ int iBucket;
+ int index;
+ int iCount;
+ Entry *p = 0;
+
+ iBuckets = m_iBuckets*2 -1;
+ rgBuckets = new (nothrow) int[iBuckets];
+ if (!rgBuckets)
+ return (OutOfMemory());
+ memset(rgBuckets, ~0, sizeof(int) * iBuckets);
+
+ // loop through each of data and rehash them
+ iCount = m_Heap.Count();
+ for (index = 0; index < iCount; index++)
+ {
+ // get the hash value of the entry
+ p = m_Heap.Get(index);
+
+ // rehash the entry
+ iBucket = p->ulHash % iBuckets;
+
+ // Chain the item to the front of the new heap.
+ p->iNext = rgBuckets[iBucket];
+ rgBuckets[iBucket] = index;
+ }
+
+ // swap the hash table
+ delete [] m_rgBuckets;
+ m_rgBuckets = rgBuckets;
+ m_iBuckets = iBuckets;
+ return NOERROR;
+
+ }
+
+//*****************************************************************************
+// Find first/find next node for a chain given the hash.
+//*****************************************************************************
+ Entry *FindFirst( // Return entry.
+ ULONG iHash, // The hash value for the entry.
+ int &POS) // Current position.
+ {
+ int iBucket = iHash % m_iBuckets;
+ POS = m_rgBuckets[iBucket];
+ return (FindNext(POS));
+ }
+
+ Entry *FindNext( // Return entry or 0.
+ int &POS) // Current location.
+ {
+ Entry *p;
+
+ if (POS == ~0)
+ return (0);
+
+ p = m_Heap.Get(POS);
+ POS = p->iNext;
+ return (p);
+ }
+
+private:
+ CDynArray<Entry> m_Heap; // First heap in the list.
+ int *m_rgBuckets; // Bucket list.
+ int m_iBuckets; // How many buckets.
+ int m_cItems; // Number of items in the hash
+};
+
+
+class CMetaDataHashBase : public CMetaDataHashTemplate<TOKENHASHENTRY>
+{
+};
+
+class CMemberDefHash : public CMetaDataHashTemplate<MEMBERDEFHASHENTRY>
+{
+};
+
+#endif // _MetaDataHash_h_
diff --git a/src/md/inc/metamodel.h b/src/md/inc/metamodel.h
new file mode 100644
index 0000000000..317501bfa5
--- /dev/null
+++ b/src/md/inc/metamodel.h
@@ -0,0 +1,2072 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MetaModel.h -- header file for compressed COM+ metadata.
+//
+
+//
+//*****************************************************************************
+#ifndef _METAMODEL_H_
+#define _METAMODEL_H_
+
+#if _MSC_VER >= 1100
+#pragma once
+#endif
+
+#include <cor.h>
+#include <stgpool.h>
+#include <metamodelpub.h>
+#include "metadatatracker.h"
+
+#include "../datablob.h"
+#include "../debug_metadata.h"
+
+#undef __unaligned
+
+#define ALLOCATED_MEMORY_MARKER 0xff
+
+// Version numbers for metadata format.
+
+#define METAMODEL_MAJOR_VER_V1_0 1 // Major version for v1.0
+#define METAMODEL_MINOR_VER_V1_0 0 // Minor version for v1.0
+
+#define METAMODEL_MAJOR_VER_V2_0 2 // Major version for v2.0
+#define METAMODEL_MINOR_VER_V2_0 0 // Minor version for v2.0
+
+#define METAMODEL_MAJOR_VER 2
+#define METAMODEL_MINOR_VER 0
+
+// Metadata version number up through Whidbey Beta2
+#define METAMODEL_MAJOR_VER_B1 1
+#define METAMODEL_MINOR_VER_B1 1
+
+
+typedef enum MetadataVersion
+{
+ MDVersion1 = 0x00000001,
+ MDVersion2 = 0x00000002,
+
+ // @TODO - this value should be updated when we increase the version number
+ MDDefaultVersion = 0x00000002
+} MetadataVersion;
+
+
+
+struct HENUMInternal;
+extern const CCodedTokenDef g_CodedTokens[CDTKN_COUNT];
+extern const CMiniTableDefEx g_Tables[TBL_COUNT]; // The table definitions.
+
+struct TblCol
+{
+ ULONG m_ixtbl; // Table ID.
+ ULONG m_ixcol; // Column ID.
+};
+extern TblCol g_PtrTableIxs[TBL_COUNT];
+
+// This abstract defines the common functions that can be used for RW and RO internally
+// (The primary user for this is Compiler\ImportHelper.cpp)
+class IMetaModelCommon
+{
+public:
+ __checkReturn
+ virtual HRESULT CommonGetScopeProps(
+ LPCUTF8 *pszName,
+ GUID *pMvid) = 0;
+
+ __checkReturn
+ virtual HRESULT CommonGetTypeRefProps(
+ mdTypeRef tr,
+ LPCUTF8 *pszNamespace,
+ LPCUTF8 *pszName,
+ mdToken *ptkResolution) = 0;
+
+ __checkReturn
+ virtual HRESULT CommonGetTypeDefProps(
+ mdTypeDef td,
+ LPCUTF8 *pszNameSpace,
+ LPCUTF8 *pszName,
+ DWORD *pdwFlags,
+ mdToken *pdwExtends,
+ ULONG *pMethodList) = 0;
+
+ __checkReturn
+ virtual HRESULT CommonGetTypeSpecProps(
+ mdTypeSpec ts,
+ PCCOR_SIGNATURE *ppvSig,
+ ULONG *pcbSig) = 0;
+
+ __checkReturn
+ virtual HRESULT CommonGetEnclosingClassOfTypeDef(
+ mdTypeDef td,
+ mdTypeDef *ptkEnclosingTypeDef) = 0;
+
+ __checkReturn
+ virtual HRESULT CommonGetAssemblyProps(
+ USHORT *pusMajorVersion,
+ USHORT *pusMinorVersion,
+ USHORT *pusBuildNumber,
+ USHORT *pusRevisionNumber,
+ DWORD *pdwFlags,
+ const void **ppbPublicKey,
+ ULONG *pcbPublicKey,
+ LPCUTF8 *pszName,
+ LPCUTF8 *pszLocale) = 0;
+
+ __checkReturn
+ virtual HRESULT CommonGetAssemblyRefProps(
+ mdAssemblyRef tkAssemRef,
+ USHORT *pusMajorVersion,
+ USHORT *pusMinorVersion,
+ USHORT *pusBuildNumber,
+ USHORT *pusRevisionNumber,
+ DWORD *pdwFlags,
+ const void **ppbPublicKeyOrToken,
+ ULONG *pcbPublicKeyOrToken,
+ LPCUTF8 *pszName,
+ LPCUTF8 *pszLocale,
+ const void **ppbHashValue,
+ ULONG *pcbHashValue) = 0;
+
+ __checkReturn
+ virtual HRESULT CommonGetModuleRefProps(
+ mdModuleRef tkModuleRef,
+ LPCUTF8 *pszName) = 0;
+
+ __checkReturn
+ virtual HRESULT CommonFindExportedType(
+ LPCUTF8 szNamespace,
+ LPCUTF8 szName,
+ mdToken tkEnclosingType,
+ mdExportedType *ptkExportedType) = 0;
+
+ __checkReturn
+ virtual HRESULT CommonGetExportedTypeProps(
+ mdToken tkExportedType,
+ LPCUTF8 *pszNamespace,
+ LPCUTF8 *pszName,
+ mdToken *ptkImpl) = 0;
+
+ virtual int CommonIsRo() = 0;
+
+ __checkReturn
+ virtual HRESULT CommonGetCustomAttributeByName( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) // [OUT] Put size of data here.
+ {
+ return CommonGetCustomAttributeByNameEx(tkObj, szName, NULL, ppData, pcbData);
+ }
+
+ __checkReturn
+ virtual HRESULT 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) = 0; // [OUT] Put size of data here.
+
+ __checkReturn
+ virtual HRESULT FindParentOfMethodHelper(mdMethodDef md, mdTypeDef *ptd) = 0;
+
+}; // class IMetaModelCommon
+
+
+
+// An extension of IMetaModelCommon, exposed by read-only importers only.
+// (The primary user for this is the WinMD import adapter which needs
+// a unified view of RegMeta and MDInternalRO.)
+//
+// These methods were separated from IMetaModelCommon as they are only used by
+// the WinMDAdapter and we don't want the maintainence and code-coverage cost
+// of providing Enc-aware versions of these methods.
+class IMetaModelCommonRO : public IMetaModelCommon
+{
+public:
+ virtual HRESULT CommonGetMethodDefProps(
+ mdMethodDef tkMethodDef,
+ LPCUTF8 *pszName,
+ DWORD *pdwFlags,
+ PCCOR_SIGNATURE *ppvSigBlob,
+ ULONG *pcbSigBlob
+ ) = 0;
+
+ virtual HRESULT CommonGetMemberRefProps(
+ mdMemberRef tkMemberRef,
+ mdToken *pParentToken
+ ) = 0;
+
+ virtual ULONG CommonGetRowCount( // return hresult
+ DWORD tkKind) = 0; // [IN] pass in the kind of token.
+
+ virtual HRESULT CommonGetMethodImpls(
+ mdTypeDef tkTypeDef, // [IN] typeDef to scope search
+ mdToken *ptkMethodImplFirst, // [OUT] returns first methodImpl token
+ ULONG *pMethodImplCount // [OUT] returns # of methodImpl tokens scoped to type
+ ) = 0;
+
+ virtual HRESULT CommonGetMethodImplProps(
+ mdToken tkMethodImpl, // [IN] methodImpl
+ mdToken *pBody, // [OUT] returns body token
+ mdToken *pDecl // [OUT] returns decl token
+ ) = 0;
+
+ virtual HRESULT CommonGetCustomAttributeProps(
+ mdCustomAttribute cv, // [IN] CustomAttribute token.
+ mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here.
+ mdToken *ptkType, // [OUT, OPTIONAL] Put AttrType token here.
+ const void **ppBlob, // [OUT, OPTIONAL] Put pointer to data here.
+ ULONG *pcbSize) = 0; // [OUT, OPTIONAL] Put size of date here.
+
+ virtual HRESULT CommonGetFieldDefProps(
+ mdFieldDef tkFieldDef,
+ mdTypeDef *ptkParent,
+ LPCUTF8 *pszName,
+ DWORD *pdwFlags
+ ) = 0;
+}; // class IMetaModelCommonRO
+
+
+//*****************************************************************************
+// The mini, hard-coded schema. For each table, we persist the count of
+// records. We also persist the size of string, blob, guid, and rid
+// columns. From this information, we can calculate the record sizes, and
+// then the sizes of the tables.
+//*****************************************************************************
+
+class CMiniMdSchemaBase
+{
+public:
+ ULONG m_ulReserved; // Reserved, must be zero.
+ BYTE m_major; // Version numbers.
+ BYTE m_minor;
+ BYTE m_heaps; // Bits for heap sizes.
+ BYTE m_rid; // log-base-2 of largest rid.
+
+ // Bits for heap sizes.
+ enum {
+ HEAP_STRING_4 = 0x01,
+ HEAP_GUID_4 = 0x02,
+ HEAP_BLOB_4 = 0x04,
+
+ PADDING_BIT = 0x08, // Tables can be created with an extra bit in columns, for growth.
+
+ DELTA_ONLY = 0x20, // If set, only deltas were persisted.
+ EXTRA_DATA = 0x40, // If set, schema persists an extra 4 bytes of data.
+ HAS_DELETE = 0x80, // If set, this metadata can contain _Delete tokens.
+ };
+
+ unsigned __int64 m_maskvalid; // Bit mask of present table counts.
+
+ unsigned __int64 m_sorted; // Bit mask of sorted tables.
+ FORCEINLINE bool IsSorted(ULONG ixTbl)
+ { return m_sorted & BIT(ixTbl) ? true : false; }
+ void SetSorted(ULONG ixTbl, int bVal)
+ { if (bVal) m_sorted |= BIT(ixTbl);
+ else m_sorted &= ~BIT(ixTbl); }
+
+#if BIGENDIAN
+ // Verify that the size hasn't changed (Update if necessary)
+ void ConvertEndianness()
+ {
+ _ASSERTE(sizeof(CMiniMdSchemaBase) == 0x18);
+ m_ulReserved = VAL32(m_ulReserved);
+ m_maskvalid = VAL64(m_maskvalid);
+ m_sorted = VAL64(m_sorted);
+ }
+#else
+ // Nothing to do on little endian machine
+ void ConvertEndianness() {return ;}
+#endif
+
+private:
+ FORCEINLINE unsigned __int64 BIT(ULONG ixBit)
+ { _ASSERTE(ixBit < (sizeof(__int64)*CHAR_BIT));
+ return UI64(1) << ixBit; }
+
+};
+
+class CMiniMdSchema : public CMiniMdSchemaBase
+{
+public:
+ // These are not all persisted to disk. See LoadFrom() for details.
+ ULONG m_cRecs[TBL_COUNT]; // Counts of various tables.
+
+ ULONG m_ulExtra; // Extra data, only persisted if non-zero. (m_heaps&EXTRA_DATA flags)
+
+ ULONG LoadFrom(const void*, ULONG); // Load from a compressed version. Return bytes consumed.
+ ULONG SaveTo(void *); // Store a compressed version. Return bytes used in buffer.
+ __checkReturn
+ HRESULT InitNew(MetadataVersion);
+};
+
+//*****************************************************************************
+// Helper macros and inline functions for navigating through the data. Many
+// of the macros are used to define inline accessor functions. Everything
+// is based on the naming conventions outlined at the top of the file.
+//*****************************************************************************
+#define _GETTER(tbl,fld) get##fld##Of##tbl(tbl##Rec *pRec)
+#define _GETTER2(tbl,fld,x) get##fld##Of##tbl(tbl##Rec *pRec, x)
+#define _GETTER3(tbl,fld,x,y) get##fld##Of##tbl(tbl##Rec *pRec, x, y)
+#define _GETTER4(tbl,fld,x,y,z) get##fld##Of##tbl(tbl##Rec *pRec, x, y,z)
+
+// Direct getter for a field. Defines an inline function like:
+// getSomeFieldOfXyz(XyzRec *pRec) { return pRec->m_SomeField;}
+// Note that the returned value declaration is NOT included.
+#if METADATATRACKER_ENABLED
+#define _GETFLD(tbl,fld) _GETTER(tbl,fld){ PVOID pVal = (BYTE*)pRec + offsetof(tbl##Rec, m_##fld); \
+ pVal = MetaDataTracker::NoteAccess(pVal); \
+ return ((tbl##Rec*)((BYTE*)pVal - offsetof(tbl##Rec, m_##fld)))->Get##fld(); }
+#else
+#define _GETFLD(tbl,fld) _GETTER(tbl,fld){ return pRec->Get##fld();}
+#endif
+
+// These functions call the helper function getIX to get a two or four byte value from a record,
+// and then use that value as an index into the appropriate pool.
+// getSomeFieldOfXyz(XyzRec *pRec) { return m_pStrings->GetString(getIX(pRec, _COLDEF(tbl,fld))); }
+// Note that the returned value declaration is NOT included.
+
+// Column definition of a field: Looks like:
+// m_XyzCol[XyzRec::COL_SomeField]
+#define _COLDEF(tbl,fld) m_TableDefs[TBL_##tbl].m_pColDefs[tbl##Rec::COL_##fld]
+#define _COLPAIR(tbl,fld) _COLDEF(tbl,fld), tbl##Rec::COL_##fld
+// Size of a record.
+#define _CBREC(tbl) m_TableDefs[TBL_##tbl].m_cbRec
+// Count of records in a table.
+#define _TBLCNT(tbl) m_Schema.m_cRecs[TBL_##tbl]
+
+#define _GETSTRA(tbl,fld) _GETTER2(tbl, fld, LPCSTR *pszString) \
+{ return getString(getI4(pRec, _COLDEF(tbl,fld)) & m_iStringsMask, pszString); }
+
+#define _GETSTRW(tbl,fld) _GETTER4(tbl, fld, LPWSTR szOut, ULONG cchBuffer, ULONG *pcchBuffer) \
+{ return getStringW(getI4(pRec, _COLDEF(tbl,fld)) & m_iStringsMask, szOut, cchBuffer, pcchBuffer); }
+
+#define _GETSTR(tbl, fld) \
+ __checkReturn HRESULT _GETSTRA(tbl, fld); \
+ __checkReturn HRESULT _GETSTRW(tbl, fld);
+
+
+#define _GETGUID(tbl,fld) _GETTER2(tbl,fld,GUID *pGuid) \
+{ return getGuid(getI4(pRec, _COLDEF(tbl,fld)) & m_iGuidsMask, pGuid); }
+
+#define _GETBLOB(tbl,fld) __checkReturn HRESULT _GETTER3(tbl,fld,const BYTE **ppbData,ULONG *pcbSize) \
+{ \
+ MetaData::DataBlob data; \
+ HRESULT hr = getBlob(getI4(pRec, _COLDEF(tbl,fld)) & m_iBlobsMask, &data); \
+ *ppbData = data.GetDataPointer(); \
+ *pcbSize = (ULONG)data.GetSize(); \
+ return hr; \
+}
+
+#define _GETSIGBLOB(tbl,fld) __checkReturn HRESULT _GETTER3(tbl,fld,PCCOR_SIGNATURE *ppbData,ULONG *pcbSize) \
+{ \
+ MetaData::DataBlob data; \
+ HRESULT hr = getBlob(getI4(pRec, _COLDEF(tbl,fld)) & m_iBlobsMask, &data); \
+ *ppbData = (PCCOR_SIGNATURE)data.GetDataPointer(); \
+ *pcbSize = (ULONG)data.GetSize(); \
+ return hr; \
+}
+
+// Like the above functions, but just returns the RID, not a looked-up value.
+#define _GETRID(tbl,fld) _GETTER(tbl,fld) \
+{ return getIX(pRec, _COLDEF(tbl,fld)); }
+
+// Like a RID, but turn into an actual token.
+#define _GETTKN(tbl,fld,tok) _GETTER(tbl,fld) \
+{ return TokenFromRid(getIX(pRec, _COLDEF(tbl,fld)), tok); }
+
+// Get a coded token.
+#define _GETCDTKN(tbl,fld,toks) _GETTER(tbl,fld) \
+{ return decodeToken(getIX(pRec, _COLDEF(tbl,fld)), toks, sizeof(toks)/sizeof(toks[0])); }
+
+// Functions for the start and end of a list.
+#define _GETLIST(tbl,fld,tbl2) \
+ RID _GETRID(tbl,fld); \
+ __checkReturn HRESULT getEnd##fld##Of##tbl(RID nRowIndex, RID *pEndRid) { return getEndRidForColumn(TBL_##tbl, nRowIndex, _COLDEF(tbl,fld), TBL_##tbl2, pEndRid); }
+
+
+#define BYTEARRAY_TO_COLDES(bytearray) (CMiniColDef*)((bytearray) + 1)
+#define COLDES_TO_BYTEARRAY(coldes) (((BYTE*)(coldes))-1)
+
+
+//*****************************************************************************
+// Base class for the MiniMd. This class provides the schema to derived
+// classes. It defines some virtual functions for access to data, suitable
+// for use by functions where utmost performance is NOT a requirement.
+// Finally, it provides some searching functions, built on the virtual
+// data access functions (it is here assumed that if we are searching a table
+// for some value, the cost of a virtual function call is acceptable).
+// Some static utility functions and associated static data, shared across
+// implementations, is provided here.
+//
+// NB: It's unfortunate that CMiniMDBase "implements" IMetaModelCommonRO rather
+// than IMetaModelCommon, as methods on IMetaModelCommonRO are by definition,
+// not Enc-aware. Ideally, CMiniMDBase should only implement IMetaModelCommon
+// and CMiniMd should be the one implementing IMetaModelCommonRO.
+//
+// To make that happen would be a substantial refactoring job as RegMeta
+// always embeds CMiniMdRW even when it was opened for ReadOnly.
+//*****************************************************************************
+class CMiniMdBase : public IMetaModelCommonRO
+{
+
+ friend class VerifyLayoutsMD; // verifies class layout doesn't accidentally change
+
+public:
+ CMiniMdBase();
+ ~CMiniMdBase();
+ __checkReturn
+ virtual HRESULT vGetRow(UINT32 nTableIndex, UINT32 nRowIndex, void **ppRow) = 0;
+ ULONG GetCountRecs(ULONG ixTbl);
+ ULONG GetCountTables() { return m_TblCount;}
+
+ // Search a table for the row containing the given key value.
+ // EG. Constant table has pointer back to Param or Field.
+ __checkReturn
+ virtual HRESULT 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) = 0;
+
+ // 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.
+ __checkReturn
+ virtual HRESULT 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) = 0;
+
+ // Search a table for multiple (adjacent) rows containing the given
+ // key value. EG, InterfaceImpls all point back to the implementing class.
+ __checkReturn
+ HRESULT SearchTableForMultipleRows( // First RID found, or 0.
+ ULONG ixTbl, // Table to search.
+ CMiniColDef sColumn, // Sorted key column, containing search value.
+ ULONG ulTarget, // Target for search.
+ RID *pEnd, // [OPTIONAL, OUT]
+ RID *pFoundRid);
+
+ // Search for a custom value with a given type.
+ __checkReturn
+ HRESULT FindCustomAttributeFor(// RID of custom value, or 0.
+ RID rid, // The object's rid.
+ mdToken tkOjb, // The object's type.
+ mdToken tkType, // Type of custom value.
+ RID *pFoundRid);
+
+ // Search for the specified Column Definition array in the global cache
+ BOOL FindSharedColDefs(// TRUE if we found a match in the global cache and updated pTable, FALSE otherwise
+ CMiniTableDef *pTable, // The table def that wants the column definition array
+ CMiniColDef *pColsToMatch, // The columns that we need to match
+ DWORD ixTbl);
+
+ // Return RID to EventMap table, given the rid to a TypeDef.
+ __checkReturn
+ HRESULT FindEventMapFor(RID ridParent, RID *pFoundRid);
+
+ // Return RID to PropertyMap table, given the rid to a TypeDef.
+ __checkReturn
+ HRESULT FindPropertyMapFor(RID ridParent, RID *pFoundRid);
+
+#if BIGENDIAN
+ // Swap a constant
+ __checkReturn
+ HRESULT SwapConstant(const void *pBlobValue, DWORD dwType, VOID *pConstant, ULONG BlobLength);
+#endif
+
+ // Pull two or four bytes out of a record.
+ inline static ULONG getIX(const void *pRec, CMiniColDef &def)
+ {
+ PVOID pVal = (BYTE *)pRec + def.m_oColumn;
+ if (def.m_cbColumn == 2)
+ {
+ METADATATRACKER_ONLY(pVal = MetaDataTracker::NoteAccess(pVal));
+ ULONG ix = GET_UNALIGNED_VAL16(pVal);
+ return ix;
+ }
+ _ASSERTE(def.m_cbColumn == 4);
+ METADATATRACKER_ONLY(pVal = MetaDataTracker::NoteAccess(pVal));
+ return GET_UNALIGNED_VAL32(pVal);
+ }
+
+ inline static ULONG getIX_NoLogging(const void *pRec, CMiniColDef &def)
+ {
+ PVOID pVal = (BYTE *)pRec + def.m_oColumn;
+ if (def.m_cbColumn == 2)
+ {
+ ULONG ix = GET_UNALIGNED_VAL16(pVal);
+ return ix;
+ }
+ _ASSERTE(def.m_cbColumn == 4);
+ return GET_UNALIGNED_VAL32(pVal);
+ }
+
+ // Pull four bytes out of a record.
+ FORCEINLINE static ULONG getI1(const void *pRec, CMiniColDef &def)
+ {
+ PVOID pVal = (BYTE *)pRec + def.m_oColumn;
+ METADATATRACKER_ONLY(pVal = MetaDataTracker::NoteAccess(pVal));
+ return *(BYTE*)pVal;
+ }
+
+ // Pull four bytes out of a record.
+ FORCEINLINE static ULONG getI4(const void *pRec, CMiniColDef &def)
+ {
+ PVOID pVal = (BYTE *)pRec + def.m_oColumn;
+ METADATATRACKER_ONLY(pVal = MetaDataTracker::NoteAccess(pVal));
+ return GET_UNALIGNED_VAL32(pVal);
+ }
+
+ // Function to encode a token into fewer bits. Looks up token type in array of types.
+ ULONG static encodeToken(RID rid, mdToken typ, const mdToken rTokens[], ULONG32 cTokens);
+
+ // Decode a token.
+ inline static mdToken decodeToken(mdToken val, const mdToken rTokens[], ULONG32 cTokens)
+ {
+ //<TODO>@FUTURE: make compile-time calculation</TODO>
+ ULONG32 ix = (ULONG32)(val & ~(-1 << m_cb[cTokens]));
+ // If the coded token has an invalid table index, return the first entry
+ // from the array of valid token types. It would be preferable to
+ // return an error or to raise an exception.
+ if (ix >= cTokens)
+ return rTokens[0];
+ return TokenFromRid(val >> m_cb[cTokens], rTokens[ix]);
+ }
+ static const int m_cb[];
+
+ // Given a token, what table does it live in?
+ inline ULONG GetTblForToken(mdToken tk)
+ {
+ tk = TypeFromToken(tk);
+ return (tk < mdtString) ? tk >> 24 : (ULONG) -1;
+ }
+
+ //*****************************************************************************
+ // Returns whether the data has been verified, which means it was verified by a
+ // trusted source and has not changed.
+ //
+ // If so, this means the following aspects of the data can be trusted as accurate:
+ // - m_Schema.IsSorted[TBL_PropertyMap] reflects whether that table is sorted by Parent (see CMiniMdRW::PreSaveFull)
+ // - m_Schema.IsSorted[TBL_EventMap] reflects whether that table is sorted by Parent (see CMiniMdRW::PreSaveFull)
+ //
+ // Currently, metadata saved in NGen images is the only trusted source.
+ //*****************************************************************************
+ BOOL IsVerified()
+ {
+ return m_fVerifiedByTrustedSource && CommonIsRo();
+ }
+
+ void SetVerifiedByTrustedSource(BOOL fVerifiedByTrustedSource)
+ {
+ m_fVerifiedByTrustedSource = fVerifiedByTrustedSource;
+ }
+
+ STDMETHODIMP GetRvaOffsetData(// S_OK or error
+ DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table.
+ DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table.
+ DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table.
+ DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table.
+ DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table.
+ DWORD *pFieldRvaCount) // [OUT] Number of records in FieldRVA table.
+ {
+ _ASSERTE("Not implemented");
+ return E_NOTIMPL;
+ }
+
+ //*****************************************************************************
+ // Some of the tables need coded tokens, not just rids (ie, the column can
+ // refer to more than one other table). Code the tokens into as few bits
+ // as possible, by using 1, 2, 3, etc., bits to code the token type, then
+ // use that value to index into an array of token types.
+ //*****************************************************************************
+ static const mdToken mdtTypeDefOrRef[3];
+ static const mdToken mdtHasConstant[3];
+ static const mdToken mdtHasCustomAttribute[24];
+ static const mdToken mdtHasFieldMarshal[2];
+ static const mdToken mdtHasDeclSecurity[3];
+ static const mdToken mdtMemberRefParent[5];
+ static const mdToken mdtHasSemantic[2];
+ static const mdToken mdtMethodDefOrRef[2];
+ static const mdToken mdtMemberForwarded[2];
+ static const mdToken mdtImplementation[3];
+ static const mdToken mdtCustomAttributeType[5];
+ static const mdToken mdtResolutionScope[4];
+ static const mdToken mdtTypeOrMethodDef[2];
+
+
+public:
+ virtual BOOL IsWritable() = 0;
+
+
+protected:
+ CMiniMdSchema m_Schema; // data header.
+ ULONG m_TblCount; // Tables in this database.
+ BOOL m_fVerifiedByTrustedSource; // whether the data was verified by a trusted source
+
+ // Declare CMiniColDefs for every table. They look like either:
+ // static const BYTE s_xyz[];
+ // or
+ // static const BYTE* s_xyz;
+
+ #include "mdcolumndescriptors.h"
+
+ static const BYTE* const s_TableColumnDescriptors[TBL_COUNT];
+ CMiniTableDef m_TableDefs[TBL_COUNT];
+
+ ULONG m_iStringsMask;
+ ULONG m_iGuidsMask;
+ ULONG m_iBlobsMask;
+
+ __checkReturn
+ HRESULT SchemaPopulate(const void *pvData, ULONG cbData, ULONG *pcbUsed);
+ __checkReturn
+ HRESULT SchemaPopulate(const CMiniMdBase &that);
+ __checkReturn
+ HRESULT InitColsForTable(CMiniMdSchema &Schema, int ixTbl, CMiniTableDef *pTable, int bExtra, BOOL fUsePointers);
+ __checkReturn
+ HRESULT SchemaPopulate2(ULONG *pcbTables, int bExtra=false);
+ const CMiniTableDef* GetTableDefTemplate(int ixTbl);
+ __checkReturn
+ HRESULT SetNewColumnDefinition(CMiniTableDef *pTable, CMiniColDef* pCols, DWORD ixTbl);
+
+private:
+
+ BOOL UsesAllocatedMemory(CMiniColDef* pCols);
+};
+
+
+#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
+#define MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED() MarkUnsafeToDelete()
+#else
+#define MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED()
+#endif
+
+//*****************************************************************************
+// This class defines the interface to the MiniMd. The template parameter is
+// a derived class which provides implementations for a few primitives that
+// the interface is built upon.
+// To use, declare a class:
+// class CMyMiniMd : public CMiniMdTemplate<CMyMiniMd> {...};
+// and provide implementations of the primitives. Any non-trivial
+// implementation will also provide initialization, and probably serialization
+// functions as well.
+//*****************************************************************************
+template <class Impl> class CMiniMdTemplate : public CMiniMdBase
+{
+#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
+protected:
+ CMiniMdTemplate() : m_isSafeToDelete(TRUE) { }
+#endif
+
+ // Primitives -- these must be implemented in the Impl class.
+public:
+ __checkReturn
+ FORCEINLINE HRESULT getString(UINT32 nIndex, __out LPCSTR *pszString)
+ {
+ MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+ return static_cast<Impl*>(this)->Impl_GetString(nIndex, pszString);
+ }
+ __checkReturn
+ FORCEINLINE HRESULT getStringW(ULONG nIndex, __inout_ecount (cchBuffer) LPWSTR szOut, ULONG cchBuffer, ULONG *pcchBuffer)
+ {
+ MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+ return static_cast<Impl*>(this)->Impl_GetStringW(nIndex, szOut, cchBuffer, pcchBuffer);
+ }
+ __checkReturn
+ FORCEINLINE HRESULT getGuid(UINT32 nIndex, GUID *pGuid)
+ {
+ MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+ return static_cast<Impl*>(this)->Impl_GetGuid(nIndex, pGuid);
+ }
+ __checkReturn
+ FORCEINLINE HRESULT getBlob(UINT32 nIndex, __out MetaData::DataBlob *pData)
+ {
+ MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+ return static_cast<Impl*>(this)->Impl_GetBlob(nIndex, pData);
+ }
+ __checkReturn
+ FORCEINLINE HRESULT getRow(UINT32 nTableIndex, UINT32 nRowIndex, __deref_out void **ppRow)
+ {
+ MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+ return static_cast<Impl*>(this)->Impl_GetRow(nTableIndex, nRowIndex, reinterpret_cast<BYTE **>(ppRow));
+ }
+ __checkReturn
+ FORCEINLINE HRESULT getEndRidForColumn(
+ UINT32 nTableIndex,
+ RID nRowIndex,
+ CMiniColDef &columnDefinition,
+ UINT32 nTargetTableIndex,
+ __out RID *pEndRid)
+ {
+ MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+ return static_cast<Impl*>(this)->Impl_GetEndRidForColumn(nTableIndex, nRowIndex, columnDefinition, nTargetTableIndex, pEndRid);
+ }
+ __checkReturn
+ FORCEINLINE HRESULT doSearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ixColumn, ULONG ulTarget, RID *pFoundRid)
+ {
+ MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+ return static_cast<Impl*>(this)->Impl_SearchTable(ixTbl, sColumn, ixColumn, ulTarget, pFoundRid);
+ }
+
+ // IMetaModelCommonRO interface beginning
+ __checkReturn
+ HRESULT CommonGetScopeProps(
+ LPCUTF8 *pszName,
+ GUID *pMvid)
+ {
+ HRESULT hr = S_OK;
+ ModuleRec *pRec;
+ IfFailRet(GetModuleRecord(1, &pRec));
+ if (pszName != NULL)
+ {
+ IfFailRet(getNameOfModule(pRec, pszName));
+ }
+ if (pMvid != NULL)
+ {
+ IfFailRet(getMvidOfModule(pRec, pMvid));
+ }
+ return hr;
+ }
+
+ //*****************************************************************************
+ // Get name and sig of a methodDef
+ //*****************************************************************************
+ HRESULT CommonGetMethodDefProps(
+ mdMethodDef tkMethodDef,
+ LPCUTF8 *pszName,
+ DWORD *pdwFlags,
+ PCCOR_SIGNATURE *ppvSigBlob,
+ ULONG *pcbSigBlob
+ )
+ {
+
+ _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable.");
+
+ HRESULT hr;
+
+ LPCUTF8 szName;
+ DWORD dwFlags;
+ PCCOR_SIGNATURE pvSigBlob;
+ ULONG cbSigBlob;
+
+ _ASSERTE(TypeFromToken(tkMethodDef) == mdtMethodDef);
+ MethodRec *pMethodRec;
+ IfFailRet(GetMethodRecord(RidFromToken(tkMethodDef), &pMethodRec));
+ IfFailRet(getNameOfMethod(pMethodRec, &szName));
+ dwFlags = getFlagsOfMethod(pMethodRec);
+ IfFailRet(getSignatureOfMethod(pMethodRec, &pvSigBlob, &cbSigBlob));
+
+ if (pszName)
+ *pszName = szName;
+ if (pdwFlags)
+ *pdwFlags = dwFlags;
+ if (ppvSigBlob)
+ *ppvSigBlob = pvSigBlob;
+ if (pcbSigBlob)
+ *pcbSigBlob = cbSigBlob;
+
+ return S_OK;
+ }
+
+ HRESULT CommonGetMemberRefProps(
+ mdMemberRef tkMemberRef,
+ mdToken *pParentToken
+ )
+ {
+ _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable.");
+
+ HRESULT hr;
+
+ _ASSERTE(TypeFromToken(tkMemberRef) == mdtMemberRef);
+
+ MemberRefRec *pMemberRefRec;
+ IfFailRet(GetMemberRefRecord(RidFromToken(tkMemberRef), &pMemberRefRec));
+ if (pParentToken != NULL)
+ *pParentToken = getClassOfMemberRef(pMemberRefRec);
+
+ return S_OK;
+ }
+
+
+
+
+ __checkReturn
+ HRESULT CommonGetTypeRefProps(
+ mdTypeRef tr,
+ LPCUTF8 *pszNamespace,
+ LPCUTF8 *pszName,
+ mdToken *ptkResolution)
+ {
+ HRESULT hr = S_OK;
+ TypeRefRec *pRec;
+ IfFailRet(GetTypeRefRecord(RidFromToken(tr), &pRec));
+ if (pszNamespace != NULL)
+ {
+ IfFailRet(getNamespaceOfTypeRef(pRec, pszNamespace));
+ }
+ if (pszName != NULL)
+ {
+ IfFailRet(getNameOfTypeRef(pRec, pszName));
+ }
+ if (ptkResolution != NULL)
+ {
+ *ptkResolution = getResolutionScopeOfTypeRef(pRec);
+ }
+ return hr;
+ }
+
+ __checkReturn
+ virtual HRESULT CommonGetTypeDefProps(
+ mdTypeDef td,
+ LPCUTF8 *pszNamespace,
+ LPCUTF8 *pszName,
+ DWORD *pdwFlags,
+ mdToken *pdwExtends,
+ ULONG *pMethodList)
+ {
+ HRESULT hr = S_OK;
+ TypeDefRec *pRec;
+ IfFailRet(GetTypeDefRecord(RidFromToken(td), &pRec));
+ if (pszNamespace != NULL)
+ {
+ IfFailRet(getNamespaceOfTypeDef(pRec, pszNamespace));
+ }
+ if (pszName != NULL)
+ {
+ IfFailRet(getNameOfTypeDef(pRec, pszName));
+ }
+ if (pdwFlags != NULL)
+ {
+ *pdwFlags = getFlagsOfTypeDef(pRec);
+ }
+ if (pdwExtends != NULL)
+ {
+ *pdwExtends = getExtendsOfTypeDef(pRec);
+ }
+ if (pMethodList != NULL)
+ {
+ *pMethodList = getMethodListOfTypeDef(pRec);
+ }
+ return hr;
+ }
+
+ __checkReturn
+ virtual HRESULT CommonGetTypeSpecProps(
+ mdTypeSpec ts,
+ PCCOR_SIGNATURE *ppvSig,
+ ULONG *pcbSig)
+ {
+ HRESULT hr = S_OK;
+ TypeSpecRec *pRec;
+ IfFailRet(GetTypeSpecRecord(RidFromToken(ts), &pRec));
+ ULONG cb;
+ IfFailRet(getSignatureOfTypeSpec(pRec, ppvSig, &cb));
+ *pcbSig = cb;
+ return hr;
+ }
+
+ __checkReturn
+ virtual HRESULT CommonGetEnclosingClassOfTypeDef(
+ mdTypeDef td,
+ mdTypeDef *ptkEnclosingTypeDef)
+ {
+ _ASSERTE(ptkEnclosingTypeDef != NULL);
+
+ HRESULT hr;
+ NestedClassRec *pRec;
+ RID iRec;
+
+ IfFailRet(FindNestedClassFor(RidFromToken(td), &iRec));
+ if (iRec == 0)
+ {
+ *ptkEnclosingTypeDef = mdTypeDefNil;
+ return S_OK;
+ }
+
+ IfFailRet(GetNestedClassRecord(iRec, &pRec));
+ *ptkEnclosingTypeDef = getEnclosingClassOfNestedClass(pRec);
+ return S_OK;
+ }
+
+
+ __checkReturn
+ virtual HRESULT CommonGetAssemblyProps(
+ USHORT *pusMajorVersion,
+ USHORT *pusMinorVersion,
+ USHORT *pusBuildNumber,
+ USHORT *pusRevisionNumber,
+ DWORD *pdwFlags,
+ const void **ppbPublicKey,
+ ULONG *pcbPublicKey,
+ LPCUTF8 *pszName,
+ LPCUTF8 *pszLocale)
+ {
+ HRESULT hr = S_OK;
+ AssemblyRec *pRec;
+
+ IfFailRet(GetAssemblyRecord(1, &pRec));
+
+ if (pusMajorVersion) *pusMajorVersion = pRec->GetMajorVersion();
+ if (pusMinorVersion) *pusMinorVersion = pRec->GetMinorVersion();
+ if (pusBuildNumber) *pusBuildNumber = pRec->GetBuildNumber();
+ if (pusRevisionNumber) *pusRevisionNumber = pRec->GetRevisionNumber();
+ if (pdwFlags != NULL)
+ {
+ *pdwFlags = pRec->GetFlags();
+ }
+
+ // Turn on the afPublicKey if PublicKey blob is not empty
+ if (pdwFlags != NULL)
+ {
+ DWORD cbPublicKey;
+ const BYTE *pbPublicKey;
+ IfFailRet(getPublicKeyOfAssembly(pRec, &pbPublicKey, &cbPublicKey));
+ if (cbPublicKey)
+ *pdwFlags |= afPublicKey;
+ }
+ if (ppbPublicKey != NULL)
+ {
+ IfFailRet(getPublicKeyOfAssembly(pRec, reinterpret_cast<const BYTE **>(ppbPublicKey), pcbPublicKey));
+ }
+ if (pszName != NULL)
+ {
+ IfFailRet(getNameOfAssembly(pRec, pszName));
+ }
+ if (pszLocale != NULL)
+ {
+ IfFailRet(getLocaleOfAssembly(pRec, pszLocale));
+ }
+ return hr;
+ }
+
+ __checkReturn
+ virtual HRESULT CommonGetAssemblyRefProps(
+ mdAssemblyRef tkAssemRef,
+ USHORT *pusMajorVersion,
+ USHORT *pusMinorVersion,
+ USHORT *pusBuildNumber,
+ USHORT *pusRevisionNumber,
+ DWORD *pdwFlags,
+ const void **ppbPublicKeyOrToken,
+ ULONG *pcbPublicKeyOrToken,
+ LPCUTF8 *pszName,
+ LPCUTF8 *pszLocale,
+ const void **ppbHashValue,
+ ULONG *pcbHashValue)
+ {
+ HRESULT hr = S_OK;
+ AssemblyRefRec *pRec;
+
+ IfFailRet(GetAssemblyRefRecord(RidFromToken(tkAssemRef), &pRec));
+
+ if (pusMajorVersion) *pusMajorVersion = pRec->GetMajorVersion();
+ if (pusMinorVersion) *pusMinorVersion = pRec->GetMinorVersion();
+ if (pusBuildNumber) *pusBuildNumber = pRec->GetBuildNumber();
+ if (pusRevisionNumber) *pusRevisionNumber = pRec->GetRevisionNumber();
+ if (pdwFlags) *pdwFlags = pRec->GetFlags();
+ if (ppbPublicKeyOrToken != NULL)
+ {
+ IfFailRet(getPublicKeyOrTokenOfAssemblyRef(pRec, reinterpret_cast<const BYTE **>(ppbPublicKeyOrToken), pcbPublicKeyOrToken));
+ }
+ if (pszName != NULL)
+ {
+ IfFailRet(getNameOfAssemblyRef(pRec, pszName));
+ }
+ if (pszLocale != NULL)
+ {
+ IfFailRet(getLocaleOfAssemblyRef(pRec, pszLocale));
+ }
+ if (ppbHashValue != NULL)
+ {
+ IfFailRet(getHashValueOfAssemblyRef(pRec, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue));
+ }
+ return hr;
+ }
+
+ __checkReturn
+ virtual HRESULT CommonGetModuleRefProps(
+ mdModuleRef tkModuleRef,
+ LPCUTF8 *pszName)
+ {
+ HRESULT hr = S_OK;
+ ModuleRefRec *pRec;
+
+ IfFailRet(GetModuleRefRecord(RidFromToken(tkModuleRef), &pRec));
+ IfFailRet(getNameOfModuleRef(pRec, pszName));
+ return hr;
+ }
+
+ __checkReturn
+ HRESULT CommonFindExportedType(
+ LPCUTF8 szNamespace,
+ LPCUTF8 szName,
+ mdToken tkEnclosingType,
+ mdExportedType *ptkExportedType)
+ {
+ HRESULT hr;
+ ExportedTypeRec *pRec;
+ ULONG ulCount;
+ LPCUTF8 szTmp;
+ mdToken tkImpl;
+
+ _ASSERTE(szName && ptkExportedType);
+
+ // Set NULL namespace to empty string.
+ if (!szNamespace)
+ szNamespace = "";
+
+ // Set output to Nil.
+ *ptkExportedType = mdTokenNil;
+
+ ulCount = getCountExportedTypes();
+ while (ulCount)
+ {
+ IfFailRet(GetExportedTypeRecord(ulCount--, &pRec));
+
+ // Handle the case of nested vs. non-nested classes.
+ tkImpl = getImplementationOfExportedType(pRec);
+ if (TypeFromToken(tkImpl) == mdtExportedType && !IsNilToken(tkImpl))
+ {
+ // Current ExportedType being looked at is a nested type, so
+ // comparing the implementation token.
+ if (tkImpl != tkEnclosingType)
+ continue;
+ }
+ else if (TypeFromToken(tkEnclosingType) == mdtExportedType &&
+ !IsNilToken(tkEnclosingType))
+ {
+ // ExportedType passed in is nested but the current ExportedType is not.
+ continue;
+ }
+
+ // Compare name and namespace.
+ IfFailRet(getTypeNameOfExportedType(pRec, &szTmp));
+ if (strcmp(szTmp, szName))
+ continue;
+ IfFailRet(getTypeNamespaceOfExportedType(pRec, &szTmp));
+ if (!strcmp(szTmp, szNamespace))
+ {
+ *ptkExportedType = TokenFromRid(ulCount+1, mdtExportedType);
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+
+ __checkReturn
+ virtual HRESULT CommonGetExportedTypeProps(
+ mdToken tkExportedType,
+ LPCUTF8 *pszNamespace,
+ LPCUTF8 *pszName,
+ mdToken *ptkImpl)
+ {
+ HRESULT hr = S_OK;
+ ExportedTypeRec *pRec;
+
+ IfFailRet(GetExportedTypeRecord(RidFromToken(tkExportedType), &pRec));
+
+ if (pszNamespace != NULL)
+ {
+ IfFailRet(getTypeNamespaceOfExportedType(pRec, pszNamespace));
+ }
+ if (pszName != NULL)
+ {
+ IfFailRet(getTypeNameOfExportedType(pRec, pszName));
+ }
+ if (ptkImpl) *ptkImpl = getImplementationOfExportedType(pRec);
+ return hr;
+ }
+
+ int CommonIsRo()
+ {
+ return static_cast<Impl*>(this)->Impl_IsRo();
+ }
+
+
+
+ HRESULT CommonGetCustomAttributeProps(
+ mdCustomAttribute cv, // [IN] CustomAttribute token.
+ mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here.
+ mdToken *ptkType, // [OUT, OPTIONAL] Put AttrType token here.
+ const void **ppBlob, // [OUT, OPTIONAL] Put pointer to data here.
+ ULONG *pcbSize) // [OUT, OPTIONAL] Put size of date here.
+ {
+ _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable.");
+
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(cv) == mdtCustomAttribute);
+ CustomAttributeRec *pRec; // A CustomAttribute record.
+ const void *pBlob;
+ ULONG cbSize;
+ IfFailRet(GetCustomAttributeRecord(RidFromToken(cv), &pRec));
+ if (ptkObj)
+ *ptkObj = getParentOfCustomAttribute(pRec);
+ if (ptkType)
+ *ptkType = getTypeOfCustomAttribute(pRec);
+
+ if (!ppBlob)
+ ppBlob = &pBlob;
+ if (!pcbSize)
+ pcbSize = &cbSize;
+ IfFailRet(getValueOfCustomAttribute(pRec, (const BYTE **)ppBlob, pcbSize));
+ return S_OK;
+ }
+
+ //*****************************************************************************
+ // Get name, parent and flags of a fieldDef
+ //*****************************************************************************
+ HRESULT CommonGetFieldDefProps(
+ mdFieldDef tkFieldDef,
+ mdTypeDef *ptkParent,
+ LPCUTF8 *pszName,
+ DWORD *pdwFlags
+ )
+ {
+ _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable.");
+ _ASSERTE(TypeFromToken(tkFieldDef) == mdtFieldDef);
+
+ HRESULT hr;
+
+ FieldRec *pFieldRec;
+ IfFailRet(GetFieldRecord(RidFromToken(tkFieldDef), &pFieldRec));
+
+ if(ptkParent)
+ {
+ IfFailRet(FindParentOfField(RidFromToken(tkFieldDef), (RID *) ptkParent));
+ RidToToken(*ptkParent, mdtTypeDef);
+ }
+ if (pszName)
+ IfFailRet(getNameOfField(pFieldRec, pszName));
+ if (pdwFlags)
+ *pdwFlags = getFlagsOfField(pFieldRec);
+
+ return S_OK;
+ }
+
+ __checkReturn
+ HRESULT FindParentOfMethodHelper(mdMethodDef md, mdTypeDef *ptd)
+ {
+ HRESULT hr;
+ IfFailRet(FindParentOfMethod(RidFromToken(md), (RID *)ptd));
+ RidToToken(*ptd, mdtTypeDef);
+ return NOERROR;
+ }
+
+ //*****************************************************************************
+ // Helper function to lookup and retrieve a CustomAttribute.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT CompareCustomAttribute(
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ ULONG rid) // [IN] the rid of the custom attribute to compare to
+ {
+ CustomAttributeRec *pRec; // A CustomAttribute record.
+ LPCUTF8 szNamespaceTmp = NULL; // Namespace of a CustomAttribute's type.
+ LPCUTF8 szNameTmp = NULL; // Name of a CustomAttribute's type.
+ int iLen; // Length of a component name.
+ HRESULT hr = S_FALSE;
+ HRESULT hrMatch = S_FALSE;
+
+ if (!_IsValidTokenBase(tkObj))
+ return COR_E_BADIMAGEFORMAT;
+
+ // Get the row.
+ IfFailGo(GetCustomAttributeRecord(rid, &pRec));
+
+ // Check the parent. In debug, always check. In retail, only when scanning.
+ mdToken tkParent;
+ tkParent = getParentOfCustomAttribute(pRec);
+ if (tkObj != tkParent)
+ {
+ goto ErrExit;
+ }
+
+ hr = CommonGetNameOfCustomAttribute(rid, &szNamespaceTmp, &szNameTmp);
+ if (hr != S_OK)
+ goto ErrExit;
+
+ iLen = -1;
+ if (*szNamespaceTmp)
+ {
+ iLen = (int)strlen(szNamespaceTmp);
+ if (strncmp(szName, szNamespaceTmp, iLen) != 0)
+ goto ErrExit;
+ // Namespace separator after the Namespace?
+ if (szName[iLen] != NAMESPACE_SEPARATOR_CHAR)
+ goto ErrExit;
+ }
+ // Check the type name after the separator.
+ if (strcmp(szName+iLen+1, szNameTmp) != 0)
+ goto ErrExit;
+
+ hrMatch = S_OK;
+ ErrExit:
+
+ if (FAILED(hr))
+ return hr;
+
+ return hrMatch;
+ } // CompareCustomAttribute
+
+
+ //*****************************************************************************
+ // Helper function to lookup the name of a custom attribute
+ // Note that this function can return S_FALSE to support being called
+ // by CompareCustomAttribute. See GetTypeDefRefTokenInTypeSpec for
+ // details on when this will happen.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT CommonGetNameOfCustomAttribute(
+ ULONG rid, // [IN] the rid of the custom attribute
+ LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute.
+ LPCUTF8 *pszName) // [OUT] Name of Custom Attribute.
+ {
+ CustomAttributeRec *pRec; // A CustomAttribute record.
+ mdToken tkTypeTmp; // Type of some CustomAttribute.
+ RID ridTmp; // Rid of some custom value.
+ HRESULT hr = S_FALSE;
+
+ // Get the row.
+ IfFailGo(GetCustomAttributeRecord(rid, &pRec));
+
+ // Get the type.
+ tkTypeTmp = getTypeOfCustomAttribute(pRec);
+
+ // If the record is a MemberRef or a MethodDef, we will come back here to check
+ // the type of the parent.
+ CheckParentType:
+
+ if (!_IsValidTokenBase(tkTypeTmp))
+ return COR_E_BADIMAGEFORMAT;
+
+ ridTmp = RidFromToken(tkTypeTmp);
+
+ // Get the name of the type.
+ switch (TypeFromToken(tkTypeTmp))
+ {
+ case mdtTypeRef:
+ {
+ TypeRefRec *pTR;
+ IfFailGo(GetTypeRefRecord(ridTmp, &pTR));
+ IfFailGo(getNamespaceOfTypeRef(pTR, pszNamespace));
+ IfFailGo(getNameOfTypeRef(pTR, pszName));
+ }
+ break;
+ case mdtTypeDef:
+ {
+ TypeDefRec *pTD;
+ IfFailGo(GetTypeDefRecord(ridTmp, &pTD));
+ IfFailGo(getNamespaceOfTypeDef(pTD, pszNamespace));
+ IfFailGo(getNameOfTypeDef(pTD, pszName));
+ }
+ break;
+ case mdtTypeSpec:
+ {
+ // If this has an encoded token, we'll take a look. If it contains
+ // a base type, we'll just return a non-match.
+
+ hr = GetTypeDefRefTokenInTypeSpec(tkTypeTmp, &tkTypeTmp);
+ IfFailGo(hr);
+
+ if (hr == S_OK)
+ // Ok, tkTypeTmp should be the type token now.
+ goto CheckParentType;
+
+ // This doesn't have a coded typedef or typeref.
+ goto ErrExit;
+ }
+ case mdtMethodDef:
+ {
+ // Follow the parent.
+ IfFailGo( FindParentOfMethodHelper(tkTypeTmp, &tkTypeTmp));
+ goto CheckParentType;
+ }
+ break;
+ case mdtMemberRef:
+ {
+ MemberRefRec *pMember;
+ IfFailGo(GetMemberRefRecord(ridTmp, &pMember));
+ // Follow the parent.
+ tkTypeTmp = getClassOfMemberRef(pMember);
+ goto CheckParentType;
+ }
+ break;
+ case mdtString:
+ default:
+ if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 0))
+ _ASSERTE(!"Unexpected token type in FindCustomAttributeByName");
+ hr = COR_E_BADIMAGEFORMAT;
+ goto ErrExit;
+ } // switch (TypeFromToken(tkTypeTmp))
+
+ hr = S_OK;
+ ErrExit:
+
+ return hr;
+ } // CommonGetNameOfCustomAttribute
+
+ __checkReturn
+ HRESULT GetTypeDefRefTokenInTypeSpec(mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look
+ mdToken *tkEnclosedToken) // [OUT] The enclosed type token
+ {
+ _ASSERTE(TypeFromToken(tkTypeSpec) == mdtTypeSpec);
+ if (TypeFromToken(tkTypeSpec) != mdtTypeSpec || !_IsValidTokenBase(tkTypeSpec))
+ return COR_E_BADIMAGEFORMAT;
+
+ HRESULT hr;
+ TypeSpecRec *pTS;
+ IfFailRet(GetTypeSpecRecord(RidFromToken(tkTypeSpec), &pTS));
+ ULONG cbSig = 0;
+ PCCOR_SIGNATURE pSig;
+ PCCOR_SIGNATURE pEnd;
+ ULONG data = 0;
+
+ IfFailRet(getSignatureOfTypeSpec(pTS, &pSig, &cbSig));
+ pEnd = pSig + cbSig;
+
+ if (cbSig == 0)
+ return COR_E_BADIMAGEFORMAT;
+
+ pSig += CorSigUncompressData(pSig, &data);
+
+ while (pSig < pEnd && CorIsModifierElementType((CorElementType) data))
+ {
+ pSig += CorSigUncompressData(pSig, &data);
+ }
+
+ // See if the signature was bad
+ if (pSig >= pEnd)
+ return COR_E_BADIMAGEFORMAT;
+
+ // pSig should point to the element type now.
+ if (data == ELEMENT_TYPE_VALUETYPE || data == ELEMENT_TYPE_CLASS)
+ {
+ // Get the new type token
+ if (CorSigUncompressToken(pSig, tkEnclosedToken) == 0)
+ return COR_E_BADIMAGEFORMAT;
+
+ // Ok, tkEnclosedToken should be the type token now
+ return S_OK;
+ }
+
+ // The enclosed type is a base type or an array. We don't have a token to hand out
+ *tkEnclosedToken = mdTokenNil;
+
+ return S_FALSE;
+ }
+
+
+
+ //*****************************************************************************
+ // Given a scope, return the number of tokens in a given table
+ //*****************************************************************************
+ ULONG CommonGetRowCount( // return hresult
+ DWORD tkKind) // [IN] pass in the kind of token.
+ {
+ _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable.");
+
+ ULONG ulCount = 0;
+
+ switch (tkKind)
+ {
+ case mdtTypeDef:
+ ulCount = getCountTypeDefs();
+ break;
+ case mdtTypeRef:
+ ulCount = getCountTypeRefs();
+ break;
+ case mdtMethodDef:
+ ulCount = getCountMethods();
+ break;
+ case mdtFieldDef:
+ ulCount = getCountFields();
+ break;
+ case mdtMemberRef:
+ ulCount = getCountMemberRefs();
+ break;
+ case mdtInterfaceImpl:
+ ulCount = getCountInterfaceImpls();
+ break;
+ case mdtParamDef:
+ ulCount = getCountParams();
+ break;
+ case mdtFile:
+ ulCount = getCountFiles();
+ break;
+ case mdtAssemblyRef:
+ ulCount = getCountAssemblyRefs();
+ break;
+ case mdtAssembly:
+ ulCount = getCountAssemblys();
+ break;
+ case mdtCustomAttribute:
+ ulCount = getCountCustomAttributes();
+ break;
+ case mdtModule:
+ ulCount = getCountModules();
+ break;
+ case mdtPermission:
+ ulCount = getCountDeclSecuritys();
+ break;
+ case mdtSignature:
+ ulCount = getCountStandAloneSigs();
+ break;
+ case mdtEvent:
+ ulCount = getCountEvents();
+ break;
+ case mdtProperty:
+ ulCount = getCountPropertys();
+ break;
+ case mdtModuleRef:
+ ulCount = getCountModuleRefs();
+ break;
+ case mdtTypeSpec:
+ ulCount = getCountTypeSpecs();
+ break;
+ case mdtExportedType:
+ ulCount = getCountExportedTypes();
+ break;
+ case mdtManifestResource:
+ ulCount = getCountManifestResources();
+ break;
+ case mdtGenericParam:
+ ulCount = getCountGenericParams();
+ break;
+ case mdtMethodSpec:
+ ulCount = getCountMethodSpecs();
+ break;
+ default:
+ Debug_ReportError("Invalid token kind (table)");
+ ulCount = 0;
+ break;
+ }
+ return ulCount;
+ } // ULONG CommonGetRowCount()
+
+
+
+ //*****************************************************************************
+ // Locate methodimpl token range for a given typeDef
+ //*****************************************************************************
+ HRESULT CommonGetMethodImpls(
+ mdTypeDef tkTypeDef, // [IN] typeDef to scope search
+ mdToken *ptkMethodImplFirst, // [OUT] returns first methodImpl token
+ ULONG *pMethodImplCount // [OUT] returns # of methodImpl tokens scoped to type
+ )
+ {
+ _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable.");
+
+ _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef);
+ _ASSERTE(ptkMethodImplFirst != NULL);
+ _ASSERTE(pMethodImplCount != NULL);
+
+ HRESULT hr;
+
+ RID ridEnd;
+ RID ridStart;
+ IfFailGo(getMethodImplsForClass(RidFromToken(tkTypeDef), &ridEnd, &ridStart));
+ *pMethodImplCount = ridEnd - ridStart;
+ if (*pMethodImplCount)
+ {
+ *ptkMethodImplFirst = TokenFromRid(TBL_MethodImpl << 24, ridStart);
+ }
+ hr = S_OK;
+
+ ErrExit:
+ return hr;
+ }
+
+ //*****************************************************************************
+ // Extract row info for methodImpl
+ //*****************************************************************************
+ HRESULT CommonGetMethodImplProps(
+ mdToken tkMethodImpl, // [IN] methodImpl
+ mdToken *pBody, // [OUT] returns body token
+ mdToken *pDecl // [OUT] returns decl token
+ )
+ {
+ _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable.");
+
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(tkMethodImpl) == (TBL_MethodImpl << 24));
+ _ASSERTE(pBody != NULL);
+ _ASSERTE(pDecl != NULL);
+ MethodImplRec *pRec;
+ IfFailGo(GetMethodImplRecord(RidFromToken(tkMethodImpl), &pRec));
+ *pBody = getMethodBodyOfMethodImpl(pRec);
+ *pDecl = getMethodDeclarationOfMethodImpl(pRec);
+ hr = S_OK;
+ ErrExit:
+ return hr;
+ }
+
+ // IMetaModelCommonRO interface end
+
+
+
+
+public:
+// friend class CLiteWeightStgdb;
+
+ __checkReturn
+ virtual HRESULT vGetRow(UINT32 nTableIndex, UINT32 nRowIndex, void **ppRow)
+ {
+ return getRow(nTableIndex, nRowIndex, ppRow);
+ }
+
+public:
+
+ //*************************************************************************
+ // This group of functions are table-level (one function per table). Functions like
+ // getting a count of rows.
+
+ // Functions to get the count of rows in a table. Functions like:
+ // ULONG getCountXyzs() { return m_Schema.m_cRecs[TBL_Xyz];}
+#undef MiniMdTable
+#define MiniMdTable(tbl) ULONG getCount##tbl##s() { return _TBLCNT(tbl); }
+ MiniMdTables();
+ // macro misspells some names.
+ ULONG getCountProperties() {return getCountPropertys();}
+ ULONG getCountMethodSemantics() {return getCountMethodSemanticss();}
+
+ // Functions for getting a row by rid. Look like:
+ // HRESULT GetXyzRecord(RID rid, XyzRec **ppRecord) { return m_Tables[TBL_Xyz].GetRecord(rid, ppRecord); }
+ // e.g.:
+ // HRESULT GetMethodRecord(RID rid, MethodRec **ppRecord) { return m_Tables[TBL_Method].GetRecord(rid, ppRecord); }
+ #undef MiniMdTable
+#define MiniMdTable(tbl) __checkReturn HRESULT Get##tbl##Record(RID rid, tbl##Rec **ppRecord) { \
+ return getRow(TBL_##tbl, rid, reinterpret_cast<void **>(ppRecord)); }
+ MiniMdTables();
+
+ //*************************************************************************
+ // These are specialized searching functions. Mostly generic (ie, find
+ // a custom value for any object).
+
+ // Functions to search for a record relating to another record.
+ // Return RID to Constant table.
+ __checkReturn
+ HRESULT FindConstantFor(RID rid, mdToken typ, RID *pFoundRid)
+ { return doSearchTable(TBL_Constant, _COLPAIR(Constant,Parent), encodeToken(rid,typ,mdtHasConstant,lengthof(mdtHasConstant)), pFoundRid); }
+
+ // Return RID to FieldMarshal table.
+ __checkReturn
+ HRESULT FindFieldMarshalFor(RID rid, mdToken typ, RID *pFoundRid)
+ { return doSearchTable(TBL_FieldMarshal, _COLPAIR(FieldMarshal,Parent), encodeToken(rid,typ,mdtHasFieldMarshal,lengthof(mdtHasFieldMarshal)), pFoundRid); }
+
+ // Return RID to ClassLayout table, given the rid to a TypeDef.
+ __checkReturn
+ HRESULT FindClassLayoutFor(RID rid, RID *pFoundRid)
+ { return doSearchTable(TBL_ClassLayout, _COLPAIR(ClassLayout,Parent), RidFromToken(rid), pFoundRid); }
+
+ // given a rid to the Event table, find an entry in EventMap table that contains the back pointer
+ // to its typedef parent
+ __checkReturn
+ HRESULT FindEventMapParentOfEvent(RID rid, RID *pFoundRid)
+ {
+ return vSearchTableNotGreater(TBL_EventMap, _COLDEF(EventMap,EventList), rid, pFoundRid);
+ }
+ // return the parent eventmap rid given a event rid
+ __checkReturn
+ HRESULT FindParentOfEvent(RID rid, RID *pFoundRid)
+ {
+ return vSearchTableNotGreater(TBL_EventMap, _COLDEF(EventMap,EventList), rid, pFoundRid);
+ }
+
+ // given a rid to the Event table, find an entry in EventMap table that contains the back pointer
+ // to its typedef parent
+ __checkReturn
+ HRESULT FindPropertyMapParentOfProperty(RID rid, RID *pFoundRid)
+ {
+ return vSearchTableNotGreater(TBL_PropertyMap, _COLDEF(PropertyMap,PropertyList), rid, pFoundRid);
+ }
+ // return the parent propertymap rid given a property rid
+ __checkReturn
+ HRESULT FindParentOfProperty(RID rid, RID *pFoundRid)
+ {
+ return vSearchTableNotGreater(TBL_PropertyMap, _COLDEF(PropertyMap,PropertyList), rid, pFoundRid);
+ }
+
+ // Return RID to MethodSemantics table, given the rid to a MethodDef.
+ __checkReturn
+ HRESULT FindMethodSemanticsFor(RID rid, RID *pFoundRid)
+ { return doSearchTable(TBL_MethodSemantics, _COLPAIR(MethodSemantics,Method), RidFromToken(rid), pFoundRid); }
+
+ // return the parent typedef rid given a field def rid
+ __checkReturn
+ HRESULT FindParentOfField(RID rid, RID *pFoundRid)
+ {
+ return vSearchTableNotGreater(TBL_TypeDef, _COLDEF(TypeDef,FieldList), rid, pFoundRid);
+ }
+
+ // return the parent typedef rid given a method def rid
+ __checkReturn
+ HRESULT FindParentOfMethod(RID rid, RID *pFoundRid)
+ {
+ return vSearchTableNotGreater(TBL_TypeDef, _COLDEF(TypeDef,MethodList), rid, pFoundRid);
+ }
+
+ __checkReturn
+ HRESULT FindParentOfParam(RID rid, RID *pFoundRid)
+ {
+ return vSearchTableNotGreater(TBL_Method, _COLDEF(Method,ParamList), rid, pFoundRid);
+ }
+
+ // Find a FieldLayout record given the corresponding Field.
+ __checkReturn
+ HRESULT FindFieldLayoutFor(RID rid, RID *pFoundRid)
+ { return doSearchTable(TBL_FieldLayout, _COLPAIR(FieldLayout, Field), rid, pFoundRid); }
+
+ // Return RID to Constant table.
+ __checkReturn
+ HRESULT FindImplMapFor(RID rid, mdToken typ, RID *pFoundRid)
+ { return doSearchTable(TBL_ImplMap, _COLPAIR(ImplMap,MemberForwarded), encodeToken(rid,typ,mdtMemberForwarded,lengthof(mdtMemberForwarded)), pFoundRid); }
+
+ // Return RID to FieldRVA table.
+ __checkReturn
+ HRESULT FindFieldRVAFor(RID rid, RID *pFoundRid)
+ { return doSearchTable(TBL_FieldRVA, _COLPAIR(FieldRVA, Field), rid, pFoundRid); }
+
+ // Find a NestedClass record given the corresponding Field.
+ __checkReturn
+ HRESULT FindNestedClassFor(RID rid, RID *pFoundRid)
+ { return doSearchTable(TBL_NestedClass, _COLPAIR(NestedClass, NestedClass), rid, pFoundRid); }
+
+ //*************************************************************************
+ // These are table-specific functions.
+
+ // ModuleRec
+ _GETSTR(Module,Name);
+ __checkReturn HRESULT _GETGUID(Module,Mvid);
+ __checkReturn HRESULT _GETGUID(Module,EncId);
+ __checkReturn HRESULT _GETGUID(Module,EncBaseId);
+
+ // TypeRefRec
+ mdToken _GETCDTKN(TypeRef, ResolutionScope, mdtResolutionScope);
+ _GETSTR(TypeRef, Name);
+ _GETSTR(TypeRef, Namespace);
+
+ // TypeDefRec
+ ULONG _GETFLD(TypeDef,Flags); // USHORT getFlagsOfTypeDef(TypeDefRec *pRec);
+ _GETSTR(TypeDef,Name);
+ _GETSTR(TypeDef,Namespace);
+
+ _GETLIST(TypeDef,FieldList,Field); // RID getFieldListOfTypeDef(TypeDefRec *pRec);
+ _GETLIST(TypeDef,MethodList,Method); // RID getMethodListOfTypeDef(TypeDefRec *pRec);
+ mdToken _GETCDTKN(TypeDef,Extends,mdtTypeDefOrRef); // mdToken getExtendsOfTypeDef(TypeDefRec *pRec);
+
+ __checkReturn
+ HRESULT getGenericParamsForTypeDef(RID rid, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_GenericParam,
+ _COLDEF(GenericParam,Owner),
+ encodeToken(rid, mdtTypeDef, mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)),
+ pEnd,
+ pFoundRid);
+ }
+ __checkReturn
+ HRESULT getGenericParamsForMethodDef(RID rid, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_GenericParam,
+ _COLDEF(GenericParam,Owner),
+ encodeToken(rid, mdtMethodDef, mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)),
+ pEnd,
+ pFoundRid);
+ }
+ __checkReturn
+ HRESULT getMethodSpecsForMethodDef(RID rid, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_MethodSpec,
+ _COLDEF(MethodSpec,Method),
+ encodeToken(rid, mdtMethodDef, mdtMethodDefOrRef, lengthof(mdtMethodDefOrRef)),
+ pEnd,
+ pFoundRid);
+ }
+ __checkReturn
+ HRESULT getMethodSpecsForMemberRef(RID rid, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_MethodSpec,
+ _COLDEF(MethodSpec,Method),
+ encodeToken(rid, mdtMemberRef, mdtMethodDefOrRef, lengthof(mdtMethodDefOrRef)),
+ pEnd,
+ pFoundRid);
+ }
+ __checkReturn
+ HRESULT getInterfaceImplsForTypeDef(RID rid, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_InterfaceImpl,
+ _COLDEF(InterfaceImpl,Class),
+ rid,
+ pEnd,
+ pFoundRid);
+ }
+
+ // FieldPtr
+ ULONG _GETRID(FieldPtr,Field);
+
+ // FieldRec
+ USHORT _GETFLD(Field,Flags); // USHORT getFlagsOfField(FieldRec *pRec);
+ _GETSTR(Field,Name); // HRESULT getNameOfField(FieldRec *pRec, LPCUTF8 *pszString);
+ _GETSIGBLOB(Field,Signature); // HRESULT getSignatureOfField(FieldRec *pRec, PCCOR_SIGNATURE *ppbData, ULONG *pcbSize);
+
+ // MethodPtr
+ ULONG _GETRID(MethodPtr,Method);
+
+ // MethodRec
+ ULONG _GETFLD(Method,RVA);
+ USHORT _GETFLD(Method,ImplFlags);
+ USHORT _GETFLD(Method,Flags);
+ _GETSTR(Method,Name); // HRESULT getNameOfMethod(MethodRec *pRec, LPCUTF8 *pszString);
+ _GETSIGBLOB(Method,Signature); // HRESULT getSignatureOfMethod(MethodRec *pRec, PCCOR_SIGNATURE *ppbData, ULONG *pcbSize);
+ _GETLIST(Method,ParamList,Param);
+
+ // ParamPtr
+ ULONG _GETRID(ParamPtr,Param);
+
+ // ParamRec
+ USHORT _GETFLD(Param,Flags);
+ USHORT _GETFLD(Param,Sequence);
+ _GETSTR(Param,Name);
+
+ // InterfaceImplRec
+ mdToken _GETTKN(InterfaceImpl,Class,mdtTypeDef);
+ mdToken _GETCDTKN(InterfaceImpl,Interface,mdtTypeDefOrRef);
+
+ // MemberRefRec
+ mdToken _GETCDTKN(MemberRef,Class,mdtMemberRefParent);
+ _GETSTR(MemberRef,Name);
+ _GETSIGBLOB(MemberRef,Signature); // HRESULT getSignatureOfMemberRef(MemberRefRec *pRec, PCCOR_SIGNATURE *ppbData, ULONG *pcbSize);
+
+ // ConstantRec
+ BYTE _GETFLD(Constant,Type);
+ mdToken _GETCDTKN(Constant,Parent,mdtHasConstant);
+ _GETBLOB(Constant,Value);
+
+ // CustomAttributeRec
+ __checkReturn
+ HRESULT getCustomAttributeForToken(mdToken tk, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_CustomAttribute,
+ _COLDEF(CustomAttribute,Parent),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasCustomAttribute, lengthof(mdtHasCustomAttribute)),
+ pEnd,
+ pFoundRid);
+ }
+
+ mdToken _GETCDTKN(CustomAttribute,Parent,mdtHasCustomAttribute);
+ mdToken _GETCDTKN(CustomAttribute,Type,mdtCustomAttributeType);
+ _GETBLOB(CustomAttribute,Value);
+
+ // FieldMarshalRec
+ mdToken _GETCDTKN(FieldMarshal,Parent,mdtHasFieldMarshal);
+ _GETSIGBLOB(FieldMarshal,NativeType);
+
+ // DeclSecurityRec
+ __checkReturn
+ HRESULT getDeclSecurityForToken(mdToken tk, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_DeclSecurity,
+ _COLDEF(DeclSecurity,Parent),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasDeclSecurity, lengthof(mdtHasDeclSecurity)),
+ pEnd,
+ pFoundRid);
+ }
+
+ short _GETFLD(DeclSecurity,Action);
+ mdToken _GETCDTKN(DeclSecurity,Parent,mdtHasDeclSecurity);
+ _GETBLOB(DeclSecurity,PermissionSet);
+
+ // ClassLayoutRec
+ USHORT _GETFLD(ClassLayout,PackingSize);
+ ULONG _GETFLD(ClassLayout,ClassSize);
+ ULONG _GETTKN(ClassLayout,Parent, mdtTypeDef);
+
+ // FieldLayout
+ ULONG _GETFLD(FieldLayout,OffSet);
+ ULONG _GETTKN(FieldLayout, Field, mdtFieldDef);
+
+ // Event map.
+ _GETLIST(EventMap,EventList,Event);
+ ULONG _GETRID(EventMap, Parent);
+
+ // EventPtr
+ ULONG _GETRID(EventPtr, Event);
+
+ // Event.
+ USHORT _GETFLD(Event,EventFlags);
+ _GETSTR(Event,Name);
+ mdToken _GETCDTKN(Event,EventType,mdtTypeDefOrRef);
+
+ // Property map.
+ _GETLIST(PropertyMap,PropertyList,Property);
+ ULONG _GETRID(PropertyMap, Parent);
+
+ // PropertyPtr
+ ULONG _GETRID(PropertyPtr, Property);
+
+ // Property.
+ USHORT _GETFLD(Property,PropFlags);
+ _GETSTR(Property,Name);
+ _GETSIGBLOB(Property,Type);
+
+ // MethodSemantics.
+ // Given an event or a property token, return the beginning/ending
+ // associates.
+ //
+ __checkReturn
+ HRESULT getAssociatesForToken(mdToken tk, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_MethodSemantics,
+ _COLDEF(MethodSemantics,Association),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasSemantic, lengthof(mdtHasSemantic)),
+ pEnd,
+ pFoundRid);
+ }
+
+ USHORT _GETFLD(MethodSemantics,Semantic);
+ mdToken _GETTKN(MethodSemantics,Method,mdtMethodDef);
+ mdToken _GETCDTKN(MethodSemantics,Association,mdtHasSemantic);
+
+ // MethodImpl
+ // Given a class token, return the beginning/ending MethodImpls.
+ //
+ __checkReturn
+ HRESULT getMethodImplsForClass(RID rid, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_MethodImpl,
+ _COLDEF(MethodImpl, Class),
+ rid,
+ pEnd,
+ pFoundRid);
+ }
+
+ mdToken _GETTKN(MethodImpl,Class,mdtTypeDef);
+ mdToken _GETCDTKN(MethodImpl,MethodBody, mdtMethodDefOrRef);
+ mdToken _GETCDTKN(MethodImpl, MethodDeclaration, mdtMethodDefOrRef);
+
+ // StandAloneSigRec
+ _GETSIGBLOB(StandAloneSig,Signature); // HRESULT getSignatureOfStandAloneSig(StandAloneSigRec *pRec, PCCOR_SIGNATURE *ppbData, ULONG *pcbSize);
+
+ // TypeSpecRec
+ // const BYTE* getSignatureOfTypeSpec(TypeSpecRec *pRec, ULONG *pcb);
+ _GETSIGBLOB(TypeSpec,Signature);
+
+ // ModuleRef
+ _GETSTR(ModuleRef,Name);
+
+ // ENCLog
+ ULONG _GETFLD(ENCLog, FuncCode); // ULONG getFuncCodeOfENCLog(ENCLogRec *pRec);
+
+ // ImplMap
+ USHORT _GETFLD(ImplMap, MappingFlags); // USHORT getMappingFlagsOfImplMap(ImplMapRec *pRec);
+ mdToken _GETCDTKN(ImplMap, MemberForwarded, mdtMemberForwarded); // mdToken getMemberForwardedOfImplMap(ImplMapRec *pRec);
+ _GETSTR(ImplMap, ImportName); // HRESULT getImportNameOfImplMap(ImplMapRec *pRec, LPCUTF8 *pszString);
+ mdToken _GETTKN(ImplMap, ImportScope, mdtModuleRef); // mdToken getImportScopeOfImplMap(ImplMapRec *pRec);
+
+ // FieldRVA
+ ULONG _GETFLD(FieldRVA, RVA); // ULONG getRVAOfFieldRVA(FieldRVARec *pRec);
+ mdToken _GETTKN(FieldRVA, Field, mdtFieldDef); // mdToken getFieldOfFieldRVA(FieldRVARec *pRec);
+
+ // Assembly
+ ULONG _GETFLD(Assembly, HashAlgId);
+ USHORT _GETFLD(Assembly, MajorVersion);
+ USHORT _GETFLD(Assembly, MinorVersion);
+ USHORT _GETFLD(Assembly, BuildNumber);
+ USHORT _GETFLD(Assembly, RevisionNumber);
+ ULONG _GETFLD(Assembly, Flags);
+ _GETBLOB(Assembly, PublicKey);
+ _GETSTR(Assembly, Name);
+ _GETSTR(Assembly, Locale);
+
+ // AssemblyRef
+ USHORT _GETFLD(AssemblyRef, MajorVersion);
+ USHORT _GETFLD(AssemblyRef, MinorVersion);
+ USHORT _GETFLD(AssemblyRef, BuildNumber);
+ USHORT _GETFLD(AssemblyRef, RevisionNumber);
+ ULONG _GETFLD(AssemblyRef, Flags);
+ _GETBLOB(AssemblyRef, PublicKeyOrToken);
+ _GETSTR(AssemblyRef, Name);
+ _GETSTR(AssemblyRef, Locale);
+ _GETBLOB(AssemblyRef, HashValue);
+
+ // File
+ ULONG _GETFLD(File, Flags);
+ _GETSTR(File, Name);
+ _GETBLOB(File, HashValue);
+
+ // ExportedType
+ ULONG _GETFLD(ExportedType, Flags);
+ ULONG _GETFLD(ExportedType, TypeDefId);
+ _GETSTR(ExportedType, TypeName);
+ _GETSTR(ExportedType, TypeNamespace);
+ mdToken _GETCDTKN(ExportedType, Implementation, mdtImplementation);
+
+ // ManifestResource
+ ULONG _GETFLD(ManifestResource, Offset);
+ ULONG _GETFLD(ManifestResource, Flags);
+ _GETSTR(ManifestResource, Name);
+ mdToken _GETCDTKN(ManifestResource, Implementation, mdtImplementation);
+
+ // NestedClass
+ mdToken _GETTKN(NestedClass, NestedClass, mdtTypeDef);
+ mdToken _GETTKN(NestedClass, EnclosingClass, mdtTypeDef);
+
+ int GetSizeOfMethodNameColumn()
+ {
+ return _COLDEF(Method,Name).m_cbColumn;
+ }
+
+ // GenericParRec
+ USHORT _GETFLD(GenericParam,Number);
+ USHORT _GETFLD(GenericParam,Flags);
+ mdToken _GETCDTKN(GenericParam,Owner,mdtTypeOrMethodDef);
+ _GETSTR(GenericParam,Name);
+
+ __checkReturn
+ HRESULT getGenericParamConstraintsForGenericParam(RID rid, RID *pEnd, RID *pFoundRid)
+ {
+ return SearchTableForMultipleRows(TBL_GenericParamConstraint,
+ _COLDEF(GenericParamConstraint,Owner),
+ rid,
+ pEnd,
+ pFoundRid);
+ }
+
+ // MethodSpecRec
+ mdToken _GETCDTKN(MethodSpec,Method,mdtMethodDefOrRef);
+ _GETSIGBLOB(MethodSpec,Instantiation);
+
+ //GenericParamConstraintRec
+ mdToken _GETTKN(GenericParamConstraint,Owner,mdtGenericParam);
+ mdToken _GETCDTKN(GenericParamConstraint,Constraint,mdtTypeDefOrRef);
+
+ BOOL SupportsGenerics()
+ {
+ // Only 2.0 of the metadata (and 1.1) support generics
+ return (m_Schema.m_major >= METAMODEL_MAJOR_VER_V2_0 ||
+ (m_Schema.m_major == METAMODEL_MAJOR_VER_B1 && m_Schema.m_minor == METAMODEL_MINOR_VER_B1));
+ }// SupportGenerics
+
+ protected:
+ //*****************************************************************************
+ // Helper: determine if a token is valid or not
+ //*****************************************************************************
+ BOOL _IsValidTokenBase(
+ mdToken tk)
+ {
+ BOOL bRet = FALSE;
+ RID rid = RidFromToken(tk);
+
+ if (rid != 0)
+ {
+ switch (TypeFromToken(tk))
+ {
+ case mdtModule:
+ // can have only one module record
+ bRet = (rid <= getCountModules());
+ break;
+ case mdtTypeRef:
+ bRet = (rid <= getCountTypeRefs());
+ break;
+ case mdtTypeDef:
+ bRet = (rid <= getCountTypeDefs());
+ break;
+ case mdtFieldDef:
+ bRet = (rid <= getCountFields());
+ break;
+ case mdtMethodDef:
+ bRet = (rid <= getCountMethods());
+ break;
+ case mdtParamDef:
+ bRet = (rid <= getCountParams());
+ break;
+ case mdtInterfaceImpl:
+ bRet = (rid <= getCountInterfaceImpls());
+ break;
+ case mdtMemberRef:
+ bRet = (rid <= getCountMemberRefs());
+ break;
+ case mdtCustomAttribute:
+ bRet = (rid <= getCountCustomAttributes());
+ break;
+ case mdtPermission:
+ bRet = (rid <= getCountDeclSecuritys());
+ break;
+ case mdtSignature:
+ bRet = (rid <= getCountStandAloneSigs());
+ break;
+ case mdtEvent:
+ bRet = (rid <= getCountEvents());
+ break;
+ case mdtProperty:
+ bRet = (rid <= getCountPropertys());
+ break;
+ case mdtModuleRef:
+ bRet = (rid <= getCountModuleRefs());
+ break;
+ case mdtTypeSpec:
+ bRet = (rid <= getCountTypeSpecs());
+ break;
+ case mdtAssembly:
+ bRet = (rid <= getCountAssemblys());
+ break;
+ case mdtAssemblyRef:
+ bRet = (rid <= getCountAssemblyRefs());
+ break;
+ case mdtFile:
+ bRet = (rid <= getCountFiles());
+ break;
+ case mdtExportedType:
+ bRet = (rid <= getCountExportedTypes());
+ break;
+ case mdtManifestResource:
+ bRet = (rid <= getCountManifestResources());
+ break;
+ case mdtGenericParam:
+ bRet = (rid <= getCountGenericParams());
+ break;
+ case mdtGenericParamConstraint:
+ bRet = (rid <= getCountGenericParamConstraints());
+ break;
+ case mdtMethodSpec:
+ bRet = (rid <= getCountMethodSpecs());
+ break;
+ default:
+ _ASSERTE(!bRet);
+ Debug_ReportError("Unknown token kind!");
+ }
+ }
+ return bRet;
+ } // _IsValidToken
+
+#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
+ bool IsSafeToDelete()
+ {
+ return m_isSafeToDelete;
+ }
+
+ FORCEINLINE void MarkUnsafeToDelete() { m_isSafeToDelete = false; }
+
+ bool m_isSafeToDelete; // This starts out true, but gets set to FALSE if we detect
+ // a MiniMd API call that might have given out an internal pointer.
+#endif
+
+}; //class CMiniMdTemplate<Impl>
+
+
+
+#undef SETP
+#undef _GETCDTKN
+#undef _GETTKN
+#undef _GETRID
+#undef _GETBLOB
+#undef _GETGUID
+#undef _GETSTR
+#undef SCHEMA
+
+#endif // _METAMODEL_H_
+// eof ------------------------------------------------------------------------
diff --git a/src/md/inc/metamodelro.h b/src/md/inc/metamodelro.h
new file mode 100644
index 0000000000..00d4ead5f9
--- /dev/null
+++ b/src/md/inc/metamodelro.h
@@ -0,0 +1,240 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MetaModelRO.h -- header file for Read-Only compressed COM+ metadata.
+//
+
+//
+// Used by the EE.
+//
+//*****************************************************************************
+#ifndef _METAMODELRO_H_
+#define _METAMODELRO_H_
+
+#if _MSC_VER >= 1100
+ # pragma once
+#endif
+
+#include "metamodel.h"
+
+#include "../heaps/export.h"
+#include "../tables/export.h"
+
+//*****************************************************************************
+// A read-only MiniMd. This is the fastest and smallest possible MiniMd,
+// and as such, is the preferred EE metadata provider.
+//*****************************************************************************
+
+template <class MiniMd> class CLiteWeightStgdb;
+class CMiniMdRW;
+class MDInternalRO;
+class CMiniMd : public CMiniMdTemplate<CMiniMd>
+{
+public:
+ friend class CLiteWeightStgdb<CMiniMd>;
+ friend class CMiniMdTemplate<CMiniMd>;
+ friend class CMiniMdRW;
+ friend class MDInternalRO;
+
+ __checkReturn
+ HRESULT InitOnMem(void *pBuf, ULONG ulBufLen);
+ __checkReturn
+ HRESULT PostInit(int iLevel); // higher number : more checking
+
+ // Returns TRUE if token (tk) is valid.
+ // For user strings, consideres 0 as valid token.
+ BOOL _IsValidToken(
+ mdToken tk) // [IN] token to be checked
+ {
+ if (TypeFromToken(tk) == mdtString)
+ {
+ return m_UserStringHeap.IsValidIndex(RidFromToken(tk));
+ }
+ // Base type doesn't know about user string blob (yet)
+ return _IsValidTokenBase(tk);
+ } // CMiniMdRO::_IsValidToken
+
+ __checkReturn
+ FORCEINLINE HRESULT GetUserString(ULONG nIndex, MetaData::DataBlob *pData)
+ {
+ MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED();
+ return m_UserStringHeap.GetBlob(nIndex, pData);
+ }
+
+#ifdef FEATURE_PREJIT
+ void DisableHotDataUsage()
+ {
+ MetaData::HotHeap emptyHotHeap;
+ // Initialize hot data again with empty heap to disable their usage
+ m_StringHeap.InitializeHotData(emptyHotHeap);
+ m_BlobHeap.InitializeHotData(emptyHotHeap);
+ m_UserStringHeap.InitializeHotData(emptyHotHeap);
+ m_GuidHeap.InitializeHotData(emptyHotHeap);
+ // Disable usage of hot table data (throw it away)
+ m_pHotTablesDirectory = NULL;
+ }
+#endif //FEATURE_PREJIT
+
+protected:
+ // Table info.
+ MetaData::TableRO m_Tables[TBL_COUNT];
+#ifdef FEATURE_PREJIT
+ struct MetaData::HotTablesDirectory * m_pHotTablesDirectory;
+#endif //FEATURE_PREJIT
+
+ __checkReturn
+ HRESULT InitializeTables(MetaData::DataBlob tablesData);
+
+ __checkReturn
+ virtual HRESULT vSearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ulTarget, RID *pRid);
+ __checkReturn
+ virtual HRESULT vSearchTableNotGreater(ULONG ixTbl, CMiniColDef sColumn, ULONG ulTarget, RID *pRid);
+
+ // Heaps
+ MetaData::StringHeapRO m_StringHeap;
+ MetaData::BlobHeapRO m_BlobHeap;
+ MetaData::BlobHeapRO m_UserStringHeap;
+ MetaData::GuidHeapRO m_GuidHeap;
+
+protected:
+
+ //*************************************************************************
+ // Overridables -- must be provided in derived classes.
+ __checkReturn
+ FORCEINLINE HRESULT Impl_GetString(UINT32 nIndex, __out LPCSTR *pszString)
+ { return m_StringHeap.GetString(nIndex, pszString); }
+ __checkReturn
+ HRESULT Impl_GetStringW(ULONG ix, __inout_ecount (cchBuffer) LPWSTR szOut, ULONG cchBuffer, ULONG *pcchBuffer);
+ __checkReturn
+ FORCEINLINE HRESULT Impl_GetGuid(UINT32 nIndex, GUID *pTargetGuid)
+ {
+ HRESULT hr;
+ GUID UNALIGNED *pSourceGuid;
+ IfFailRet(m_GuidHeap.GetGuid(
+ nIndex,
+ &pSourceGuid));
+ // Add void* casts so that the compiler can't make assumptions about alignment.
+ CopyMemory((void *)pTargetGuid, (void *)pSourceGuid, sizeof(GUID));
+ SwapGuid(pTargetGuid);
+ return S_OK;
+ }
+ __checkReturn
+ FORCEINLINE HRESULT Impl_GetBlob(UINT32 nIndex, __out MetaData::DataBlob *pData)
+ { return m_BlobHeap.GetBlob(nIndex, pData); }
+
+ __checkReturn
+ FORCEINLINE HRESULT Impl_GetRow(
+ UINT32 nTableIndex,
+ UINT32 nRowIndex,
+ __deref_out_opt BYTE **ppRecord)
+ {
+ _ASSERTE(nTableIndex < TBL_COUNT);
+ return m_Tables[nTableIndex].GetRecord(
+ nRowIndex,
+ ppRecord,
+ m_TableDefs[nTableIndex].m_cbRec,
+ m_Schema.m_cRecs[nTableIndex],
+#ifdef FEATURE_PREJIT
+ m_pHotTablesDirectory,
+#endif //FEATURE_PREJIT
+ nTableIndex);
+ }
+
+ // Count of rows in tbl2, pointed to by the column in tbl.
+ __checkReturn
+ HRESULT Impl_GetEndRidForColumn(
+ UINT32 nTableIndex,
+ RID nRowIndex,
+ CMiniColDef &def, // Column containing the RID into other table.
+ UINT32 nTargetTableIndex, // The other table.
+ RID *pEndRid);
+
+ __checkReturn
+ FORCEINLINE HRESULT Impl_SearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ixCol, ULONG ulTarget, RID *pFoundRid)
+ {
+ return vSearchTable(ixTbl, sColumn, ulTarget, pFoundRid);
+ }
+
+ // given a rid to the Property table, find an entry in PropertyMap table that contains the back pointer
+ // to its typedef parent
+ __checkReturn
+ HRESULT FindPropertyMapParentOfProperty(RID rid, RID *pFoundRid)
+ {
+ return vSearchTableNotGreater(TBL_PropertyMap, _COLDEF(PropertyMap,PropertyList), rid, pFoundRid);
+ }
+
+ __checkReturn
+ HRESULT FindParentOfPropertyHelper(
+ mdProperty pr,
+ mdTypeDef *ptd)
+ {
+ HRESULT hr = NOERROR;
+
+ RID ridPropertyMap;
+ PropertyMapRec *pRec;
+
+ IfFailRet(FindPropertyMapParentOfProperty(RidFromToken(pr), &ridPropertyMap));
+ IfFailRet(GetPropertyMapRecord(ridPropertyMap, &pRec));
+ *ptd = getParentOfPropertyMap( pRec );
+
+ RidToToken(*ptd, mdtTypeDef);
+
+ return hr;
+ } // HRESULT CMiniMdRW::FindParentOfPropertyHelper()
+
+ // given a rid to the Event table, find an entry in EventMap table that contains the back pointer
+ // to its typedef parent
+ __checkReturn
+ HRESULT FindEventMapParentOfEvent(RID rid, RID *pFoundRid)
+ {
+ return vSearchTableNotGreater(TBL_EventMap, _COLDEF(EventMap, EventList), rid, pFoundRid);
+ }
+
+ __checkReturn
+ HRESULT FindParentOfEventHelper(
+ mdEvent pr,
+ mdTypeDef *ptd)
+ {
+ HRESULT hr = NOERROR;
+
+ RID ridEventMap;
+ EventMapRec *pRec;
+
+ IfFailRet(FindEventMapParentOfEvent(RidFromToken(pr), &ridEventMap));
+ IfFailRet(GetEventMapRecord(ridEventMap, &pRec));
+ *ptd = getParentOfEventMap( pRec );
+
+ RidToToken(*ptd, mdtTypeDef);
+
+ return hr;
+ } // HRESULT CMiniMdRW::FindParentOfEventHelper()
+
+ FORCEINLINE int Impl_IsRo()
+ { return 1; }
+ //*************************************************************************
+
+ __checkReturn
+ HRESULT CommonEnumCustomAttributeByName( // S_OK or error.
+ 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
+
+ __checkReturn
+ HRESULT 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.
+
+ public:
+ virtual BOOL IsWritable()
+ {
+ return FALSE;
+ }
+
+}; // class CMiniMd
+
+#endif // _METAMODELRO_H_
diff --git a/src/md/inc/metamodelrw.h b/src/md/inc/metamodelrw.h
new file mode 100644
index 0000000000..6e00d3cf3e
--- /dev/null
+++ b/src/md/inc/metamodelrw.h
@@ -0,0 +1,1479 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//*****************************************************************************
+// MetaModelRW.h -- header file for Read/Write compressed COM+ metadata.
+//
+
+//
+// Used by Emitters and by E&C.
+//
+//*****************************************************************************
+#ifndef _METAMODELRW_H_
+#define _METAMODELRW_H_
+
+#if _MSC_VER >= 1100
+ # pragma once
+#endif
+
+#include "metamodel.h" // Base classes for the MetaModel.
+#include "metadatahash.h"
+#include "rwutil.h"
+#include "shash.h"
+
+#include "../heaps/export.h"
+#include "../hotdata/export.h"
+#include "../tables/export.h"
+
+struct HENUMInternal;
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+struct IMDCustomDataSource;
+#endif
+
+// ENUM for marking bit
+enum
+{
+ InvalidMarkedBit = 0x00000000,
+ ModuleMarkedBit = 0x00000001,
+ TypeRefMarkedBit = 0x00000002,
+ TypeDefMarkedBit = 0x00000004,
+ FieldMarkedBit = 0x00000008,
+ MethodMarkedBit = 0x00000010,
+ ParamMarkedBit = 0x00000020,
+ MemberRefMarkedBit = 0x00000040,
+ CustomAttributeMarkedBit = 0x00000080,
+ DeclSecurityMarkedBit = 0x00000100,
+ SignatureMarkedBit = 0x00000200,
+ EventMarkedBit = 0x00000400,
+ PropertyMarkedBit = 0x00000800,
+ MethodImplMarkedBit = 0x00001000,
+ ModuleRefMarkedBit = 0x00002000,
+ TypeSpecMarkedBit = 0x00004000,
+ InterfaceImplMarkedBit = 0x00008000,
+ AssemblyRefMarkedBit = 0x00010000,
+ MethodSpecMarkedBit = 0x00020000,
+
+};
+
+// entry for marking UserString
+struct FilterUserStringEntry
+{
+ DWORD m_tkString;
+ bool m_fMarked;
+};
+
+class FilterTable : public CDynArray<DWORD>
+{
+public:
+ FilterTable() { m_daUserStringMarker = NULL; }
+ ~FilterTable();
+
+ __checkReturn FORCEINLINE HRESULT MarkTypeRef(mdToken tk) { return MarkToken(tk, TypeRefMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkTypeDef(mdToken tk) { return MarkToken(tk, TypeDefMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkField(mdToken tk) { return MarkToken(tk, FieldMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkMethod(mdToken tk) { return MarkToken(tk, MethodMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkParam(mdToken tk) { return MarkToken(tk, ParamMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkMemberRef(mdToken tk) { return MarkToken(tk, MemberRefMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkCustomAttribute(mdToken tk) { return MarkToken(tk, CustomAttributeMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkDeclSecurity(mdToken tk) { return MarkToken(tk, DeclSecurityMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkSignature(mdToken tk) { return MarkToken(tk, SignatureMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkEvent(mdToken tk) { return MarkToken(tk, EventMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkProperty(mdToken tk) { return MarkToken(tk, PropertyMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkMethodImpl(RID rid)
+ {
+ return MarkToken(TokenFromRid(rid, TBL_MethodImpl << 24), MethodImplMarkedBit);
+ }
+ __checkReturn FORCEINLINE HRESULT MarkModuleRef(mdToken tk) { return MarkToken(tk, ModuleRefMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkTypeSpec(mdToken tk) { return MarkToken(tk, TypeSpecMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkInterfaceImpl(mdToken tk) { return MarkToken(tk, InterfaceImplMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkAssemblyRef(mdToken tk) { return MarkToken(tk, AssemblyRefMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT MarkMethodSpec(mdToken tk) { return MarkToken(tk, MethodSpecMarkedBit); }
+
+ // It may look inconsistent but it is because taht UserString an offset to the heap.
+ // We don't want to grow the FilterTable to the size of the UserString heap.
+ // So we use the heap's marking system instead...
+ //
+ __checkReturn HRESULT MarkUserString(mdString str);
+
+ __checkReturn HRESULT MarkNewUserString(mdString str);
+
+ FORCEINLINE bool IsTypeRefMarked(mdToken tk) { return IsTokenMarked(tk, TypeRefMarkedBit); }
+ FORCEINLINE bool IsTypeDefMarked(mdToken tk) { return IsTokenMarked(tk, TypeDefMarkedBit); }
+ FORCEINLINE bool IsFieldMarked(mdToken tk) { return IsTokenMarked(tk, FieldMarkedBit); }
+ FORCEINLINE bool IsMethodMarked(mdToken tk) { return IsTokenMarked(tk, MethodMarkedBit); }
+ FORCEINLINE bool IsParamMarked(mdToken tk) { return IsTokenMarked(tk, ParamMarkedBit); }
+ FORCEINLINE bool IsMemberRefMarked(mdToken tk) { return IsTokenMarked(tk, MemberRefMarkedBit); }
+ FORCEINLINE bool IsCustomAttributeMarked(mdToken tk) { return IsTokenMarked(tk, CustomAttributeMarkedBit); }
+ FORCEINLINE bool IsDeclSecurityMarked(mdToken tk) { return IsTokenMarked(tk, DeclSecurityMarkedBit); }
+ FORCEINLINE bool IsSignatureMarked(mdToken tk) { return IsTokenMarked(tk, SignatureMarkedBit); }
+ FORCEINLINE bool IsEventMarked(mdToken tk) { return IsTokenMarked(tk, EventMarkedBit); }
+ FORCEINLINE bool IsPropertyMarked(mdToken tk) { return IsTokenMarked(tk, PropertyMarkedBit); }
+ FORCEINLINE bool IsMethodImplMarked(RID rid)
+ {
+ return IsTokenMarked(TokenFromRid(rid, TBL_MethodImpl << 24), MethodImplMarkedBit);
+ }
+ FORCEINLINE bool IsModuleRefMarked(mdToken tk) { return IsTokenMarked(tk, ModuleRefMarkedBit); }
+ FORCEINLINE bool IsTypeSpecMarked(mdToken tk) { return IsTokenMarked(tk, TypeSpecMarkedBit); }
+ FORCEINLINE bool IsInterfaceImplMarked(mdToken tk){ return IsTokenMarked(tk, InterfaceImplMarkedBit); }
+ FORCEINLINE bool IsAssemblyRefMarked(mdToken tk){ return IsTokenMarked(tk, AssemblyRefMarkedBit); }
+ FORCEINLINE bool IsMethodSpecMarked(mdToken tk){ return IsTokenMarked(tk, MethodSpecMarkedBit); }
+
+ bool IsUserStringMarked(mdString str);
+
+ __checkReturn HRESULT UnmarkAll(CMiniMdRW *pMiniMd, ULONG ulSize);
+ __checkReturn HRESULT MarkAll(CMiniMdRW *pMiniMd, ULONG ulSize);
+ bool IsTokenMarked(mdToken);
+
+ __checkReturn FORCEINLINE HRESULT UnmarkTypeDef(mdToken tk) { return UnmarkToken(tk, TypeDefMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT UnmarkField(mdToken tk) { return UnmarkToken(tk, FieldMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT UnmarkMethod(mdToken tk) { return UnmarkToken(tk, MethodMarkedBit); }
+ __checkReturn FORCEINLINE HRESULT UnmarkCustomAttribute(mdToken tk) { return UnmarkToken(tk, CustomAttributeMarkedBit); }
+
+private:
+ CDynArray<FilterUserStringEntry> *m_daUserStringMarker;
+ bool IsTokenMarked(mdToken tk, DWORD bitMarked);
+ __checkReturn HRESULT MarkToken(mdToken tk, DWORD bit);
+ __checkReturn HRESULT UnmarkToken(mdToken tk, DWORD bit);
+}; // class FilterTable : public CDynArray<DWORD>
+
+class CMiniMdRW;
+
+//*****************************************************************************
+// This class is used to keep a list of RID. This list of RID can be sorted
+// base on the m_ixCol's value of the m_ixTbl table.
+//*****************************************************************************
+class VirtualSort
+{
+public:
+ void Init(ULONG ixTbl, ULONG ixCol, CMiniMdRW *pMiniMd);
+ void Uninit();
+ TOKENMAP *m_pMap; // RID for m_ixTbl table. Sorted by on the ixCol
+ bool m_isMapValid;
+ ULONG m_ixTbl; // Table this is a sorter for.
+ ULONG m_ixCol; // Key column in the table.
+ CMiniMdRW *m_pMiniMd; // The MiniMd with the data.
+ __checkReturn
+ HRESULT Sort();
+private:
+ mdToken m_tkBuf;
+ __checkReturn
+ HRESULT SortRange(int iLeft, int iRight);
+public:
+ __checkReturn
+ HRESULT Compare(
+ RID iLeft, // First item to compare.
+ RID iRight, // Second item to compare.
+ int *pnResult); // -1, 0, or 1
+
+private:
+ FORCEINLINE void Swap(
+ RID iFirst,
+ RID iSecond)
+ {
+ if ( iFirst == iSecond ) return;
+ m_tkBuf = *(m_pMap->Get(iFirst));
+ *(m_pMap->Get(iFirst)) = *(m_pMap->Get(iSecond));
+ *(m_pMap->Get(iSecond)) = m_tkBuf;
+ }
+
+
+}; // class VirtualSort
+
+class ReorderData
+{
+public:
+ typedef enum
+ {
+ MinReorderBucketType=0, // bucket# shouldn't be less than this value
+ Undefined=0, // use this for initialization
+ Duplicate=1, // duplicate string
+ ProfileData=2, // bucket# for IBC data
+ PublicData=3, // bucket# for public data
+ OtherData=4, // bucket# for other data
+ NonPublicData=5, // bucket# for non-public data
+ MaxReorderBucketType=255 // bucket# shouldn't exceeed this value
+ } ReorderBucketType;
+};
+
+typedef CMetaDataHashBase CMemberRefHash;
+typedef CMetaDataHashBase CLookUpHash;
+
+class MDTOKENMAP;
+class MDInternalRW;
+class CorProfileData;
+class UTSemReadWrite;
+
+enum MetaDataReorderingOptions {
+ NoReordering=0x0,
+ ReArrangeStringPool=0x1
+};
+
+#ifdef FEATURE_PREJIT
+
+// {0702E333-8D64-4ca7-B564-4AA56B1FCEA3}
+EXTERN_GUID(IID_IMetaDataCorProfileData, 0x702e333, 0x8d64, 0x4ca7, 0xb5, 0x64, 0x4a, 0xa5, 0x6b, 0x1f, 0xce, 0xa3 );
+
+#undef INTERFACE
+#define INTERFACE IMetaDataCorProfileData
+DECLARE_INTERFACE_(IMetaDataCorProfileData, IUnknown)
+{
+ STDMETHOD(SetCorProfileData)(
+ CorProfileData *pProfileData) PURE; // [IN] Pointer to profile data
+};
+
+// {2B464817-C0F6-454e-99E7-C352D8384D7B}
+EXTERN_GUID(IID_IMDInternalMetadataReorderingOptions, 0x2B464817, 0xC0F6, 0x454e, 0x99, 0xE7, 0xC3, 0x52, 0xD8, 0x38, 0x4D, 0x7B );
+
+#undef INTERFACE
+#define INTERFACE IMDInternalMetadataReorderingOptions
+DECLARE_INTERFACE_(IMDInternalMetadataReorderingOptions, IUnknown)
+{
+ STDMETHOD(SetMetaDataReorderingOptions)(
+ MetaDataReorderingOptions options) PURE; // [IN] metadata reordering options
+};
+
+#endif //FEATURE_PREJIT
+
+template <class MiniMd> class CLiteWeightStgdb;
+//*****************************************************************************
+// Read/Write MiniMd.
+//*****************************************************************************
+class CMiniMdRW : public CMiniMdTemplate<CMiniMdRW>
+{
+public:
+ friend class CLiteWeightStgdb<CMiniMdRW>;
+ friend class CLiteWeightStgdbRW;
+ friend class CMiniMdTemplate<CMiniMdRW>;
+ friend class CQuickSortMiniMdRW;
+ friend class VirtualSort;
+ friend class MDInternalRW;
+ friend class RegMeta;
+ friend class FilterTable;
+ friend class ImportHelper;
+ friend class VerifyLayoutsMD;
+
+ CMiniMdRW();
+ ~CMiniMdRW();
+
+ __checkReturn
+ HRESULT InitNew();
+ __checkReturn
+ HRESULT InitOnMem(const void *pBuf, ULONG ulBufLen, int bReadOnly);
+ __checkReturn
+ HRESULT PostInit(int iLevel);
+ __checkReturn
+ HRESULT InitPoolOnMem(int iPool, void *pbData, ULONG cbData, int bReadOnly);
+ __checkReturn
+ HRESULT InitOnRO(CMiniMd *pMd, int bReadOnly);
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ __checkReturn
+ HRESULT InitOnCustomDataSource(IMDCustomDataSource* pDataSouce);
+#endif
+ __checkReturn
+ HRESULT ConvertToRW();
+
+ __checkReturn
+ HRESULT GetSaveSize(
+ CorSaveSize fSave,
+ UINT32 *pcbSize,
+ DWORD *pbCompressed,
+ MetaDataReorderingOptions reorderingOptions = NoReordering,
+ CorProfileData *pProfileData = NULL);
+ int IsPoolEmpty(int iPool);
+ __checkReturn
+ HRESULT GetPoolSaveSize(int iPool, UINT32 *pcbSize);
+
+ __checkReturn
+ HRESULT SaveTablesToStream(IStream *pIStream, MetaDataReorderingOptions reorderingOptions, CorProfileData *pProfileData);
+ __checkReturn
+ HRESULT SavePoolToStream(int iPool, IStream *pIStream);
+ __checkReturn
+ HRESULT SaveDone();
+
+ __checkReturn
+ HRESULT SetHandler(IUnknown *pIUnk);
+
+ __checkReturn
+ HRESULT SetOption(OptionValue *pOptionValue);
+ __checkReturn
+ HRESULT GetOption(OptionValue *pOptionValue);
+
+ static ULONG GetTableForToken(mdToken tkn);
+ static mdToken GetTokenForTable(ULONG ixTbl);
+
+ FORCEINLINE static ULONG TblFromRecId(ULONG ul) { return (ul >> 24)&0x7f; }
+ FORCEINLINE static ULONG RidFromRecId(ULONG ul) { return ul & 0xffffff; }
+ FORCEINLINE static ULONG RecIdFromRid(ULONG rid, ULONG ixTbl) { return rid | ((ixTbl|0x80) << 24); }
+ FORCEINLINE static int IsRecId(ULONG ul) { return (ul & 0x80000000) != 0;}
+
+ // Place in every API function before doing any allocations.
+ __checkReturn
+ FORCEINLINE HRESULT PreUpdate()
+ {
+ if (m_eGrow == eg_grow)
+ {
+ return ExpandTables();
+ }
+ return S_OK;
+ }
+
+ __checkReturn
+ HRESULT AddRecord(
+ UINT32 nTableIndex,
+ void **ppRow,
+ RID *pRid);
+
+ __checkReturn
+ FORCEINLINE HRESULT PutCol(ULONG ixTbl, ULONG ixCol, void *pRecord, ULONG uVal)
+ { _ASSERTE(ixTbl < TBL_COUNT); _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols);
+ return PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pRecord, uVal);
+ } // HRESULT CMiniMdRW::PutCol()
+ __checkReturn
+ HRESULT PutString(ULONG ixTbl, ULONG ixCol, void *pRecord, LPCSTR szString);
+ __checkReturn
+ HRESULT PutStringW(ULONG ixTbl, ULONG ixCol, void *pRecord, LPCWSTR wszString);
+ __checkReturn
+ HRESULT PutGuid(ULONG ixTbl, ULONG ixCol, void *pRecord, REFGUID guid);
+ __checkReturn
+ HRESULT ChangeMvid(REFGUID newMvid);
+ __checkReturn
+ HRESULT PutToken(ULONG ixTbl, ULONG ixCol, void *pRecord, mdToken tk);
+ __checkReturn
+ HRESULT PutBlob(ULONG ixTbl, ULONG ixCol, void *pRecord, const void *pvData, ULONG cbData);
+
+ __checkReturn
+ HRESULT PutUserString(MetaData::DataBlob data, UINT32 *pnIndex)
+ { return m_UserStringHeap.AddBlob(data, pnIndex); }
+
+ ULONG GetCol(ULONG ixTbl, ULONG ixCol, void *pRecord);
+ mdToken GetToken(ULONG ixTbl, ULONG ixCol, void *pRecord);
+
+ // Add a record to a table, and return a typed XXXRec *.
+// #undef AddTblRecord
+ #define AddTblRecord(tbl) \
+ __checkReturn HRESULT Add##tbl##Record(tbl##Rec **ppRow, RID *pnRowIndex) \
+ { return AddRecord(TBL_##tbl, reinterpret_cast<void **>(ppRow), pnRowIndex); }
+
+ AddTblRecord(Module)
+ AddTblRecord(TypeRef)
+ __checkReturn HRESULT AddTypeDefRecord( // Specialized implementation.
+ TypeDefRec **ppRow,
+ RID *pnRowIndex);
+ AddTblRecord(Field)
+ __checkReturn HRESULT AddMethodRecord( // Specialized implementation.
+ MethodRec **ppRow,
+ RID *pnRowIndex);
+ AddTblRecord(Param)
+ AddTblRecord(InterfaceImpl)
+ AddTblRecord(MemberRef)
+ AddTblRecord(Constant)
+ AddTblRecord(CustomAttribute)
+ AddTblRecord(FieldMarshal)
+ AddTblRecord(DeclSecurity)
+ AddTblRecord(ClassLayout)
+ AddTblRecord(FieldLayout)
+ AddTblRecord(StandAloneSig)
+ __checkReturn HRESULT AddEventMapRecord( // Specialized implementation.
+ EventMapRec **ppRow,
+ RID *pnRowIndex);
+ AddTblRecord(Event)
+ __checkReturn HRESULT AddPropertyMapRecord( // Specialized implementation.
+ PropertyMapRec **ppRow,
+ RID *pnRowIndex);
+ AddTblRecord(Property)
+ AddTblRecord(MethodSemantics)
+ AddTblRecord(MethodImpl)
+ AddTblRecord(ModuleRef)
+ AddTblRecord(FieldPtr)
+ AddTblRecord(MethodPtr)
+ AddTblRecord(ParamPtr)
+ AddTblRecord(PropertyPtr)
+ AddTblRecord(EventPtr)
+
+ AddTblRecord(ENCLog)
+ AddTblRecord(TypeSpec)
+ AddTblRecord(ImplMap)
+ AddTblRecord(ENCMap)
+ AddTblRecord(FieldRVA)
+
+ // Assembly Tables.
+ AddTblRecord(Assembly)
+ AddTblRecord(AssemblyProcessor)
+ AddTblRecord(AssemblyOS)
+ AddTblRecord(AssemblyRef)
+ AddTblRecord(AssemblyRefProcessor)
+ AddTblRecord(AssemblyRefOS)
+ AddTblRecord(File)
+ AddTblRecord(ExportedType)
+ AddTblRecord(ManifestResource)
+
+ AddTblRecord(NestedClass)
+ AddTblRecord(GenericParam)
+ AddTblRecord(MethodSpec)
+ AddTblRecord(GenericParamConstraint)
+
+ // Specialized AddXxxToYyy() functions.
+ __checkReturn HRESULT AddMethodToTypeDef(RID td, RID md);
+ __checkReturn HRESULT AddFieldToTypeDef(RID td, RID md);
+ __checkReturn HRESULT AddParamToMethod(RID md, RID pd);
+ __checkReturn HRESULT AddPropertyToPropertyMap(RID pmd, RID pd);
+ __checkReturn HRESULT AddEventToEventMap(ULONG emd, RID ed);
+
+ // does the MiniMdRW has the indirect tables, such as FieldPtr, MethodPtr
+ FORCEINLINE int HasIndirectTable(ULONG ix)
+ { if (g_PtrTableIxs[ix].m_ixtbl < TBL_COUNT) return GetCountRecs(g_PtrTableIxs[ix].m_ixtbl); return 0;}
+
+ FORCEINLINE int IsVsMapValid(ULONG ixTbl)
+ { _ASSERTE(ixTbl<TBL_COUNT); return (m_pVS[ixTbl] && m_pVS[ixTbl]->m_isMapValid); }
+
+ // translate index returned by getMethodListOfTypeDef to a rid into Method table
+ __checkReturn
+ FORCEINLINE HRESULT GetMethodRid(ULONG index, ULONG *pRid)
+ {
+ HRESULT hr;
+ if (HasIndirectTable(TBL_Method))
+ {
+ MethodPtrRec *pMethodPtrRecord;
+ IfFailGo(GetMethodPtrRecord(index, &pMethodPtrRecord));
+ *pRid = getMethodOfMethodPtr(pMethodPtrRecord);
+ }
+ else
+ {
+ *pRid = index;
+ }
+ return S_OK;
+ ErrExit:
+ *pRid = 0;
+ return hr;
+ }
+
+ // translate index returned by getFieldListOfTypeDef to a rid into Field table
+ __checkReturn
+ FORCEINLINE HRESULT GetFieldRid(ULONG index, ULONG *pRid)
+ {
+ HRESULT hr;
+ if (HasIndirectTable(TBL_Field))
+ {
+ FieldPtrRec *pFieldPtrRecord;
+ IfFailGo(GetFieldPtrRecord(index, &pFieldPtrRecord));
+ *pRid = getFieldOfFieldPtr(pFieldPtrRecord);
+ }
+ else
+ {
+ *pRid = index;
+ }
+ return S_OK;
+ ErrExit:
+ *pRid = 0;
+ return hr;
+ }
+
+ // translate index returned by getParamListOfMethod to a rid into Param table
+ __checkReturn
+ FORCEINLINE HRESULT GetParamRid(ULONG index, ULONG *pRid)
+ {
+ HRESULT hr;
+ if (HasIndirectTable(TBL_Param))
+ {
+ ParamPtrRec *pParamPtrRecord;
+ IfFailGo(GetParamPtrRecord(index, &pParamPtrRecord));
+ *pRid = getParamOfParamPtr(pParamPtrRecord);
+ }
+ else
+ {
+ *pRid = index;
+ }
+ return S_OK;
+ ErrExit:
+ *pRid = 0;
+ return hr;
+ }
+
+ // translate index returned by getEventListOfEventMap to a rid into Event table
+ __checkReturn
+ FORCEINLINE HRESULT GetEventRid(ULONG index, ULONG *pRid)
+ {
+ HRESULT hr;
+ if (HasIndirectTable(TBL_Event))
+ {
+ EventPtrRec *pEventPtrRecord;
+ IfFailGo(GetEventPtrRecord(index, &pEventPtrRecord));
+ *pRid = getEventOfEventPtr(pEventPtrRecord);
+ }
+ else
+ {
+ *pRid = index;
+ }
+ return S_OK;
+ ErrExit:
+ *pRid = 0;
+ return hr;
+ }
+
+ // translate index returned by getPropertyListOfPropertyMap to a rid into Property table
+ __checkReturn
+ FORCEINLINE HRESULT GetPropertyRid(ULONG index, ULONG *pRid)
+ {
+ HRESULT hr;
+ if (HasIndirectTable(TBL_Property))
+ {
+ PropertyPtrRec *pPropertyPtrRecord;
+ IfFailGo(GetPropertyPtrRecord(index, &pPropertyPtrRecord));
+ *pRid = getPropertyOfPropertyPtr(pPropertyPtrRecord);
+ }
+ else
+ {
+ *pRid = index;
+ }
+ return S_OK;
+ ErrExit:
+ *pRid = 0;
+ return hr;
+ }
+
+ // Convert a pseudo-RID from a Virtual Sort into a real RID.
+ FORCEINLINE ULONG GetRidFromVirtualSort(ULONG ixTbl, ULONG index)
+ { return IsVsMapValid(ixTbl) ? *(m_pVS[ixTbl]->m_pMap->Get(index)) : index; }
+
+ // Index returned by GetInterfaceImplForTypeDef. It could be index to VirtualSort table
+ // or directly to InterfaceImpl
+ FORCEINLINE ULONG GetInterfaceImplRid(ULONG index)
+ { return GetRidFromVirtualSort(TBL_InterfaceImpl, index); }
+
+ // Index returned by GetGenericParamForToken. It could be index to VirtualSort table
+ // or directly to GenericParam
+ FORCEINLINE ULONG GetGenericParamRid(ULONG index)
+ { return GetRidFromVirtualSort(TBL_GenericParam, index); }
+
+ // Index returned by GetGenericParamConstraintForToken. It could be index to VirtualSort table
+ // or directly to GenericParamConstraint
+ FORCEINLINE ULONG GetGenericParamConstraintRid(ULONG index)
+ { return GetRidFromVirtualSort(TBL_GenericParamConstraint, index); }
+
+ // Index returned by GetDeclSecurityForToken. It could be index to VirtualSort table
+ // or directly to DeclSecurity
+ FORCEINLINE ULONG GetDeclSecurityRid(ULONG index)
+ { return GetRidFromVirtualSort(TBL_DeclSecurity, index); }
+
+ // Index returned by GetCustomAttributeForToken. It could be index to VirtualSort table
+ // or directly to CustomAttribute
+ FORCEINLINE ULONG GetCustomAttributeRid(ULONG index)
+ { return GetRidFromVirtualSort(TBL_CustomAttribute, index); }
+
+ // add method, field, property, event, param to the map table
+ __checkReturn HRESULT AddMethodToLookUpTable(mdMethodDef md, mdTypeDef td);
+ __checkReturn HRESULT AddFieldToLookUpTable(mdFieldDef fd, mdTypeDef td);
+ __checkReturn HRESULT AddPropertyToLookUpTable(mdProperty pr, mdTypeDef td);
+ __checkReturn HRESULT AddEventToLookUpTable(mdEvent ev, mdTypeDef td);
+ __checkReturn HRESULT AddParamToLookUpTable(mdParamDef pd, mdMethodDef md);
+
+ // look up the parent of method, field, property, event, or param
+ __checkReturn HRESULT FindParentOfMethodHelper(mdMethodDef md, mdTypeDef *ptd);
+ __checkReturn HRESULT FindParentOfFieldHelper(mdFieldDef fd, mdTypeDef *ptd);
+ __checkReturn HRESULT FindParentOfPropertyHelper(mdProperty pr, mdTypeDef *ptd);
+ __checkReturn HRESULT FindParentOfEventHelper(mdEvent ev, mdTypeDef *ptd);
+ __checkReturn HRESULT FindParentOfParamHelper(mdParamDef pd, mdMethodDef *pmd);
+
+ bool IsMemberDefHashPresent() { return m_pMemberDefHash != NULL; }
+
+ // Function to reorganize the string pool based on IBC profile data (if available) and static analysis.
+ // Throws on error.
+ VOID OrganizeStringPool(CorProfileData *pProfileData);
+
+ // Result of hash search
+ enum HashSearchResult
+ {
+ Found, // Item was found.
+ NotFound, // Item not found.
+ NoTable // Table hasn't been built.
+ };
+
+ // Create MemberRef hash table.
+ __checkReturn
+ HRESULT CreateMemberRefHash();
+
+ // Add a new MemberRef to the hash table.
+ __checkReturn
+ HRESULT AddMemberRefToHash( // Return code.
+ mdMemberRef mr); // Token of new guy.
+
+ // If the hash is built, search for the item. Ignore token *ptkMemberRef.
+ HashSearchResult 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.
+
+ //*************************************************************************
+ // Check a given mr token to see if this one is a match.
+ //*************************************************************************
+ __checkReturn
+ HRESULT 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.
+
+ // Add a new MemberDef to the hash table.
+ __checkReturn
+ HRESULT AddMemberDefToHash(
+ mdToken tkMember, // Token of new guy. It can be MethodDef or FieldDef
+ mdToken tkParent); // Parent token.
+
+ // Create MemberDef Hash
+ __checkReturn
+ HRESULT CreateMemberDefHash();
+
+ // If the hash is built, search for the item. Ignore token *ptkMember.
+ HashSearchResult 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 a given Method/Field token to see if this one is a match.
+ //*************************************************************************
+ __checkReturn
+ HRESULT 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.
+
+ //*************************************************************************
+ // Add a new CustomAttributes to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddCustomAttributesToHash( // Return code.
+ mdCustomAttribute cv) // Token of new guy.
+ { return GenericAddToHash(TBL_CustomAttribute, CustomAttributeRec::COL_Parent, RidFromToken(cv)); }
+
+ inline ULONG HashMemberRef(mdToken tkPar, LPCUTF8 szName)
+ {
+ ULONG l = HashBytes((const BYTE *) &tkPar, sizeof(mdToken)) + HashStringA(szName);
+ return (l);
+ }
+
+ inline ULONG HashMemberDef(mdToken tkPar, LPCUTF8 szName)
+ {
+ return HashMemberRef(tkPar, szName);
+ }
+
+ // helper to calculate the hash value given a token
+ inline ULONG HashCustomAttribute(mdToken tkObject)
+ {
+ return HashToken(tkObject);
+ }
+
+ CMemberRefHash *m_pMemberRefHash;
+
+ // Hash table for Methods and Fields
+ CMemberDefHash *m_pMemberDefHash;
+
+ // helper to calculate the hash value given a pair of tokens
+ inline ULONG HashToken(mdToken tkObject)
+ {
+ ULONG l = HashBytes((const BYTE *) &tkObject, sizeof(mdToken));
+ return (l);
+ }
+
+
+ //*************************************************************************
+ // Add a new FieldMarhsal Rid to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddFieldMarshalToHash( // Return code.
+ RID rid) // Token of new guy.
+ { return GenericAddToHash(TBL_FieldMarshal, FieldMarshalRec::COL_Parent, rid); }
+
+ //*************************************************************************
+ // Add a new Constant Rid to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddConstantToHash( // Return code.
+ RID rid) // Token of new guy.
+ { return GenericAddToHash(TBL_Constant, ConstantRec::COL_Parent, rid); }
+
+ //*************************************************************************
+ // Add a new MethodSemantics Rid to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddMethodSemanticsToHash( // Return code.
+ RID rid) // Token of new guy.
+ { return GenericAddToHash(TBL_MethodSemantics, MethodSemanticsRec::COL_Association, rid); }
+
+ //*************************************************************************
+ // Add a new ClassLayout Rid to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddClassLayoutToHash( // Return code.
+ RID rid) // Token of new guy.
+ { return GenericAddToHash(TBL_ClassLayout, ClassLayoutRec::COL_Parent, rid); }
+
+ //*************************************************************************
+ // Add a new FieldLayout Rid to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddFieldLayoutToHash( // Return code.
+ RID rid) // Token of new guy.
+ { return GenericAddToHash(TBL_FieldLayout, FieldLayoutRec::COL_Field, rid); }
+
+ //*************************************************************************
+ // Add a new ImplMap Rid to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddImplMapToHash( // Return code.
+ RID rid) // Token of new guy.
+ { return GenericAddToHash(TBL_ImplMap, ImplMapRec::COL_MemberForwarded, rid); }
+
+ //*************************************************************************
+ // Add a new FieldRVA Rid to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddFieldRVAToHash( // Return code.
+ RID rid) // Token of new guy.
+ { return GenericAddToHash(TBL_FieldRVA, FieldRVARec::COL_Field, rid); }
+
+ //*************************************************************************
+ // Add a new nested class Rid to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddNestedClassToHash( // Return code.
+ RID rid) // Token of new guy.
+ { return GenericAddToHash(TBL_NestedClass, NestedClassRec::COL_NestedClass, rid); }
+
+ //*************************************************************************
+ // Add a new MethodImpl Rid to the hash table.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddMethodImplToHash( // Return code.
+ RID rid) // Token of new guy.
+ { return GenericAddToHash(TBL_MethodImpl, MethodImplRec::COL_Class, rid); }
+
+
+ //*************************************************************************
+ // Build a hash table for the specified table if the size exceed the thresholds.
+ //*************************************************************************
+ __checkReturn
+ HRESULT GenericBuildHashTable( // Return code.
+ ULONG ixTbl, // Table with hash
+ ULONG ixCol); // col that we hash.
+
+ //*************************************************************************
+ // Add a rid from a table into a hash
+ //*************************************************************************
+ __checkReturn
+ HRESULT GenericAddToHash( // Return code.
+ ULONG ixTbl, // Table with hash
+ ULONG ixCol, // col that we hash.
+ RID rid); // new row of the table.
+
+ //*************************************************************************
+ // Add a rid from a table into a hash
+ //*************************************************************************
+ __checkReturn
+ HRESULT 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);
+
+
+ // look up hash table for tokenless tables.
+ // They are constant, FieldMarshal, MethodSemantics, ClassLayout, FieldLayout, ImplMap, FieldRVA, NestedClass, and MethodImpl
+ CLookUpHash * m_pLookUpHashs[TBL_COUNT];
+
+#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE)
+ MapSHash<UINT32, UINT32> m_StringPoolOffsetHash;
+#endif
+
+ //*************************************************************************
+ // Hash for named items.
+ //*************************************************************************
+ __checkReturn
+ HRESULT AddNamedItemToHash( // Return code.
+ 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.
+
+ HashSearchResult FindNamedItemFromHash(
+ ULONG ixTbl, // Table with the item.
+ LPCUTF8 szName, // Name of item.
+ mdToken tkParent, // Token of parent, if any.
+ mdToken * ptk); // Return if found.
+
+ __checkReturn
+ HRESULT 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.
+
+ FORCEINLINE ULONG HashNamedItem(mdToken tkPar, LPCUTF8 szName)
+ { return HashBytes((const BYTE *) &tkPar, sizeof(mdToken)) + HashStringA(szName); }
+
+ CMetaDataHashBase *m_pNamedItemHash;
+
+ //*****************************************************************************
+ // IMetaModelCommon - RW specific versions for some of the functions.
+ //*****************************************************************************
+ __checkReturn
+ virtual HRESULT CommonGetEnclosingClassOfTypeDef(
+ mdTypeDef td,
+ mdTypeDef *ptkEnclosingTypeDef)
+ {
+ _ASSERTE(ptkEnclosingTypeDef != NULL);
+
+ HRESULT hr;
+ NestedClassRec *pRec;
+ RID iRec;
+
+ IfFailRet(FindNestedClassHelper(td, &iRec));
+ if (iRec == 0)
+ {
+ *ptkEnclosingTypeDef = mdTypeDefNil;
+ return S_OK;
+ }
+
+ IfFailRet(GetNestedClassRecord(iRec, &pRec));
+ *ptkEnclosingTypeDef = getEnclosingClassOfNestedClass(pRec);
+ return S_OK;
+ }
+
+ __checkReturn
+ HRESULT CommonEnumCustomAttributeByName( // S_OK or error.
+ 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
+
+ __checkReturn
+ HRESULT 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.
+
+ //*****************************************************************************
+ // Find helper for a constant.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindConstantHelper( // return index to the constant table
+ mdToken tkParent, // Parent token. Can be ParamDef, FieldDef, or Property.
+ RID *pFoundRid);
+
+ //*****************************************************************************
+ // Find helper for a FieldMarshal.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindFieldMarshalHelper( // return index to the field marshal table
+ mdToken tkParent, // Parent token. Can be a FieldDef or ParamDef.
+ RID *pFoundRid);
+
+ //*****************************************************************************
+ // Find helper for a method semantics.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindMethodSemanticsHelper( // return HRESULT
+ mdToken tkAssociate, // Event or property token
+ HENUMInternal *phEnum); // fill in the enum
+
+ //*****************************************************************************
+ // 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 FindAssociateHelper(// return HRESULT
+ 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
+
+ //*****************************************************************************
+ // Find helper for a MethodImpl.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindMethodImplHelper(// return HRESULT
+ mdTypeDef td, // TypeDef token for the Class.
+ HENUMInternal *phEnum); // fill in the enum
+
+ //*****************************************************************************
+ // Find helper for a GenericParams
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindGenericParamHelper( // Return HRESULT
+ mdToken tkOwner, // Token for the GenericParams' owner
+ HENUMInternal *phEnum); // Fill in the enum.
+
+ //*****************************************************************************
+ // Find helper for a Generic Constraints
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindGenericParamConstraintHelper( // Return HRESULT
+ mdGenericParam tkParam, // Token for the GenericParam
+ HENUMInternal *phEnum); // Fill in the enum.
+
+ //*****************************************************************************
+ // Find helper for a ClassLayout.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindClassLayoutHelper( // return index to the ClassLayout table
+ mdTypeDef tkParent, // Parent token.
+ RID *pFoundRid);
+
+ //*****************************************************************************
+ // Find helper for a FieldLayout.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindFieldLayoutHelper( // return index to the FieldLayout table
+ mdFieldDef tkField, // Token for the field.
+ RID *pFoundRid);
+
+ //*****************************************************************************
+ // Find helper for a ImplMap.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindImplMapHelper( // return index to the constant table
+ mdToken tk, // Member forwarded token.
+ RID *pFoundRid);
+
+ //*****************************************************************************
+ // Find helper for a FieldRVA.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindFieldRVAHelper( // return index to the FieldRVA table
+ mdFieldDef tkField, // Token for the field.
+ RID *pFoundRid);
+
+ //*****************************************************************************
+ // Find helper for a NestedClass.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT FindNestedClassHelper( // return index to the NestedClass table
+ mdTypeDef tkClass, // Token for the NestedClass.
+ RID *pFoundRid);
+
+ //*****************************************************************************
+ // IMPORTANT!!!!!!!! Use these set of functions if you are dealing with RW rather
+ // getInterfaceImplsForTypeDef, getDeclSecurityForToken, etc.
+ // The following functions can deal with these tables when they are not sorted and
+ // build the VirtualSort tables for quick lookup.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT GetInterfaceImplsForTypeDef(mdTypeDef td, RID *pRidStart, RID *pRidEnd = 0)
+ {
+ return LookUpTableByCol( RidFromToken(td), m_pVS[TBL_InterfaceImpl], pRidStart, pRidEnd);
+ }
+
+ __checkReturn
+ HRESULT GetGenericParamsForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0)
+ {
+ return LookUpTableByCol(
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)),
+ m_pVS[TBL_GenericParam], pRidStart, pRidEnd);
+ }
+
+ __checkReturn
+ HRESULT GetGenericParamConstraintsForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0)
+ {
+ return LookUpTableByCol( RidFromToken(tk),
+ m_pVS[TBL_GenericParamConstraint], pRidStart, pRidEnd);
+ }
+
+ __checkReturn
+ HRESULT GetMethodSpecsForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0)
+ {
+ return LookUpTableByCol(
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtMethodDefOrRef, lengthof(mdtMethodDefOrRef)),
+ m_pVS[TBL_MethodSpec], pRidStart, pRidEnd);
+ }
+
+ __checkReturn
+ HRESULT GetDeclSecurityForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0)
+ {
+ return LookUpTableByCol(
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasDeclSecurity, lengthof(mdtHasDeclSecurity)),
+ m_pVS[TBL_DeclSecurity],
+ pRidStart,
+ pRidEnd);
+ }
+
+ __checkReturn
+ HRESULT GetCustomAttributeForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0)
+ {
+ return LookUpTableByCol(
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasCustomAttribute, lengthof(mdtHasCustomAttribute)),
+ m_pVS[TBL_CustomAttribute],
+ pRidStart,
+ pRidEnd);
+ }
+
+ __checkReturn
+ FORCEINLINE HRESULT GetUserString(ULONG nIndex, MetaData::DataBlob *pData)
+ { return m_UserStringHeap.GetBlob(nIndex, pData); }
+ // 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 GetUserStringAndNextIndex(
+ UINT32 nIndex,
+ MetaData::DataBlob *pData,
+ UINT32 *pnNextIndex);
+
+ FORCEINLINE int IsSorted(ULONG ixTbl) { return m_Schema.IsSorted(ixTbl);}
+ FORCEINLINE int IsSortable(ULONG ixTbl) { return m_bSortable[ixTbl];}
+ FORCEINLINE bool HasDelete() { return ((m_Schema.m_heaps & CMiniMdSchema::HAS_DELETE) ? true : false); }
+ FORCEINLINE int IsPreSaveDone() { return m_bPreSaveDone; }
+
+protected:
+ __checkReturn HRESULT PreSave(MetaDataReorderingOptions reorderingOptions=NoReordering, CorProfileData *pProfileData=NULL);
+ __checkReturn HRESULT PostSave();
+
+ __checkReturn HRESULT PreSaveFull();
+ __checkReturn HRESULT PreSaveEnc();
+
+ __checkReturn HRESULT GetFullPoolSaveSize(int iPool, UINT32 *pcbSize);
+ __checkReturn HRESULT GetENCPoolSaveSize(int iPool, UINT32 *pcbSize);
+
+ __checkReturn HRESULT SaveFullPoolToStream(int iPool, IStream *pIStream);
+ __checkReturn HRESULT SaveENCPoolToStream(int iPool, IStream *pIStream);
+
+ __checkReturn
+ HRESULT GetHotMetadataTokensSearchAware(
+ CorProfileData *pProfileData,
+ ULONG ixTbl,
+ ULONG *pResultCount,
+ mdToken *tokenBuffer,
+ ULONG maxCount);
+
+ __checkReturn
+ HRESULT GetFullSaveSize(
+ CorSaveSize fSave,
+ UINT32 *pcbSize,
+ DWORD *pbCompressed,
+ MetaDataReorderingOptions reorderingOptions = NoReordering,
+ CorProfileData *pProfileData = NULL);
+ __checkReturn
+ HRESULT GetENCSaveSize(UINT32 *pcbSize);
+ __checkReturn
+ HRESULT GetHotPoolsSaveSize(
+ UINT32 *pcbSize,
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData);
+
+ __checkReturn
+ HRESULT SaveFullTablesToStream(IStream *pIStream, MetaDataReorderingOptions reorderingOptions=NoReordering, CorProfileData *pProfileData = NULL );
+ __checkReturn
+ HRESULT SaveENCTablesToStream(IStream *pIStream);
+ __checkReturn
+ HRESULT SaveHotPoolsToStream(
+ IStream *pStream,
+ MetaDataReorderingOptions reorderingOptions,
+ CorProfileData *pProfileData,
+ UINT32 *pnPoolDirSize,
+ UINT32 *pnSavedPoolsSize);
+ __checkReturn
+ HRESULT SaveHotPoolToStream(
+ IStream *pStream,
+ CorProfileData *pProfileData,
+ MetaData::HotHeapWriter *pHotHeapWriter,
+ UINT32 *pnSavedSize);
+
+ // TO ELIMINATE:
+ __checkReturn
+ HRESULT AddGuid(REFGUID pGuid, UINT32 *pnIndex)
+ { return m_GuidHeap.AddGuid(&pGuid, pnIndex); }
+
+ // Allows putting into tables outside this MiniMd, specifically the temporary
+ // table used on save.
+ __checkReturn
+ HRESULT PutCol(CMiniColDef ColDef, void *pRecord, ULONG uVal);
+
+ // Returns TRUE if token (tk) is valid.
+ // For user strings, consideres 0 as valid token.
+ BOOL _IsValidToken(
+ mdToken tk) // [IN] token to be checked
+ {
+ if (TypeFromToken(tk) == mdtString)
+ {
+ // need to check the user string heap
+ return m_UserStringHeap.IsValidIndex(RidFromToken(tk));
+ }
+ // Base type doesn't know about user string blob (yet)
+ return _IsValidTokenBase(tk);
+ } // CMiniMdRW::_IsValidToken
+
+#ifdef _DEBUG
+ bool CanHaveCustomAttribute(ULONG ixTbl);
+#endif
+
+ __checkReturn
+ HRESULT ExpandTables();
+ __checkReturn
+ HRESULT ExpandTableColumns(CMiniMdSchema &Schema, ULONG ixTbl);
+
+ __checkReturn
+ HRESULT InitWithLargeTables();
+
+ void ComputeGrowLimits(int bSmall=TRUE); // Set max, lim, based on param.
+ ULONG m_maxRid; // Highest RID so far allocated.
+ ULONG m_limRid; // Limit on RID before growing.
+ ULONG m_maxIx; // Highest pool index so far.
+ ULONG m_limIx; // Limit on pool index before growing.
+ enum {eg_ok, eg_grow, eg_grown} m_eGrow; // Is a grow required? done?
+ #define AUTO_GROW_CODED_TOKEN_PADDING 5
+
+ // fix up these tables after PreSave has move the tokens
+ __checkReturn HRESULT FixUpTable(ULONG ixTbl);
+ __checkReturn HRESULT FixUpRefToDef();
+
+ // Table info.
+ MetaData::TableRW m_Tables[TBL_COUNT];
+ VirtualSort *m_pVS[TBL_COUNT]; // Virtual sorters, one per table, but sparse.
+
+ //*****************************************************************************
+ // look up a table by a col given col value is ulVal.
+ //*****************************************************************************
+ __checkReturn
+ HRESULT LookUpTableByCol(
+ ULONG ulVal,
+ VirtualSort *pVSTable,
+ RID *pRidStart,
+ RID *pRidEnd);
+
+ __checkReturn
+ HRESULT Impl_SearchTableRW(ULONG ixTbl, ULONG ixCol, ULONG ulTarget, RID *pFoundRid);
+ __checkReturn
+ virtual HRESULT vSearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ulTarget, RID *pRid);
+ __checkReturn
+ virtual HRESULT vSearchTableNotGreater(ULONG ixTbl, CMiniColDef sColumn, ULONG ulTarget, RID *pRid);
+
+ void SetSorted(ULONG ixTbl, int bSorted)
+ { m_Schema.SetSorted(ixTbl, bSorted); }
+
+ void SetPreSaveDone(int bPreSaveDone)
+ { m_bPreSaveDone = bPreSaveDone; }
+
+ // Heaps
+ MetaData::StringHeapRW m_StringHeap;
+ MetaData::BlobHeapRW m_BlobHeap;
+ MetaData::BlobHeapRW m_UserStringHeap;
+ MetaData::GuidHeapRW m_GuidHeap;
+
+ IMapToken *m_pHandler; // Remap handler.
+ __checkReturn HRESULT MapToken(RID from, RID to, mdToken type);
+
+ ULONG m_cbSaveSize; // Estimate of save size.
+
+ int m_fIsReadOnly : 1; // Is this db read-only?
+ int m_bPreSaveDone : 1; // Has save optimization been done?
+ int m_bSaveCompressed : 1; // Can the data be saved as fully compressed?
+ int m_bPostGSSMod : 1; // true if a change was made post GetSaveSize.
+
+
+ //*************************************************************************
+ // Overridables -- must be provided in derived classes.
+ __checkReturn
+ FORCEINLINE HRESULT Impl_GetString(UINT32 nIndex, __out LPCSTR *pszString)
+ { return m_StringHeap.GetString(nIndex, pszString); }
+ __checkReturn
+ HRESULT Impl_GetStringW(ULONG ix, __inout_ecount (cchBuffer) LPWSTR szOut, ULONG cchBuffer, ULONG *pcchBuffer);
+ __checkReturn
+ FORCEINLINE HRESULT Impl_GetGuid(UINT32 nIndex, GUID *pTargetGuid)
+ {
+ HRESULT hr;
+ GUID UNALIGNED *pSourceGuid;
+ IfFailRet(m_GuidHeap.GetGuid(
+ nIndex,
+ &pSourceGuid));
+ // Add void* casts so that the compiler can't make assumptions about alignment.
+ CopyMemory((void *)pTargetGuid, (void *)pSourceGuid, sizeof(GUID));
+ SwapGuid(pTargetGuid);
+ return S_OK;
+ }
+
+ __checkReturn
+ FORCEINLINE HRESULT Impl_GetBlob(ULONG nIndex, __out MetaData::DataBlob *pData)
+ { return m_BlobHeap.GetBlob(nIndex, pData); }
+
+ __checkReturn
+ FORCEINLINE HRESULT Impl_GetRow(
+ UINT32 nTableIndex,
+ UINT32 nRowIndex,
+ __deref_out_opt BYTE **ppRecord)
+ {
+ _ASSERTE(nTableIndex < TBL_COUNT);
+ return m_Tables[nTableIndex].GetRecord(nRowIndex, ppRecord);
+ }
+
+ // Count of rows in tbl2, pointed to by the column in tbl.
+ __checkReturn
+ HRESULT Impl_GetEndRidForColumn(
+ UINT32 nTableIndex,
+ RID nRowIndex,
+ CMiniColDef &def, // Column containing the RID into other table.
+ UINT32 nTargetTableIndex, // The other table.
+ RID *pEndRid);
+
+ __checkReturn
+ FORCEINLINE HRESULT Impl_SearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ixCol, ULONG ulTarget, RID *pFoundRid)
+ { return Impl_SearchTableRW(ixTbl, ixCol, ulTarget, pFoundRid); }
+
+ FORCEINLINE int Impl_IsRo()
+ { return 0; }
+
+
+ //*************************************************************************
+ enum {END_OF_TABLE = 0};
+ FORCEINLINE ULONG NewRecordPointerEndValue(ULONG ixTbl)
+ { if (HasIndirectTable(ixTbl)) return m_Schema.m_cRecs[ixTbl]+1; else return END_OF_TABLE; }
+
+ __checkReturn HRESULT ConvertMarkerToEndOfTable(ULONG tblParent, ULONG colParent, ULONG ridChild, RID ridParent);
+
+ // Add a child row, adjust pointers in parent rows.
+ __checkReturn
+ HRESULT AddChildRowIndirectForParent(
+ ULONG tblParent,
+ ULONG colParent,
+ ULONG tblChild,
+ RID ridParent,
+ void **ppRow);
+
+ // Update pointers in the parent table to reflect the addition of a child, if required
+ // create the indirect table in which case don't update pointers.
+ __checkReturn
+ HRESULT AddChildRowDirectForParent(ULONG tblParent, ULONG colParent, ULONG tblChild, RID ridParent);
+
+ // Given a table id, create the corresponding indirect table.
+ __checkReturn
+ HRESULT CreateIndirectTable(ULONG ixtbl, BOOL bOneLess = true);
+
+ // If the last param is not added in the right sequence, fix it up.
+ __checkReturn
+ HRESULT FixParamSequence(RID md);
+
+
+ // these are the map tables to map a method, a field, a property, a event, or a param to its parent
+ TOKENMAP *m_pMethodMap;
+ TOKENMAP *m_pFieldMap;
+ TOKENMAP *m_pPropertyMap;
+ TOKENMAP *m_pEventMap;
+ TOKENMAP *m_pParamMap;
+
+ // This table keep tracks tokens that are marked( or filtered)
+ FilterTable *m_pFilterTable;
+ IHostFilter *m_pHostFilter;
+
+ // TOKENMAP *m_pTypeRefToTypeDefMap;
+ TokenRemapManager *m_pTokenRemapManager;
+
+ OptionValue m_OptionValue;
+
+ CMiniMdSchema m_StartupSchema; // Schema at start time. Keep count of records.
+ BYTE m_bSortable[TBL_COUNT]; // Is a given table sortable? (Can it be reorganized?)
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ ReleaseHolder<IMDCustomDataSource> m_pCustomDataSource;
+#endif
+
+#ifdef _DEBUG
+
+protected:
+ UTSemReadWrite * dbg_m_pLock;
+
+public:
+ // Checks that MetaData is locked for write operation (if thread-safety is enabled and the lock exists)
+ void Debug_CheckIsLockedForWrite();
+
+ void Debug_SetLock(UTSemReadWrite * pLock)
+ {
+ dbg_m_pLock = pLock;
+ }
+
+#endif //_DEBUG
+
+public:
+
+ FilterTable *GetFilterTable();
+ __checkReturn HRESULT UnmarkAll();
+ __checkReturn HRESULT MarkAll();
+
+ FORCEINLINE IHostFilter *GetHostFilter() { return m_pHostFilter;}
+
+ __checkReturn HRESULT CalculateTypeRefToTypeDefMap();
+
+ FORCEINLINE TOKENMAP *GetTypeRefToTypeDefMap()
+ { return m_pTokenRemapManager ? m_pTokenRemapManager->GetTypeRefToTypeDefMap() : NULL; };
+
+ FORCEINLINE TOKENMAP *GetMemberRefToMemberDefMap()
+ { return m_pTokenRemapManager ? m_pTokenRemapManager->GetMemberRefToMemberDefMap() : NULL; };
+
+ FORCEINLINE MDTOKENMAP *GetTokenMovementMap()
+ { return m_pTokenRemapManager ? m_pTokenRemapManager->GetTokenMovementMap() : NULL; };
+
+ FORCEINLINE TokenRemapManager *GetTokenRemapManager() { return m_pTokenRemapManager; };
+
+ __checkReturn HRESULT InitTokenRemapManager();
+
+ virtual ULONG vGetCol(ULONG ixTbl, ULONG ixCol, void *pRecord)
+ { return GetCol(ixTbl, ixCol, pRecord);}
+
+public:
+ virtual BOOL IsWritable()
+ {
+ return !m_fIsReadOnly;
+ }
+
+
+ //*************************************************************************
+ // Delta MetaData (EditAndContinue) functions.
+public:
+ enum eDeltaFuncs{
+ eDeltaFuncDefault = 0,
+ eDeltaMethodCreate,
+ eDeltaFieldCreate,
+ eDeltaParamCreate,
+ eDeltaPropertyCreate,
+ eDeltaEventCreate,
+ };
+
+ __checkReturn HRESULT ApplyDelta(CMiniMdRW &mdDelta);
+
+public:
+ // Functions for updating ENC log tables ENC log.
+ FORCEINLINE BOOL IsENCOn()
+ {
+ return (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC;
+ }
+
+ __checkReturn
+ FORCEINLINE HRESULT UpdateENCLog(mdToken tk, CMiniMdRW::eDeltaFuncs funccode = CMiniMdRW::eDeltaFuncDefault)
+ {
+ if (IsENCOn())
+ return UpdateENCLogHelper(tk, funccode);
+ else
+ return S_OK;
+ }
+
+ __checkReturn
+ FORCEINLINE HRESULT UpdateENCLog2(ULONG ixTbl, ULONG iRid, CMiniMdRW::eDeltaFuncs funccode = CMiniMdRW::eDeltaFuncDefault)
+ {
+ if (IsENCOn())
+ return UpdateENCLogHelper2(ixTbl, iRid, funccode);
+ else
+ return S_OK;
+ }
+
+ __checkReturn HRESULT ResetENCLog();
+
+private:
+ BOOL m_fMinimalDelta;
+
+ //
+ // String heap reorganization
+ //
+
+ // Check to see if it is safe to reorder the string pool.
+ BOOL IsSafeToReorderStringPool();
+ // Function to mark hot strings in the marks array based on the token information in profile data.
+ VOID MarkHotStrings(CorProfileData *pProfileData, BYTE * pMarks, ULONG poolSize);
+ // Function to mark hot strings referenced by hot tables based on token information in profile data.
+ VOID MarkStringsInHotTables(CorProfileData *pProfileData, BYTE * pMarks, ULONG poolSize);
+ // Function to mark strings referenced by the different metadata tables.
+ VOID MarkStringsInTables(BYTE * pMarks, ULONG poolSize);
+ // Function to mark duplicate strings in the mark array.
+ // Throws on error.
+ VOID MarkDuplicateStrings(BYTE * pMarks, ULONG poolSize);
+ // Function to update the tables with the modified string offsets.
+ VOID FixStringsInTables();
+ // Function to fill the given string pool with strings from the existing string pool using the mark array.
+ // Throws on error.
+ VOID CreateReorderedStringPool(
+ MetaData::StringHeapRW *pStringHeap,
+ BYTE *pMarks,
+ ULONG cbHeapSize,
+ CorProfileData *pProfileData);
+
+public:
+ BOOL IsMinimalDelta()
+ {
+ return m_fMinimalDelta;
+ }
+
+
+ // Turns on/off the ability to emit delta metadatas
+
+ // Unfortunately, we can't allow this to be set via the SetOption method anymore. In v1.0 and v1.1, this flag
+ // could be set but would still result in generating full metadatas. We can't automatically start generating
+ // true deltas for people... it could break them.
+ void EnableDeltaMetadataGeneration()
+ {
+ _ASSERTE(m_OptionValue.m_UpdateMode == MDUpdateENC);
+#ifndef FEATURE_CORECLR
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_UseMinimalDeltas))
+ m_OptionValue.m_UpdateMode = MDUpdateDelta;
+#endif //!FEATURE_CORECLR
+ }
+ void DisableDeltaMetadataGeneration() {m_OptionValue.m_UpdateMode = MDUpdateENC;}
+
+protected:
+ // Internal Helper functions for ENC log.
+ __checkReturn
+ HRESULT UpdateENCLogHelper(mdToken tk, CMiniMdRW::eDeltaFuncs funccode);
+ __checkReturn
+ HRESULT UpdateENCLogHelper2(ULONG ixTbl, ULONG iRid, CMiniMdRW::eDeltaFuncs funccode);
+
+protected:
+ static ULONG m_TruncatedEncTables[];
+ static ULONG m_SuppressedDeltaColumns[TBL_COUNT];
+
+ ULONGARRAY *m_rENCRecs; // Array of RIDs affected by ENC.
+
+ __checkReturn
+ HRESULT ApplyRecordDelta(CMiniMdRW &mdDelta, ULONG ixTbl, void *pDelta, void *pRecord);
+ __checkReturn
+ HRESULT ApplyTableDelta(CMiniMdRW &mdDelta, ULONG ixTbl, RID iRid, int fc);
+ __checkReturn
+ HRESULT GetDeltaRecord(ULONG ixTbl, ULONG iRid, void **ppRecord);
+ __checkReturn
+ HRESULT ApplyHeapDeltas(CMiniMdRW &mdDelta);
+ __checkReturn
+ HRESULT ApplyHeapDeltasWithMinimalDelta(CMiniMdRW &mdDelta);
+ __checkReturn
+ HRESULT ApplyHeapDeltasWithFullDelta(CMiniMdRW &mdDelta);
+ __checkReturn
+ HRESULT StartENCMap(); // Call, on a delta MD, to prepare to access sparse rows.
+ __checkReturn
+ HRESULT EndENCMap(); // Call, on a delta MD, when done with sparse rows.
+
+public:
+ // Workaround for compiler performance issue VSW 584653 for 2.0 RTM.
+ // Get the table's VirtualSort validity state.
+ bool IsTableVirtualSorted(ULONG ixTbl);
+ // 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 ValidateVirtualSortAfterAddRecord(
+ ULONG ixTbl,
+ bool * pfIsTableVirtualSortValid);
+
+}; // class CMiniMdRW : public CMiniMdTemplate<CMiniMdRW>
+
+#endif // _METAMODELRW_H_
diff --git a/src/md/inc/recordpool.h b/src/md/inc/recordpool.h
new file mode 100644
index 0000000000..36bd67a656
--- /dev/null
+++ b/src/md/inc/recordpool.h
@@ -0,0 +1,161 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// RecordPool.h -- header file for record heaps.
+//
+
+//
+//*****************************************************************************
+#ifndef _RECORDPOOL_H_
+#define _RECORDPOOL_H_
+
+#if _MSC_VER >= 1100
+#pragma once
+#endif
+
+#include <stgpool.h>
+
+//*****************************************************************************
+// This Record pool class collects user Records into a big consecutive heap.
+// The list of Records is kept in memory while adding, and
+// finally flushed to a stream at the caller's request.
+//*****************************************************************************
+class RecordPool : public StgPool
+{
+ friend class VerifyLayoutsMD;
+
+ using StgPool::InitNew;
+ using StgPool::InitOnMem;
+
+public:
+ RecordPool() :
+ StgPool(1024, 1)
+ { }
+
+//*****************************************************************************
+// Init the pool for use. This is called for the create empty case.
+//*****************************************************************************
+ __checkReturn
+ HRESULT InitNew(
+ UINT32 cbRec, // Record size.
+ UINT32 cRecsInit); // Initial guess of count of record.
+
+//*****************************************************************************
+// Load a Record heap from persisted memory. If a copy of the data is made
+// (so that it may be updated), then a new hash table is generated which can
+// be used to elminate duplicates with new Records.
+//*****************************************************************************
+ __checkReturn
+ HRESULT InitOnMem(
+ ULONG cbRec, // Record size.
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ BOOL fReadOnly); // true if append is forbidden.
+
+//*****************************************************************************
+// Allocate memory if we don't have any, or grow what we have. If successful,
+// then at least iRequired bytes will be allocated.
+//*****************************************************************************
+ bool Grow( // true if successful.
+ ULONG iRequired); // Min required bytes to allocate.
+
+//*****************************************************************************
+// The Record will be added to the pool. The index of the Record in the pool
+// is returned in *piIndex. If the Record is already in the pool, then the
+// index will be to the existing copy of the Record.
+//*****************************************************************************
+ HRESULT AddRecord(
+ BYTE **ppRecord,
+ UINT32 *pnIndex); // Return 1-based index of Record here.
+
+//*****************************************************************************
+// Insert a Record into the pool. The index of the Record before which to
+// insert is specified. Shifts all records down. Return a pointer to the
+// new record.
+//*****************************************************************************
+ HRESULT InsertRecord(
+ UINT32 nIndex, // [IN] Insert record before this.
+ BYTE **ppRecord);
+
+//*****************************************************************************
+// Return a pointer to a Record given an index previously handed out by
+// AddRecord or FindRecord.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetRecord(
+ UINT32 nIndex, // 1-based index of Record in pool.
+ BYTE **ppRecord);
+
+//*****************************************************************************
+// Given a pointer to a record, determine the index corresponding to the
+// record.
+//*****************************************************************************
+ virtual ULONG GetIndexForRecord( // 1-based index of Record in pool.
+ const void *pRecord); // Pointer to Record in pool.
+
+//*****************************************************************************
+// Given a purported pointer to a record, determine if the pointer is valid.
+//*****************************************************************************
+ virtual int IsValidPointerForRecord( // true or false.
+ const void *pRecord); // Pointer to Record in pool.
+
+//*****************************************************************************
+// How many objects are there in the pool? If the count is 0, you don't need
+// to persist anything at all to disk.
+//*****************************************************************************
+ UINT32 Count()
+ { return GetNextOffset() / m_cbRec; }
+
+//*****************************************************************************
+// Indicate if heap is empty. This has to be based on the size of the data
+// we are keeping. If you open in r/o mode on memory, there is no hash
+// table.
+//*****************************************************************************
+ virtual int IsEmpty() // true if empty.
+ { return (GetNextOffset() == 0); }
+
+//*****************************************************************************
+// Is the index valid for the Record?
+//*****************************************************************************
+ virtual int IsValidCookie(ULONG ulCookie)
+ { return (ulCookie == 0 || IsValidOffset((ulCookie-1) * m_cbRec)); }
+
+//*****************************************************************************
+// Return the size of the heap.
+//*****************************************************************************
+ ULONG GetNextIndex()
+ { return (GetNextOffset() / m_cbRec); }
+
+//*****************************************************************************
+// Replace the contents of this pool with those from another pool. The other
+// pool loses ownership of the memory.
+//*****************************************************************************
+ __checkReturn
+ HRESULT ReplaceContents(
+ RecordPool *pOther); // The other record pool.
+
+//*****************************************************************************
+// Return the first record in a pool, and set up a context for fast
+// iterating through the pool. Note that this scheme does pretty minimal
+// error checking.
+//*****************************************************************************
+ void *GetFirstRecord( // Pointer to Record in pool.
+ void **pContext); // Store context here.
+
+//*****************************************************************************
+// Given a pointer to a record, return a pointer to the next record.
+// Note that this scheme does pretty minimal error checking. In particular,
+// this will let the caller walk off of the end of valid data in the last
+// segment.
+//*****************************************************************************
+ void *GetNextRecord( // Pointer to Record in pool.
+ void *pRecord, // Current record.
+ void **pContext); // Stored context here.
+
+private:
+ UINT32 m_cbRec; // How large is each record?
+
+}; // class RecordPool
+
+#endif // _RECORDPOOL_H_
diff --git a/src/md/inc/rwutil.h b/src/md/inc/rwutil.h
new file mode 100644
index 0000000000..5d7f98919c
--- /dev/null
+++ b/src/md/inc/rwutil.h
@@ -0,0 +1,369 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// RWUtil.h
+//
+
+//
+// Contains utility code for MD directory
+//
+//*****************************************************************************
+#ifndef __RWUtil__h__
+#define __RWUtil__h__
+
+class UTSemReadWrite;
+
+#define UTF8STR(wszInput, szOutput) \
+ do { \
+ if ((wszInput) == NULL) \
+ { \
+ (szOutput) = NULL; \
+ } \
+ else \
+ { \
+ int cbBuffer = ((int)wcslen(wszInput) * 3) + 1; \
+ (szOutput) = (char *)_alloca(cbBuffer); \
+ Unicode2UTF((wszInput), (szOutput), cbBuffer); \
+ } \
+ } while (0)
+
+//*****************************************************************************
+// 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.
+
+//*********************************************************************
+// The token remap record.
+//*********************************************************************
+struct TOKENREC
+{
+ mdToken m_tkFrom; // The imported token
+ bool m_isDuplicate; // Is record duplicate? This information is recorded during merge
+ bool m_isDeleted; // This information is recorded during RegMeta::ProcessFilter when we might have deleted a record
+ bool m_isFoundInImport; // This information is also recorded during RegMeta::ProcessFilter
+ mdToken m_tkTo; // The new token in the merged scope
+
+ void SetEmpty() {m_tkFrom = m_tkTo = (mdToken) -1;}
+ BOOL IsEmpty() {return m_tkFrom == (mdToken) -1;}
+};
+
+
+//*********************************************************************
+//
+// This structure keeps track on token remap for an imported scope. This map is initially sorted by from
+// tokens. It can then become sorted by To tokens. This usually happen during PreSave remap lookup. Thus
+// we assert if we try to look up or sort by From token.
+//
+//*********************************************************************
+class MDTOKENMAP : public CDynArray<TOKENREC>
+{
+public:
+
+ enum SortKind{
+ Unsorted = 0,
+ SortByFromToken = 1,
+ SortByToToken = 2,
+ Indexed = 3, // Indexed by table/rid. Implies that strings are sorted by "From".
+ };
+
+ MDTOKENMAP()
+ : m_pNextMap(NULL),
+ m_pMap(NULL),
+ m_iCountTotal(0),
+ m_iCountSorted(0),
+ m_sortKind(SortByFromToken),
+ m_iCountIndexed(0)
+#if defined(_DEBUG)
+ ,m_pImport(0)
+#endif
+ { }
+ ~MDTOKENMAP();
+
+ HRESULT Init(IUnknown *pImport);
+
+ // find a token in the tokenmap.
+ bool Find(mdToken tkFrom, TOKENREC **ppRec);
+
+ // remap a token. We assert if we don't find the tkFind in the table
+ HRESULT Remap(mdToken tkFrom, mdToken *ptkTo);
+
+ // Insert a record. This function will keep the inserted record in a sorted sequence
+ HRESULT InsertNotFound(mdToken tkFrom, bool fDuplicate, mdToken tkTo, TOKENREC **ppRec);
+
+ // This function will just append the record to the end of the list
+ HRESULT AppendRecord(
+ mdToken tkFrom,
+ bool fDuplicate,
+ mdToken tkTo,
+ TOKENREC **ppRec);
+
+ // This is a safe remap. *tpkTo will be tkFind if we cannot find tkFind in the lookup table.
+ mdToken SafeRemap(mdToken tkFrom); // [IN] the token value to find
+
+ bool FindWithToToken(
+ mdToken tkFind, // [IN] the token value to find
+ int *piPosition); // [OUT] return the first from-token that has the matching to-token
+
+ FORCEINLINE void SortTokensByFromToken()
+ {
+ _ASSERTE(m_sortKind == SortByFromToken || m_sortKind == Indexed);
+ // Only sort if there are unsorted records.
+ if (m_iCountSorted < m_iCountTotal)
+ {
+ SortRangeFromToken(m_iCountIndexed, m_iCountIndexed+m_iCountTotal - 1);
+ m_iCountSorted = m_iCountTotal;
+ }
+ } // void MDTOKENMAP::SortTokensByFromToken()
+
+ HRESULT EmptyMap();
+
+ void SortTokensByToToken();
+
+ MDTOKENMAP *m_pNextMap;
+ IMapToken *m_pMap;
+
+private:
+ FORCEINLINE int CompareFromToken( // -1, 0, or 1
+ int iLeft, // First item to compare.
+ int iRight) // Second item to compare.
+ {
+ if ( Get(iLeft)->m_tkFrom < Get(iRight)->m_tkFrom )
+ return -1;
+ if ( Get(iLeft)->m_tkFrom == Get(iRight)->m_tkFrom )
+ return 0;
+ return 1;
+ }
+
+ FORCEINLINE int CompareToToken( // -1, 0, or 1
+ int iLeft, // First item to compare.
+ int iRight) // Second item to compare.
+ {
+ if ( Get(iLeft)->m_tkTo < Get(iRight)->m_tkTo )
+ return -1;
+ if ( Get(iLeft)->m_tkTo == Get(iRight)->m_tkTo )
+ return 0;
+ return 1;
+ }
+
+ FORCEINLINE void Swap(
+ int iFirst,
+ int iSecond)
+ {
+ if ( iFirst == iSecond ) return;
+ memcpy( &m_buf, Get(iFirst), sizeof(TOKENREC) );
+ memcpy( Get(iFirst), Get(iSecond),sizeof(TOKENREC) );
+ memcpy( Get(iSecond), &m_buf, sizeof(TOKENREC) );
+ }
+
+ void SortRangeFromToken(int iLeft, int iRight);
+ void SortRangeToToken(int iLeft, int iRight);
+
+ TOKENREC m_buf;
+ ULONG m_iCountTotal; // total entry in the map
+ ULONG m_iCountSorted; // number of entries that are sorted
+
+ SortKind m_sortKind;
+
+ ULONG m_TableOffset[TBL_COUNT+1]; // Start of each table in map.
+ ULONG m_iCountIndexed; // number of entries that are indexed.
+#if defined(_DEBUG)
+ IMetaDataImport *m_pImport; // For data validation.
+#endif
+};
+
+
+
+//*********************************************************************
+//
+// Merge Token manager. This class is created in GetSaveSize as an agent to
+// notify linker regarding token movements. It does not have the ability to
+// keep track token movement.
+//
+//*********************************************************************
+class MergeTokenManager : public IMapToken
+{
+public:
+ STDMETHODIMP QueryInterface(REFIID riid, PVOID *pp);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+ STDMETHODIMP Map(mdToken tkImp, mdToken tkEmit);
+ MergeTokenManager(MDTOKENMAP *pTkMapList, IUnknown *pHandler);
+ virtual ~MergeTokenManager();
+private:
+ LONG m_cRef;
+ MDTOKENMAP *m_pTkMapList;
+ IMapToken *m_pDefaultHostRemap;
+};
+
+
+
+//*********************************************************************
+//
+// This CMapToken class implemented the IMapToken. It is used in RegMeta for
+// filter process. This class can track all of the tokens are mapped. It also
+// supplies a Find function.
+//
+//*********************************************************************
+class CMapToken : public IMapToken
+{
+ friend class RegMeta;
+
+public:
+ STDMETHODIMP QueryInterface(REFIID riid, PVOID *pp);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+ STDMETHODIMP Map(mdToken tkImp, mdToken tkEmit);
+ bool Find(mdToken tkFrom, TOKENREC **pRecTo);
+ CMapToken();
+ virtual ~CMapToken();
+ MDTOKENMAP *m_pTKMap;
+private:
+ LONG m_cRef;
+ bool m_isSorted;
+};
+
+typedef CDynArray<mdToken> TOKENMAP;
+
+//*********************************************************************
+//
+// This class records all sorts of token movement during optimization phase.
+// This including Ref to Def optimization. This also includes token movement
+// due to sorting or eleminating the pointer tables.
+//
+//*********************************************************************
+class TokenRemapManager
+{
+public:
+ //*********************************************************************
+ //
+ // This function is called when a TypeRef is resolved to a TypeDef.
+ //
+ //*********************************************************************
+ FORCEINLINE void RecordTypeRefToTypeDefOptimization(
+ mdToken tkFrom,
+ mdToken tkTo)
+ {
+ _ASSERTE( TypeFromToken(tkFrom) == mdtTypeRef );
+ _ASSERTE( TypeFromToken(tkTo) == mdtTypeDef );
+
+ m_TypeRefToTypeDefMap[RidFromToken(tkFrom)] = tkTo;
+ } // RecordTypeRefToTypeDefOptimization
+
+
+ //*********************************************************************
+ //
+ // This function is called when a MemberRef is resolved to a MethodDef or FieldDef.
+ //
+ //*********************************************************************
+ FORCEINLINE void RecordMemberRefToMemberDefOptimization(
+ mdToken tkFrom,
+ mdToken tkTo)
+ {
+ _ASSERTE( TypeFromToken(tkFrom) == mdtMemberRef );
+ _ASSERTE( TypeFromToken(tkTo) == mdtMethodDef || TypeFromToken(tkTo) == mdtFieldDef);
+
+ m_MemberRefToMemberDefMap[RidFromToken(tkFrom)] = tkTo;
+ } // RecordMemberRefToMemberDefOptimization
+
+ //*********************************************************************
+ //
+ // This function is called when the token kind does not change but token
+ // is moved. For example, when we sort CustomAttribute table or when we optimize
+ // away MethodPtr table. These operation will not change the token type.
+ //
+ //*********************************************************************
+ FORCEINLINE HRESULT RecordTokenMovement(
+ mdToken tkFrom,
+ mdToken tkTo)
+ {
+ TOKENREC *pTokenRec;
+
+ _ASSERTE( TypeFromToken(tkFrom) == TypeFromToken(tkTo) );
+ return m_TKMap.AppendRecord( tkFrom, false, tkTo, &pTokenRec );
+ } // RecordTokenMovement
+
+ bool ResolveRefToDef(
+ mdToken tkRef, // [IN] ref token
+ mdToken *ptkDef); // [OUT] def token that it resolves to. If it does not resolve to a def
+
+ FORCEINLINE TOKENMAP *GetTypeRefToTypeDefMap() { return &m_TypeRefToTypeDefMap; }
+ FORCEINLINE TOKENMAP *GetMemberRefToMemberDefMap() { return &m_MemberRefToMemberDefMap; }
+ FORCEINLINE MDTOKENMAP *GetTokenMovementMap() { return &m_TKMap; }
+
+ ~TokenRemapManager();
+ HRESULT ClearAndEnsureCapacity(ULONG cTypeRef, ULONG cMemberRef);
+private:
+ MDTOKENMAP m_TKMap;
+ TOKENMAP m_TypeRefToTypeDefMap;
+ TOKENMAP m_MemberRefToMemberDefMap;
+}; // class TokenRemapManager
+
+// value that can be set by SetOption APIs
+struct OptionValue
+{
+ CorCheckDuplicatesFor m_DupCheck; // Bit Map for checking duplicates during emit.
+ CorRefToDefCheck m_RefToDefCheck; // Bit Map for specifying whether to do a ref to def optimization.
+ CorNotificationForTokenMovement m_NotifyRemap; // Bit Map for token remap notification.
+ ULONG m_UpdateMode; // (CorSetENC) Specifies whether ENC or Extension mode is on.
+ CorErrorIfEmitOutOfOrder m_ErrorIfEmitOutOfOrder; // Do not generate pointer tables
+ CorThreadSafetyOptions m_ThreadSafetyOptions; // specify if thread safety is turn on or not.
+ CorImportOptions m_ImportOption; // import options such as to skip over deleted items or not
+ CorLinkerOptions m_LinkerOption; // Linker option. Currently only used in UnmarkAll
+ BOOL m_GenerateTCEAdapters; // Do not generate the TCE adapters for COM CPC.
+ LPSTR m_RuntimeVersion; // CLR Version stamp
+ MetadataVersion m_MetadataVersion; // Version of the metadata to emit
+ MergeFlags m_MergeOptions; // Options to pass to the merger
+ UINT32 m_InitialSize; // Initial size of MetaData with values: code:CorMetaDataInitialSize.
+ CorLocalRefPreservation m_LocalRefPreservation; // Preserve module-local refs instead of optimizing them to defs
+}; // struct OptionValue
+
+//*********************************************************************
+//
+// Helper class to ensure calling UTSemReadWrite correctly.
+// The destructor will call the correct UnlockRead or UnlockWrite depends what lock it is holding.
+// User should use macro defined in below instead of calling functions on this class directly.
+// They are LOCKREAD(), LOCKWRITE(), and CONVERT_READ_TO_WRITE_LOCK.
+//
+//*********************************************************************
+class CMDSemReadWrite
+{
+public:
+ CMDSemReadWrite(UTSemReadWrite *pSem);
+ ~CMDSemReadWrite();
+ HRESULT LockRead();
+ HRESULT LockWrite();
+ void UnlockWrite();
+ HRESULT ConvertReadLockToWriteLock();
+private:
+ bool m_fLockedForRead;
+ bool m_fLockedForWrite;
+ UTSemReadWrite *m_pSem;
+};
+
+
+#define LOCKREADIFFAILRET() CMDSemReadWrite cSem(m_pSemReadWrite);\
+ IfFailRet(cSem.LockRead());
+#define LOCKWRITEIFFAILRET() CMDSemReadWrite cSem(m_pSemReadWrite);\
+ IfFailRet(cSem.LockWrite());
+
+#define LOCKREADNORET() CMDSemReadWrite cSem(m_pSemReadWrite);\
+ hr = cSem.LockRead();
+#define LOCKWRITENORET() CMDSemReadWrite cSem(m_pSemReadWrite);\
+ hr = cSem.LockWrite();
+
+#define LOCKREAD() CMDSemReadWrite cSem(m_pSemReadWrite);\
+ IfFailGo(cSem.LockRead());
+#define LOCKWRITE() CMDSemReadWrite cSem(m_pSemReadWrite);\
+ IfFailGo(cSem.LockWrite());
+
+#define UNLOCKWRITE() cSem.UnlockWrite();
+#define CONVERT_READ_TO_WRITE_LOCK() IfFailGo(cSem.ConvertReadLockToWriteLock());
+
+
+#endif // __RWUtil__h__
diff --git a/src/md/inc/stgio.h b/src/md/inc/stgio.h
new file mode 100644
index 0000000000..96a2f1dddb
--- /dev/null
+++ b/src/md/inc/stgio.h
@@ -0,0 +1,293 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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.
+//<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.
+//</TODO>
+//*****************************************************************************
+#ifndef __STGIO_H_
+#define __STGIO_H_
+
+#define MAXSHMEM 32
+
+#define STGIO_READ 0x1
+#define STGIO_WRITE 0x2
+
+enum DBPROPMODE
+ { DBPROP_TMODEF_READ = 0x1,
+ DBPROP_TMODEF_WRITE = 0x2,
+ DBPROP_TMODEF_EXCLUSIVE = 0x4,
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ // Shared memory uses ole32.dll - we cannot depend on it in the standalone WinRT Read-Only DLL
+ DBPROP_TMODEF_SHAREDMEM = 0x8,
+#endif
+ DBPROP_TMODEF_CREATE = 0x10,
+ DBPROP_TMODEF_FAILIFTHERE = 0x20,
+ DBPROP_TMODEF_SLOWSAVE = 0x100,
+ // Means it is OK to use LoadLibrary to map the file. Used by code:ofTrustedImage.
+ // We prefer that because it is shared with loader's image loading.
+ DBPROP_TMODEF_TRYLOADLIBRARY = 0x400,
+#if 0 // dead code
+ DBPROP_TMODEF_NOTXNBACKUPFILE = 0x200,
+ DBPROP_TMODEF_COMPLUS = 0x1000,
+ DBPROP_TMODEF_SMEMCREATE = 0x2000,
+ DBPROP_TMODEF_SMEMOPEN = 0x4000,
+ DBPROP_TMODEF_ALIGNBLOBS = 0x10000
+ DBPROP_TMODEF_RESERVED = 0x80000000,
+#endif
+ DBPROP_TMODEF_DFTWRITEMASK = 0x113,
+ DBPROP_TMODEF_DFTREADWRITEMASK = 0x103,
+ };
+
+
+// Types of IO we can handle.
+enum STGIOTYPE
+{
+ STGIO_NODATA = 0, // Currently not open.
+ STGIO_HFILE = 1, // File handle contains data.
+ STGIO_HMODULE = 2, // The file was loaded via LoadLibrary as module.
+ STGIO_STREAM = 3, // Stream pointer has data.
+ STGIO_MEM = 4, // In memory pointer has data.
+#ifndef FEATURE_METADATA_STANDALONE_WINRT_RO
+ // Shared memory uses ole32.dll - we cannot depend on it in the standalone WinRT Read-Only DLL
+ STGIO_SHAREDMEM = 5, // Shared memory handle.
+#endif
+ STGIO_HFILEMEM = 6 // Handle open, but memory allocated.
+};
+
+class StgIO
+{
+ friend class CLiteWeightStgdbRW; // for low-level access to data for metainfo and such.
+ friend class TiggerStorage;
+public:
+ StgIO(
+ bool bAutoMap=true); // Memory map for read on open?
+
+ ~StgIO();
+
+//*****************************************************************************
+// 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 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.
+
+//*****************************************************************************
+// Shut down the file handles and allocated objects.
+//*****************************************************************************
+ void Close();
+
+//*****************************************************************************
+// 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 Read( // Return code.
+ void *pbBuff, // Write buffer here.
+ ULONG cbBuff, // How much to read.
+ ULONG *pcbRead); // How much read.
+
+//*****************************************************************************
+// 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 Write( // Return code.
+ const void *pbBuff, // Buffer to write.
+ ULONG cbWrite, // How much.
+ ULONG *pcbWritten); // Return how much written.
+
+//*****************************************************************************
+// Moves the file pointer to the new location. This handles the different
+// types of storage systems.
+//*****************************************************************************
+ HRESULT Seek( // New offset.
+ int lVal, // How much to move.
+ ULONG fMoveType); // Direction, use Win32 FILE_xxxx.
+
+//*****************************************************************************
+// Retrieves the current offset for the storage being used. This value is
+// tracked based on Read, Write, and Seek operations.
+//*****************************************************************************
+ ULONG GetCurrentOffset(); // Current offset.
+
+//*****************************************************************************
+// 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 MapFileToMem( // Return code.
+ void *&ptr, // Return pointer to file data.
+ ULONG *pcbSize, // Return size of data.
+ LPSECURITY_ATTRIBUTES pAttributes=0); // Security token.
+
+//*****************************************************************************
+// Free the mapping object for shared memory but keep the rest of the internal
+// state intact.
+//*****************************************************************************
+ HRESULT ReleaseMappingObject(); // Return code.
+
+//*****************************************************************************
+// 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 SetBaseRange( // Return code.
+ void *pbStart, // Start of file data.
+ ULONG cbSize); // How big is the range.
+
+//*****************************************************************************
+// For read/write case, get a pointer to a chunk of the file at cbStart for
+// size cbSize. Return the pointer. This will page in parts of the file from
+// disk if not already loaded.
+//*****************************************************************************
+ HRESULT GetPtrForMem( // Return code.
+ ULONG cbStart, // Offset from beginning to load.
+ ULONG cbSize, // How much, rounded to page.
+ void *&ptr); // Return pointer on success.
+
+//*****************************************************************************
+// For cached writes, flush the cache to the data store.
+//*****************************************************************************
+ HRESULT FlushCache();
+
+//*****************************************************************************
+// 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 FlushFileBuffers();
+
+//*****************************************************************************
+// 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 ResetBackingStore(); // Return code.
+
+ FILETYPE GetFileType()
+ { return m_FileType; }
+
+ int IsReadOnly()
+ { return ((m_fFlags & STGIO_WRITE) == 0); }
+
+ ULONG GetFlags()
+ { return (m_fFlags); }
+
+ ULONG SetFlags(ULONG fFlags)
+ { m_fFlags = fFlags;
+ return (m_fFlags); }
+
+ ULONG GetDataSize()
+ { return (m_cbData); }
+
+ LONG AddRef()
+ {
+ return (++m_cRef);
+ }
+
+ LONG Release()
+ {
+ LONG cRef = --m_cRef;
+ if (cRef == 0)
+ delete this;
+ return (cRef);
+ }
+
+ int IsAlignedPtr(ULONG_PTR Value, int iAlignment);
+ MAPPINGTYPE GetMemoryMappedType()
+ { return m_mtMappedType;}
+
+
+//*****************************************************************************
+// Called to read the data into allocated memory and release the backing store.
+// Only available on read-only data.
+//*****************************************************************************
+ HRESULT LoadFileToMemory();
+
+
+private:
+ int IsBackingStore()
+ { return (m_rgPageMap != 0); }
+ int IsMemoryMapped()
+ { return ((m_hMapping != NULL) || (m_hModule != NULL)); }
+
+ void CtorInit();
+ HRESULT WriteToDisk(const void *pbBuff, ULONG cbWrite, ULONG *pcbWritten);
+ HRESULT ReadFromDisk(void *pbBuff, ULONG cbBuff, ULONG *pcbRead);
+ HRESULT CopyFileInternal(LPCWSTR szTo, int bFailIfThere, int bWriteThrough);
+ void FreePageMap();
+
+private:
+
+ // Flags and state data.
+ FILETYPE m_FileType; // Cached type of the file (based on extension).
+ LONG m_cRef; // Ref count on this object.
+ bool m_bWriteThrough : 1; // true for write through mode.
+ bool m_bRewrite : 1; // State check for rewrite mode.
+ bool m_bAutoMap : 1; // true to automatically memory map file.
+ bool m_bFreeMem : 1; // true to free allocated memory.
+
+ // Handles.
+ IStream * m_pIStream; // For save to stream instead of file.
+ HANDLE m_hFile; // The actual file with contents.
+ HANDLE m_hMapping; // Mapping handle.
+ HMODULE m_hModule; // If we load with LoadLibrary, this is the module (otherwise NULL).
+ void * m_pBaseData; // Base address for memory mapped file.
+ void * m_pData; // For memory mapped file read.
+ ULONG m_cbData; // Size of in memory data.
+ int m_fFlags; // Flags for open/create mode.
+ STGIOTYPE m_iType; // Where is the data.
+ MAPPINGTYPE m_mtMappedType; // How the file was memory mapped
+
+ // File cache information.
+ BYTE * m_rgBuff; // Cache buffer for writing.
+ ULONG m_cbBuff; // Current cache size.
+ ULONG m_cbOffset; // Current offset in file.
+
+ // Buffer read management.
+ static int m_iPageSize; // Size of an OS page.
+ static int m_iCacheSize; // How big a write back cache to use.
+ BYTE * m_rgPageMap; // Track loaded pages on read/write.
+
+}; // class StgIO
+
+#endif // __STGIO_H_
diff --git a/src/md/inc/stgtiggerstorage.h b/src/md/inc/stgtiggerstorage.h
new file mode 100644
index 0000000000..42407bab9f
--- /dev/null
+++ b/src/md/inc/stgtiggerstorage.h
@@ -0,0 +1,328 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// StgTiggerStorage.h
+//
+
+//
+// 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 exspensive.
+//
+// The storage itself is made up of a bunch of streams (each aligned to a 4 byte
+// value), followed at the end of the file with the header. The header is
+// put at the end so that you can continue to write as many streams as you
+// like without thrashing the disk.
+// +-------------------+
+// | Signature |
+// +-------------------+
+// | Stream 1, 2, [] |
+// +-------------------+
+// | STORAGEHEADER |
+// | Extra data |
+// | STORAGESTREAM[] |
+// +-------------------+
+// | offset |
+// +-------------------+
+//
+// The STORAGEHEADER contains flags describing the rest of the file, including
+// the ability to have extra data stored in the header. If there is extra
+// data, then immediately after the STORAGEHEADER struct is a 4 byte size of
+// that data, followed immediately by the extra data. The length must be
+// 4 byte aligned so that the first STORAGESTREAM starts on an aligned
+// boundary. The contents of the extra data is caller defined.
+//
+// This code handles the signature at the start of the file, and the list of
+// streams at the end (kept in the header). The data in each stream is, of
+// course, caller specific.
+//
+// This code requires the StgIO code to handle the input and output from the
+// backing storage, whatever scheme that may be. There are no consistency
+// checks on the data (for example crc's) due to the expense in computation
+// required. There is a signature at the front of the file and in the header.
+//
+//*****************************************************************************
+#ifndef __StgTiggerStorage_h__
+#define __StgTiggerStorage_h__
+
+//#include "utilcode.h" // Helpers.
+
+#include "mdfileformat.h"
+
+typedef CDynArray<STORAGESTREAM> STORAGESTREAMLST;
+
+
+// Forwards.
+class TiggerStream;
+class StgIO;
+
+
+
+class TiggerStorage :
+ public IStorage
+{
+friend class TiggerStream;
+public:
+ TiggerStorage();
+ virtual ~TiggerStorage();
+
+// IUnknown so you can ref count this thing.
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *pp)
+ { return (BadError(E_NOTIMPL)); }
+ virtual ULONG STDMETHODCALLTYPE AddRef()
+ { return (InterlockedIncrement(&m_cRef)); }
+ virtual ULONG STDMETHODCALLTYPE Release()
+ {
+ SUPPORTS_DAC_HOST_ONLY;
+ ULONG cRef;
+ if ((cRef = InterlockedDecrement(&m_cRef)) == 0)
+ delete this;
+ return (cRef);
+ }
+
+
+//*****************************************************************************
+// Init this storage object on top of the given storage unit.
+//*****************************************************************************
+ HRESULT Init( // Return code.
+ StgIO *pStgIO, // The I/O subsystem.
+ __in __in_z LPSTR pVersion); // Compiler-supplied CLR version
+
+//*****************************************************************************
+// 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.
+//*****************************************************************************
+ HRESULT GetExtraData( // Return code.
+ ULONG *pcbExtra, // Return size of extra data.
+ BYTE *&pbData); // Return a pointer to extra data.
+
+//*****************************************************************************
+// Flushes the header to disk.
+//*****************************************************************************
+ HRESULT WriteHeader( // Return code.
+ STORAGESTREAMLST *pList, // List of streams.
+ ULONG cbExtraData, // Size of extra data, may be 0.
+ BYTE *pbExtraData); // Pointer to extra data for header.
+
+//*****************************************************************************
+// Called when all data has been written. Forces cached data to be flushed
+// and stream lists to be validated.
+//*****************************************************************************
+ HRESULT WriteFinished( // Return code.
+ STORAGESTREAMLST *pList, // List of streams.
+ ULONG *pcbSaveSize, // Return size of total data.
+ BOOL fDeltaSave); // Was this a delta
+
+//*****************************************************************************
+// 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 ResetBackingStore(); // Return code.
+
+//*****************************************************************************
+// Called to restore the original file. If this operation is successful, then
+// the backup file is deleted as requested. The restore of the file is done
+// in write through mode to the disk help ensure the contents are not lost.
+// This is not good enough to fulfill ACID props, but it ain't that bad.
+//*****************************************************************************
+ HRESULT Restore( // Return code.
+ __in __in_z LPWSTR szBackup, // If non-0, backup the file.
+ int bDeleteOnSuccess); // Delete backup file if successful.
+
+//*****************************************************************************
+// 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.
+//*****************************************************************************
+ static HRESULT GetStreamSaveSize( // Return code.
+ LPCWSTR szStreamName, // Name of stream.
+ UINT32 cbDataSize, // Size of data to go into stream.
+ UINT32 *pcbSaveSize); // Return data size plus stream overhead.
+
+//*****************************************************************************
+// 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.
+//*****************************************************************************
+ static HRESULT GetStorageSaveSize( // Return code.
+ ULONG *pcbSaveSize, // [in] current size, [out] plus overhead.
+ ULONG cbExtra, // How much extra data to store in header.
+ LPCSTR pRuntimeVersion); // The version string as it's length is part of the total size.
+
+//*****************************************************************************
+// Adjust the offset in each known stream to match where it will wind up after
+// a save operation.
+//*****************************************************************************
+ static HRESULT 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.
+
+
+
+//*****************************************************************************
+// Returns the size of the signature plus the verion information
+//*****************************************************************************
+ static HRESULT SizeOfStorageSignature(
+ LPCSTR pRuntimeVersion, // The version string as it's length is part of the total size.
+ ULONG *pcbSignatureSize);
+
+// IStorage
+ virtual HRESULT STDMETHODCALLTYPE CreateStream(
+ const OLECHAR *pwcsName,
+ DWORD grfMode,
+ DWORD reserved1,
+ DWORD reserved2,
+ IStream **ppstm);
+
+ virtual HRESULT STDMETHODCALLTYPE CreateStream(
+ LPCSTR szName,
+ DWORD grfMode,
+ DWORD reserved1,
+ DWORD reserved2,
+ IStream **ppstm)
+ DAC_UNEXPECTED();
+
+ virtual HRESULT STDMETHODCALLTYPE OpenStream(
+ const OLECHAR *pwcsName,
+ void *reserved1,
+ DWORD grfMode,
+ DWORD reserved2,
+ IStream **ppstm);
+
+ virtual HRESULT STDMETHODCALLTYPE CreateStorage(
+ const OLECHAR *pwcsName,
+ DWORD grfMode,
+ DWORD dwStgFmt,
+ DWORD reserved2,
+ IStorage **ppstg);
+
+ virtual HRESULT STDMETHODCALLTYPE OpenStorage(
+ const OLECHAR * wcsName,
+ IStorage * pStgPriority,
+ DWORD dwMode,
+ __in
+ SNB snbExclude,
+ DWORD reserved,
+ IStorage ** ppStg);
+
+ virtual HRESULT STDMETHODCALLTYPE CopyTo(
+ DWORD cIidExclude,
+ const IID * rgIidExclude,
+ __in
+ SNB snbExclude,
+ IStorage * pStgDest);
+
+ virtual HRESULT STDMETHODCALLTYPE MoveElementTo(
+ const OLECHAR *pwcsName,
+ IStorage *pstgDest,
+ const OLECHAR *pwcsNewName,
+ DWORD grfFlags);
+
+ virtual HRESULT STDMETHODCALLTYPE Commit(
+ DWORD grfCommitFlags);
+
+ virtual HRESULT STDMETHODCALLTYPE Revert();
+
+ virtual HRESULT STDMETHODCALLTYPE EnumElements(
+ DWORD reserved1,
+ void *reserved2,
+ DWORD reserved3,
+ IEnumSTATSTG **ppenum);
+
+ virtual HRESULT STDMETHODCALLTYPE DestroyElement(
+ const OLECHAR *pwcsName);
+
+ virtual HRESULT STDMETHODCALLTYPE RenameElement(
+ const OLECHAR *pwcsOldName,
+ const OLECHAR *pwcsNewName);
+
+ virtual HRESULT STDMETHODCALLTYPE SetElementTimes(
+ const OLECHAR *pwcsName,
+ const FILETIME *pctime,
+ const FILETIME *patime,
+ const FILETIME *pmtime);
+
+ virtual HRESULT STDMETHODCALLTYPE SetClass(
+ REFCLSID clsid);
+
+ virtual HRESULT STDMETHODCALLTYPE SetStateBits(
+ DWORD grfStateBits,
+ DWORD grfMask);
+
+ virtual HRESULT STDMETHODCALLTYPE Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag);
+
+ virtual HRESULT STDMETHODCALLTYPE OpenStream(
+ LPCWSTR szStream,
+ ULONG *pcbData,
+ void **ppAddress);
+
+ // Access storage object.
+ StgIO *GetStgIO()
+ { return (m_pStgIO); }
+
+#if defined(_DEBUG)
+ ULONG PrintSizeInfo( // Size of streams.
+ bool verbose); // Be verbose?
+#endif
+
+protected:
+ HRESULT Write( // Return code.
+ 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.
+
+private:
+ HRESULT FindStream(LPCSTR szName, __out PSTORAGESTREAM *stream);
+ HRESULT WriteSignature(LPCSTR pVersion);
+ HRESULT VerifySignature(PSTORAGESIGNATURE pSig);
+ HRESULT ReadHeader();
+ HRESULT VerifyHeader();
+
+ static HRESULT GetDefaultVersion(LPCSTR* ppVersion);
+
+public:
+ // This function is a workaround to allow access to the "version requested" string.
+ HRESULT GetHeaderPointer(const void **ppv, ULONG *pcb);
+
+private:
+ // State data.
+ StgIO *m_pStgIO; // Storage subsystem.
+ LONG m_cRef; // Ref count for COM.
+
+ // Header data.
+ STORAGEHEADER m_StgHdr; // Header for storage.
+ STORAGESTREAMLST m_Streams; // List of streams in the storage.
+ PSTORAGESTREAM m_pStreamList; // For read mode.
+ void *m_pbExtra; // Pointer to extra data if on disk.
+};
+
+
+//*****************************************************************************
+// Debugging helpers. #define __SAVESIZE_TRACE__ to enable.
+//*****************************************************************************
+
+// #define __SAVESIZE_TRACE__
+#ifdef __SAVESIZE_TRACE__
+#define SAVETRACE(func) DEBUG_STMT(func)
+#else
+#define SAVETRACE(func)
+#endif // __SAVESIZE_TRACE__
+
+#endif // StgTiggerStorage
+
+
+
+// EOF
diff --git a/src/md/inc/stgtiggerstream.h b/src/md/inc/stgtiggerstream.h
new file mode 100644
index 0000000000..51a2f967de
--- /dev/null
+++ b/src/md/inc/stgtiggerstream.h
@@ -0,0 +1,112 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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.
+//
+//*****************************************************************************
+#ifndef __StgTiggerStream_h__
+#define __StgTiggerStream_h__
+
+
+
+#include "stgtiggerstorage.h" // Data definitions.
+
+enum
+{
+ STREAM_DATA_NAME
+};
+
+
+class TiggerStorage;
+
+
+class TiggerStream :
+ public IStream
+{
+public:
+ TiggerStream() :
+ m_pStorage(0),
+ m_cRef(1)
+ {}
+
+ virtual ~TiggerStream() {}
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *pp)
+ { return (BadError(E_NOTIMPL)); }
+ virtual ULONG STDMETHODCALLTYPE AddRef()
+ { return InterlockedIncrement(&m_cRef); }
+ virtual ULONG STDMETHODCALLTYPE Release()
+ {
+ ULONG cRef;
+ if ((cRef = InterlockedDecrement(&m_cRef)) == 0)
+ delete this;
+ return (cRef);
+ }
+
+// IStream
+ virtual HRESULT STDMETHODCALLTYPE Read(
+ void *pv,
+ ULONG cb,
+ ULONG *pcbRead);
+
+ virtual HRESULT STDMETHODCALLTYPE Write(
+ const void *pv,
+ ULONG cb,
+ ULONG *pcbWritten);
+
+ virtual HRESULT STDMETHODCALLTYPE Seek(
+ LARGE_INTEGER dlibMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER *plibNewPosition);
+
+ virtual HRESULT STDMETHODCALLTYPE SetSize(
+ ULARGE_INTEGER libNewSize);
+
+ virtual HRESULT STDMETHODCALLTYPE CopyTo(
+ IStream *pstm,
+ ULARGE_INTEGER cb,
+ ULARGE_INTEGER *pcbRead,
+ ULARGE_INTEGER *pcbWritten);
+
+ virtual HRESULT STDMETHODCALLTYPE Commit(
+ DWORD grfCommitFlags);
+
+ virtual HRESULT STDMETHODCALLTYPE Revert( void);
+
+ virtual HRESULT STDMETHODCALLTYPE LockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType);
+
+ virtual HRESULT STDMETHODCALLTYPE UnlockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType);
+
+ virtual HRESULT STDMETHODCALLTYPE Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag);
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(
+ IStream **ppstm);
+
+
+ HRESULT Init( // Return code.
+ TiggerStorage *pStorage, // Parent storage.
+ LPCSTR szStream); // Stream name.
+
+ ULONG GetStreamSize();
+
+private:
+ TiggerStorage *m_pStorage; // Our parent storage.
+ char m_rcStream[MAXSTREAMNAME]; // Name of the stream.
+ LONG m_cRef; // Ref count.
+};
+
+#endif // __StgTiggerStream_h__
diff --git a/src/md/inc/streamutil.h b/src/md/inc/streamutil.h
new file mode 100644
index 0000000000..89c493476f
--- /dev/null
+++ b/src/md/inc/streamutil.h
@@ -0,0 +1,221 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+#if !defined( __STREAMUTIL_H__ )
+#define __STREAMUTIL_H__
+
+namespace StreamUtil
+{
+
+// Write data to stream and advance the totalBytes counter
+//
+inline
+HRESULT WriteToStream( IStream * strm, void const * data, UINT32 sizeInBytes, UINT32 * totalBytes = NULL )
+{
+ HRESULT hr = strm->Write( data, sizeInBytes, NULL );
+ if ( SUCCEEDED( hr ) && totalBytes != NULL )
+ *totalBytes += sizeInBytes;
+ return hr;
+}
+
+
+// Write a POD to stream
+//
+template < typename T >
+HRESULT WritePODToStream( IStream * strm, T val, UINT32 * totalBytes )
+{
+ return WriteToStream( strm, & val, sizeof( val ), totalBytes );
+}
+
+
+// Write concrete data types to stream
+// Add additional overloads as needed
+//
+
+inline
+HRESULT WriteToStream( IStream * strm, int val, UINT32 * totalBytes = NULL )
+{
+ return WritePODToStream( strm, val, totalBytes );
+}
+
+
+inline
+HRESULT WriteToStream( IStream * strm, DWORD val, UINT32 * totalBytes = NULL )
+{
+ return WritePODToStream( strm, val, totalBytes );
+}
+
+
+inline
+HRESULT WriteToStream( IStream * strm, WORD val, UINT32 * totalBytes = NULL )
+{
+ return WritePODToStream( strm, val, totalBytes );
+}
+
+
+inline
+HRESULT WriteToStream( IStream * strm, BYTE val, UINT32 * totalBytes = NULL )
+{
+ return WritePODToStream( strm, val, totalBytes );
+}
+
+
+// Align to DWORD boundary
+//
+inline
+HRESULT AlignDWORD( IStream * strm, UINT32 * totalBytes )
+{
+ HRESULT hr = S_OK;
+
+ UINT32 aligned = *totalBytes + 3 & ~3;
+ if (aligned > *totalBytes)
+ { // The *totalBytes were not aligned to DWORD, we need to add padding
+ DWORD data = 0;
+ hr = WriteToStream( strm, & data, aligned - *totalBytes, totalBytes );
+ }
+ else if (aligned < *totalBytes)
+ { // We got an integer overflow in 'aligned' expression above
+ hr = COR_E_OVERFLOW;
+ }
+
+ return hr;
+}
+
+
+// Get stream position
+//
+inline
+HRESULT GetPos( IStream * strm, UINT32 * pos )
+{
+ LARGE_INTEGER temp = { {0} };
+ ULARGE_INTEGER ul_pos = { {0} };
+ HRESULT hr = strm->Seek( temp, STREAM_SEEK_CUR, & ul_pos );
+ * pos = ul_pos.u.LowPart;
+ return hr;
+}
+
+
+class NullStream : public IStream
+{
+public:
+ NullStream()
+ : m_pos( 0 )
+ {}
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ _ASSERTE( false );
+ return 0;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ SUPPORTS_DAC_HOST_ONLY;
+ _ASSERTE( false );
+ return 0;
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface( REFIID, PVOID* )
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead)
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Write(const void *pv, ULONG cb, ULONG *pcbWritten)
+ {
+ m_pos += cb;
+ if (pcbWritten != NULL)
+ {
+ *pcbWritten = cb;
+ }
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove,DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
+ {
+ if ( dwOrigin != STREAM_SEEK_CUR || dlibMove.QuadPart != 0 || plibNewPosition == NULL )
+ return E_NOTIMPL;
+
+ plibNewPosition->u.HighPart = 0;
+ plibNewPosition->u.LowPart = m_pos;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize)
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE CopyTo(
+ IStream *pstm,
+ ULARGE_INTEGER cb,
+ ULARGE_INTEGER *pcbRead,
+ ULARGE_INTEGER *pcbWritten)
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Commit(
+ DWORD grfCommitFlags)
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Revert()
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE LockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE UnlockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag)
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Clone(
+ IStream **ppstm)
+ {
+ _ASSERTE( false );
+ return E_NOTIMPL;
+ }
+
+private:
+ UINT32 m_pos;
+}; // class NullStream
+
+}; // namespace StreamUtil
+
+#endif
diff --git a/src/md/inc/verifylayouts.h b/src/md/inc/verifylayouts.h
new file mode 100644
index 0000000000..035b52d3ec
--- /dev/null
+++ b/src/md/inc/verifylayouts.h
@@ -0,0 +1,186 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// VerifyLayouts.h
+//
+
+//
+// Make sure that layouts of MD data strucutres doesn't change accidentally
+//
+//*****************************************************************************
+
+// The code in MD\DataSource\TargetTypes.* takes a direct dependency on
+// the layouts of types in MD. This is used by the debugger to read metadata
+// from a seperate process by deserializing the memory for these datastructures.
+//
+// You are probably reading this comment because you changed a layout and
+// one of the static_asserts failed during build. This is what you should
+// do to fix it:
+//
+// a) Go to clr\src\Debug\EE\Debugger.cpp and increment the global version counter
+// m_mdDataStructureVersion set in Debugger::Debugger()
+// Please comment the change there with a new entry in the version table
+//
+// b) If a define is conditionally changing the layout:
+// i) add, if needed, an entry to the list of define bits in
+// clr\src\Debug\EE\debugger.h Debugger::_Target_Defines
+// ii) add code like this that sets the bit in the Debugger::_defines static
+// variable
+// #ifdef MY_DEFINE
+// | DEFINE_MY_DEFINE
+// #endif
+//
+// c) Update the code in MD\DataSource\TargetTypes.h/cpp to deserialize your
+// new layout correctly. The code needs to work for any version of the layouts
+// with or without defines set. Your reader code can access the current version
+// and defines by calling:
+// reader.GetMDStructuresVersion()
+// reader.IsDefined(Define_XYZ)
+//
+// d) If your changes affect what a debugger should be reading in order to fetch
+// metadata then you probably need to change other parts of the debugger
+// implementation as well. In general the debugger cares about the schema,
+// TableDefs, storage signature, table records, and storage pools.
+//
+// e) AFTER you have fixed up the debugger stuff above, now its time to update
+// layout definitions so the static asserts will quiet down. Check out
+// the comments in VerifyLayouts.inc for how to do that.
+//
+// Thanks for helping us keep the debugger working :)
+//
+
+
+
+
+//-------------------------------------------------------------------------------
+// Type layout verification
+//
+//
+// These macros includes VerifyLayouts.inc a few times with different definitions to build up
+// the source. The final result should look something like this:
+// (don't assume specific type names/fields/offsets/sizes are accurate in this example)
+//
+//
+// class VerifyLayoutsMD
+// {
+//
+// static const expected_offset_of_first_field_in_CMiniMdRW = 208;
+// static const actual_offset_of_first_field_in_CMiniMdRW =
+// 208;
+// static const offset_of_field_after_CMiniMdRW_m_Schema =
+// 312;
+// static const offset_of_field_after_CMiniMdRW_m_Tables =
+// 316;
+// ... many more lines like this covering all fields in all marked up types ...
+//
+//
+// static const alignment_of_first_field_in_CMiniMdRW =
+// 4;
+// static const alignment_of_field_after_CMiniMdRW_m_Schema =
+// 8;
+// static const alignment_of_field_after_CMiniMdRW_m_Tables =
+// 8;
+// ... many more lines like this cover all fields in all marked up types ...
+//
+//
+// static_assert_no_msg(expected_offset_of_first_field_in_CMiniMdRW == actual_offset_of_first_field_in_CMiniMdRW);
+// static_assert_no_msg(offset_of_field_after_CMiniMdRW_m_Schema ==
+// ALIGN_UP(offsetof(CMiniMdRW, m_Schema) + 104, alignment_of_field_after_CMiniMdRW_m_Schema));
+// static_assert_no_msg(offset_of_field_after_CMiniMdRW_m_Tables ==
+// ALIGN_UP(offsetof(CMiniMdRW, m_Tables) + 4, alignment_of_field_after_CMiniMdRW_m_Tables));
+// ... many more lines like this cover all fields in all marked up types ...
+//
+// };
+//
+//
+//
+//
+
+#ifdef FEATURE_METADATA_VERIFY_LAYOUTS
+
+#include <stddef.h> // offsetof
+#include "static_assert.h"
+#include "metamodel.h"
+#include "WinMDInterfaces.h"
+#include "MDInternalRW.h"
+
+// other types provide friend access to this type so that the
+// offsetof macro can access their private fields
+class VerifyLayoutsMD
+{
+ // we have a bunch of arrays with this fixed size, make sure it doesn't change
+ static_assert_no_msg(TBL_COUNT == 45);
+
+
+
+#define IGNORE_COMMAS(...) __VA_ARGS__ // use this to surround templated types with commas in the name
+
+#define BEGIN_TYPE(typeName, initialFieldOffset) BEGIN_TYPE_ESCAPED(typeName, typeName, initialFieldOffset)
+#define FIELD(typeName, fieldName, fieldSize) ALIGN_FIELD_ESCAPED(typeName, typeName, fieldName, fieldSize, fieldSize)
+#define ALIGN_FIELD(typeName, fieldName, fieldSize, fieldAlign) ALIGN_FIELD_ESCAPED(typeName, typeName, fieldName, fieldSize, fieldAlign)
+#define FIELD_ESCAPED(typeName, escapedTypeName, fieldName, fieldSize) ALIGN_FIELD_ESCAPED(typeName, escapedTypeName, fieldName, fieldSize, fieldSize)
+#define END_TYPE(typeName, typeAlign) END_TYPE_ESCAPED(typeName, typeName, typeAlign)
+
+#define BEGIN_TYPE_ESCAPED(typeName, typeNameEscaped, initialFieldOffset) \
+ static const expected_offset_of_first_field_in_##typeNameEscaped## = initialFieldOffset; \
+ static const actual_offset_of_first_field_in_##typeNameEscaped## =
+
+#define ALIGN_FIELD_ESCAPED(typeName, typeNameEscaped, fieldName, fieldSize, fieldAlign) \
+ offsetof(IGNORE_COMMAS(typeName), fieldName); \
+ static const offset_of_field_after_##typeNameEscaped##_##fieldName =
+
+#define BITFIELD(typeName, fieldName, fieldOffset, fieldSize) \
+ fieldOffset; \
+ static const offset_of_field_after_##typeName##_##fieldName =
+
+#define END_TYPE_ESCAPED(typeName, typeNameEscaped, typeAlignentSize) \
+ sizeof(typeName);
+
+#include "VerifyLayouts.inc"
+
+#undef BEGIN_TYPE_ESCAPED
+#undef ALIGN_FIELD_ESCAPED
+#undef END_TYPE_ESCAPED
+#undef BITFIELD
+
+#define BEGIN_TYPE_ESCAPED(typeName, escapedTypeName, initialFieldOffset) \
+ static const alignment_of_first_field_in_##escapedTypeName =
+#define ALIGN_FIELD_ESCAPED(typeName, escapedTypeName, fieldName, fieldSize, fieldAlign) \
+ fieldAlign; \
+ static const alignment_of_field_after_##escapedTypeName##_##fieldName =
+#define BITFIELD(typeName, fieldName, fieldOffset, fieldSize) \
+ fieldSize; \
+ static const alignment_of_field_after_##typeName##_##fieldName =
+#define END_TYPE_ESCAPED(typeName, escapedTypeName, typeAlignmentSize) \
+ typeAlignmentSize;
+
+#include "VerifyLayouts.inc"
+
+#undef BEGIN_TYPE_ESCAPED
+#undef ALIGN_FIELD_ESCAPED
+#undef END_TYPE_ESCAPED
+#undef BITFIELD
+
+
+#define BEGIN_TYPE_ESCAPED(typeName, escapedTypeName, initialFieldOffset) \
+ static_assert_no_msg(expected_offset_of_first_field_in_##escapedTypeName == actual_offset_of_first_field_in_##escapedTypeName);
+
+
+#define ALIGN_UP(value, alignment) (((value) + (alignment) - 1)&~((alignment) - 1))
+#define ALIGN_FIELD_ESCAPED(typeName, escapedTypeName, fieldName, fieldSize, fieldAlign) \
+ static_assert_no_msg(offset_of_field_after_##escapedTypeName##_##fieldName == \
+ ALIGN_UP(offsetof(IGNORE_COMMAS(typeName), fieldName) + fieldSize, alignment_of_field_after_##escapedTypeName##_##fieldName));
+#define BITFIELD(typeName, fieldName, fieldOffset, fieldSize) \
+ static_assert_no_msg(offset_of_field_after_##typeName##_##fieldName == \
+ ALIGN_UP(fieldOffset + fieldSize, alignment_of_field_after_##typeName##_##fieldName));
+
+#define END_TYPE_ESCAPED(typeName, escapedTypeName, typeAlignmentSize)
+#include "VerifyLayouts.inc"
+
+};
+
+
+
+
+#endif //FEATURE_METADATA_VERIFY_LAYOUTS
diff --git a/src/md/inc/winmdinterfaces.h b/src/md/inc/winmdinterfaces.h
new file mode 100644
index 0000000000..4233e615d3
--- /dev/null
+++ b/src/md/inc/winmdinterfaces.h
@@ -0,0 +1,120 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+// =======================================================================================================
+// Defines the surface area between md\WinMD and md\everything-else.
+//
+// md\WinMD contains adapter importers that wrap RegMeta and MDInternalRO to make .winmd files look like
+// regular .NET assemblies.
+// =======================================================================================================
+
+#ifndef __WINMDINTERFACES_H__
+#define __WINMDINTERFACES_H__
+
+#include "metamodel.h"
+
+
+//-----------------------------------------------------------------------------------------------------
+// A common interface unifying RegMeta and MDInternalRO, giving the adapter a common interface to
+// access the raw metadata.
+//-----------------------------------------------------------------------------------------------------
+
+// {4F8EE8A3-24F8-4241-BC75-C8CAEC0255B5}
+EXTERN_GUID(IID_IMDCommon, 0x4f8ee8a3, 0x24f8, 0x4241, 0xbc, 0x75, 0xc8, 0xca, 0xec, 0x2, 0x55, 0xb5);
+
+#undef INTERFACE
+#define INTERFACE IID_IMDCommon
+DECLARE_INTERFACE_(IMDCommon, IUnknown)
+{
+ STDMETHOD_(IMetaModelCommon*, GetMetaModelCommon)() PURE;
+ STDMETHOD_(IMetaModelCommonRO*, GetMetaModelCommonRO)() PURE;
+ STDMETHOD(GetVersionString)(LPCSTR *pszVersionString) PURE;
+};
+
+
+//-----------------------------------------------------------------------------------------------------
+// Returns:
+// S_OK: if WinMD adapter should be used.
+// S_FALSE: if not
+//-----------------------------------------------------------------------------------------------------
+HRESULT CheckIfWinMDAdapterNeeded(IMDCommon *pRawMDCommon);
+
+
+
+//-----------------------------------------------------------------------------------------------------
+// Factory method that creates an WinMD adapter that implements:
+//
+// IMetaDataImport2
+// IMetaDataAssemblyImport
+// IMetaDataValidate
+// IMarshal
+// IMDCommon (subset)
+//
+// IMDCommon is included as a concession to the fact that certain IMetaDataEmit apis have
+// an (apparently undocumented) dependency on their importer arguments supporting this.
+//
+// You must provide a regular MD importer that implements:
+//
+// IMDCommon
+// IMetaDataImport2
+// IMetaDataAssemblyImport
+// IMetaDataValidate
+//
+// The underlying metadata file must follow these restrictions:
+//
+// - Have an existing assemblyRef to "mscorlib"
+//
+//-----------------------------------------------------------------------------------------------------
+HRESULT CreateWinMDImport(IMDCommon * pRawMDCommon, REFIID riid, /*[out]*/ void **ppWinMDImport);
+
+
+//-----------------------------------------------------------------------------------------------------
+// Factory method that creates an WinMD adapter that implements IMDInternalImport.
+// You must provide a regular MD importer that implements:
+//
+// IMDCommon
+// IMDInternalImport
+//
+// The underlying metadata file must follow these restrictions:
+//
+// - Have an existing assemblyRef to "mscorlib"
+//
+//-----------------------------------------------------------------------------------------------------
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+HRESULT CreateWinMDInternalImportRO(IMDCommon * pRawMDCommon, REFIID riid, /*[out]*/ void **ppWinMDInternalImport);
+
+#endif // FEATURE_METADATA_INTERNAL_APIS
+//-----------------------------------------------------------------------------------------------------
+// S_OK if pUnknown is really a WinMD wrapper. This is just a polite way of asking "is it bad to
+// to static cast pUnknown to RegMeta/MDInternalRO."
+//-----------------------------------------------------------------------------------------------------
+HRESULT CheckIfImportingWinMD(IUnknown *pUnknown);
+
+
+//-----------------------------------------------------------------------------------------------------
+// E_NOTIMPL if pUnknown is really a WinMD wrapper.
+//-----------------------------------------------------------------------------------------------------
+HRESULT VerifyNotWinMDHelper(IUnknown *pUnknown
+#ifdef _DEBUG
+ ,LPCSTR assertMsg
+ ,LPCSTR file
+ ,int line
+#endif //_DEBUG
+ );
+#if defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR)
+#ifdef _DEBUG
+#define VerifyNotWinMD(pUnknown, assertMsg) VerifyNotWinMDHelper(pUnknown, assertMsg, __FILE__, __LINE__)
+#else
+#define VerifyNotWinMD(pUnknown, assertMsg) VerifyNotWinMDHelper(pUnknown)
+#endif
+#else
+#define VerifyNotWinMD(pUnknown, assertMsg) S_OK
+#endif // FEATURE_METADATA_IN_VM
+
+
+#endif //__WINMDINTERFACES_H__
+
+
+
diff --git a/src/md/md_dac.cmake b/src/md/md_dac.cmake
new file mode 100644
index 0000000000..1820a555aa
--- /dev/null
+++ b/src/md/md_dac.cmake
@@ -0,0 +1,3 @@
+add_definitions(-DFEATURE_METADATA_EMIT)
+add_definitions(-DFEATURE_METADATA_INTERNAL_APIS)
+add_definitions(-DFEATURE_METADATA_EMIT_IN_DEBUGGER) \ No newline at end of file
diff --git a/src/md/md_dbi.cmake b/src/md/md_dbi.cmake
new file mode 100644
index 0000000000..7622cf04c9
--- /dev/null
+++ b/src/md/md_dbi.cmake
@@ -0,0 +1,17 @@
+add_definitions(-DFEATURE_METADATA_EMIT)
+add_definitions(-DFEATURE_METADATA_EMIT_IN_DEBUGGER)
+add_definitions(-DFEATURE_METADATA_INTERNAL_APIS)
+add_definitions(-DFEATURE_METADATA_CUSTOM_DATA_SOURCE)
+add_definitions(-DFEATURE_METADATA_DEBUGGEE_DATA_SOURCE)
+# Enable mscordbi-only (perf) feature -->
+add_definitions(-DFEATURE_METADATA_LOAD_TRUSTED_IMAGES -DFEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN)
+
+
+if(WIN32)
+ # using static crt for dbi
+ if (CMAKE_BUILD_TYPE STREQUAL DEBUG)
+ add_definitions(-MTd)
+ else()
+ add_definitions(-MT)
+ endif(CMAKE_BUILD_TYPE STREQUAL DEBUG)
+endif(WIN32)
diff --git a/src/md/md_wks.cmake b/src/md/md_wks.cmake
new file mode 100644
index 0000000000..ab9df6c667
--- /dev/null
+++ b/src/md/md_wks.cmake
@@ -0,0 +1,6 @@
+add_definitions(-DFEATURE_METADATA_EMIT)
+add_definitions(-DFEATURE_METADATA_INTERNAL_APIS)
+add_definitions(-DFEATURE_METADATA_IN_VM)
+if(WIN32)
+ add_definitions(-DFEATURE_METADATA_VERIFY_LAYOUTS)
+endif(WIN32) \ No newline at end of file
diff --git a/src/md/runtime/.gitmirror b/src/md/runtime/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/runtime/.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/runtime/CMakeLists.txt b/src/md/runtime/CMakeLists.txt
new file mode 100644
index 0000000000..96c9b5114a
--- /dev/null
+++ b/src/md/runtime/CMakeLists.txt
@@ -0,0 +1,23 @@
+add_definitions(-DNO_COR)
+
+set(MDRUNTIME_SOURCES
+ mdcolumndescriptors.cpp
+ liteweightstgdb.cpp
+ mdfileformat.cpp
+ metamodel.cpp
+ metamodelro.cpp
+ recordpool.cpp
+ mdinternaldisp.cpp
+ mdinternalro.cpp
+)
+
+convert_to_absolute_path(MDRUNTIME_SOURCES ${MDRUNTIME_SOURCES})
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+add_subdirectory(dac)
+add_subdirectory(wks)
+add_subdirectory(dbi)
+add_subdirectory(crossgen)
diff --git a/src/md/runtime/Runtime.settings.targets b/src/md/runtime/Runtime.settings.targets
new file mode 100644
index 0000000000..1ac62d7181
--- /dev/null
+++ b/src/md/runtime/Runtime.settings.targets
@@ -0,0 +1,43 @@
+<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>
+ <MDRuntimeSrcDirectory>$(ClrSrcDirectory)\MD\Runtime\</MDRuntimeSrcDirectory>
+ <UserIncludes>
+ $(UserIncludes);
+ $(ClrSrcDirectory)\MD\inc;
+ $(ClrSrcDirectory)\vm;
+ $(ClrSrcDirectory)\strongname\inc</UserIncludes>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE -DNO_COR</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>$(MDRuntimeSrcDirectory)\stdafx.cpp</PCHCompile>
+ <PCHObject>stdafx_mdruntime.obj</PCHObject>
+ <LinkUseCMT>false</LinkUseCMT>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="$(ClrSrcDirectory)inc\corguids.nativeproj">
+ <Comment>clrinternal.h</Comment>
+ </ProjectReference>
+ </ItemGroup>
+
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <CppCompile Include="$(MDRuntimeSrcDirectory)\MDColumnDescriptors.cpp" />
+ <CppCompile Include="$(MDRuntimeSrcDirectory)\LiteWeightStgdb.cpp" />
+ <CppCompile Include="$(MDRuntimeSrcDirectory)\MDFileFormat.cpp" />
+ <CppCompile Include="$(MDRuntimeSrcDirectory)\MetaModel.cpp" />
+ <CppCompile Include="$(MDRuntimeSrcDirectory)\MetaModelRO.cpp" />
+ <CppCompile Include="$(MDRuntimeSrcDirectory)\RecordPool.cpp" />
+
+ <!-- These sources contain internal Read-Only API implementation (IMDInternalRO) -->
+ <CppCompile Include="$(MDRuntimeSrcDirectory)\MDInternalDisp.cpp" />
+ <CppCompile Include="$(MDRuntimeSrcDirectory)\MDInternalRO.cpp" />
+ </ItemGroup>
+</Project>
diff --git a/src/md/runtime/crossgen/.gitmirror b/src/md/runtime/crossgen/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/runtime/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/runtime/crossgen/CMakeLists.txt b/src/md/runtime/crossgen/CMakeLists.txt
new file mode 100644
index 0000000000..dfd0f34665
--- /dev/null
+++ b/src/md/runtime/crossgen/CMakeLists.txt
@@ -0,0 +1,5 @@
+include(${CLR_DIR}/crossgen.cmake)
+include(../../md_wks.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDRUNTIME_SOURCES)
+add_library_clr(mdruntime_crossgen ${MDRUNTIME_SOURCES})
diff --git a/src/md/runtime/crossgen/MDRuntime_crossgen.nativeproj b/src/md/runtime/crossgen/MDRuntime_crossgen.nativeproj
new file mode 100644
index 0000000000..6b8cc00ffc
--- /dev/null
+++ b/src/md/runtime/crossgen/MDRuntime_crossgen.nativeproj
@@ -0,0 +1,15 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <PropertyGroup>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <!-- All features are set in file:..\..\MD.props -->
+ <MetadataFlavor>wks</MetadataFlavor>
+ <OutputName>mdruntime_crossgen</OutputName>
+ </PropertyGroup>
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\Runtime\Runtime.settings.targets" />
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/runtime/dac/.gitmirror b/src/md/runtime/dac/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/runtime/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/runtime/dac/CMakeLists.txt b/src/md/runtime/dac/CMakeLists.txt
new file mode 100644
index 0000000000..337968e2ed
--- /dev/null
+++ b/src/md/runtime/dac/CMakeLists.txt
@@ -0,0 +1,7 @@
+
+include(${CLR_DIR}/dac.cmake)
+include(../../md_dac.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDRUNTIME_SOURCES)
+
+add_library_clr(mdruntime_dac ${MDRUNTIME_SOURCES}) \ No newline at end of file
diff --git a/src/md/runtime/dac/dirs.proj b/src/md/runtime/dac/dirs.proj
new file mode 100644
index 0000000000..cc9e7a722c
--- /dev/null
+++ b/src/md/runtime/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" />
+
+ <!--The following projects will build during PHASE 1-->
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="HostLocal\mdruntime_dac.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/runtime/dbi/.gitmirror b/src/md/runtime/dbi/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/runtime/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/runtime/dbi/CMakeLists.txt b/src/md/runtime/dbi/CMakeLists.txt
new file mode 100644
index 0000000000..6f706d2bfb
--- /dev/null
+++ b/src/md/runtime/dbi/CMakeLists.txt
@@ -0,0 +1,3 @@
+include(../../md_dbi.cmake)
+add_precompiled_header(stdafx.h ../stdafx.cpp MDRUNTIME_SOURCES)
+add_library_clr(mdruntime-dbi ${MDRUNTIME_SOURCES}) \ No newline at end of file
diff --git a/src/md/runtime/dbi/MDRuntime-dbi.props b/src/md/runtime/dbi/MDRuntime-dbi.props
new file mode 100644
index 0000000000..ce3ff3224b
--- /dev/null
+++ b/src/md/runtime/dbi/MDRuntime-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>
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\Runtime\Runtime.settings.targets" />
+
+</Project>
diff --git a/src/md/runtime/dbi/dirs.proj b/src/md/runtime/dbi/dirs.proj
new file mode 100644
index 0000000000..0f2f9b0613
--- /dev/null
+++ b/src/md/runtime/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" />
+
+ <!--The following projects will build during PHASE 1-->
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Condition="'$(FeatureDbiDebugging)'=='true'" Include="HostLocal\mdruntime-dbi.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/runtime/dirs.proj b/src/md/runtime/dirs.proj
new file mode 100644
index 0000000000..2bd75ad013
--- /dev/null
+++ b/src/md/runtime/dirs.proj
@@ -0,0 +1,22 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <!--The following projects will build during PHASE 1-->
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="wks\mdruntime.nativeproj" />
+ <ProjectFile Include="dbi\dirs.proj" />
+ <ProjectFile Include="dac\dirs.proj" />
+ <ProjectFile Include="winrt\mdruntime-winrt.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/runtime/liteweightstgdb.cpp b/src/md/runtime/liteweightstgdb.cpp
new file mode 100644
index 0000000000..8e64ad16e6
--- /dev/null
+++ b/src/md/runtime/liteweightstgdb.cpp
@@ -0,0 +1,262 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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 "mdfileformat.h"
+#include "metamodelro.h"
+#include "liteweightstgdb.h"
+#include "metadatatracker.h"
+
+#include "../hotdata/export.h"
+
+__checkReturn
+HRESULT _CallInitOnMemHelper(CLiteWeightStgdb<CMiniMd> *pStgdb, ULONG cbData, LPCVOID pData)
+{
+ return pStgdb->InitOnMem(cbData,pData);
+}
+
+//*****************************************************************************
+// Open an in-memory metadata section for read
+//*****************************************************************************
+template <class MiniMd>
+__checkReturn
+HRESULT
+CLiteWeightStgdb<MiniMd>::InitOnMem(
+ ULONG cbData, // count of bytes in pData
+ LPCVOID pData) // points to meta data section in memory
+{
+ STORAGEHEADER sHdr; // Header for the storage.
+ PSTORAGESTREAM pStream; // Pointer to each stream.
+ int bFoundMd = false; // true when compressed data found.
+ int i; // Loop control.
+ HRESULT hr = S_OK;
+ ULONG cbStreamBuffer;
+
+ // Don't double open.
+ _ASSERTE((m_pvMd == NULL) && (m_cbMd == 0));
+
+ // Validate the signature of the format, or it isn't ours.
+ IfFailGo(MDFormat::VerifySignature((PSTORAGESIGNATURE)pData, cbData));
+
+#ifdef FEATURE_PREJIT
+ m_MiniMd.m_pHotTablesDirectory = NULL;
+#endif //FEATURE_PREJIT
+
+ // Remaining buffer size behind the stream header (pStream).
+ cbStreamBuffer = cbData;
+ // Get back the first stream.
+ pStream = MDFormat::GetFirstStream_Verify(&sHdr, pData, &cbStreamBuffer);
+ if (pStream == NULL)
+ {
+ Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Loop through each stream and pick off the ones we need.
+ for (i = 0; i < sHdr.GetiStreams(); i++)
+ {
+ // Do we have enough buffer to read stream header?
+ if (cbStreamBuffer < sizeof(*pStream))
+ {
+ Debug_ReportError("Stream header is not within MetaData block.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ // Pick off the location and size of the data.
+ if (pStream->GetOffset() >= cbData)
+ { // Stream data are not in the buffer. Stream header is corrupted.
+ Debug_ReportError("Stream data are not within MetaData block.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ void *pvCurrentData = (void *)((BYTE *)pData + pStream->GetOffset());
+ ULONG cbCurrentData = pStream->GetSize();
+
+ // Get next stream.
+ PSTORAGESTREAM pNext = pStream->NextStream_Verify();
+ if (pNext == NULL)
+ { // Stream header is corrupted.
+ Debug_ReportError("Invalid stream header - cannot get next stream header.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Range check
+ if ((LPBYTE)pNext > ((LPBYTE)pData + cbData))
+ {
+ Debug_ReportError("Stream header is not within MetaData block.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Stream end must fit into the buffer and we have to check integer overflow (stream start is already checked)
+ if ((((LPBYTE)pvCurrentData + cbCurrentData) < (LPBYTE)pvCurrentData) ||
+ (((LPBYTE)pvCurrentData + cbCurrentData) > ((LPBYTE)pData + cbData)))
+ {
+ Debug_ReportError("Stream data are not within MetaData block.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+
+ // String pool.
+ if (strcmp(pStream->GetName(), STRING_POOL_STREAM_A) == 0)
+ {
+ METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolStrings, pvCurrentData, cbCurrentData, 1));
+ // 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 (cbCurrentData != 0)
+ {
+ if (((LPBYTE)pvCurrentData)[cbCurrentData - 1] == 0)
+ { // We have found last null terminator
+ break;
+ }
+ // Shrink size of the pool
+ cbCurrentData--;
+ Debug_ReportError("String heap/pool does not end with null-terminator ... shrinking the heap.");
+ }
+ // Initialize string heap with null-terminated block of data
+ IfFailGo(m_MiniMd.m_StringHeap.Initialize(
+ MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData),
+ FALSE)); // fCopyData
+ }
+
+ // Literal String Blob pool.
+ else if (strcmp(pStream->GetName(), US_BLOB_POOL_STREAM_A) == 0)
+ {
+ METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolUSBlobs, pvCurrentData, cbCurrentData, 1));
+ // Initialize user string heap with block of data
+ IfFailGo(m_MiniMd.m_UserStringHeap.Initialize(
+ MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData),
+ FALSE)); // fCopyData
+ }
+
+ // GUID pool.
+ else if (strcmp(pStream->GetName(), GUID_POOL_STREAM_A) == 0)
+ {
+ METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolGuids, pvCurrentData, cbCurrentData, 1));
+ // Initialize guid heap with block of data
+ IfFailGo(m_MiniMd.m_GuidHeap.Initialize(
+ MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData),
+ FALSE)); // fCopyData
+ }
+
+ // Blob pool.
+ else if (strcmp(pStream->GetName(), BLOB_POOL_STREAM_A) == 0)
+ {
+ METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolBlobs, pvCurrentData, cbCurrentData, 1));
+ // Initialize blob heap with block of data
+ IfFailGo(m_MiniMd.m_BlobHeap.Initialize(
+ MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData),
+ FALSE)); // fCopyData
+ }
+
+ // Found the compressed meta data stream.
+ else if (strcmp(pStream->GetName(), COMPRESSED_MODEL_STREAM_A) == 0)
+ {
+ IfFailGo( m_MiniMd.InitOnMem(pvCurrentData, cbCurrentData) );
+ bFoundMd = true;
+ }
+
+ // Found the hot meta data stream
+ else if (strcmp(pStream->GetName(), HOT_MODEL_STREAM_A) == 0)
+ {
+#ifdef FEATURE_PREJIT
+ BYTE * hotStreamEnd = reinterpret_cast< BYTE * >( pvCurrentData ) + cbCurrentData;
+ ULONG * hotMetadataDir = reinterpret_cast< ULONG * >( hotStreamEnd ) - 2;
+ ULONG hotPoolsSize = *hotMetadataDir;
+
+ m_MiniMd.m_pHotTablesDirectory = (struct MetaData::HotTablesDirectory *)
+ (reinterpret_cast<BYTE *>(hotMetadataDir) - hotPoolsSize - sizeof(struct MetaData::HotTablesDirectory));
+ MetaData::HotTable::CheckTables(m_MiniMd.m_pHotTablesDirectory);
+
+ DataBuffer hotMetaData(
+ reinterpret_cast<BYTE *>(pvCurrentData),
+ cbCurrentData);
+ IfFailGo(InitHotPools(hotMetaData));
+#else //!FEATURE_PREJIT
+ Debug_ReportError("MetaData hot stream is peresent, but ngen is not supported.");
+ // Ignore the stream
+#endif //!FEATURE_PREJIT
+ }
+ // Pick off the next stream if there is one.
+ pStream = pNext;
+ cbStreamBuffer = (ULONG)((LPBYTE)pData + cbData - (LPBYTE)pNext);
+ }
+
+ // If the meta data wasn't found, we can't handle this file.
+ if (!bFoundMd)
+ {
+ Debug_ReportError("MetaData compressed model stream #~ not found.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ else
+ { // Validate sensible heaps.
+ IfFailGo(m_MiniMd.PostInit(0));
+ }
+
+ // Save off the location.
+ m_pvMd = pData;
+ m_cbMd = cbData;
+
+ErrExit:
+ return hr;
+} // CLiteWeightStgdb<MiniMd>::InitOnMem
+
+
+template <class MiniMd>
+__checkReturn
+HRESULT
+CLiteWeightStgdb<MiniMd>::InitHotPools(
+ DataBuffer hotMetaDataBuffer)
+{
+ HRESULT hr;
+ MetaData::HotMetaData hotMetaData;
+ MetaData::HotHeapsDirectoryIterator heapsIterator;
+
+ IfFailRet(hotMetaData.Initialize(hotMetaDataBuffer));
+
+ IfFailRet(hotMetaData.GetHeapsDirectoryIterator(&heapsIterator));
+
+ for (;;)
+ {
+ MetaData::HotHeap hotHeap;
+ MetaData::HeapIndex hotHeapIndex;
+
+ hr = heapsIterator.GetNext(&hotHeap, &hotHeapIndex);
+ if (hr == S_FALSE)
+ { // End of iteration
+ return S_OK;
+ }
+
+ switch (hotHeapIndex.Get())
+ {
+ case MetaData::HeapIndex::StringHeapIndex:
+ {
+ m_MiniMd.m_StringHeap.InitializeHotData(hotHeap);
+ break;
+ }
+ case MetaData::HeapIndex::GuidHeapIndex:
+ {
+ m_MiniMd.m_GuidHeap.InitializeHotData(hotHeap);
+ break;
+ }
+ case MetaData::HeapIndex::UserStringHeapIndex:
+ {
+ m_MiniMd.m_UserStringHeap.InitializeHotData(hotHeap);
+ break;
+ }
+ case MetaData::HeapIndex::BlobHeapIndex:
+ {
+ m_MiniMd.m_BlobHeap.InitializeHotData(hotHeap);
+ break;
+ }
+ default:
+ Debug_ReportInternalError("There's a bug in HotHeapsDirectoryIterator - it should verify the heap index.");
+ IfFailRet(METADATA_E_INTERNAL_ERROR);
+ }
+ }
+} // CLiteWeightStgdb<MiniMd>::InitHotPools
diff --git a/src/md/runtime/mdcolumndescriptors.cpp b/src/md/runtime/mdcolumndescriptors.cpp
new file mode 100644
index 0000000000..dfd063ecb6
--- /dev/null
+++ b/src/md/runtime/mdcolumndescriptors.cpp
@@ -0,0 +1,214 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+#include "stdafx.h"
+
+const BYTE CMiniMdBase::s_ModuleCol[] = {2,
+ 97,0,2, 101,2,2, 102,4,2, 102,6,2, 102,8,2,
+ 97,0,2, 101,2,4, 102,6,2, 102,8,2, 102,10,2,
+};
+const BYTE CMiniMdBase::s_TypeRefCol[] = {2,
+ 75,0,2, 101,2,2, 101,4,2,
+ 75,0,2, 101,2,4, 101,6,4,
+};
+const BYTE CMiniMdBase::s_TypeDefCol[] = {2,
+ 99,0,4, 101,4,2, 101,6,2, 64,8,2, 4,10,2, 6,12,2,
+ 99,0,4, 101,4,4, 101,8,4, 64,12,2, 4,14,2, 6,16,2,
+};
+const BYTE CMiniMdBase::s_FieldPtrCol[] = {1,
+ 4,0,2,
+};
+const BYTE CMiniMdBase::s_FieldCol[] = {3,
+ 97,0,2, 101,2,2, 103,4,2,
+ 97,0,2, 101,2,4, 103,6,4,
+ 97,0,2, 101,2,4, 103,6,2,
+};
+const BYTE CMiniMdBase::s_MethodPtrCol[] = {1,
+ 6,0,2,
+};
+const BYTE CMiniMdBase::s_MethodCol[] = {3,
+ 99,0,4, 97,4,2, 97,6,2, 101,8,2, 103,10,2, 8,12,2,
+ 99,0,4, 97,4,2, 97,6,2, 101,8,4, 103,12,4, 8,16,2,
+ 99,0,4, 97,4,2, 97,6,2, 101,8,4, 103,12,2, 8,14,2,
+};
+const BYTE CMiniMdBase::s_ParamPtrCol[] = {1,
+ 8,0,2,
+};
+const BYTE CMiniMdBase::s_ParamCol[] = {2,
+ 97,0,2, 97,2,2, 101,4,2,
+ 97,0,2, 97,2,2, 101,4,4,
+};
+const BYTE CMiniMdBase::s_InterfaceImplCol[] = {1,
+ 2,0,2, 64,2,2,
+};
+const BYTE CMiniMdBase::s_MemberRefCol[] = {3,
+ 69,0,2, 101,2,2, 103,4,2,
+ 69,0,4, 101,4,4, 103,8,4,
+ 69,0,2, 101,2,4, 103,6,2,
+};
+const BYTE CMiniMdBase::s_ConstantCol[] = {3,
+ 100,0,1, 65,2,2, 103,4,2,
+ 100,0,1, 65,2,4, 103,6,4,
+ 100,0,1, 65,2,2, 103,4,4,
+};
+const BYTE CMiniMdBase::s_CustomAttributeCol[] = {3,
+ 66,0,2, 74,2,2, 103,4,2,
+ 66,0,4, 74,4,4, 103,8,4,
+ 66,0,4, 74,4,2, 103,6,2,
+};
+const BYTE CMiniMdBase::s_FieldMarshalCol[] = {2,
+ 67,0,2, 103,2,2,
+ 67,0,2, 103,2,4,
+};
+const BYTE CMiniMdBase::s_DeclSecurityCol[] = {3,
+ 96,0,2, 68,2,2, 103,4,2,
+ 96,0,2, 68,2,4, 103,6,4,
+ 96,0,2, 68,2,2, 103,4,4,
+};
+const BYTE CMiniMdBase::s_ClassLayoutCol[] = {1,
+ 97,0,2, 99,2,4, 2,6,2,
+};
+const BYTE CMiniMdBase::s_FieldLayoutCol[] = {1,
+ 99,0,4, 4,4,2,
+};
+const BYTE CMiniMdBase::s_StandAloneSigCol[] = {2,
+ 103,0,2,
+ 103,0,4,
+};
+const BYTE CMiniMdBase::s_EventMapCol[] = {1,
+ 2,0,2, 20,2,2,
+};
+const BYTE CMiniMdBase::s_EventPtrCol[] = {1,
+ 20,0,2,
+};
+const BYTE CMiniMdBase::s_EventCol[] = {2,
+ 97,0,2, 101,2,2, 64,4,2,
+ 97,0,2, 101,2,4, 64,6,2,
+};
+const BYTE CMiniMdBase::s_PropertyMapCol[] = {1,
+ 2,0,2, 23,2,2,
+};
+const BYTE CMiniMdBase::s_PropertyPtrCol[] = {1,
+ 23,0,2,
+};
+const BYTE* CMiniMdBase::s_PropertyCol = s_FieldCol;
+const BYTE CMiniMdBase::s_MethodSemanticsCol[] = {1,
+ 97,0,2, 6,2,2, 70,4,2,
+};
+const BYTE CMiniMdBase::s_MethodImplCol[] = {1,
+ 2,0,2, 71,2,2, 71,4,2,
+};
+const BYTE CMiniMdBase::s_ModuleRefCol[] = {2,
+ 101,0,2,
+ 101,0,4,
+};
+const BYTE* CMiniMdBase::s_TypeSpecCol = s_StandAloneSigCol;
+const BYTE CMiniMdBase::s_ImplMapCol[] = {2,
+ 97,0,2, 72,2,2, 101,4,2, 26,6,2,
+ 97,0,2, 72,2,2, 101,4,4, 26,8,2,
+};
+const BYTE* CMiniMdBase::s_FieldRVACol = s_FieldLayoutCol;
+const BYTE CMiniMdBase::s_ENCLogCol[] = {1,
+ 99,0,4, 99,4,4,
+};
+const BYTE CMiniMdBase::s_ENCMapCol[] = {1,
+ 99,0,4,
+};
+const BYTE CMiniMdBase::s_AssemblyCol[] = {3,
+ 99,0,4, 97,4,2, 97,6,2, 97,8,2, 97,10,2, 99,12,4, 103,16,2, 101,18,2, 101,20,2,
+ 99,0,4, 97,4,2, 97,6,2, 97,8,2, 97,10,2, 99,12,4, 103,16,4, 101,20,4, 101,24,4,
+ 99,0,4, 97,4,2, 97,6,2, 97,8,2, 97,10,2, 99,12,4, 103,16,2, 101,18,4, 101,22,4,
+};
+const BYTE* CMiniMdBase::s_AssemblyProcessorCol = s_ENCMapCol;
+const BYTE CMiniMdBase::s_AssemblyOSCol[] = {1,
+ 99,0,4, 99,4,4, 99,8,4,
+};
+const BYTE CMiniMdBase::s_AssemblyRefCol[] = {3,
+ 97,0,2, 97,2,2, 97,4,2, 97,6,2, 99,8,4, 103,12,2, 101,14,2, 101,16,2, 103,18,2,
+ 97,0,2, 97,2,2, 97,4,2, 97,6,2, 99,8,4, 103,12,4, 101,16,4, 101,20,4, 103,24,4,
+ 97,0,2, 97,2,2, 97,4,2, 97,6,2, 99,8,4, 103,12,2, 101,14,4, 101,18,4, 103,22,2,
+};
+const BYTE CMiniMdBase::s_AssemblyRefProcessorCol[] = {1,
+ 99,0,4, 35,4,2,
+};
+const BYTE CMiniMdBase::s_AssemblyRefOSCol[] = {1,
+ 99,0,4, 99,4,4, 99,8,4, 35,12,2,
+};
+const BYTE CMiniMdBase::s_FileCol[] = {3,
+ 99,0,4, 101,4,2, 103,6,2,
+ 99,0,4, 101,4,4, 103,8,4,
+ 99,0,4, 101,4,4, 103,8,2,
+};
+const BYTE CMiniMdBase::s_ExportedTypeCol[] = {2,
+ 99,0,4, 99,4,4, 101,8,2, 101,10,2, 73,12,2,
+ 99,0,4, 99,4,4, 101,8,4, 101,12,4, 73,16,2,
+};
+const BYTE CMiniMdBase::s_ManifestResourceCol[] = {2,
+ 99,0,4, 99,4,4, 101,8,2, 73,10,2,
+ 99,0,4, 99,4,4, 101,8,4, 73,12,2,
+};
+const BYTE CMiniMdBase::s_NestedClassCol[] = {1,
+ 2,0,2, 2,2,2,
+};
+const BYTE CMiniMdBase::s_GenericParamCol[] = {2,
+ 97,0,2, 97,2,2, 76,4,2, 101,6,2, 64,8,2, 64,10,2,
+ 97,0,2, 97,2,2, 76,4,2, 101,6,4, 64,10,2, 64,12,2,
+};
+const BYTE CMiniMdBase::s_MethodSpecCol[] = {2,
+ 71,0,2, 103,2,2,
+ 71,0,2, 103,2,4,
+};
+const BYTE CMiniMdBase::s_GenericParamConstraintCol[] = {1,
+ 42,0,2, 64,2,2,
+};
+
+const BYTE* const CMiniMdBase::s_TableColumnDescriptors[] = {
+s_ModuleCol,
+s_TypeRefCol,
+s_TypeDefCol,
+s_FieldPtrCol,
+s_FieldCol,
+s_MethodPtrCol,
+s_MethodCol,
+s_ParamPtrCol,
+s_ParamCol,
+s_InterfaceImplCol,
+s_MemberRefCol,
+s_ConstantCol,
+s_CustomAttributeCol,
+s_FieldMarshalCol,
+s_DeclSecurityCol,
+s_ClassLayoutCol,
+s_FieldLayoutCol,
+s_StandAloneSigCol,
+s_EventMapCol,
+s_EventPtrCol,
+s_EventCol,
+s_PropertyMapCol,
+s_PropertyPtrCol,
+s_FieldCol,
+s_MethodSemanticsCol,
+s_MethodImplCol,
+s_ModuleRefCol,
+s_StandAloneSigCol,
+s_ImplMapCol,
+s_FieldLayoutCol,
+s_ENCLogCol,
+s_ENCMapCol,
+s_AssemblyCol,
+s_ENCMapCol,
+s_AssemblyOSCol,
+s_AssemblyRefCol,
+s_AssemblyRefProcessorCol,
+s_AssemblyRefOSCol,
+s_FileCol,
+s_ExportedTypeCol,
+s_ManifestResourceCol,
+s_NestedClassCol,
+s_GenericParamCol,
+s_MethodSpecCol,
+s_GenericParamConstraintCol
+};
diff --git a/src/md/runtime/mdfileformat.cpp b/src/md/runtime/mdfileformat.cpp
new file mode 100644
index 0000000000..2edd2e0292
--- /dev/null
+++ b/src/md/runtime/mdfileformat.cpp
@@ -0,0 +1,195 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDFileFormat.cpp
+//
+
+//
+// This file contains a set of helpers to verify and read the file format.
+// This code does not handle the paging of the data, or different types of
+// I/O. See the StgTiggerStorage and StgIO code for this level of support.
+//
+//*****************************************************************************
+
+#include "stdafx.h" // Standard header file.
+#include "mdfileformat.h" // The format helpers.
+#include "posterror.h" // Error handling code.
+
+//*****************************************************************************
+// Verify the signature at the front of the file to see what type it is.
+//*****************************************************************************
+#define STORAGE_MAGIC_OLD_SIG 0x2B4D4F43 // +MOC (old version of BSJB signature code:STORAGE_MAGIC_SIG)
+HRESULT
+MDFormat::VerifySignature(
+ PSTORAGESIGNATURE pSig, // The signature to check.
+ ULONG cbData)
+{
+ HRESULT hr = S_OK;
+
+ // If signature didn't match, you shouldn't be here.
+ ULONG dwSignature = pSig->GetSignature();
+ if (dwSignature == STORAGE_MAGIC_OLD_SIG)
+ {
+ Debug_ReportError("Invalid MetaData storage signature - old magic signature +MOC.");
+ return PostError(CLDB_E_FILE_OLDVER, 1, 0);
+ }
+ if (dwSignature != STORAGE_MAGIC_SIG)
+ {
+ Debug_ReportError("Invalid MetaData storage signature - unrecognized magic signature, should be BSJB.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Check for overflow
+ ULONG lVersionString = pSig->GetVersionStringLength();
+ ULONG sum = sizeof(STORAGESIGNATURE) + lVersionString;
+ if ((sum < sizeof(STORAGESIGNATURE)) || (sum < lVersionString))
+ {
+ Debug_ReportError("Invalid MetaData storage signature - version string too long, integer overflow.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Check for invalid version string size
+ if ((sizeof(STORAGESIGNATURE) + lVersionString) > cbData)
+ {
+ Debug_ReportError("Invalid MetaData storage signature - version string too long.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Check that the version string is null terminated. This string
+ // is ANSI, so no double-null checks need to be made.
+ {
+ BYTE *pStart = &pSig->pVersion[0];
+ BYTE *pEnd = pStart + lVersionString + 1; // Account for terminating NULL
+ BYTE *pCur;
+
+ for (pCur = pStart; pCur < pEnd; pCur++)
+ {
+ if (*pCur == 0)
+ break;
+ }
+
+ // If we got to the end without hitting a NULL, we have a bad version string
+ if (pCur == pEnd)
+ {
+ Debug_ReportError("Invalid MetaData storage signature - version string has not null-terminator.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+ }
+
+#if !defined(FEATURE_METADATA_STANDALONE_WINRT)
+ // Only a specific version of the 0.x format is supported by this code
+ // in order to support the NT 5 beta clients which used this format.
+ if (pSig->GetMajorVer() == FILE_VER_MAJOR_v0)
+ {
+ if (pSig->GetMinorVer() < FILE_VER_MINOR_v0)
+ {
+ Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1.");
+ hr = CLDB_E_FILE_OLDVER;
+ }
+ }
+ else
+#endif // !defined(FEATURE_METADATA_STANDALONE_WINRT)
+ // There is currently no code to migrate an old format of the 1.x. This
+ // would be added only under special circumstances.
+ if ((pSig->GetMajorVer() != FILE_VER_MAJOR) || (pSig->GetMinorVer() != FILE_VER_MINOR))
+ {
+ Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1.");
+ hr = CLDB_E_FILE_OLDVER;
+ }
+
+ if (FAILED(hr))
+ hr = PostError(hr, (int)pSig->GetMajorVer(), (int)pSig->GetMinorVer());
+ return hr;
+} // MDFormat::VerifySignature
+
+//*****************************************************************************
+// Skip over the header and find the actual stream data.
+// It doesn't perform any checks for buffer overflow - use GetFirstStream_Verify
+// instead.
+//*****************************************************************************
+PSTORAGESTREAM
+MDFormat::GetFirstStream(
+ PSTORAGEHEADER pHeader, // Return copy of header struct.
+ const void *pvMd) // Pointer to the full file.
+{
+ const BYTE *pbMd;
+
+ // Header data starts after signature.
+ pbMd = (const BYTE *) pvMd;
+ pbMd += sizeof(STORAGESIGNATURE);
+ pbMd += ((STORAGESIGNATURE*)pvMd)->GetVersionStringLength();
+ PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd;
+ *pHeader = *pHdr;
+ pbMd += sizeof(STORAGEHEADER);
+
+ // ECMA specifies that the flags field is "reserved, must be 0".
+ if (pHdr->GetFlags() != 0)
+ return NULL;
+
+ // The pointer is now at the first stream in the list.
+ return ((PSTORAGESTREAM) pbMd);
+} // MDFormat::GetFirstStream
+
+//*****************************************************************************
+// Skip over the header and find the actual stream data. Secure version of
+// GetFirstStream method.
+// The header is supposed to be verified by VerifySignature.
+//
+// Returns pointer to the first stream (behind storage header) and the size of
+// the remaining buffer in *pcbMd (could be 0).
+// Returns NULL if there is not enough buffer for reading the headers. The *pcbMd
+// could be changed if NULL returned.
+//
+// Caller has to check available buffer size before using the first stream.
+//*****************************************************************************
+PSTORAGESTREAM
+MDFormat::GetFirstStream_Verify(
+ PSTORAGEHEADER pHeader, // Return copy of header struct.
+ const void *pvMd, // Pointer to the full file.
+ ULONG *pcbMd) // [in, out] Size of pvMd buffer (we don't want to read behind it)
+{
+ const BYTE *pbMd;
+
+ // Header data starts after signature.
+ pbMd = (const BYTE *)pvMd;
+ // Check read buffer overflow
+ if (*pcbMd < sizeof(STORAGESIGNATURE))
+ {
+ Debug_ReportError("Invalid MetaData - Storage signature doesn't fit.");
+ return NULL;
+ }
+ pbMd += sizeof(STORAGESIGNATURE);
+ *pcbMd -= sizeof(STORAGESIGNATURE);
+
+ ULONG cbVersionString = ((STORAGESIGNATURE *)pvMd)->GetVersionStringLength();
+ // Check read buffer overflow
+ if (*pcbMd < cbVersionString)
+ {
+ Debug_ReportError("Invalid MetaData storage signature - Version string doesn't fit.");
+ return NULL;
+ }
+ pbMd += cbVersionString;
+ *pcbMd -= cbVersionString;
+
+ // Is there enough space for storage header?
+ if (*pcbMd < sizeof(STORAGEHEADER))
+ {
+ Debug_ReportError("Invalid MetaData storage header - Storage header doesn't fit.");
+ return NULL;
+ }
+ PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd;
+ *pHeader = *pHdr;
+ pbMd += sizeof(STORAGEHEADER);
+ *pcbMd -= sizeof(STORAGEHEADER);
+
+ // ECMA specifies that the flags field is "reserved, must be 0".
+ if (pHdr->GetFlags() != 0)
+ {
+ Debug_ReportError("Invalid MetaData storage header - Flags are not 0.");
+ return NULL;
+ }
+
+ // The pointer is now at the first stream in the list.
+ return (PSTORAGESTREAM)pbMd;
+} // MDFormat::GetFirstStream
diff --git a/src/md/runtime/mdinternaldisp.cpp b/src/md/runtime/mdinternaldisp.cpp
new file mode 100644
index 0000000000..1f5725ff01
--- /dev/null
+++ b/src/md/runtime/mdinternaldisp.cpp
@@ -0,0 +1,1834 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ===========================================================================
+// File: MDInternalDisp.CPP
+//
+
+// Notes:
+//
+//
+// ===========================================================================
+#include "stdafx.h"
+#include "mdinternaldisp.h"
+#include "mdinternalro.h"
+#include "posterror.h"
+#include "corpriv.h"
+#include "assemblymdinternaldisp.h"
+#include "pedecoder.h"
+#include "winmdinterfaces.h"
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+// forward declaration
+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.
+
+//*****************************************************************************
+// CheckFileFormat
+// This function will determine if the in-memory image is a readonly, readwrite,
+// or ICR format.
+//*****************************************************************************
+HRESULT
+CheckFileFormat(
+ LPVOID pData,
+ ULONG cbData,
+ MDFileFormat *pFormat) // [OUT] the file format
+{
+ HRESULT hr = NOERROR;
+ STORAGEHEADER sHdr; // Header for the storage.
+ PSTORAGESTREAM pStream; // Pointer to each stream.
+ int i;
+ ULONG cbStreamBuffer;
+
+ _ASSERTE(pFormat != NULL);
+
+ *pFormat = MDFormat_Invalid;
+
+ // Validate the signature of the format, or it isn't ours.
+ if (FAILED(hr = MDFormat::VerifySignature((PSTORAGESIGNATURE) pData, cbData)))
+ goto ErrExit;
+
+ // Remaining buffer size behind the stream header (pStream).
+ cbStreamBuffer = cbData;
+ // Get back the first stream.
+ pStream = MDFormat::GetFirstStream_Verify(&sHdr, pData, &cbStreamBuffer);
+ if (pStream == NULL)
+ {
+ Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+
+ // Loop through each stream and pick off the ones we need.
+ for (i = 0; i < sHdr.GetiStreams(); i++)
+ {
+ // Do we have enough buffer to read stream header?
+ if (cbStreamBuffer < sizeof(*pStream))
+ {
+ Debug_ReportError("Stream header is not within MetaData block.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ // Get next stream.
+ PSTORAGESTREAM pNext = pStream->NextStream_Verify();
+
+ // 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()) < pStream->GetOffset()) ||
+ ((pStream->GetSize() + pStream->GetOffset()) > cbData))
+ {
+ Debug_ReportError("Stream data are not within MetaData block.");
+ hr = CLDB_E_FILE_CORRUPT;
+ goto ErrExit;
+ }
+
+ // Pick off the location and size of the data.
+
+ if (strcmp(pStream->GetName(), COMPRESSED_MODEL_STREAM_A) == 0)
+ {
+ // Validate that only one of compressed/uncompressed is present.
+ if (*pFormat != MDFormat_Invalid)
+ { // Already found a good stream.
+ Debug_ReportError("Compressed model stream #~ is second important stream.");
+ hr = CLDB_E_FILE_CORRUPT;
+ goto ErrExit;
+ }
+ // Found the compressed meta data stream.
+ *pFormat = MDFormat_ReadOnly;
+ }
+ else if (strcmp(pStream->GetName(), ENC_MODEL_STREAM_A) == 0)
+ {
+#ifdef FEATURE_METADATA_STANDALONE_WINRT
+ Debug_ReportError("ENC model stream #- is not supported.");
+ hr = CLDB_E_FILE_CORRUPT;
+ goto ErrExit;
+#else //!FEATURE_METADATA_STANDALONE_WINRT
+ // Validate that only one of compressed/uncompressed is present.
+ if (*pFormat != MDFormat_Invalid)
+ { // Already found a good stream.
+ Debug_ReportError("ENC model stream #- is second important stream.");
+ hr = CLDB_E_FILE_CORRUPT;
+ goto ErrExit;
+ }
+ // Found the ENC meta data stream.
+ *pFormat = MDFormat_ReadWrite;
+#endif //!FEATURE_METADATA_STANDALONE_WINRT
+ }
+ else if (strcmp(pStream->GetName(), SCHEMA_STREAM_A) == 0)
+ {
+#ifdef FEATURE_METADATA_STANDALONE_WINRT
+ Debug_ReportError("Schema stream #Schema is not supported.");
+ hr = CLDB_E_FILE_CORRUPT;
+ goto ErrExit;
+#else //!FEATURE_METADATA_STANDALONE_WINRT
+ // Found the uncompressed format
+ *pFormat = MDFormat_ICR;
+
+ // keep going. We may find the compressed format later.
+ // If so, we want to use the compressed format.
+#endif //!FEATURE_METADATA_STANDALONE_WINRT
+ }
+
+ // Pick off the next stream if there is one.
+ pStream = pNext;
+ cbStreamBuffer = (ULONG)((LPBYTE)pData + cbData - (LPBYTE)pNext);
+ }
+
+ if (*pFormat == MDFormat_Invalid)
+ { // Didn't find a good stream.
+ Debug_ReportError("Cannot find MetaData stream.");
+ hr = CLDB_E_FILE_CORRUPT;
+ }
+
+ErrExit:
+ return hr;
+} // CheckFileFormat
+
+
+
+//*****************************************************************************
+// GetMDInternalInterface.
+// This function will check the metadata section and determine if it should
+// return an interface which implements ReadOnly or ReadWrite.
+//*****************************************************************************
+STDAPI GetMDInternalInterface(
+ LPVOID pData,
+ ULONG cbData,
+ DWORD flags, // [IN] ofRead or ofWrite.
+ REFIID riid, // [in] The interface desired.
+ void **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr = NOERROR;
+ MDInternalRO *pInternalRO = NULL;
+ IMDCommon *pInternalROMDCommon = NULL;
+ MDFileFormat format;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+
+ if (ppIUnk == NULL)
+ IfFailGo(E_INVALIDARG);
+
+ // Determine the file format we're trying to read.
+ IfFailGo( CheckFileFormat(pData, cbData, &format) );
+
+ // Found a fully-compressed, read-only format.
+ if ( format == MDFormat_ReadOnly )
+ {
+ pInternalRO = new (nothrow) MDInternalRO;
+ IfNullGo( pInternalRO );
+
+ IfFailGo( pInternalRO->Init(const_cast<void*>(pData), cbData) );
+
+#ifdef FEATURE_COMINTEROP
+ IfFailGo(pInternalRO->QueryInterface(IID_IMDCommon, (void**)&pInternalROMDCommon));
+ IfFailGo( (flags & ofNoTransform) ? S_FALSE : CheckIfWinMDAdapterNeeded(pInternalROMDCommon));
+ if (hr == S_OK)
+ {
+ IfFailGo(CreateWinMDInternalImportRO(pInternalROMDCommon, riid, (void**)ppIUnk));
+ }
+ else
+#endif // FEATURE_COMINTEROP
+ {
+ IfFailGo(pInternalRO->QueryInterface(riid, ppIUnk));
+ }
+
+ }
+ else
+ {
+ // Found a not-fully-compressed, ENC format.
+ _ASSERTE( format == MDFormat_ReadWrite );
+ IfFailGo( GetInternalWithRWFormat( pData, cbData, flags, riid, ppIUnk ) );
+ }
+
+ErrExit:
+
+ // clean up
+ if ( pInternalRO )
+ pInternalRO->Release();
+ if ( pInternalROMDCommon )
+ pInternalROMDCommon->Release();
+
+ END_SO_INTOLERANT_CODE;
+
+ return hr;
+} // GetMDInternalInterface
+
+
+#ifdef FEATURE_FUSION
+
+#ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+// GetAssemblyMDInternalImport.
+// Instantiating an instance of AssemblyMDInternalImport.
+// This class can support the IMetaDataAssemblyImport and some functionalities
+// of IMetaDataImport on the internal import interface (IMDInternalImport).
+//*****************************************************************************
+STDAPI GetAssemblyMDInternalImport( // Return code.
+ LPCWSTR szFileName, // [in] The scope to open.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ return GetAssemblyMDInternalImportEx(szFileName, riid, MDInternalImport_Default, ppIUnk);
+}
+
+STDAPI GetAssemblyMDInternalImportEx( // Return code.
+ LPCWSTR szFileName, // [in] The scope to open.
+ REFIID riid, // [in] The interface desired.
+ MDInternalImportFlags flags, // [in] Flags to control opening the assembly
+ IUnknown **ppIUnk, // [out] Return interface on success.
+ HANDLE hFile)
+{
+ HRESULT hr;
+
+ if (!szFileName || !szFileName[0] || !ppIUnk)
+ return E_INVALIDARG;
+
+ // Sanity check the name.
+ if (wcslen(szFileName) >= _MAX_PATH)
+ return E_INVALIDARG;
+
+ if (memcmp(szFileName, W("file:"), 10) == 0)
+ szFileName = &szFileName[5];
+
+ HCORMODULEHolder hModule;
+ DWORD dwFileLength;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+
+ IfFailGoto(RuntimeOpenImageInternal(szFileName, &hModule, &dwFileLength, flags, hFile), ErrAsmExpected);
+
+ IfFailGo(GetAssemblyMDInternalImportHelper(hModule, riid, flags, ppIUnk));
+
+
+ErrAsmExpected:
+ if(hr == COR_E_BADIMAGEFORMAT)
+ hr = COR_E_ASSEMBLYEXPECTED;
+
+ErrExit:
+;
+ END_SO_INTOLERANT_CODE;
+
+ return hr;
+}
+
+HRESULT GetAssemblyMDInternalImportFromImage(
+ HCORMODULE hImage,
+ REFIID riid,
+ IUnknown **ppIUnk)
+{
+
+ HRESULT hr;
+
+ IfFailGo(GetAssemblyMDInternalImportHelper(hImage, riid, MDInternalImport_Default, ppIUnk));
+
+ErrExit:
+ return hr;
+}
+
+STDAPI GetAssemblyMDInternalImportByStream( // Return code.
+ IStream *pIStream, // [in] The IStream for the file
+ UINT64 AssemblyId, // [in] Unique Id for the assembly
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ return GetAssemblyMDInternalImportByStreamEx(pIStream, AssemblyId, riid, MDInternalImport_Default, ppIUnk);
+}
+
+STDAPI GetAssemblyMDInternalImportByStreamEx( // Return code.
+ IStream *pIStream, // [in] The IStream for the file
+ UINT64 AssemblyId, // [in] Unique Id for the assembly
+ REFIID riid, // [in] The interface desired.
+ MDInternalImportFlags flags, // [in[ Flags to control opening the assembly
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ if (!pIStream || !ppIUnk)
+ return E_INVALIDARG;
+
+ HRESULT hr;
+ DWORD dwFileLength;
+ HCORMODULEHolder hModule;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+
+ IfFailGoto(RuntimeOpenImageByStream(pIStream, AssemblyId, 0, &hModule, &dwFileLength, flags), ErrAsmExpected);
+
+ IfFailGo(GetAssemblyMDInternalImportHelper(hModule, riid, flags, ppIUnk));
+
+
+ErrAsmExpected:
+ if(hr == COR_E_BADIMAGEFORMAT)
+ hr = COR_E_ASSEMBLYEXPECTED;
+
+ErrExit:
+ ;
+ END_SO_INTOLERANT_CODE;
+
+ return hr;
+}
+
+
+HRESULT GetAssemblyMDInternalImportHelper(HCORMODULE hModule,
+ REFIID riid,
+ MDInternalImportFlags flags,
+ IUnknown **ppIUnk)
+{
+ AssemblyMDInternalImport *pAssemblyMDInternalImport = NULL;
+ HRESULT hr;
+ LPVOID base;
+ PEDecoder pe;
+ IfFailGoto(RuntimeGetImageBase(hModule,&base,TRUE,NULL), ErrAsmExpected);
+
+ if (base!=NULL)
+ IfFailGoto(pe.Init(base), ErrAsmExpected);
+ else
+ {
+ COUNT_T lgth;
+ IfFailGoto(RuntimeGetImageBase(hModule,&base,FALSE,&lgth), ErrAsmExpected);
+ pe.Init(base, lgth);
+ }
+
+ // Both of these need to pass.
+ if (!pe.HasCorHeader() || !pe.CheckCorHeader())
+ IfFailGo(COR_E_ASSEMBLYEXPECTED);
+ // Only one of these needs to.
+ if (!pe.CheckILFormat() && !pe.CheckNativeFormat())
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+
+ COUNT_T cbMetaData;
+ LPCVOID pMetaData;
+ pMetaData = pe.GetMetadata(&cbMetaData);
+
+ // Get the IL metadata.
+ IMDInternalImport *pMDInternalImport;
+ IfFailGo(RuntimeGetMDInternalImport(hModule, flags, &pMDInternalImport));
+ if (pMDInternalImport == NULL)
+ IfFailGo(E_OUTOFMEMORY);
+
+ _ASSERTE(pMDInternalImport);
+ pAssemblyMDInternalImport = new (nothrow) AssemblyMDInternalImport (pMDInternalImport);
+ if (!pAssemblyMDInternalImport) {
+ pMDInternalImport->Release();
+ IfFailGo(E_OUTOFMEMORY);
+ }
+
+ { // identify PE kind and machine type, plus the version string location
+ DWORD dwKind=0;
+ DWORD dwMachine=0;
+ RuntimeGetImageKind(hModule,&dwKind,&dwMachine);
+ pAssemblyMDInternalImport->SetPEKind(dwKind);
+ pAssemblyMDInternalImport->SetMachine(dwMachine);
+
+ {
+ LPCSTR pString = NULL;
+ IfFailGo(GetImageRuntimeVersionString((PVOID)pMetaData, &pString));
+
+ pAssemblyMDInternalImport->SetVersionString(pString);
+ }
+
+ }
+
+ pAssemblyMDInternalImport->SetHandle(hModule);
+
+#ifdef FEATURE_PREJIT
+ // Check for zap header for INativeImageInstallInfo
+ // Dont do this if we are returning the IL metadata as CORCOMPILE_DEPENDENCY
+ // references the native image metadata, not the IL metadata.
+
+ if (pe.HasNativeHeader() && !(flags & MDInternalImport_ILMetaData))
+ {
+ CORCOMPILE_VERSION_INFO *pNativeVersionInfo = pe.GetNativeVersionInfo();
+
+ COUNT_T cDeps;
+ CORCOMPILE_DEPENDENCY *pDeps = pe.GetNativeDependencies(&cDeps);
+
+ pAssemblyMDInternalImport->SetZapVersionInfo(pNativeVersionInfo, pDeps, cDeps);
+ }
+#endif // FEATURE_PREJIT
+
+ IfFailGo(pAssemblyMDInternalImport->QueryInterface(riid, (void**)ppIUnk));
+
+ return hr;
+
+ErrAsmExpected:
+ if(hr == COR_E_BADIMAGEFORMAT)
+ hr = COR_E_ASSEMBLYEXPECTED;
+
+ErrExit:
+
+ if (pAssemblyMDInternalImport)
+ delete pAssemblyMDInternalImport;
+
+ return hr;
+}
+
+AssemblyMDInternalImport::AssemblyMDInternalImport (IMDInternalImport *pMDInternalImport)
+: m_cRef(0),
+ m_pHandle(0),
+ m_pBase(NULL),
+#ifdef FEATURE_PREJIT
+ m_pZapVersionInfo(NULL),
+#endif // FEATURE_PREJIT
+ m_pMDInternalImport(pMDInternalImport),
+ m_dwPEKind(0),
+ m_dwMachine(0),
+ m_szVersionString("")
+{
+ _ASSERTE(m_pMDInternalImport);
+} // AssemblyMDInternalImport
+
+AssemblyMDInternalImport::~AssemblyMDInternalImport ()
+{
+ m_pMDInternalImport->Release();
+
+ if (m_pBase)
+ {
+ UnmapViewOfFile(m_pBase);
+ m_pBase = NULL;
+ CloseHandle(m_pHandle);
+ }
+ else if(m_pHandle)
+ {
+ HRESULT hr;
+ hr = RuntimeReleaseHandle(m_pHandle);
+ _ASSERTE(SUCCEEDED(hr));
+ }
+
+ m_pHandle = NULL;
+}
+
+ULONG AssemblyMDInternalImport::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+} // ULONG AssemblyMDInternalImport::AddRef()
+
+ULONG AssemblyMDInternalImport::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (!cRef)
+ {
+ VALIDATE_BACKOUT_STACK_CONSUMPTION;
+ delete this;
+ }
+ return (cRef);
+} // ULONG AssemblyMDInternalImport::Release()
+
+HRESULT AssemblyMDInternalImport::QueryInterface(REFIID riid, void **ppUnk)
+{
+ *ppUnk = 0;
+
+ if (riid == IID_IUnknown)
+ *ppUnk = (IUnknown *) (IMetaDataAssemblyImport *) this;
+ else if (riid == IID_IMetaDataAssemblyImport)
+ *ppUnk = (IMetaDataAssemblyImport *) this;
+ else if (riid == IID_IMetaDataImport)
+ *ppUnk = (IMetaDataImport *) this;
+ else if (riid == IID_IMetaDataImport2)
+ *ppUnk = (IMetaDataImport2 *) this;
+ else if (riid == IID_ISNAssemblySignature)
+ *ppUnk = (ISNAssemblySignature *) this;
+#ifdef FEATURE_PREJIT
+ else if (riid == IID_IGetIMDInternalImport)
+ *ppUnk = (IGetIMDInternalImport *) this;
+ else if (riid == IID_INativeImageInstallInfo && m_pZapVersionInfo)
+ *ppUnk = (INativeImageInstallInfo *) this;
+#endif // FEATURE_PREJIT
+ else
+ return (E_NOINTERFACE);
+ AddRef();
+ return (S_OK);
+}
+
+
+STDMETHODIMP AssemblyMDInternalImport::GetAssemblyProps ( // S_OK or error.
+ 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.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ DWORD *pdwAssemblyFlags) // [OUT] Flags.
+{
+ HRESULT hr;
+ LPCSTR _szName;
+ AssemblyMetaDataInternal _AssemblyMetaData;
+
+ _AssemblyMetaData.ulProcessor = 0;
+ _AssemblyMetaData.ulOS = 0;
+
+ IfFailRet(m_pMDInternalImport->GetAssemblyProps(
+ mda, // [IN] The Assembly for which to get the properties.
+ ppbPublicKey, // [OUT] Pointer to the public key.
+ pcbPublicKey, // [OUT] Count of bytes in the public key.
+ pulHashAlgId, // [OUT] Hash Algorithm.
+ &_szName, // [OUT] Buffer to fill with name.
+ &_AssemblyMetaData, // [OUT] Assembly MetaData.
+ pdwAssemblyFlags)); // [OUT] Flags.
+
+ if (pchName != NULL)
+ {
+ *pchName = WszMultiByteToWideChar(CP_UTF8, 0, _szName, -1, szName, cchName);
+ if (*pchName == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ if (pMetaData)
+ {
+ pMetaData->usMajorVersion = _AssemblyMetaData.usMajorVersion;
+ pMetaData->usMinorVersion = _AssemblyMetaData.usMinorVersion;
+ pMetaData->usBuildNumber = _AssemblyMetaData.usBuildNumber;
+ pMetaData->usRevisionNumber = _AssemblyMetaData.usRevisionNumber;
+ pMetaData->ulProcessor = 0;
+ pMetaData->ulOS = 0;
+
+ pMetaData->cbLocale = WszMultiByteToWideChar(CP_UTF8, 0, _AssemblyMetaData.szLocale, -1, pMetaData->szLocale, pMetaData->cbLocale);
+ if (pMetaData->cbLocale == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetAssemblyRefProps ( // S_OK or error.
+ 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.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ const void **ppbHashValue, // [OUT] Hash blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ DWORD *pdwAssemblyRefFlags) // [OUT] Flags.
+{
+ HRESULT hr;
+ LPCSTR _szName;
+ AssemblyMetaDataInternal _AssemblyMetaData;
+
+ _AssemblyMetaData.ulProcessor = 0;
+ _AssemblyMetaData.ulOS = 0;
+
+ IfFailRet(m_pMDInternalImport->GetAssemblyRefProps(
+ mdar, // [IN] The Assembly for which to get the properties.
+ ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token.
+ pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token.
+ &_szName, // [OUT] Buffer to fill with name.
+ &_AssemblyMetaData, // [OUT] Assembly MetaData.
+ ppbHashValue, // [OUT] Hash blob.
+ pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ pdwAssemblyRefFlags)); // [OUT] Flags.
+
+ if (pchName != NULL)
+ {
+ *pchName = WszMultiByteToWideChar(CP_UTF8, 0, _szName, -1, szName, cchName);
+ if (*pchName == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ pMetaData->usMajorVersion = _AssemblyMetaData.usMajorVersion;
+ pMetaData->usMinorVersion = _AssemblyMetaData.usMinorVersion;
+ pMetaData->usBuildNumber = _AssemblyMetaData.usBuildNumber;
+ pMetaData->usRevisionNumber = _AssemblyMetaData.usRevisionNumber;
+ pMetaData->ulProcessor = 0;
+ pMetaData->ulOS = 0;
+
+ pMetaData->cbLocale = WszMultiByteToWideChar(CP_UTF8, 0, _AssemblyMetaData.szLocale, -1, pMetaData->szLocale, pMetaData->cbLocale);
+ if (pMetaData->cbLocale == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetFileProps ( // S_OK or error.
+ mdFile mdf, // [IN] The File for which to get the properties.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in 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.
+{
+ HRESULT hr;
+ LPCSTR _szName;
+ IfFailRet(m_pMDInternalImport->GetFileProps(
+ mdf,
+ &_szName,
+ ppbHashValue,
+ pcbHashValue,
+ pdwFileFlags));
+
+ if (pchName != NULL)
+ {
+ *pchName = WszMultiByteToWideChar(CP_UTF8, 0, _szName, -1, szName, cchName);
+ if (*pchName == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetExportedTypeProps ( // S_OK or error.
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef or mdExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags) // [OUT] Flags.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetManifestResourceProps ( // S_OK or error.
+ mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ManifestResource.
+ DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file.
+ DWORD *pdwResourceFlags) // [OUT] Flags.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumAssemblyRefs ( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdAssemblyRef rAssemblyRefs[], // [OUT] Put AssemblyRefs here.
+ ULONG cMax, // [IN] Max AssemblyRefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HRESULT hr = NOERROR;
+ HENUMInternal *pEnum;
+
+ if (*ppmdEnum == NULL)
+ {
+ // create the enumerator.
+ IfFailGo(HENUMInternal::CreateSimpleEnum(
+ mdtAssemblyRef,
+ 0,
+ 1,
+ &pEnum));
+
+ IfFailGo(m_pMDInternalImport->EnumInit(mdtAssemblyRef, 0, pEnum));
+
+ // set the output parameter.
+ *ppmdEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppmdEnum;
+ }
+
+ // we can only fill the minimum of what the caller asked for or what we have left.
+ IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rAssemblyRefs, pcTokens));
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+
+ return hr;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumFiles ( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdFile rFiles[], // [OUT] Put Files here.
+ ULONG cMax, // [IN] Max Files to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HRESULT hr = NOERROR;
+ HENUMInternal *pEnum;
+
+ if (*ppmdEnum == NULL)
+ {
+ // create the enumerator.
+ IfFailGo(HENUMInternal::CreateSimpleEnum(
+ mdtFile,
+ 0,
+ 1,
+ &pEnum));
+
+ IfFailGo(m_pMDInternalImport->EnumInit(mdtFile, 0, pEnum));
+
+ // set the output parameter.
+ *ppmdEnum = pEnum;
+ }
+ else
+ {
+ pEnum = *ppmdEnum;
+ }
+
+ // we can only fill the minimum of what the caller asked for or what we have left.
+ IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rFiles, pcTokens));
+
+ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+ return hr;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumExportedTypes ( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdExportedType rExportedTypes[], // [OUT] Put ExportedTypes here.
+ ULONG cMax, // [IN] Max ExportedTypes to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumManifestResources ( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdManifestResource rManifestResources[], // [OUT] Put ManifestResources here.
+ ULONG cMax, // [IN] Max Resources to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetAssemblyFromScope ( // S_OK or error
+ mdAssembly *ptkAssembly) // [OUT] Put token here.
+{
+ return m_pMDInternalImport->GetAssemblyFromScope (ptkAssembly);
+}
+
+STDMETHODIMP AssemblyMDInternalImport::FindExportedTypeByName (// S_OK or error
+ LPCWSTR szName, // [IN] Name of the ExportedType.
+ mdToken mdtExportedType, // [IN] ExportedType for the enclosing class.
+ mdExportedType *ptkExportedType) // [OUT] Put the ExportedType token here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::FindManifestResourceByName ( // S_OK or error
+ LPCWSTR szName, // [IN] Name of the ManifestResource.
+ mdManifestResource *ptkManifestResource) // [OUT] Put the ManifestResource token here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+void AssemblyMDInternalImport::CloseEnum (
+ HCORENUM hEnum) // Enum to be closed.
+{
+ HENUMInternal *pmdEnum = reinterpret_cast<HENUMInternal *> (hEnum);
+
+ if (pmdEnum == NULL)
+ return;
+
+ HENUMInternal::DestroyEnum(pmdEnum);
+}
+
+STDMETHODIMP AssemblyMDInternalImport::FindAssembliesByName ( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
+ IUnknown *ppIUnk[], // [OUT] put IMetaDataAssemblyImport pointers here
+ ULONG cMax, // [IN] The max number to put
+ ULONG *pcAssemblies) // [OUT] The number of assemblies returned.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::CountEnum (HCORENUM hEnum, ULONG *pulCount)
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::ResetEnum (HCORENUM hEnum, ULONG ulPos)
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumTypeDefs (HCORENUM *phEnum, mdTypeDef rTypeDefs[],
+ ULONG cMax, ULONG *pcTypeDefs)
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumInterfaceImpls (HCORENUM *phEnum, mdTypeDef td,
+ mdInterfaceImpl rImpls[], ULONG cMax,
+ ULONG* pcImpls)
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumTypeRefs (HCORENUM *phEnum, mdTypeRef rTypeRefs[],
+ ULONG cMax, ULONG* pcTypeRefs)
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::FindTypeDefByName ( // S_OK or error.
+ LPCWSTR szTypeDef, // [IN] Name of the Type.
+ mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef for Enclosing class.
+ mdTypeDef *ptd) // [OUT] Put the TypeDef token here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetScopeProps ( // S_OK or error.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Put the name here.
+ ULONG cchName, // [IN] Size of name buffer in wide chars.
+ ULONG *pchName, // [OUT] Put size of name (wide chars) here.
+ GUID *pmvid) // [OUT, OPTIONAL] Put MVID here.
+{
+ HRESULT hr;
+ LPCSTR _szName;
+
+ if (!m_pMDInternalImport->IsValidToken(m_pMDInternalImport->GetModuleFromScope()))
+ return COR_E_BADIMAGEFORMAT;
+
+ IfFailRet(m_pMDInternalImport->GetScopeProps(&_szName, pmvid));
+
+ if (pchName != NULL)
+ {
+ *pchName = WszMultiByteToWideChar(CP_UTF8, 0, _szName, -1, szName, cchName);
+ if (*pchName == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ return S_OK;
+
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetModuleFromScope ( // S_OK.
+ mdModule *pmd) // [OUT] Put mdModule token here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetTypeDefProps ( // S_OK or error.
+ mdTypeDef td, // [IN] TypeDef token for inquiry.
+ __out_ecount (cchTypeDef) LPWSTR szTypeDef, // [OUT] Put name here.
+ ULONG cchTypeDef, // [IN] size of name buffer in wide chars.
+ ULONG *pchTypeDef, // [OUT] put size of name (wide chars) here.
+ DWORD *pdwTypeDefFlags, // [OUT] Put flags here.
+ mdToken *ptkExtends) // [OUT] Put base class TypeDef/TypeRef here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetInterfaceImplProps ( // S_OK or error.
+ mdInterfaceImpl iiImpl, // [IN] InterfaceImpl token.
+ mdTypeDef *pClass, // [OUT] Put implementing class token here.
+ mdToken *ptkIface) // [OUT] Put implemented interface token here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetTypeRefProps ( // S_OK or error.
+ mdTypeRef tr, // [IN] TypeRef token.
+ mdToken *ptkResolutionScope, // [OUT] Resolution scope, ModuleRef or AssemblyRef.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Name of the TypeRef.
+ ULONG cchName, // [IN] Size of buffer.
+ ULONG *pchName) // [OUT] Size of Name.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::ResolveTypeRef (mdTypeRef tr, REFIID riid, IUnknown **ppIScope, mdTypeDef *ptd)
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumMembers ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumMembersWithName ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumMethods ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdMethodDef rMethods[], // [OUT] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumMethodsWithName ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdMethodDef rMethods[], // [OU] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumFields ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdFieldDef rFields[], // [OUT] Put FieldDefs here.
+ ULONG cMax, // [IN] Max FieldDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumFieldsWithName ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdFieldDef rFields[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+
+STDMETHODIMP AssemblyMDInternalImport::EnumParams ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdParamDef rParams[], // [OUT] Put ParamDefs here.
+ ULONG cMax, // [IN] Max ParamDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumMemberRefs ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tkParent, // [IN] Parent token to scope the enumeration.
+ mdMemberRef rMemberRefs[], // [OUT] Put MemberRefs here.
+ ULONG cMax, // [IN] Max MemberRefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumMethodImpls ( // S_OK, S_FALSE, or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdToken rMethodBody[], // [OUT] Put Method Body tokens here.
+ mdToken rMethodDecl[], // [OUT] Put Method Declaration tokens here.
+ ULONG cMax, // [IN] Max tokens to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumPermissionSets ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] if !NIL, token to scope the enumeration.
+ DWORD dwActions, // [IN] if !0, return only these actions.
+ mdPermission rPermission[], // [OUT] Put Permissions here.
+ ULONG cMax, // [IN] Max Permissions to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::FindMember (
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdToken *pmb) // [OUT] matching memberdef
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::FindMethod (
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmb) // [OUT] matching memberdef
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::FindField (
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdFieldDef *pmb) // [OUT] matching memberdef
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::FindMemberRef (
+ mdTypeRef td, // [IN] given typeRef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMemberRef *pmr) // [OUT] matching memberref
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetMethodProps (
+ mdMethodDef mb, // The method for which to get props.
+ mdTypeDef *pClass, // Put method's class here.
+ __out_ecount (cchMethod) LPWSTR szMethod, // Put method's name here.
+ ULONG cchMethod, // Size of szMethod buffer in wide chars.
+ ULONG *pchMethod, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG *pulCodeRVA, // [OUT] codeRVA
+ DWORD *pdwImplFlags) // [OUT] Impl. Flags
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetMemberRefProps ( // S_OK or error.
+ mdMemberRef mr, // [IN] given memberref
+ mdToken *ptk, // [OUT] Put classref or classdef here.
+ __out_ecount (cchMember) LPWSTR szMember, // [OUT] buffer to fill for member's name
+ ULONG cchMember, // [IN] the count of char of szMember
+ ULONG *pchMember, // [OUT] actual count of char in member name
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to meta data blob value
+ ULONG *pbSig) // [OUT] actual size of signature blob
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumProperties ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdProperty rProperties[], // [OUT] Put Properties here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcProperties) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumEvents ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdEvent rEvents[], // [OUT] Put events here.
+ ULONG cMax, // [IN] Max events to put.
+ ULONG *pcEvents) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetEventProps ( // S_OK, S_FALSE, or error.
+ mdEvent ev, // [IN] event token
+ mdTypeDef *pClass, // [OUT] typedef containing the event declarion.
+ LPCWSTR szEvent, // [OUT] Event name
+ ULONG cchEvent, // [IN] the count of wchar of szEvent
+ ULONG *pchEvent, // [OUT] actual count of wchar for event's name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType, // [OUT] EventType class
+ mdMethodDef *pmdAddOn, // [OUT] AddOn method of the event
+ mdMethodDef *pmdRemoveOn, // [OUT] RemoveOn method of the event
+ mdMethodDef *pmdFire, // [OUT] Fire method of the event
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the event
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod) // [OUT] total number of other method of this event
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumMethodSemantics ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdToken rEventProp[], // [OUT] Put Event/Property here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcEventProp) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetMethodSemantics ( // S_OK, S_FALSE, or error.
+ mdMethodDef mb, // [IN] method token
+ mdToken tkEventProp, // [IN] event/property token.
+ DWORD *pdwSemanticsFlags) // [OUT] the role flags for the method/propevent pair
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetClassLayout (
+ mdTypeDef td, // [IN] give typedef
+ DWORD *pdwPackSize, // [OUT] 1, 2, 4, 8, or 16
+ COR_FIELD_OFFSET rFieldOffset[], // [OUT] field offset array
+ ULONG cMax, // [IN] size of the array
+ ULONG *pcFieldOffset, // [OUT] needed array size
+ ULONG *pulClassSize) // [OUT] the size of the class
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetFieldMarshal (
+ mdToken tk, // [IN] given a field's memberdef
+ PCCOR_SIGNATURE *ppvNativeType, // [OUT] native type of this field
+ ULONG *pcbNativeType) // [OUT] the count of bytes of *ppvNativeType
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetRVA ( // S_OK or error.
+ mdToken tk, // Member for which to set offset
+ ULONG *pulCodeRVA, // The offset
+ DWORD *pdwImplFlags) // the implementation flags
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::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.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetSigFromToken ( // S_OK or error.
+ mdSignature mdSig, // [IN] Signature token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token.
+ ULONG *pcbSig) // [OUT] return size of signature.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetModuleRefProps ( // S_OK or error.
+ mdModuleRef mur, // [IN] moduleref token.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] buffer to fill with the moduleref name.
+ ULONG cchName, // [IN] size of szName in wide characters.
+ ULONG *pchName) // [OUT] actual count of characters in the name.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumModuleRefs ( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdModuleRef rModuleRefs[], // [OUT] put modulerefs here.
+ ULONG cmax, // [IN] max memberrefs to put.
+ ULONG *pcModuleRefs) // [OUT] put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetTypeSpecFromToken ( // S_OK or error.
+ mdTypeSpec typespec, // [IN] TypeSpec token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to TypeSpec signature
+ ULONG *pcbSig) // [OUT] return size of signature.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetNameFromToken ( // Not Recommended! May be removed!
+ mdToken tk, // [IN] Token to get name from. Must have a name.
+ MDUTF8CSTR *pszUtf8NamePtr) // [OUT] Return pointer to UTF8 name in heap.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumUnresolvedMethods ( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken rMethods[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetUserString ( // S_OK or error.
+ mdString stk, // [IN] String token.
+ __out_ecount (cchString) LPWSTR szString, // [OUT] Copy of string.
+ ULONG cchString, // [IN] Max chars of room in szString.
+ ULONG *pchString) // [OUT] How many chars in actual string.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetPinvokeMap ( // S_OK or error.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ __out_ecount (cchImportName) LPWSTR szImportName, // [OUT] Import name.
+ ULONG cchImportName, // [IN] Size of the name buffer.
+ ULONG *pchImportName, // [OUT] Actual number of characters stored.
+ mdModuleRef *pmrImportDLL) // [OUT] ModuleRef token for the target DLL.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumSignatures ( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdSignature rSignatures[], // [OUT] put signatures here.
+ ULONG cmax, // [IN] max signatures to put.
+ ULONG *pcSignatures) // [OUT] put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumTypeSpecs ( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdTypeSpec rTypeSpecs[], // [OUT] put TypeSpecs here.
+ ULONG cmax, // [IN] max TypeSpecs to put.
+ ULONG *pcTypeSpecs) // [OUT] put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumUserStrings ( // S_OK or error.
+ HCORENUM *phEnum, // [IN/OUT] pointer to the enum.
+ mdString rStrings[], // [OUT] put Strings here.
+ ULONG cmax, // [IN] max Strings to put.
+ ULONG *pcStrings) // [OUT] put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetParamForMethodIndex ( // S_OK or error.
+ mdMethodDef md, // [IN] Method token.
+ ULONG ulParamSeq, // [IN] Parameter sequence.
+ mdParamDef *ppd) // [IN] Put Param token here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumCustomAttributes ( // S_OK or error.
+ HCORENUM *phEnum, // [IN, OUT] COR enumerator.
+ mdToken tk, // [IN] Token to scope the enumeration, 0 for all.
+ mdToken tkType, // [IN] Type of interest, 0 for all.
+ mdCustomAttribute rCustomAttributes[], // [OUT] Put custom attribute tokens here.
+ ULONG cMax, // [IN] Size of rCustomAttributes.
+ ULONG *pcCustomAttributes) // [OUT, OPTIONAL] Put count of token values here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetCustomAttributeProps ( // S_OK or error.
+ mdCustomAttribute cv, // [IN] CustomAttribute token.
+ mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here.
+ mdToken *ptkType, // [OUT, OPTIONAL] Put AttrType token here.
+ void const **ppBlob, // [OUT, OPTIONAL] Put pointer to data here.
+ ULONG *pcbSize) // [OUT, OPTIONAL] Put size of date here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::FindTypeRef (
+ mdToken tkResolutionScope, // [IN] ModuleRef, AssemblyRef or TypeRef.
+ LPCWSTR szName, // [IN] TypeRef Name.
+ mdTypeRef *ptr) // [OUT] matching TypeRef.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetMemberProps (
+ mdToken mb, // The member for which to get props.
+ mdTypeDef *pClass, // Put member's class here.
+ __out_ecount (cchMember) LPWSTR szMember, // Put member's name here.
+ ULONG cchMember, // Size of szMember buffer in wide chars.
+ ULONG *pchMember, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG *pulCodeRVA, // [OUT] codeRVA
+ DWORD *pdwImplFlags, // [OUT] Impl. Flags
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppValue, // [OUT] constant value
+ ULONG *pcchValue) // [OUT] size of constant string in chars, 0 for non-strings.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetFieldProps (
+ mdFieldDef mb, // The field for which to get props.
+ mdTypeDef *pClass, // Put field's class here.
+ __out_ecount (cchField) LPWSTR szField, // Put field's name here.
+ ULONG cchField, // Size of szField buffer in wide chars.
+ ULONG *pchField, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppValue, // [OUT] constant value
+ ULONG *pcchValue) // [OUT] size of constant string in chars, 0 for non-strings.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetPropertyProps ( // S_OK, S_FALSE, or error.
+ mdProperty prop, // [IN] property token
+ mdTypeDef *pClass, // [OUT] typedef containing the property declarion.
+ LPCWSTR szProperty, // [OUT] Property name
+ ULONG cchProperty, // [IN] the count of wchar of szProperty
+ ULONG *pchProperty, // [OUT] actual count of wchar for property name
+ DWORD *pdwPropFlags, // [OUT] property flags.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob
+ ULONG *pbSig, // [OUT] count of bytes in *ppvSig
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppDefaultValue, // [OUT] constant value
+ ULONG *pcchDefaultValue, // [OUT] size of constant string in chars, 0 for non-strings.
+ mdMethodDef *pmdSetter, // [OUT] setter method of the property
+ mdMethodDef *pmdGetter, // [OUT] getter method of the property
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the property
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod) // [OUT] total number of other method of this property
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetParamProps ( // S_OK or error.
+ mdParamDef tk, // [IN]The Parameter.
+ mdMethodDef *pmd, // [OUT] Parent Method token.
+ ULONG *pulSequence, // [OUT] Parameter sequence.
+ __out_ecount (cchName) LPWSTR szName, // [OUT] Put name here.
+ ULONG cchName, // [OUT] Size of name buffer.
+ ULONG *pchName, // [OUT] Put actual size of name here.
+ DWORD *pdwAttr, // [OUT] Put flags here.
+ DWORD *pdwCPlusTypeFlag, // [OUT] Flag for value type. selected ELEMENT_TYPE_*.
+ UVCP_CONSTANT *ppValue, // [OUT] Constant value.
+ ULONG *pcchValue) // [OUT] size of constant string in chars, 0 for non-strings.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetCustomAttributeByName ( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCWSTR szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) // [OUT] Put size of data here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+BOOL AssemblyMDInternalImport::IsValidToken ( // True or False.
+ mdToken tk) // [IN] Given token.
+{
+ _ASSERTE(!"NYI");
+ return FALSE;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetNestedClassProps ( // S_OK or error.
+ mdTypeDef tdNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptdEnclosingClass) // [OUT] EnclosingClass token.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetNativeCallConvFromSig ( // S_OK or error.
+ void const *pvSig, // [IN] Pointer to signature.
+ ULONG cbSig, // [IN] Count of signature bytes.
+ ULONG *pCallConv) // [OUT] Put calling conv here (see CorPinvokemap).
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::IsGlobal ( // S_OK or error.
+ mdToken pd, // [IN] Type, Field, or Method token.
+ int *pbGlobal) // [OUT] Put 1 if global, 0 otherwise.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetMethodSpecProps(
+ 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
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+// *** ISNAssemblySignature methods ***
+
+STDMETHODIMP AssemblyMDInternalImport::GetSNAssemblySignature(
+ BYTE *pbSig, // [IN, OUT] Buffer to write signature
+ DWORD *pcbSig) // [IN, OUT] Size of buffer, bytes written
+{
+ return RuntimeGetAssemblyStrongNameHashForModule(m_pHandle, this, pbSig, pcbSig);
+}
+
+
+#include "strongname.h"
+
+#ifdef FEATURE_PREJIT
+// *** IGetIMDInternalImport ***
+STDMETHODIMP AssemblyMDInternalImport::GetIMDInternalImport(
+ IMDInternalImport ** pIMDInternalImport)
+{
+ m_pMDInternalImport->AddRef();
+ *pIMDInternalImport = m_pMDInternalImport;
+ return S_OK;
+}
+
+
+
+// ===========================================================================
+
+class CNativeImageDependency : public INativeImageDependency
+{
+public:
+ CNativeImageDependency(CORCOMPILE_DEPENDENCY * pDependency)
+ : m_cRef(1), m_pDependency(pDependency)
+ {
+ }
+
+ ~CNativeImageDependency()
+ {
+ }
+
+ //
+ // IUnknown
+ //
+
+ STDMETHODIMP_(ULONG) AddRef()
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ STDMETHODIMP_(ULONG) Release()
+ {
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (!cRef)
+ delete this;
+ return (cRef);
+ }
+
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppUnk)
+ {
+ *ppUnk = 0;
+
+ if (riid == IID_IUnknown)
+ *ppUnk = (IUnknown *) (IMetaDataAssemblyImport *) this;
+ else if (riid == IID_INativeImageDependency)
+ *ppUnk = (INativeImageDependency *) this;
+ else
+ return (E_NOINTERFACE);
+ AddRef();
+ return (S_OK);
+ }
+
+ //
+ // INativeImageDependency
+ //
+
+ STDMETHODIMP GetILAssemblyRef(mdAssemblyRef * pAssemblyRef)
+ {
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pAssemblyRef = m_pDependency->dwAssemblyRef;
+ END_ENTRYPOINT_NOTHROW;
+
+ return S_OK;
+ }
+
+ STDMETHODIMP GetILAssemblyDef(
+ mdAssemblyRef * ppAssemblyDef,
+ CORCOMPILE_ASSEMBLY_SIGNATURE * pSign)
+ {
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *ppAssemblyDef = m_pDependency->dwAssemblyDef;
+ *pSign = m_pDependency->signAssemblyDef;
+ END_ENTRYPOINT_NOTHROW;
+
+ return S_OK;
+ }
+
+ STDMETHODIMP GetNativeAssemblyDef(CORCOMPILE_NGEN_SIGNATURE * pNativeSign)
+ {
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pNativeSign = m_pDependency->signNativeImage;
+ END_ENTRYPOINT_NOTHROW;
+
+ return S_OK;
+ }
+
+ STDMETHODIMP GetPEKind(PEKIND *pPEKind)
+ {
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pPEKind = PEKIND((m_pDependency->dependencyInfo & CORCOMPILE_DEPENDENCY_PEKIND_MASK) >> CORCOMPILE_DEPENDENCY_PEKIND_SHIFT);
+ END_ENTRYPOINT_NOTHROW;
+
+ return S_OK;
+ }
+
+protected:
+
+ LONG m_cRef;
+ CORCOMPILE_DEPENDENCY * m_pDependency;
+};
+
+// ===========================================================================
+// *** INativeImageInstallInfo ***
+// ===========================================================================
+
+STDMETHODIMP AssemblyMDInternalImport::GetSignature(CORCOMPILE_NGEN_SIGNATURE * pNgenSign)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pNgenSign = m_pZapVersionInfo->signature;
+ END_ENTRYPOINT_NOTHROW;
+
+ return S_OK;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetVersionInfo(CORCOMPILE_VERSION_INFO * pVersionInfo)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pVersionInfo = *m_pZapVersionInfo;
+ END_ENTRYPOINT_NOTHROW;
+ return S_OK;
+}
+
+
+
+STDMETHODIMP AssemblyMDInternalImport::GetILSignature(CORCOMPILE_ASSEMBLY_SIGNATURE * pILSign)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pILSign = m_pZapVersionInfo->sourceAssembly;
+ END_ENTRYPOINT_NOTHROW;
+ return S_OK;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetConfigMask(DWORD * pConfigMask)
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ *pConfigMask = m_pZapVersionInfo->wConfigFlags;
+ END_ENTRYPOINT_NOTHROW;
+
+ return S_OK;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumDependencies (
+ HCORENUM * phEnum, // [IN/OUT] - Pointer to the enum
+ INativeImageDependency *rDeps[], // [OUT]
+ ULONG cMax, // Max dependancies to enumerate in this iteration
+ DWORD * pdwCount // [OUT] - Number of dependancies actually enumerated
+ )
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ CORCOMPILE_DEPENDENCY * pDependenciesEnd = m_pZapDependencies + m_cZapDependencies;
+
+ CORCOMPILE_DEPENDENCY * pNextDependency;
+
+ // Is the enum just being initialized, or are we walking an existing one?
+ if ((*phEnum) == NULL)
+ pNextDependency = m_pZapDependencies;
+ else
+ pNextDependency = (CORCOMPILE_DEPENDENCY *)(*phEnum);
+
+ DWORD count;
+ for (count = 0;
+ pNextDependency < pDependenciesEnd && count < cMax;
+ count++, pNextDependency++)
+ {
+ CNativeImageDependency * pDep = new (nothrow) CNativeImageDependency(pNextDependency);
+ IfNullGo( pDep );
+
+ rDeps[count] = pDep;
+ }
+
+ *phEnum = (HCORENUM)(pNextDependency < pDependenciesEnd) ? pNextDependency : NULL;
+ *pdwCount = count;
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+}
+
+
+STDMETHODIMP AssemblyMDInternalImport::GetDependency (
+ const CORCOMPILE_NGEN_SIGNATURE *pcngenSign, // [IN] ngenSig of dependency you want
+ CORCOMPILE_DEPENDENCY *pDep // [OUT] matching dependency
+ )
+{
+ HRESULT hr = S_OK;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(pcngenSign != NULL);
+ _ASSERTE(*pcngenSign != INVALID_NGEN_SIGNATURE);
+ _ASSERTE(pDep != NULL);
+
+ CORCOMPILE_DEPENDENCY * pDependenciesEnd = m_pZapDependencies + m_cZapDependencies;
+ CORCOMPILE_DEPENDENCY * pNextDependency = m_pZapDependencies;
+ while (pNextDependency != pDependenciesEnd)
+ {
+ if (pNextDependency->signNativeImage == *pcngenSign)
+ {
+ *pDep = *pNextDependency;
+ hr = S_OK;
+ goto ErrExit;
+ }
+ pNextDependency++;
+ }
+ hr = S_FALSE;
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+}
+
+
+#endif // FEATURE_PREJIT
+
+
+//*****************************************************************************
+// IMetaDataImport2 methods
+//*****************************************************************************
+STDMETHODIMP AssemblyMDInternalImport::GetGenericParamProps( // S_OK or error.
+ mdGenericParam gp, // [IN] GenericParam
+ ULONG *pulParamSeq, // [OUT] Index of the type parameter
+ DWORD *pdwParamFlags, // [OUT] Flags, for future use (e.g. variance)
+ mdToken *ptOwner, // [OUT] Owner (TypeDef or MethodDef)
+ DWORD *reserved, // [OUT] For future use (e.g. non-type parameters)
+ __out_ecount (cchName) LPWSTR wzname, // [OUT] Put name here
+ ULONG cchName, // [IN] Size of buffer
+ ULONG *pchName) // [OUT] Put size of name (wide chars) here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetGenericParamConstraintProps( // S_OK or error.
+ mdGenericParamConstraint gpc, // [IN] GenericParamConstraint
+ mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained
+ mdToken *ptkConstraintType) // [OUT] TypeDef/Ref/Spec constraint
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumGenericParams( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] TypeDef or MethodDef whose generic parameters are requested
+ mdGenericParam rGenericParams[], // [OUT] Put GenericParams here.
+ ULONG cMax, // [IN] Max GenericParams to put.
+ ULONG *pcGenericParams) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumGenericParamConstraints( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdGenericParam tk, // [IN] GenericParam whose constraints are requested
+ mdGenericParamConstraint rGenericParamConstraints[], // [OUT] Put GenericParamConstraints here.
+ ULONG cMax, // [IN] Max GenericParamConstraints to put.
+ ULONG *pcGenericParamConstraints) // [OUT] Put # put here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::EnumMethodSpecs( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] MethodDef or MemberRef whose MethodSpecs are requested
+ mdMethodSpec rMethodSpecs[], // [OUT] Put MethodSpecs here.
+ ULONG cMax, // [IN] Max tokens to put.
+ ULONG *pcMethodSpecs) // [OUT] Put actual count here.
+{
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetPEKind( // S_OK or error.
+ DWORD* pdwPEKind, // [OUT] The kind of PE (0 - not a PE)
+ DWORD* pdwMachine) // [OUT] Machine as defined in NT header
+{
+ HRESULT hr = S_OK;
+ if(pdwPEKind) *pdwPEKind = m_dwPEKind;
+ if(pdwMachine) *pdwMachine = m_dwMachine;
+ return hr;
+}
+
+STDMETHODIMP AssemblyMDInternalImport::GetVersionString( // S_OK or error.
+ __out_ecount (ccBufSize) LPWSTR pwzBuf, // Put version string here.
+ DWORD ccBufSize, // [in] size of the buffer, in wide chars
+ DWORD *pccBufSize) // [out] Size of the version string, wide chars, including terminating nul.
+{
+ HRESULT hr=S_OK;
+ DWORD L = WszMultiByteToWideChar(CP_UTF8,0,m_szVersionString,-1,pwzBuf,ccBufSize);
+ if(ccBufSize < L)
+ hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
+
+ if(pccBufSize) *pccBufSize = L;
+ return hr;
+}
+
+#endif //!DACCESS_COMPILE
+
+#endif // FEATURE_FUSION
+
+#endif //FEATURE_METADATA_INTERNAL_APIS
diff --git a/src/md/runtime/mdinternaldisp.h b/src/md/runtime/mdinternaldisp.h
new file mode 100644
index 0000000000..689e1f498e
--- /dev/null
+++ b/src/md/runtime/mdinternaldisp.h
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDInternalDispenser.h
+//
+
+//
+// Contains utility code for MD directory
+//
+//*****************************************************************************
+#ifndef __MDInternalDispenser__h__
+#define __MDInternalDispenser__h__
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+#include "mdinternalro.h"
+
+
+enum MDFileFormat
+{
+ MDFormat_ReadOnly = 0,
+ MDFormat_ReadWrite = 1,
+ MDFormat_ICR = 2,
+ MDFormat_Invalid = 3
+};
+
+
+HRESULT CheckFileFormat(LPVOID pData, ULONG cbData, MDFileFormat *pFormat);
+STDAPI GetMDInternalInterface(
+ LPVOID pData, // [IN] Buffer with the metadata.
+ ULONG cbData, // [IN] Size of the data in the buffer.
+ DWORD flags, // [IN] MDInternal_OpenForRead or MDInternal_OpenForENC
+ REFIID riid, // [in] The interface desired.
+ void **ppIUnk); // [out] Return interface on success.
+
+HRESULT GetAssemblyMDInternalImportHelper(HCORMODULE hModule,
+ REFIID riid,
+ MDInternalImportFlags flags,
+ IUnknown **ppIUnk);
+
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+#endif // __MDInternalDispenser__h__
diff --git a/src/md/runtime/mdinternalro.cpp b/src/md/runtime/mdinternalro.cpp
new file mode 100644
index 0000000000..d16d2b02eb
--- /dev/null
+++ b/src/md/runtime/mdinternalro.cpp
@@ -0,0 +1,3742 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ===========================================================================
+// File: MDInternalRO.CPP
+//
+
+// Notes:
+//
+//
+// ===========================================================================
+#include "stdafx.h"
+#include "mdinternalro.h"
+#include "metamodelro.h"
+#include "liteweightstgdb.h"
+#include "corhlpr.h"
+#include "../compiler/regmeta.h"
+#include "caparser.h"
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+__checkReturn
+HRESULT _FillMDDefaultValue(
+ BYTE bType,
+ void const *pValue,
+ ULONG cbValue,
+ MDDefaultValue *pMDDefaultValue);
+
+#ifndef DACCESS_COMPILE
+__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
+#endif //!DACCESS_COMPILE
+
+__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.
+
+// forward declaration
+__checkReturn
+HRESULT MDApplyEditAndContinue( // S_OK or error.
+ IMDInternalImport **ppIMD, // [in, out] The metadata to be updated.
+ IMDInternalImportENC *pDeltaMD); // [in] The delta metadata.
+
+
+//*****************************************************************************
+// Constructor
+//*****************************************************************************
+MDInternalRO::MDInternalRO()
+ : m_pMethodSemanticsMap(0),
+ m_cRefs(1)
+{
+} // MDInternalRO::MDInternalRO
+
+
+
+//*****************************************************************************
+// Destructor
+//*****************************************************************************
+MDInternalRO::~MDInternalRO()
+{
+ m_LiteWeightStgdb.Uninit();
+ if (m_pMethodSemanticsMap)
+ delete[] m_pMethodSemanticsMap;
+ m_pMethodSemanticsMap = 0;
+} // MDInternalRO::~MDInternalRO
+
+//*****************************************************************************
+// IUnknown
+//*****************************************************************************
+ULONG MDInternalRO::AddRef()
+{
+ return InterlockedIncrement(&m_cRefs);
+} // MDInternalRO::AddRef
+
+ULONG MDInternalRO::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRefs);
+ if (cRef == 0)
+ delete this;
+ return cRef;
+} // MDInternalRO::Release
+
+__checkReturn
+HRESULT MDInternalRO::QueryInterface(REFIID riid, void **ppUnk)
+{
+ *ppUnk = 0;
+
+ if (riid == IID_IUnknown)
+ *ppUnk = this; // ! QI for IID_IUnknown must return MDInternalRO. ConvertRO2RW() has dependency on this.
+ else if (riid == IID_IMDInternalImport)
+ *ppUnk = (IMDInternalImport *)this;
+ else if (riid == IID_IMDCommon)
+ *ppUnk = (IMDCommon *)this;
+ else
+ return E_NOINTERFACE;
+ AddRef();
+ return S_OK;
+} // MDInternalRO::QueryInterface
+
+
+//*****************************************************************************
+// Initialize
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::Init(
+ LPVOID pData, // points to meta data section in memory
+ ULONG cbData) // count of bytes in pData
+{
+ m_tdModule = COR_GLOBAL_PARENT_TOKEN;
+
+ extern HRESULT _CallInitOnMemHelper(CLiteWeightStgdb<CMiniMd> *pStgdb, ULONG cbData, LPCVOID pData);
+
+ return _CallInitOnMemHelper(&m_LiteWeightStgdb, cbData, (BYTE*) pData);
+} // MDInternalRO::Init
+
+#ifndef DACCESS_COMPILE
+//*****************************************************************************
+// Given a scope, determine whether imported from a typelib.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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);
+} // MDInternalRO::TranslateSigWithScope
+#endif // DACCESS_COMPILE
+
+__checkReturn
+HRESULT MDInternalRO::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_LiteWeightStgdb.m_MiniMd.GetTypeDefRefTokenInTypeSpec(tkTypeSpec, tkEnclosedToken);
+} // MDInternalRO::GetTypeDefRefTokenInTypeSpec
+
+#ifndef DACCESS_COMPILE
+//*****************************************************************************
+// Given a scope, return the number of tokens in a given table
+//*****************************************************************************
+ULONG MDInternalRO::GetCountWithTokenKind( // return hresult
+ DWORD tkKind) // [IN] pass in the kind of token.
+{
+ ULONG ulCount = m_LiteWeightStgdb.m_MiniMd.CommonGetRowCount(tkKind);
+ if (tkKind == mdtTypeDef)
+ {
+ // Remove global typedef from the count of typedefs (and handle the case where there is no global typedef)
+ if (ulCount > 0)
+ ulCount--;
+ }
+ return ulCount;
+} // MDInternalRO::GetCountWithTokenKind
+#endif //!DACCESS_COMPILE
+
+//*******************************************************************************
+// Enumerator helpers
+//*******************************************************************************
+
+
+//*****************************************************************************
+// enumerator init for typedef
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::EnumTypeDefInit( // return hresult
+ HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data
+{
+ HRESULT hr = NOERROR;
+
+ _ASSERTE(phEnum);
+
+ memset(phEnum, 0, sizeof(HENUMInternal));
+ phEnum->m_tkKind = mdtTypeDef;
+ phEnum->m_EnumType = MDSimpleEnum;
+ phEnum->m_ulCount = m_LiteWeightStgdb.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 --;
+
+ return hr;
+} // MDInternalRO::EnumTypeDefInit
+
+
+//*****************************************************************************
+// get the number of typedef in a scope
+//*****************************************************************************
+ULONG MDInternalRO::EnumTypeDefGetCount(
+ HENUMInternal *phEnum) // [IN] the enumerator to retrieve information
+{
+ _ASSERTE(phEnum->m_tkKind == mdtTypeDef);
+ return phEnum->m_ulCount;
+} // MDInternalRO::EnumTypeDefGetCount
+
+
+//*****************************************************************************
+// enumerator for typedef
+//*****************************************************************************
+bool MDInternalRO::EnumTypeDefNext( // return hresult
+ HENUMInternal *phEnum, // [IN] input enum
+ mdTypeDef *ptd) // [OUT] return token
+{
+ _ASSERTE(phEnum && ptd);
+
+ if (phEnum->u.m_ulCur >= phEnum->u.m_ulEnd)
+ return false;
+
+ *ptd = phEnum->u.m_ulCur++;
+ RidToToken(*ptd, mdtTypeDef);
+ return true;
+} // MDInternalRO::EnumTypeDefNext
+
+
+//*****************************************
+// Reset the enumerator to the beginning.
+//*****************************************
+void MDInternalRO::EnumTypeDefReset(
+ HENUMInternal *phEnum) // [IN] the enumerator to be reset
+{
+ _ASSERTE(phEnum);
+ _ASSERTE( phEnum->m_EnumType == MDSimpleEnum );
+
+ // not using CRCURSOR
+ phEnum->u.m_ulCur = phEnum->u.m_ulStart;
+} // MDInternalRO::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 MDInternalRO::EnumTypeDefClose(
+ HENUMInternal *phEnum) // [IN] the enumerator to be closed
+{
+ _ASSERTE( phEnum->m_EnumType == MDSimpleEnum );
+} // MDInternalRO::EnumTypeDefClose
+
+
+//*****************************************************************************
+// Enumerator init for MethodImpl. The second HENUMInternal* parameter is
+// only used for the R/W version of the MetaData.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+{
+ return EnumInit(TBL_MethodImpl << 24, td, phEnumBody);
+} // MDInternalRO::EnumMethodImplInit
+
+//*****************************************************************************
+// get the number of MethodImpls in a scope
+//*****************************************************************************
+ULONG MDInternalRO::EnumMethodImplGetCount(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+{
+ _ASSERTE(phEnumBody && ((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl));
+ return phEnumBody->m_ulCount;
+} // MDInternalRO::EnumMethodImplGetCount
+
+
+//*****************************************************************************
+// enumerator for MethodImpl.
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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
+{
+ HRESULT hr;
+ MethodImplRec *pRecord;
+
+ _ASSERTE(phEnumBody && ((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl));
+ _ASSERTE(ptkBody && ptkDecl);
+
+ if (phEnumBody->u.m_ulCur >= phEnumBody->u.m_ulEnd)
+ {
+ return S_FALSE;
+ }
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodImplRecord(phEnumBody->u.m_ulCur, &pRecord));
+ *ptkBody = m_LiteWeightStgdb.m_MiniMd.getMethodBodyOfMethodImpl(pRecord);
+ *ptkDecl = m_LiteWeightStgdb.m_MiniMd.getMethodDeclarationOfMethodImpl(pRecord);
+ phEnumBody->u.m_ulCur++;
+
+ return S_OK;
+} // MDInternalRO::EnumMethodImplNext
+
+//*****************************************
+// Reset the enumerator to the beginning.
+//*****************************************
+void MDInternalRO::EnumMethodImplReset(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+{
+ _ASSERTE(phEnumBody && ((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl));
+ _ASSERTE(phEnumBody->m_EnumType == MDSimpleEnum);
+
+ phEnumBody->u.m_ulCur = phEnumBody->u.m_ulStart;
+} // MDInternalRO::EnumMethodImplReset
+
+
+//*****************************************
+// Close the enumerator.
+//*****************************************
+void MDInternalRO::EnumMethodImplClose(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+{
+ _ASSERTE(phEnumBody && ((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl));
+ _ASSERTE(phEnumBody->m_EnumType == MDSimpleEnum);
+} // MDInternalRO::EnumMethodImplClose
+
+
+//******************************************************************************
+// enumerator for global functions
+//******************************************************************************
+__checkReturn
+HRESULT MDInternalRO::EnumGlobalFunctionsInit( // return hresult
+ HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data
+{
+ return EnumInit(mdtMethodDef, m_tdModule, phEnum);
+}
+
+
+//******************************************************************************
+// enumerator for global Fields
+//******************************************************************************
+__checkReturn
+HRESULT MDInternalRO::EnumGlobalFieldsInit( // return hresult
+ HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data
+{
+ return EnumInit(mdtFieldDef, m_tdModule, phEnum);
+}
+
+
+//*****************************************
+// Enumerator initializer
+//*****************************************
+__checkReturn
+HRESULT MDInternalRO::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 ulMax = 0;
+
+ // Vars for query.
+ _ASSERTE(phEnum);
+ HENUMInternal::ZeroEnum(phEnum);
+
+ // cache the tkKind and the scope
+ phEnum->m_tkKind = TypeFromToken(tkKind);
+
+ TypeDefRec *pRec;
+
+ phEnum->m_EnumType = MDSimpleEnum;
+
+ switch (TypeFromToken(tkKind))
+ {
+ case mdtFieldDef:
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(tkParent), &pRec));
+ phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getFieldListOfTypeDef(pRec);
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(tkParent), &(phEnum->u.m_ulEnd)));
+ break;
+
+ case mdtMethodDef:
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(tkParent), &pRec));
+ phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getMethodListOfTypeDef(pRec);
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(tkParent), &(phEnum->u.m_ulEnd)));
+ break;
+
+ case mdtGenericParam:
+ _ASSERTE(TypeFromToken(tkParent) == mdtTypeDef || TypeFromToken(tkParent) == mdtMethodDef);
+
+ if (TypeFromToken(tkParent) != mdtTypeDef && TypeFromToken(tkParent) != mdtMethodDef)
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+
+ if (TypeFromToken(tkParent) == mdtTypeDef)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getGenericParamsForTypeDef(
+ RidFromToken(tkParent),
+ &phEnum->u.m_ulEnd,
+ &(phEnum->u.m_ulStart)));
+ }
+ else
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getGenericParamsForMethodDef(
+ RidFromToken(tkParent),
+ &phEnum->u.m_ulEnd,
+ &(phEnum->u.m_ulStart)));
+ }
+ break;
+
+ case mdtGenericParamConstraint:
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getGenericParamConstraintsForGenericParam(
+ RidFromToken(tkParent),
+ &phEnum->u.m_ulEnd,
+ &phEnum->u.m_ulStart));
+ break;
+
+ case mdtInterfaceImpl:
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getInterfaceImplsForTypeDef(RidFromToken(tkParent), &phEnum->u.m_ulEnd, &phEnum->u.m_ulStart));
+ break;
+
+ case mdtProperty:
+ RID ridPropertyMap;
+ PropertyMapRec *pPropertyMapRec;
+
+ // get the starting/ending rid of properties of this typedef
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindPropertyMapFor(RidFromToken(tkParent), &ridPropertyMap));
+ if (!InvalidRid(ridPropertyMap))
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec));
+ phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getPropertyListOfPropertyMap(pPropertyMapRec);
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndPropertyListOfPropertyMap(ridPropertyMap, &(phEnum->u.m_ulEnd)));
+ ulMax = m_LiteWeightStgdb.m_MiniMd.getCountPropertys() + 1;
+ if(phEnum->u.m_ulStart == 0) phEnum->u.m_ulStart = 1;
+ if(phEnum->u.m_ulEnd > ulMax) phEnum->u.m_ulEnd = ulMax;
+ if(phEnum->u.m_ulStart > phEnum->u.m_ulEnd) phEnum->u.m_ulStart = phEnum->u.m_ulEnd;
+ }
+ break;
+
+ case mdtEvent:
+ RID ridEventMap;
+ EventMapRec *pEventMapRec;
+
+ // get the starting/ending rid of events of this typedef
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindEventMapFor(RidFromToken(tkParent), &ridEventMap));
+ if (!InvalidRid(ridEventMap))
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetEventMapRecord(ridEventMap, &pEventMapRec));
+ phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getEventListOfEventMap(pEventMapRec);
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndEventListOfEventMap(ridEventMap, &(phEnum->u.m_ulEnd)));
+ ulMax = m_LiteWeightStgdb.m_MiniMd.getCountEvents() + 1;
+ if(phEnum->u.m_ulStart == 0) phEnum->u.m_ulStart = 1;
+ if(phEnum->u.m_ulEnd > ulMax) phEnum->u.m_ulEnd = ulMax;
+ if(phEnum->u.m_ulStart > phEnum->u.m_ulEnd) phEnum->u.m_ulStart = phEnum->u.m_ulEnd;
+ }
+ break;
+
+ case mdtParamDef:
+ _ASSERTE(TypeFromToken(tkParent) == mdtMethodDef);
+
+ MethodRec *pMethodRec;
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(tkParent), &pMethodRec));
+
+ // figure out the start rid and end rid of the parameter list of this methoddef
+ phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getParamListOfMethod(pMethodRec);
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndParamListOfMethod(RidFromToken(tkParent), &(phEnum->u.m_ulEnd)));
+ break;
+ case mdtCustomAttribute:
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getCustomAttributeForToken(tkParent, &phEnum->u.m_ulEnd, &phEnum->u.m_ulStart));
+ break;
+ case mdtAssemblyRef:
+ _ASSERTE(IsNilToken(tkParent));
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountAssemblyRefs() + 1;
+ break;
+ case mdtFile:
+ _ASSERTE(IsNilToken(tkParent));
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountFiles() + 1;
+ break;
+ case mdtExportedType:
+ _ASSERTE(IsNilToken(tkParent));
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountExportedTypes() + 1;
+ break;
+ case mdtManifestResource:
+ _ASSERTE(IsNilToken(tkParent));
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountManifestResources() + 1;
+ break;
+ case mdtModuleRef:
+ _ASSERTE(IsNilToken(tkParent));
+ phEnum->u.m_ulStart = 1;
+ phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountModuleRefs() + 1;
+ break;
+ case (TBL_MethodImpl << 24):
+ _ASSERTE(! IsNilToken(tkParent));
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getMethodImplsForClass(
+ RidFromToken(tkParent),
+ &phEnum->u.m_ulEnd,
+ &phEnum->u.m_ulStart));
+ break;
+ default:
+ _ASSERTE(!"ENUM INIT not implemented for the compressed 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;
+}
+
+
+//*****************************************
+// Enumerator initializer
+//*****************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+
+ // 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_LiteWeightStgdb.m_MiniMd.getCountTypeRefs();
+ break;
+
+ case mdtMemberRef:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountMemberRefs();
+ break;
+
+ case mdtSignature:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountStandAloneSigs();
+ break;
+
+ case mdtMethodDef:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountMethods();
+ break;
+
+ case mdtMethodSpec:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountMethodSpecs();
+ break;
+
+ case mdtFieldDef:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountFields();
+ break;
+
+ case mdtTypeSpec:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountTypeSpecs();
+ break;
+
+ case mdtAssemblyRef:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountAssemblyRefs();
+ break;
+
+ case mdtModuleRef:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountModuleRefs();
+ break;
+
+ case mdtTypeDef:
+ phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountTypeDefs();
+ break;
+
+ case mdtFile:
+ phEnum->m_ulCount = m_LiteWeightStgdb.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;
+
+ // we are done
+ return hr;
+} // MDInternalRO::EnumAllInit
+
+
+//*****************************************
+// get the count
+//*****************************************
+ULONG MDInternalRO::EnumGetCount(
+ HENUMInternal *phEnum) // [IN] the enumerator to retrieve information
+{
+ _ASSERTE(phEnum);
+ return phEnum->m_ulCount;
+}
+
+//*****************************************
+// Get next value contained in the enumerator
+//*****************************************
+bool MDInternalRO::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;
+} // MDInternalRO::EnumNext
+
+
+//*****************************************
+// Reset the enumerator to the beginning.
+//*****************************************
+void MDInternalRO::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;
+} // MDInternalRO::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 MDInternalRO::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);
+} // MDInternalRO::EnumClose
+
+
+//---------------------------------------------------------------------------------------
+//
+// 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
+MDInternalRO::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;
+
+ _ASSERTE(phEnum != NULL);
+ HENUMInternal::ZeroEnum(phEnum);
+
+ // cache the tkKind
+ phEnum->m_tkKind = mdtPermission;
+
+ DeclSecurityRec *pDecl;
+ RID ridCur;
+ RID ridEnd;
+
+ phEnum->m_EnumType = MDSimpleEnum;
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getDeclSecurityForToken(tkParent, &ridEnd, &ridCur));
+ if (Action != dclActionNil)
+ {
+ for (; ridCur < ridEnd; ridCur++)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetDeclSecurityRecord(ridCur, &pDecl));
+ if (Action == m_LiteWeightStgdb.m_MiniMd.getActionOfDeclSecurity(pDecl))
+ {
+ // found a match
+ phEnum->u.m_ulStart = phEnum->u.m_ulCur = ridCur;
+ phEnum->u.m_ulEnd = ridCur + 1;
+ phEnum->m_ulCount = 1;
+ goto ErrExit;
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ phEnum->u.m_ulStart = phEnum->u.m_ulCur = ridCur;
+ phEnum->u.m_ulEnd = ridEnd;
+ phEnum->m_ulCount = ridEnd - ridCur;
+ }
+
+ErrExit:
+ return hr;
+} // MDInternalRO::EnumPermissionSetInit
+
+
+//*****************************************
+// Enumerator initializer for CustomAttributes
+//*****************************************
+__checkReturn
+HRESULT MDInternalRO::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_LiteWeightStgdb.m_MiniMd.CommonEnumCustomAttributeByName(tkParent, szName, false, phEnum);
+} // MDInternalRO::EnumCustomAttributeByNameInit
+
+//*****************************************
+// Enumerator for CustomAttributes which doesn't
+// allocate any memory
+//*****************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getCustomAttributeForToken(tkParent, &ridEnd, &ridStart));
+ // If found none, done.
+ if (ridStart == 0)
+ goto NoMatch;
+
+ 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;
+} // MDInternalRO::SafeAndSlowEnumCustomAttributeByNameInit
+
+__checkReturn
+HRESULT MDInternalRO::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_LiteWeightStgdb.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;
+} // MDInternalRO::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 MDInternalRO::GetParentToken(
+ mdToken tkChild, // [IN] given child token
+ mdToken *ptkParent) // [OUT] returning parent
+{
+ HRESULT hr = NOERROR;
+
+ _ASSERTE(ptkParent);
+
+ switch (TypeFromToken(tkChild))
+ {
+ case mdtTypeDef:
+ hr = GetNestedClassProps(tkChild, ptkParent);
+ // If not found, the *ptkParent has to be left unchanged! (callers depend on that)
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ hr = S_OK;
+ }
+ break;
+
+ case mdtMethodDef:
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindParentOfMethod(RidFromToken(tkChild), (RID *)ptkParent));
+ RidToToken(*ptkParent, mdtTypeDef);
+ break;
+
+ case mdtMethodSpec:
+ {
+ MethodSpecRec *pRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSpecRecord(RidFromToken(tkChild), &pRec));
+ *ptkParent = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSpec(pRec);
+ break;
+ }
+
+ case mdtFieldDef:
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindParentOfField(RidFromToken(tkChild), (RID *)ptkParent));
+ RidToToken(*ptkParent, mdtTypeDef);
+ break;
+
+ case mdtParamDef:
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindParentOfParam(RidFromToken(tkChild), (RID *)ptkParent));
+ RidToToken(*ptkParent, mdtMethodDef);
+ break;
+
+ case mdtMemberRef:
+ {
+ MemberRefRec *pRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMemberRefRecord(RidFromToken(tkChild), &pRec));
+ *ptkParent = m_LiteWeightStgdb.m_MiniMd.getClassOfMemberRef(pRec);
+ break;
+ }
+
+ case mdtCustomAttribute:
+ {
+ CustomAttributeRec *pRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetCustomAttributeRecord(RidFromToken(tkChild), &pRec));
+ *ptkParent = m_LiteWeightStgdb.m_MiniMd.getParentOfCustomAttribute(pRec);
+ break;
+ }
+
+ case mdtEvent:
+ hr = m_LiteWeightStgdb.m_MiniMd.FindParentOfEventHelper(tkChild, ptkParent);
+ break;
+
+ case mdtProperty:
+ hr = m_LiteWeightStgdb.m_MiniMd.FindParentOfPropertyHelper(tkChild, ptkParent);
+ break;
+
+ default:
+ _ASSERTE(!"NYI: for compressed format!");
+ break;
+ }
+ return hr;
+} // MDInternalRO::GetParentToken
+
+
+
+//*****************************************************************************
+// Get information about a CustomAttribute.
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetCustomAttributeProps( // S_OK or error.
+ mdCustomAttribute at, // The attribute.
+ mdToken *ptkType) // Put attribute type here.
+{
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(at) == mdtCustomAttribute);
+
+ // Do a linear search on compressed version as we do not want to
+ // depends on ICR.
+ //
+ CustomAttributeRec *pCustomAttributeRec;
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetCustomAttributeRecord(RidFromToken(at), &pCustomAttributeRec));
+ *ptkType = m_LiteWeightStgdb.m_MiniMd.getTypeOfCustomAttribute(pCustomAttributeRec);
+ return S_OK;
+} // MDInternalRO::GetCustomAttributeProps
+
+//*****************************************************************************
+// return custom value
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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
+{
+ HRESULT hr;
+ _ASSERTE(ppBlob && pcbSize && TypeFromToken(cv) == mdtCustomAttribute);
+
+ CustomAttributeRec *pCustomAttributeRec;
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetCustomAttributeRecord(RidFromToken(cv), &pCustomAttributeRec));
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getValueOfCustomAttribute(pCustomAttributeRec, (const BYTE **)ppBlob, pcbSize));
+ return S_OK;
+} // MDInternalRO::GetCustomAttributeAsBlob
+
+//*****************************************************************************
+// Helper function to lookup and retrieve a CustomAttribute.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+{
+ return m_LiteWeightStgdb.m_MiniMd.CommonGetCustomAttributeByName(tkObj, szName, ppData, pcbData);
+} // MDInternalRO::GetCustomAttributeByName
+
+
+//*****************************************************************************
+// return the name of a custom attribute
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+{
+ _ASSERTE(TypeFromToken(mdAttribute) == mdtCustomAttribute);
+
+ HRESULT hr = m_LiteWeightStgdb.m_MiniMd.CommonGetNameOfCustomAttribute(RidFromToken(mdAttribute), pszNamespace, pszName);
+ return (hr == S_FALSE) ? E_FAIL : hr;
+} // MDInternalRO::GetNameOfCustomAttribute
+
+//*****************************************************************************
+// return scope properties
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetScopeProps(
+ LPCSTR *pszName, // [OUT] scope name
+ GUID *pmvid) // [OUT] version id
+{
+ HRESULT hr;
+
+ ModuleRec *pModuleRec;
+
+ // there is only one module record
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetModuleRecord(1, &pModuleRec));
+
+ if (pmvid != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getMvidOfModule(pModuleRec, pmvid));
+ }
+ if (pszName != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfModule(pModuleRec, pszName));
+ }
+
+ return S_OK;
+} // MDInternalRO::GetScopeProps
+
+
+//*****************************************************************************
+// Compare two signatures from the same scope. Varags signatures need to be
+// preprocessed so they only contain the fixed part.
+//*****************************************************************************
+BOOL MDInternalRO::CompareSignatures(PCCOR_SIGNATURE pvFirstSigBlob, // First signature
+ DWORD cbFirstSigBlob, //
+ PCCOR_SIGNATURE pvSecondSigBlob, // Second signature
+ DWORD cbSecondSigBlob, //
+ void * SigArguments) // No additional arguments required
+{
+ if (cbFirstSigBlob != cbSecondSigBlob || memcmp(pvFirstSigBlob, pvSecondSigBlob, cbSecondSigBlob))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+//*****************************************************************************
+// Find a given member in a TypeDef (typically a class).
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+{
+
+ return FindMethodDefUsingCompare(classdef,
+ szName,
+ pvSigBlob,
+ cbSigBlob,
+ CompareSignatures,
+ NULL,
+ pmethoddef);
+}
+
+//*****************************************************************************
+// Find a given member in a TypeDef (typically a class).
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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 SigCompare, // [IN] Signature comparison routine
+ void* pSigArgs, // [IN] Additional arguments passed to signature compare
+ mdMethodDef *pmethoddef) // Put MemberDef token here.
+{
+ HRESULT hr = NOERROR;
+ PCCOR_SIGNATURE pvSigTemp = pvSigBlob;
+ CQuickBytes qbSig;
+
+ _ASSERTE(szName && pmethoddef);
+
+ // initialize the output parameter
+ *pmethoddef = mdMethodDefNil;
+
+ // check to see if this is a vararg signature
+ if ( isCallConv(CorSigUncompressCallingConv(pvSigTemp), IMAGE_CEE_CS_CALLCONV_VARARG) )
+ {
+ // Get the fix part of VARARG signature
+ IfFailGo( _GetFixedSigOfVarArg(pvSigBlob, cbSigBlob, &qbSig, &cbSigBlob) );
+ pvSigBlob = (PCCOR_SIGNATURE) qbSig.Ptr();
+ }
+
+ // Do a linear search on compressed version
+ //
+ RID ridMax;
+ MethodRec *pMethodRec;
+ LPCUTF8 szCurMethodName;
+ void const *pvCurMethodSig;
+ ULONG cbSig;
+ TypeDefRec *pRec;
+ RID ridStart;
+
+ // get the typedef record
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(classdef), &pRec));
+
+ // get the range of methoddef rids given the classdef
+ ridStart = m_LiteWeightStgdb.m_MiniMd.getMethodListOfTypeDef(pRec);
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(classdef), &ridMax));
+
+ // loop through each methoddef
+ for (; ridStart < ridMax; ridStart++)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(ridStart, &pMethodRec));
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNameOfMethod(pMethodRec, &szCurMethodName));
+ if (strcmp(szCurMethodName, szName) == 0)
+ {
+ // name match, now check the signature if specified.
+ if (cbSigBlob && SigCompare)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getSignatureOfMethod(pMethodRec, (PCCOR_SIGNATURE *)&pvCurMethodSig, &cbSig));
+ // Signature comparison is required
+ // Note that if pvSigBlob is vararg, we already preprocess it so that
+ // it only contains the fix part. Therefore, it still should be an exact
+ // match!!!.
+ //
+ if(SigCompare((PCCOR_SIGNATURE) pvCurMethodSig, cbSig, pvSigBlob, cbSigBlob, pSigArgs) == FALSE)
+ continue;
+ }
+ // Ignore PrivateScope methods.
+ if (IsMdPrivateScope(m_LiteWeightStgdb.m_MiniMd.getFlagsOfMethod(pMethodRec)))
+ continue;
+ // found the match
+ *pmethoddef = TokenFromRid(ridStart, mdtMethodDef);
+ goto ErrExit;
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+
+ErrExit:
+ return hr;
+}
+
+//*****************************************************************************
+// Find a given param of a Method.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+{
+ HRESULT hr;
+ ParamRec *pParamRec;
+ RID ridStart, ridEnd;
+
+ _ASSERTE(TypeFromToken(md) == mdtMethodDef && pparamdef);
+
+ // get the methoddef record
+ MethodRec *pMethodRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec));
+
+ // figure out the start rid and end rid of the parameter list of this methoddef
+ ridStart = m_LiteWeightStgdb.m_MiniMd.getParamListOfMethod(pMethodRec);
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getEndParamListOfMethod(RidFromToken(md), &ridEnd));
+
+ // Ensure that the paramList is valid. If the count is negative, the metadata
+ // is corrupted somehow. Thus, return CLDB_E_FILE_CORRUPT.
+ if (ridEnd < ridStart)
+ return CLDB_E_FILE_CORRUPT;
+
+ // loop through each param
+ //<TODO>@consider: parameters are sorted by sequence. Maybe a binary search?
+ //</TODO>
+ for (; ridStart < ridEnd; ridStart++)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetParamRecord(ridStart, &pParamRec));
+ if (iSeq == m_LiteWeightStgdb.m_MiniMd.getSequenceOfParam(pParamRec))
+ {
+ // parameter has the sequence number matches what we are looking for
+ *pparamdef = TokenFromRid(ridStart, mdtParamDef);
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+}
+
+
+
+//*****************************************************************************
+// return a pointer which points to meta data's internal string
+// return the the type name in utf8
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetNameOfTypeDef( // return hresult
+ mdTypeDef classdef, // given typedef
+ LPCSTR* pszname, // pointer to an internal UTF8 string
+ LPCSTR* psznamespace) // pointer to the namespace.
+{
+ HRESULT hr;
+
+ if (pszname != NULL)
+ {
+ *pszname = NULL;
+ }
+ if (psznamespace != NULL)
+ {
+ *psznamespace = NULL;
+ }
+
+ if (TypeFromToken(classdef) == mdtTypeDef)
+ {
+ TypeDefRec *pTypeDefRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(classdef), &pTypeDefRec));
+
+ if (pszname != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfTypeDef(pTypeDefRec, pszname));
+ }
+
+ if (psznamespace != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeDef(pTypeDefRec, psznamespace));
+ }
+ return S_OK;
+ }
+
+ _ASSERTE(!"Invalid argument(s) of GetNameOfTypeDef");
+ return CLDB_E_INTERNALERROR;
+} // MDInternalRO::GetNameOfTypeDef
+
+
+__checkReturn
+HRESULT MDInternalRO::GetIsDualOfTypeDef(// return hresult
+ mdTypeDef classdef, // given classdef
+ ULONG *pDual) // [OUT] return dual flag here.
+{
+ ULONG iFace=0; // Iface type.
+ HRESULT hr; // A result.
+
+ hr = GetIfaceTypeOfTypeDef(classdef, &iFace);
+ if (hr == S_OK)
+ *pDual = (iFace == ifDual);
+ else
+ *pDual = 1;
+
+ return hr;
+} // MDInternalRO::GetIsDualOfTypeDef
+
+__checkReturn
+HRESULT MDInternalRO::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.
+
+ // 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)
+ {
+ CustomAttributeParser cap(pVal, cbVal);
+ BYTE u1;
+ if (SUCCEEDED(cap.SkipProlog()) &&
+ SUCCEEDED(cap.GetU1(&u1)))
+ {
+ ItfType = u1;
+ }
+ if (ItfType >= ifLast)
+ ItfType = DEFAULT_COM_INTERFACE_TYPE;
+ }
+
+ // Set the return value.
+ *pIface = ItfType;
+
+ return hr;
+} // MDInternalRO::GetIfaceTypeOfTypeDef
+
+//*****************************************************************************
+// Given a methoddef, return a pointer to methoddef's name
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetNameOfMethodDef(
+ mdMethodDef md,
+ LPCSTR *pszMethodName)
+{
+ HRESULT hr;
+ MethodRec *pMethodRec;
+ *pszMethodName = NULL;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfMethod(pMethodRec, pszMethodName));
+ return S_OK;
+} // MDInternalRO::GetNameOfMethodDef
+
+//*****************************************************************************
+// Given a methoddef, return a pointer to methoddef's signature and methoddef's name
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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;
+ // Output parameter should not be NULL
+ _ASSERTE(ppvSigBlob && pcbSigBlob);
+ _ASSERTE(TypeFromToken(methoddef) == mdtMethodDef);
+
+ MethodRec *pMethodRec;
+ *pszMethodName = NULL;
+ *ppvSigBlob = NULL;
+ *pcbSigBlob = 0;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(methoddef), &pMethodRec));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfMethod(pMethodRec, (PCCOR_SIGNATURE *)ppvSigBlob, pcbSigBlob));
+
+ return GetNameOfMethodDef(methoddef, pszMethodName);
+} // MDInternalRO::GetNameAndSigOfMethodDef
+
+//*****************************************************************************
+// Given a FieldDef, return a pointer to FieldDef's name in UTF8
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetNameOfFieldDef( // return hresult
+ mdFieldDef fd, // given field
+ LPCSTR *pszFieldName)
+{
+ HRESULT hr;
+ FieldRec *pFieldRec;
+ *pszFieldName = NULL;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfField(pFieldRec, pszFieldName));
+ return S_OK;
+} // MDInternalRO::GetNameOfFieldDef
+
+
+//*****************************************************************************
+// Given a classdef, return the name and namespace of the typeref
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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;
+ TypeRefRec *pTypeRefRec;
+
+ *psznamespace = NULL;
+ *pszname = NULL;
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeRefRecord(RidFromToken(classref), &pTypeRefRec));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, psznamespace));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfTypeRef(pTypeRefRec, pszname));
+ return S_OK;
+}
+
+//*****************************************************************************
+// return the resolutionscope of typeref
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetResolutionScopeOfTypeRef(
+ mdTypeRef classref, // given classref
+ mdToken *ptkResolutionScope)
+{
+ _ASSERTE(TypeFromToken(classref) == mdtTypeRef && RidFromToken(classref));
+ HRESULT hr;
+
+ TypeRefRec *pTypeRefRec;
+
+ *ptkResolutionScope = mdTokenNil;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeRefRecord(RidFromToken(classref), &pTypeRefRec));
+ *ptkResolutionScope = m_LiteWeightStgdb.m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec);
+ return S_OK;
+} // MDInternalRO::GetResolutionScopeOfTypeRef
+
+//*****************************************************************************
+// Given a name, find the corresponding TypeRef.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+
+ _ASSERTE(ptk);
+
+ // initialize the output parameter
+ *ptk = mdTypeRefNil;
+
+ // Treat no namespace as empty string.
+ if (!szNamespace)
+ szNamespace = "";
+
+ // Do a linear search on compressed version as we do not want to
+ // depends on ICR.
+ //
+ ULONG cTypeRefRecs = m_LiteWeightStgdb.m_MiniMd.getCountTypeRefs();
+ TypeRefRec *pTypeRefRec;
+ LPCUTF8 szNamespaceTmp;
+ LPCUTF8 szNameTmp;
+ mdToken tkRes;
+
+ for (ULONG i = 1; i <= cTypeRefRecs; i++)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetTypeRefRecord(i, &pTypeRefRec));
+ tkRes = m_LiteWeightStgdb.m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec);
+
+ if (IsNilToken(tkRes))
+ {
+ if (!IsNilToken(tkResolutionScope))
+ continue;
+ }
+ else if (tkRes != tkResolutionScope)
+ continue;
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp));
+ if (strcmp(szNamespace, szNamespaceTmp))
+ continue;
+
+ IfFailGo(m_LiteWeightStgdb.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;
+}
+
+//*****************************************************************************
+// return flags for a given class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetTypeDefProps(
+ mdTypeDef td, // given classdef
+ DWORD *pdwAttr, // return flags on class
+ mdToken *ptkExtends) // [OUT] Put base class TypeDef/TypeRef here.
+{
+ HRESULT hr;
+ TypeDefRec *pTypeDefRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+
+ if (ptkExtends)
+ {
+ *ptkExtends = m_LiteWeightStgdb.m_MiniMd.getExtendsOfTypeDef(pTypeDefRec);
+ }
+ if (pdwAttr)
+ {
+ *pdwAttr = m_LiteWeightStgdb.m_MiniMd.getFlagsOfTypeDef(pTypeDefRec);
+ }
+
+ return S_OK;
+}
+
+
+//*****************************************************************************
+// return guid pointer to MetaData internal guid pool given a given class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetItemGuid( // return hresult
+ mdToken tkObj, // given item
+ CLSID *pGuid)
+{
+
+ HRESULT hr; // A result.
+ const BYTE *pBlob = NULL; // Blob with dispid.
+ ULONG cbBlob; // Length of blob.
+ 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);
+
+ WCHAR wzBlob[40]; // Wide char format of guid.
+ 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;
+} // MDInternalRO::GetItemGuid
+
+
+//*****************************************************************************
+// // get enclosing class of NestedClass
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetNestedClassProps( // S_OK or error
+ mdTypeDef tkNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptkEnclosingClass) // [OUT] EnclosingClass token.
+{
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(tkNestedClass) == mdtTypeDef && ptkEnclosingClass);
+
+ RID rid;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindNestedClassFor(RidFromToken(tkNestedClass), &rid));
+
+ if (InvalidRid(rid))
+ {
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ NestedClassRec *pRecord;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetNestedClassRecord(rid, &pRecord));
+ *ptkEnclosingClass = m_LiteWeightStgdb.m_MiniMd.getEnclosingClassOfNestedClass(pRecord);
+ return S_OK;
+ }
+}
+
+//*******************************************************************************
+// Get count of Nested classes given the enclosing class.
+//*******************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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_LiteWeightStgdb.m_MiniMd.getCountNestedClasss();
+
+ for (ULONG i = 1; i <= ulCount; i++)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetNestedClassRecord(i, &pRecord));
+ if (tkEnclosingClass == m_LiteWeightStgdb.m_MiniMd.getEnclosingClassOfNestedClass(pRecord))
+ ulRetCount++;
+ }
+ *pcNestedClassesCount = ulRetCount;
+ return S_OK;
+} // MDInternalRO::GetCountNestedClasses
+
+//*******************************************************************************
+// Return array of Nested classes given the enclosing class.
+//*******************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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_LiteWeightStgdb.m_MiniMd.getCountNestedClasss();
+
+ for (ULONG i = 1; i <= ulCount; i++)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetNestedClassRecord(i, &pRecord));
+ if (tkEnclosingClass == m_LiteWeightStgdb.m_MiniMd.getEnclosingClassOfNestedClass(pRecord))
+ {
+ if (ovadd_le(ulRetCount, 1, ulNestedClasses)) // ulRetCount is 0 based.
+ rNestedClasses[ulRetCount] = m_LiteWeightStgdb.m_MiniMd.getNestedClassOfNestedClass(pRecord);
+ ulRetCount++;
+ }
+ }
+ *pcNestedClasses = ulRetCount;
+ return S_OK;
+} // MDInternalRO::GetNestedClasses
+
+//*******************************************************************************
+// return the ModuleRef properties
+//*******************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+
+ // Is it a valid token?
+ if (!IsValidToken(mur))
+ {
+ *pszName = NULL; // Not every caller checks returned HRESULT, allow to fail fast in that case
+ return COR_E_BADIMAGEFORMAT; // Invalid Token
+ }
+
+ ModuleRefRec *pModuleRefRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetModuleRefRecord(RidFromToken(mur), &pModuleRefRec));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfModuleRef(pModuleRefRec, pszName));
+
+ return S_OK;
+}
+
+
+
+//*****************************************************************************
+// Given a scope and a methoddef, return a pointer to methoddef's signature
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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;
+ MethodRec *pMethodRec;
+ *ppSig = NULL;
+ *pcbSigBlob = 0;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(methoddef), &pMethodRec));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfMethod(pMethodRec, ppSig, pcbSigBlob));
+ return S_OK;
+} // MDInternalRO::GetSigOfMethodDef
+
+
+//*****************************************************************************
+// Given a scope and a fielddef, return a pointer to fielddef's signature
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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;
+ FieldRec *pFieldRec;
+ *ppSig = NULL;
+ *pcbSigBlob = 0;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFieldRecord(RidFromToken(fielddef), &pFieldRec));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfField(pFieldRec, ppSig, pcbSigBlob));
+ return S_OK;
+} // MDInternalRO::GetSigOfFieldDef
+
+//*****************************************************************************
+// Get signature for the token (FieldDef, MethodDef, Signature, or TypeSpec).
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetSigFromToken(
+ mdToken tk,
+ ULONG * pcbSig,
+ PCCOR_SIGNATURE * ppSig)
+{
+ HRESULT hr;
+
+ *ppSig = NULL;
+ *pcbSig = 0;
+ switch (TypeFromToken(tk))
+ {
+ case mdtSignature:
+ {
+ StandAloneSigRec * pRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetStandAloneSigRecord(RidFromToken(tk), &pRec));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfStandAloneSig(pRec, ppSig, pcbSig));
+ return S_OK;
+ }
+ case mdtTypeSpec:
+ {
+ TypeSpecRec * pRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeSpecRecord(RidFromToken(tk), &pRec));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfTypeSpec(pRec, ppSig, pcbSig));
+ return S_OK;
+ }
+ case mdtMethodDef:
+ {
+ IfFailRet(GetSigOfMethodDef(tk, pcbSig, ppSig));
+ return S_OK;
+ }
+ case mdtFieldDef:
+ {
+ IfFailRet(GetSigOfFieldDef(tk, pcbSig, ppSig));
+ return S_OK;
+ }
+ }
+
+ // not a known token type.
+ *pcbSig = 0;
+ return META_E_INVALID_TOKEN_TYPE;
+} // MDInternalRO::GetSigFromToken
+
+
+//*****************************************************************************
+// Given methoddef, return the flags
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetMethodDefProps(
+ mdMethodDef md,
+ DWORD *pdwFlags) // return mdPublic, mdAbstract, etc
+{
+ HRESULT hr;
+ MethodRec *pMethodRec;
+
+ *pdwFlags = (DWORD)-1;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec));
+ *pdwFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfMethod(pMethodRec);
+
+ return S_OK;
+} // MDInternalRO::GetMethodDefProps
+
+//*****************************************************************************
+// Given a scope and a methoddef/methodimpl, return RVA and impl flags
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetMethodImplProps(
+ mdMethodDef tk, // [IN] MethodDef
+ ULONG *pulCodeRVA, // [OUT] CodeRVA
+ DWORD *pdwImplFlags) // [OUT] Impl. Flags
+{
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(tk) == mdtMethodDef);
+
+ MethodRec *pMethodRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethodRec));
+
+ if (pulCodeRVA)
+ {
+ *pulCodeRVA = m_LiteWeightStgdb.m_MiniMd.getRVAOfMethod(pMethodRec);
+ }
+
+ if (pdwImplFlags)
+ {
+ *pdwImplFlags = m_LiteWeightStgdb.m_MiniMd.getImplFlagsOfMethod(pMethodRec);
+ }
+
+ return S_OK;
+} // MDInternalRO::GetMethodImplProps
+
+
+//*****************************************************************************
+// return the field RVA
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetFieldRVA(
+ mdToken fd, // [IN] FieldDef
+ ULONG *pulCodeRVA) // [OUT] CodeRVA
+{
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(fd) == mdtFieldDef);
+ _ASSERTE(pulCodeRVA);
+
+ RID iRecord;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindFieldRVAFor(RidFromToken(fd), &iRecord));
+
+ if (InvalidRid(iRecord))
+ {
+ if (pulCodeRVA)
+ *pulCodeRVA = 0;
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+
+ FieldRVARec *pFieldRVARec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFieldRVARecord(iRecord, &pFieldRVARec));
+
+ *pulCodeRVA = m_LiteWeightStgdb.m_MiniMd.getRVAOfFieldRVA(pFieldRVARec);
+ return NOERROR;
+}
+
+//*****************************************************************************
+// Given a fielddef, return the flags. Such as fdPublic, fdStatic, etc
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetFieldDefProps(
+ mdFieldDef fd, // given memberdef
+ DWORD *pdwFlags) // [OUT] return fdPublic, fdPrive, etc flags
+{
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(fd) == mdtFieldDef);
+
+ FieldRec *pFieldRec;
+
+ *pdwFlags = (DWORD)-1;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec));
+ *pdwFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfField(pFieldRec);
+
+ return S_OK;
+} // MDInternalRO::GetFieldDefProps
+
+//*****************************************************************************
+// return default value of a token(could be paramdef, fielddef, or property)
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindConstantFor(RidFromToken(tk), TypeFromToken(tk), &rid));
+ if (InvalidRid(rid))
+ {
+ pMDDefaultValue->m_bType = ELEMENT_TYPE_VOID;
+ return S_OK;
+ }
+ ConstantRec *pConstantRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetConstantRecord(rid, &pConstantRec));
+
+ // get the type of constant value
+ bType = m_LiteWeightStgdb.m_MiniMd.getTypeOfConstant(pConstantRec);
+
+ // get the value blob
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getValueOfConstant(pConstantRec, reinterpret_cast<const BYTE **>(&pValue), &cbValue));
+ // convert it to our internal default value representation
+ hr = _FillMDDefaultValue(bType, pValue, cbValue, pMDDefaultValue);
+ return hr;
+} // MDInternalRO::GetDefaultValue
+
+//*****************************************************************************
+// Given a scope and a methoddef/fielddef, return the dispid
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+ UINT32 dispid; // temporary for dispid.
+
+ // Get the DISPID, if any.
+ _ASSERTE(pDispid);
+
+ *pDispid = DISPID_UNKNOWN;
+ hr = GetCustomAttributeByName(tk, INTEROP_DISPID_TYPE, (const void**)&pBlob, &cbBlob);
+ if (hr == S_OK)
+ {
+ CustomAttributeParser cap(pBlob, cbBlob);
+ IfFailGo(cap.SkipProlog());
+ IfFailGo(cap.GetU4(&dispid));
+ *pDispid = dispid;
+ }
+
+ErrExit:
+ return hr;
+#else // FEATURE_COMINTEROP
+ _ASSERTE(false);
+ return E_NOTIMPL;
+#endif // FEATURE_COMINTEROP
+} // MDInternalRO::GetDispIdOfMemberDef
+
+//*****************************************************************************
+// Given interfaceimpl, return the TypeRef/TypeDef and flags
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetTypeOfInterfaceImpl( // return hresult
+ mdInterfaceImpl iiImpl, // given a interfaceimpl
+ mdToken *ptkType)
+{
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(iiImpl) == mdtInterfaceImpl);
+
+ *ptkType = mdTypeDefNil;
+
+ InterfaceImplRec *pIIRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetInterfaceImplRecord(RidFromToken(iiImpl), &pIIRec));
+ *ptkType = m_LiteWeightStgdb.m_MiniMd.getInterfaceOfInterfaceImpl(pIIRec);
+ return S_OK;
+} // MDInternalRO::GetTypeOfInterfaceImpl
+
+//*****************************************************************************
+// This routine gets the properties for the given MethodSpec token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+
+ LOG((LOGMD, "MD RegMeta::GetMethodSpecProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
+ mi, tkParent, ppvSigBlob, pcbSigBlob));
+
+ _ASSERTE(TypeFromToken(mi) == mdtMethodSpec);
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSpecRecord(RidFromToken(mi), &pMethodSpecRec));
+
+ if (tkParent)
+ *tkParent = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSpec(pMethodSpecRec);
+
+ if (ppvSigBlob || pcbSigBlob)
+ {
+ // caller wants signature information
+ PCCOR_SIGNATURE pvSigTmp;
+ ULONG cbSig;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getInstantiationOfMethodSpec(pMethodSpecRec, &pvSigTmp, &cbSig));
+ if ( ppvSigBlob )
+ *ppvSigBlob = pvSigTmp;
+ if ( pcbSigBlob)
+ *pcbSigBlob = cbSig;
+ }
+
+
+ return hr;
+} // MDInternalRO::GetMethodSpecProps
+
+
+
+//*****************************************************************************
+// Given a classname, return the typedef
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::FindTypeDef(
+ LPCSTR szTypeDefNamespace, // [IN] Namespace for the TypeDef.
+ LPCSTR szTypeDefName, // [IN] Name of the TypeDef.
+ mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef of enclosing class.
+ mdTypeDef * ptkTypeDef) // [OUT] return typedef
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE((szTypeDefName != NULL) && (ptkTypeDef != NULL));
+ _ASSERTE((TypeFromToken(tkEnclosingClass) == mdtTypeRef) ||
+ (TypeFromToken(tkEnclosingClass) == mdtTypeDef) ||
+ IsNilToken(tkEnclosingClass));
+
+ // initialize the output parameter
+ *ptkTypeDef = mdTypeDefNil;
+
+ // Treat no namespace as empty string.
+ if (szTypeDefNamespace == NULL)
+ szTypeDefNamespace = "";
+
+ // Do a linear search
+ ULONG cTypeDefRecs = m_LiteWeightStgdb.m_MiniMd.getCountTypeDefs();
+ TypeDefRec * pTypeDefRec;
+ LPCUTF8 szName;
+ LPCUTF8 szNamespace;
+ DWORD dwFlags;
+
+ // Get TypeDef of the tkEnclosingClass passed in
+ if (TypeFromToken(tkEnclosingClass) == mdtTypeRef)
+ {
+ TypeRefRec * pTypeRefRec;
+ mdToken tkResolutionScope;
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeRefRecord(RidFromToken(tkEnclosingClass), &pTypeRefRec));
+ tkResolutionScope = m_LiteWeightStgdb.m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec);
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, &szNamespace));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfTypeRef(pTypeRefRec, &szName));
+
+ // Update tkEnclosingClass to TypeDef
+ IfFailRet(FindTypeDef(
+ szNamespace,
+ szName,
+ (TypeFromToken(tkResolutionScope) == mdtTypeRef) ? tkResolutionScope : mdTokenNil,
+ &tkEnclosingClass));
+ _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef);
+ }
+
+ // Search for the TypeDef
+ for (ULONG i = 1; i <= cTypeDefRecs; i++)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(i, &pTypeDefRec));
+
+ dwFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfTypeDef(pTypeDefRec);
+
+ if (!IsTdNested(dwFlags) && !IsNilToken(tkEnclosingClass))
+ {
+ // If the class is not Nested and EnclosingClass passed in is not nil
+ continue;
+ }
+ else if (IsTdNested(dwFlags) && IsNilToken(tkEnclosingClass))
+ {
+ // If the class is nested and EnclosingClass passed is nil
+ continue;
+ }
+ else if (!IsNilToken(tkEnclosingClass))
+ {
+ _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef);
+
+ RID iNestedClassRec;
+ NestedClassRec * pNestedClassRec;
+ mdTypeDef tkEnclosingClassTmp;
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindNestedClassFor(i, &iNestedClassRec));
+ if (InvalidRid(iNestedClassRec))
+ continue;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetNestedClassRecord(iNestedClassRec, &pNestedClassRec));
+ tkEnclosingClassTmp = m_LiteWeightStgdb.m_MiniMd.getEnclosingClassOfNestedClass(pNestedClassRec);
+ if (tkEnclosingClass != tkEnclosingClassTmp)
+ continue;
+ }
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfTypeDef(pTypeDefRec, &szName));
+ if (strcmp(szTypeDefName, szName) == 0)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeDef(pTypeDefRec, &szNamespace));
+ if (strcmp(szTypeDefNamespace, szNamespace) == 0)
+ {
+ *ptkTypeDef = TokenFromRid(i, mdtTypeDef);
+ return S_OK;
+ }
+ }
+ }
+ // Cannot find the TypeDef by name
+ return CLDB_E_RECORD_NOTFOUND;
+} // MDInternalRO::FindTypeDef
+
+//*****************************************************************************
+// Given a memberref, return a pointer to memberref's name and signature
+//*****************************************************************************
+// Warning: Even when the return value is ok, *ppvSigBlob could be NULL if
+// the metadata is corrupted! (e.g. if CPackedLen::GetLength returned -1).
+// TODO: consider returning a HRESULT to make errors evident to the caller.
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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)
+{
+ _ASSERTE(TypeFromToken(memberref) == mdtMemberRef);
+
+ HRESULT hr;
+ MemberRefRec *pMemberRefRec;
+ *pszMemberRefName = NULL;
+ if (ppvSigBlob != NULL)
+ {
+ _ASSERTE(pcbSigBlob != NULL);
+ *ppvSigBlob = NULL;
+ *pcbSigBlob = 0;
+ }
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMemberRefRecord(RidFromToken(memberref), &pMemberRefRec));
+ if (ppvSigBlob != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfMemberRef(pMemberRefRec, ppvSigBlob, pcbSigBlob));
+ }
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfMemberRef(pMemberRefRec, pszMemberRefName));
+ return S_OK;
+} // MDInternalRO::GetNameAndSigOfMemberRef
+
+//*****************************************************************************
+// Given a memberref, return parent token. It can be a TypeRef, ModuleRef, or a MethodDef
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::GetParentOfMemberRef(
+ mdMemberRef memberref, // given a typedef
+ mdToken *ptkParent) // return the parent token
+{
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(memberref) == mdtMemberRef);
+
+ MemberRefRec *pMemberRefRec;
+
+ *ptkParent = mdTokenNil;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMemberRefRecord(RidFromToken(memberref), &pMemberRefRec));
+ *ptkParent = m_LiteWeightStgdb.m_MiniMd.getClassOfMemberRef(pMemberRefRec);
+
+ return S_OK;
+} // MDInternalRO::GetParentOfMemberRef
+
+//*****************************************************************************
+// return properties of a paramdef
+//*****************************************************************************/
+__checkReturn
+HRESULT
+MDInternalRO::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
+{
+ _ASSERTE(TypeFromToken(paramdef) == mdtParamDef);
+ HRESULT hr;
+ ParamRec *pParamRec;
+
+ *pszName = NULL;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetParamRecord(RidFromToken(paramdef), &pParamRec));
+ if (pdwAttr != NULL)
+ {
+ *pdwAttr = m_LiteWeightStgdb.m_MiniMd.getFlagsOfParam(pParamRec);
+ }
+ if (pusSequence != NULL)
+ {
+ *pusSequence = m_LiteWeightStgdb.m_MiniMd.getSequenceOfParam(pParamRec);
+ }
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfParam(pParamRec, pszName));
+
+ return S_OK;
+} // MDInternalRO::GetParamDefProps
+
+//*****************************************************************************
+// Get property info for the method.
+//*****************************************************************************
+int MDInternalRO::CMethodSemanticsMapSearcher::Compare(
+ const CMethodSemanticsMap *psFirst,
+ const CMethodSemanticsMap *psSecond)
+{
+ if (psFirst->m_mdMethod < psSecond->m_mdMethod)
+ return -1;
+ if (psFirst->m_mdMethod > psSecond->m_mdMethod)
+ return 1;
+ return 0;
+} // MDInternalRO::CMethodSemanticsMapSearcher::Compare
+
+#ifndef DACCESS_COMPILE
+int MDInternalRO::CMethodSemanticsMapSorter::Compare(
+ CMethodSemanticsMap *psFirst,
+ CMethodSemanticsMap *psSecond)
+{
+ if (psFirst->m_mdMethod < psSecond->m_mdMethod)
+ return -1;
+ if (psFirst->m_mdMethod > psSecond->m_mdMethod)
+ return 1;
+ return 0;
+} // MDInternalRO::CMethodSemanticsMapSorter::Compare
+
+__checkReturn
+HRESULT MDInternalRO::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
+{
+ HRESULT hr;
+ MethodSemanticsRec *pSemantics; // A MethodSemantics record.
+ MethodSemanticsRec *pFound=0; // A MethodSemantics record that is a property for the desired function.
+ RID ridCur; // loop control.
+ RID ridMax; // Count of entries in table.
+ USHORT usSemantics = 0; // A method's semantics.
+ mdToken tk; // A method def.
+
+ ridMax = m_LiteWeightStgdb.m_MiniMd.getCountMethodSemantics();
+
+ // Lazy initialization of m_pMethodSemanticsMap
+ if ((ridMax > 10) && (m_pMethodSemanticsMap == NULL))
+ {
+ NewHolder<CMethodSemanticsMap> pMethodSemanticsMap = new (nothrow) CMethodSemanticsMap[ridMax];
+ if (pMethodSemanticsMap != NULL)
+ {
+ // Fill the table in MethodSemantics order.
+ for (ridCur = 1; ridCur <= ridMax; ridCur++)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics));
+ tk = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSemantics(pSemantics);
+ pMethodSemanticsMap[ridCur-1].m_mdMethod = tk;
+ pMethodSemanticsMap[ridCur-1].m_ridSemantics = ridCur;
+ }
+ // Sort to MethodDef order.
+ CMethodSemanticsMapSorter sorter(pMethodSemanticsMap, ridMax);
+ sorter.Sort();
+
+ if (InterlockedCompareExchangeT<CMethodSemanticsMap *>(
+ &m_pMethodSemanticsMap, pMethodSemanticsMap, NULL) == NULL)
+ { // The exchange did happen, supress of the allocated map
+ pMethodSemanticsMap.SuppressRelease();
+ }
+ }
+ }
+
+ // Use m_pMethodSemanticsMap if it has been built.
+ if (m_pMethodSemanticsMap != NULL)
+ {
+ CMethodSemanticsMapSearcher searcher(m_pMethodSemanticsMap, ridMax);
+ CMethodSemanticsMap target;
+ const CMethodSemanticsMap * pMatchedMethod;
+ target.m_mdMethod = md;
+ pMatchedMethod = searcher.Find(&target);
+
+ // Was there at least one match?
+ if (pMatchedMethod != NULL)
+ {
+ _ASSERTE(pMatchedMethod >= m_pMethodSemanticsMap);
+ _ASSERTE(pMatchedMethod < m_pMethodSemanticsMap+ridMax);
+ _ASSERTE(pMatchedMethod->m_mdMethod == md);
+
+ ridCur = pMatchedMethod->m_ridSemantics;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics));
+ usSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics);
+
+ // If the semantics record is a getter or setter for the method, that's what we want.
+ if (usSemantics == msGetter || usSemantics == msSetter)
+ pFound = pSemantics;
+ else
+ { // The semantics record was neither getter or setter. Because there can be
+ // multiple semantics records for a given method, look for other semantics
+ // records that match this record.
+ const CMethodSemanticsMap *pScan;
+ const CMethodSemanticsMap *pLo=m_pMethodSemanticsMap;
+ const CMethodSemanticsMap *pHi=pLo+ridMax-1;
+ for (pScan = pMatchedMethod-1; pScan >= pLo; --pScan)
+ {
+ if (pScan->m_mdMethod == md)
+ {
+ ridCur = pScan->m_ridSemantics;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics));
+ usSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics);
+
+ if (usSemantics == msGetter || usSemantics == msSetter)
+ {
+ pFound = pSemantics;
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ if (pFound == 0)
+ { // Not found looking down, try looking up.
+ for (pScan = pMatchedMethod+1; pScan <= pHi; ++pScan)
+ {
+ if (pScan->m_mdMethod == md)
+ {
+ ridCur = pScan->m_ridSemantics;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics));
+ usSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics);
+
+ if (usSemantics == msGetter || usSemantics == msSetter)
+ {
+ pFound = pSemantics;
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ }
+ }
+ }
+ }
+ else
+ { // Scan entire table.
+ for (ridCur = 1; ridCur <= ridMax; ridCur++)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics));
+ if (md == m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSemantics(pSemantics))
+ { // The method matched, is this a property?
+ usSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics);
+ if (usSemantics == msGetter || usSemantics == msSetter)
+ { // found a match.
+ pFound = pSemantics;
+ break;
+ }
+ }
+ }
+ }
+
+ // Did the search find anything?
+ if (pFound)
+ { // found a match. Fill out the output parameters
+ PropertyRec *pProperty;
+ mdProperty prop;
+ prop = m_LiteWeightStgdb.m_MiniMd.getAssociationOfMethodSemantics(pFound);
+
+ if (ppd)
+ *ppd = prop;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetPropertyRecord(RidFromToken(prop), &pProperty));
+
+ if (pName != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfProperty(pProperty, pName));
+ }
+
+ if (pSemantic)
+ *pSemantic = usSemantics;
+ return S_OK;
+ }
+ return S_FALSE;
+} // MDInternalRO::GetPropertyInfoForMethodDef
+#endif //!DACCESS_COMPILE
+
+//*****************************************************************************
+// return the pack size of a class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetClassPackSize(
+ mdTypeDef td, // [IN] give typedef
+ DWORD *pdwPackSize) // [OUT]
+{
+ HRESULT hr = NOERROR;
+
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && pdwPackSize);
+
+ ClassLayoutRec *pRec;
+ RID ridClassLayout;
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindClassLayoutFor(RidFromToken(td), &ridClassLayout));
+ if (InvalidRid(ridClassLayout))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec));
+ *pdwPackSize = m_LiteWeightStgdb.m_MiniMd.getPackingSizeOfClassLayout(pRec);
+ErrExit:
+ return hr;
+} // MDInternalRO::GetClassPackSize
+
+
+//*****************************************************************************
+// return the total size of a value class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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
+{
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef && pulClassSize);
+
+ ClassLayoutRec *pRec;
+ HRESULT hr = NOERROR;
+ RID ridClassLayout;
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindClassLayoutFor(RidFromToken(td), &ridClassLayout));
+ if (InvalidRid(ridClassLayout))
+ {
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec));
+ *pulClassSize = m_LiteWeightStgdb.m_MiniMd.getClassSizeOfClassLayout(pRec);
+ErrExit:
+ return hr;
+} // MDInternalRO::GetClassTotalSize
+
+
+//*****************************************************************************
+// init the layout enumerator of a class
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetClassLayoutInit(
+ mdTypeDef td, // [IN] give typedef
+ MD_CLASS_LAYOUT *pmdLayout) // [OUT] set up the status of query here
+{
+ HRESULT hr = NOERROR;
+ _ASSERTE(TypeFromToken(td) == mdtTypeDef);
+
+ // initialize the output parameter
+ _ASSERTE(pmdLayout);
+ memset(pmdLayout, 0, sizeof(MD_CLASS_LAYOUT));
+
+ TypeDefRec *pTypeDefRec;
+
+ // record for this typedef in TypeDef Table
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec));
+
+ // find the starting and end field for this typedef
+ pmdLayout->m_ridFieldCur = m_LiteWeightStgdb.m_MiniMd.getFieldListOfTypeDef(pTypeDefRec);
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(td), &(pmdLayout->m_ridFieldEnd)));
+ return hr;
+} // MDInternalRO::GetClassLayoutInit
+
+
+//*****************************************************************************
+// return the field offset for a given field
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetFieldOffset(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulOffset) // [OUT] FieldOffset
+{
+ HRESULT hr = S_OK;
+ FieldLayoutRec *pRec;
+
+ _ASSERTE(pulOffset);
+
+ RID iLayout;
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindFieldLayoutFor(RidFromToken(fd), &iLayout));
+
+ if (InvalidRid(iLayout))
+ {
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetFieldLayoutRecord(iLayout, &pRec));
+ *pulOffset = m_LiteWeightStgdb.m_MiniMd.getOffSetOfFieldLayout(pRec);
+ _ASSERTE(*pulOffset != ULONG_MAX);
+
+ErrExit:
+ return hr;
+}
+
+
+//*****************************************************************************
+// enum the next the field layout
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+
+ // Make sure no one is messing with pLayout->m_ridFieldLayoutCur, since this doesn't
+ // mean anything if we are using FieldLayout table.
+ while (pLayout->m_ridFieldCur < pLayout->m_ridFieldEnd)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindFieldLayoutFor(pLayout->m_ridFieldCur, &iLayout2));
+ pLayout->m_ridFieldCur++;
+ if (!InvalidRid(iLayout2))
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetFieldLayoutRecord(iLayout2, &pRec));
+ *pulOffset = m_LiteWeightStgdb.m_MiniMd.getOffSetOfFieldLayout(pRec);
+ _ASSERTE(*pulOffset != ULONG_MAX);
+ *pfd = TokenFromRid(pLayout->m_ridFieldCur - 1, mdtFieldDef);
+ goto ErrExit;
+ }
+ }
+
+ *pfd = mdFieldDefNil;
+ hr = S_FALSE;
+
+ // fall through
+
+ErrExit:
+ return hr;
+} // MDInternalRO::GetClassLayoutNext
+
+
+//*****************************************************************************
+// return the field's native type signature
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+
+ // find the row containing the marshal definition for tk
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindFieldMarshalFor(RidFromToken(tk), TypeFromToken(tk), &rid));
+ if (InvalidRid(rid))
+ {
+ *pSigNativeType = NULL;
+ *pcbNativeType = 0;
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetFieldMarshalRecord(rid, &pFieldMarshalRec));
+
+ // get the native type
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNativeTypeOfFieldMarshal(pFieldMarshalRec, pSigNativeType, pcbNativeType));
+ErrExit:
+ return hr;
+} // MDInternalRO::GetFieldMarshal
+
+
+
+//*****************************************
+// property APIs
+//*****************************************
+
+//*****************************************************************************
+// Find property by name
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::FindProperty(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szPropName, // [IN] property name
+ mdProperty *pProp) // [OUT] return property token
+{
+ HRESULT hr = NOERROR;
+
+ // 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_LiteWeightStgdb.m_MiniMd.FindPropertyMapFor(RidFromToken(td), &ridPropertyMap));
+ if (InvalidRid(ridPropertyMap))
+ {
+ // not found!
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pRec));
+
+ // get the starting/ending rid of properties of this typedef
+ ridCur = m_LiteWeightStgdb.m_MiniMd.getPropertyListOfPropertyMap(pRec);
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd));
+
+ for (; ridCur < ridEnd; ridCur ++)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetPropertyRecord(ridCur, &pProperty));
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNameOfProperty(pProperty, &szName));
+ if (strcmp(szName, szPropName) ==0)
+ {
+ // Found the match. Set the output parameter and we are done.
+ *pProp = TokenFromRid(ridCur, mdtProperty);
+ goto ErrExit;
+ }
+ }
+
+ // not found
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+ return hr;
+
+} // MDInternalRO::FindProperty
+
+
+
+//*****************************************************************************
+// return the properties of a property
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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
+{
+ // output parameters have to be supplied
+ _ASSERTE(TypeFromToken(prop) == mdtProperty);
+
+ HRESULT hr;
+
+ PropertyRec *pProperty;
+ ULONG cbSig;
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetPropertyRecord(RidFromToken(prop), &pProperty));
+
+ // get name of the property
+ if (pszProperty != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfProperty(pProperty, pszProperty));
+ }
+
+ // get the flags of property
+ if (pdwPropFlags)
+ *pdwPropFlags = m_LiteWeightStgdb.m_MiniMd.getPropFlagsOfProperty(pProperty);
+
+ // get the type of the property
+ if (ppvSig != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getTypeOfProperty(pProperty, ppvSig, &cbSig));
+ if (pcbSig != NULL)
+ {
+ *pcbSig = cbSig;
+ }
+ }
+
+ return S_OK;
+} // MDInternalRO::GetPropertyProps
+
+
+//**********************************
+//
+// Event APIs
+//
+//**********************************
+
+//*****************************************************************************
+// return an event by given the name
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::FindEvent(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szEventName, // [IN] event name
+ mdEvent *pEvent) // [OUT] return event token
+{
+ HRESULT hr = NOERROR;
+
+ // 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_LiteWeightStgdb.m_MiniMd.FindEventMapFor(RidFromToken(td), &ridEventMap));
+ if (InvalidRid(ridEventMap))
+ {
+ // not found!
+ hr = CLDB_E_RECORD_NOTFOUND;
+ goto ErrExit;
+ }
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetEventMapRecord(ridEventMap, &pRec));
+
+ // get the starting/ending rid of properties of this typedef
+ ridCur = m_LiteWeightStgdb.m_MiniMd.getEventListOfEventMap(pRec);
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndEventListOfEventMap(ridEventMap, &ridEnd));
+
+ for (; ridCur < ridEnd; ridCur ++)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetEventRecord(ridCur, &pEventRec));
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNameOfEvent(pEventRec, &szName));
+ if (strcmp(szName, szEventName) ==0)
+ {
+ // Found the match. Set the output parameter and we are done.
+ *pEvent = TokenFromRid(ridCur, mdtEvent);
+ goto ErrExit;
+ }
+ }
+
+ // not found
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+ return hr;
+} // MDInternalRO::FindEvent
+
+
+//*****************************************************************************
+// return the properties of an event
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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
+{
+ // output parameters have to be supplied
+ _ASSERTE(TypeFromToken(ev) == mdtEvent);
+
+ HRESULT hr;
+ EventRec *pEvent;
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetEventRecord(RidFromToken(ev), &pEvent));
+ if (pszEvent != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfEvent(pEvent, pszEvent));
+ }
+ if (pdwEventFlags)
+ *pdwEventFlags = m_LiteWeightStgdb.m_MiniMd.getEventFlagsOfEvent(pEvent);
+ if (ptkEventType)
+ *ptkEventType = m_LiteWeightStgdb.m_MiniMd.getEventTypeOfEvent(pEvent);
+
+ return S_OK;
+} // MDInternalRO::GetEventProps
+
+//*****************************************************************************
+// return the properties of a generic param
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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_LiteWeightStgdb.m_MiniMd.SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+ _ASSERTE(TypeFromToken(rd) == mdtGenericParam);
+ if (TypeFromToken(rd) != mdtGenericParam)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetGenericParamRecord(RidFromToken(rd), &pGenericParamRec));
+
+ if (pulSequence)
+ *pulSequence = m_LiteWeightStgdb.m_MiniMd.getNumberOfGenericParam(pGenericParamRec);
+ if (pdwAttr)
+ *pdwAttr = m_LiteWeightStgdb.m_MiniMd.getFlagsOfGenericParam(pGenericParamRec);
+ if (ptOwner)
+ *ptOwner = m_LiteWeightStgdb.m_MiniMd.getOwnerOfGenericParam(pGenericParamRec);
+ if (szName != NULL)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNameOfGenericParam(pGenericParamRec, szName));
+ }
+ErrExit:
+ return hr;
+} // MDInternalRO::GetGenericParamProps
+
+//*****************************************************************************
+// This routine gets the properties for the given GenericParamConstraint token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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_LiteWeightStgdb.m_MiniMd.SupportsGenerics())
+ IfFailGo(CLDB_E_INCOMPATIBLE);
+
+ if((TypeFromToken(rd) == mdtGenericParamConstraint) && (ridRD != 0))
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetGenericParamConstraintRecord(ridRD, &pGPCRec));
+
+ if (ptGenericParam)
+ *ptGenericParam = TokenFromRid(m_LiteWeightStgdb.m_MiniMd.getOwnerOfGenericParamConstraint(pGPCRec),mdtGenericParam);
+ if (ptkConstraintType)
+ *ptkConstraintType = m_LiteWeightStgdb.m_MiniMd.getConstraintOfGenericParamConstraint(pGPCRec);
+ }
+ else
+ hr = META_E_BAD_INPUT_PARAMETER;
+
+ErrExit:
+ return hr;
+} // MDInternalRO::GetGenericParamConstraintProps
+
+//*****************************************************************************
+// Find methoddef of a particular associate with a property or an event
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+
+ // output parameters have to be supplied
+ _ASSERTE(pmd);
+ _ASSERTE(TypeFromToken(evprop) == mdtEvent || TypeFromToken(evprop) == mdtProperty);
+
+ MethodSemanticsRec *pSemantics;
+ RID ridCur;
+ RID ridEnd;
+
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.getAssociatesForToken(evprop, &ridEnd, &ridCur));
+ for (; ridCur < ridEnd; ridCur++)
+ {
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics));
+ if (dwSemantics == m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics))
+ {
+ // found a match
+ *pmd = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSemantics(pSemantics);
+ goto ErrExit;
+ }
+ }
+
+ // not found
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+ return hr;
+} // MDInternalRO::FindAssociate
+
+
+//*****************************************************************************
+// get counts of methodsemantics associated with a particular property/event
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::EnumAssociateInit(
+ mdToken evprop, // [IN] given a property or an event token
+ HENUMInternal *phEnum) // [OUT] cursor to hold the query result
+{
+ HRESULT hr;
+ _ASSERTE(phEnum);
+
+ memset(phEnum, 0, sizeof(HENUMInternal));
+
+ // There is no token kind!!!
+ phEnum->m_tkKind = ULONG_MAX;
+
+ // output parameters have to be supplied
+ _ASSERTE(TypeFromToken(evprop) == mdtEvent || TypeFromToken(evprop) == mdtProperty);
+
+ phEnum->m_EnumType = MDSimpleEnum;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getAssociatesForToken(evprop, &phEnum->u.m_ulEnd, &phEnum->u.m_ulStart));
+ phEnum->u.m_ulCur = phEnum->u.m_ulStart;
+ phEnum->m_ulCount = phEnum->u.m_ulEnd - phEnum->u.m_ulStart;
+
+ return S_OK;
+} // MDInternalRO::EnumAssociateInit
+
+
+//*****************************************************************************
+// get all methodsemantics associated with a particular property/event
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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
+{
+ _ASSERTE(phEnum && pAssociateRec);
+
+ HRESULT hr;
+ MethodSemanticsRec *pSemantics;
+ RID ridCur;
+ _ASSERTE(cAssociateRec == phEnum->m_ulCount);
+
+ // Convert from row pointers to RIDs.
+ for (ridCur = phEnum->u.m_ulStart; ridCur < phEnum->u.m_ulEnd; ++ridCur)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics));
+
+ pAssociateRec[ridCur-phEnum->u.m_ulStart].m_memberdef = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSemantics(pSemantics);
+ pAssociateRec[ridCur-phEnum->u.m_ulStart].m_dwSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics);
+ }
+
+ return S_OK;
+} // MDInternalRO::GetAllAssociates
+
+
+//*****************************************************************************
+// Get the Action and Permissions blob for a given PermissionSet.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+ _ASSERTE(TypeFromToken(pm) == mdtPermission);
+ _ASSERTE(pdwAction && ppvPermission && pcbPermission);
+
+ DeclSecurityRec *pPerm;
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetDeclSecurityRecord(RidFromToken(pm), &pPerm));
+ *pdwAction = m_LiteWeightStgdb.m_MiniMd.getActionOfDeclSecurity(pPerm);
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getPermissionSetOfDeclSecurity(pPerm, (const BYTE **)ppvPermission, pcbPermission));
+
+ return S_OK;
+} // MDInternalRO::GetPermissionSetProps
+
+//*****************************************************************************
+// Get the String given the String token.
+// Return a pointer to the string, or NULL in case of error.
+//*****************************************************************************
+__checkReturn
+HRESULT
+MDInternalRO::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;
+
+ if (pfIs80Plus != NULL)
+ {
+ *pfIs80Plus = FALSE;
+ }
+ *pwszUserString = NULL;
+ *pcchStringSize = 0;
+
+ _ASSERTE(pcchStringSize != NULL);
+ MetaData::DataBlob userString;
+ IfFailRet(m_LiteWeightStgdb.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;
+} // MDInternalRO::GetUserString
+
+//*****************************************************************************
+// Return contents of Pinvoke given the forwarded member token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetPinvokeMap(
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ LPCSTR *pszImportName, // [OUT] Import name.
+ mdModuleRef *pmrImportDLL) // [OUT] ModuleRef token for the target DLL.
+{
+ HRESULT hr;
+ ImplMapRec *pRecord;
+ RID iRecord;
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindImplMapFor(RidFromToken(tk), TypeFromToken(tk), &iRecord));
+ if (InvalidRid(iRecord))
+ {
+ return CLDB_E_RECORD_NOTFOUND;
+ }
+ else
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetImplMapRecord(iRecord, &pRecord));
+ }
+
+ if (pdwMappingFlags)
+ *pdwMappingFlags = m_LiteWeightStgdb.m_MiniMd.getMappingFlagsOfImplMap(pRecord);
+ if (pszImportName != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getImportNameOfImplMap(pRecord, pszImportName));
+ }
+ if (pmrImportDLL)
+ *pmrImportDLL = m_LiteWeightStgdb.m_MiniMd.getImportScopeOfImplMap(pRecord);
+
+ return S_OK;
+} // MDInternalRO::GetPinvokeMap
+
+//*****************************************************************************
+// Get the properties for the given Assembly token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+{
+ HRESULT hr;
+ AssemblyRec *pRecord;
+
+ _ASSERTE(TypeFromToken(mda) == mdtAssembly && RidFromToken(mda));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetAssemblyRecord(RidFromToken(mda), &pRecord));
+
+ if (ppbPublicKey != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getPublicKeyOfAssembly(pRecord, reinterpret_cast<const BYTE **>(ppbPublicKey), pcbPublicKey));
+ }
+ if (pulHashAlgId)
+ *pulHashAlgId = m_LiteWeightStgdb.m_MiniMd.getHashAlgIdOfAssembly(pRecord);
+ if (pszName != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfAssembly(pRecord, pszName));
+ }
+ if (pMetaData)
+ {
+ pMetaData->usMajorVersion = m_LiteWeightStgdb.m_MiniMd.getMajorVersionOfAssembly(pRecord);
+ pMetaData->usMinorVersion = m_LiteWeightStgdb.m_MiniMd.getMinorVersionOfAssembly(pRecord);
+ pMetaData->usBuildNumber = m_LiteWeightStgdb.m_MiniMd.getBuildNumberOfAssembly(pRecord);
+ pMetaData->usRevisionNumber = m_LiteWeightStgdb.m_MiniMd.getRevisionNumberOfAssembly(pRecord);
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getLocaleOfAssembly(pRecord, &pMetaData->szLocale));
+ pMetaData->ulProcessor = 0;
+ pMetaData->ulOS = 0;
+ }
+ if (pdwAssemblyFlags)
+ {
+ *pdwAssemblyFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfAssembly(pRecord);
+
+#ifdef FEATURE_WINDOWSPHONE
+ // Turn on the afPublicKey if PublicKey blob is not empty
+ DWORD cbPublicKey;
+ const BYTE *pbPublicKey;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getPublicKeyOfAssembly(pRecord, &pbPublicKey, &cbPublicKey));
+ if (cbPublicKey != 0)
+ *pdwAssemblyFlags |= afPublicKey;
+#else
+ if (ppbPublicKey)
+ {
+ if (pcbPublicKey && *pcbPublicKey)
+ *pdwAssemblyFlags |= afPublicKey;
+ }
+ else
+ {
+#ifdef _DEBUG
+ // Assert that afPublicKey is set if PublicKey blob is not empty
+ DWORD cbPublicKey;
+ const BYTE *pPublicKey;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getPublicKeyOfAssembly(pRecord, &pPublicKey, &cbPublicKey));
+ bool hasPublicKey = cbPublicKey != 0;
+ bool hasPublicKeyFlag = ( *pdwAssemblyFlags & afPublicKey ) != 0;
+ if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 0))
+ _ASSERTE( hasPublicKey == hasPublicKeyFlag );
+#endif
+ }
+#endif // FEATURE_WINDOWSPHONE
+ }
+
+ return S_OK;
+} // MDInternalRO::GetAssemblyProps
+
+//*****************************************************************************
+// Get the properties for the given AssemblyRef token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+{
+ HRESULT hr;
+ AssemblyRefRec *pRecord;
+
+ _ASSERTE(TypeFromToken(mdar) == mdtAssemblyRef && RidFromToken(mdar));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetAssemblyRefRecord(RidFromToken(mdar), &pRecord));
+
+ if (ppbPublicKeyOrToken != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getPublicKeyOrTokenOfAssemblyRef(pRecord, reinterpret_cast<const BYTE **>(ppbPublicKeyOrToken), pcbPublicKeyOrToken));
+ }
+ if (pszName != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfAssemblyRef(pRecord, pszName));
+ }
+ if (pMetaData)
+ {
+ pMetaData->usMajorVersion = m_LiteWeightStgdb.m_MiniMd.getMajorVersionOfAssemblyRef(pRecord);
+ pMetaData->usMinorVersion = m_LiteWeightStgdb.m_MiniMd.getMinorVersionOfAssemblyRef(pRecord);
+ pMetaData->usBuildNumber = m_LiteWeightStgdb.m_MiniMd.getBuildNumberOfAssemblyRef(pRecord);
+ pMetaData->usRevisionNumber = m_LiteWeightStgdb.m_MiniMd.getRevisionNumberOfAssemblyRef(pRecord);
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getLocaleOfAssemblyRef(pRecord, &pMetaData->szLocale));
+ pMetaData->ulProcessor = 0;
+ pMetaData->ulOS = 0;
+ }
+ if (ppbHashValue != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getHashValueOfAssemblyRef(pRecord, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue));
+ }
+ if (pdwAssemblyRefFlags != NULL)
+ {
+ *pdwAssemblyRefFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfAssemblyRef(pRecord);
+ }
+
+ return S_OK;
+} // MDInternalRO::GetAssemblyRefProps
+
+//*****************************************************************************
+// Get the properties for the given File token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+{
+ HRESULT hr;
+ FileRec *pRecord;
+
+ _ASSERTE(TypeFromToken(mdf) == mdtFile && RidFromToken(mdf));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFileRecord(RidFromToken(mdf), &pRecord));
+
+ if (pszName != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfFile(pRecord, pszName));
+ }
+ if (ppbHashValue != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getHashValueOfFile(pRecord, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue));
+ }
+ if (pdwFileFlags != NULL)
+ {
+ *pdwFileFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfFile(pRecord);
+ }
+
+ return S_OK;
+} // MDInternalRO::GetFileProps
+
+//*****************************************************************************
+// Get the properties for the given ExportedType token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetExportedTypeProps(
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ LPCSTR *pszNamespace, // [OUT] Buffer to fill with namespace.
+ 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.
+{
+ HRESULT hr;
+ ExportedTypeRec *pRecord;
+
+ _ASSERTE(TypeFromToken(mdct) == mdtExportedType && RidFromToken(mdct));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetExportedTypeRecord(RidFromToken(mdct), &pRecord));
+
+ if (pszNamespace != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getTypeNamespaceOfExportedType(pRecord, pszNamespace));
+ }
+ if (pszName != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getTypeNameOfExportedType(pRecord, pszName));
+ }
+ if (ptkImplementation)
+ *ptkImplementation = m_LiteWeightStgdb.m_MiniMd.getImplementationOfExportedType(pRecord);
+ if (ptkTypeDef)
+ *ptkTypeDef = m_LiteWeightStgdb.m_MiniMd.getTypeDefIdOfExportedType(pRecord);
+ if (pdwExportedTypeFlags)
+ *pdwExportedTypeFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfExportedType(pRecord);
+
+ return S_OK;
+} // MDInternalRO::GetExportedTypeProps
+
+//*****************************************************************************
+// Get the properties for the given Resource token.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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.
+{
+ HRESULT hr;
+ ManifestResourceRec *pRecord;
+
+ _ASSERTE(TypeFromToken(mdmr) == mdtManifestResource && RidFromToken(mdmr));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetManifestResourceRecord(RidFromToken(mdmr), &pRecord));
+
+ if (pszName != NULL)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfManifestResource(pRecord, pszName));
+ }
+ if (ptkImplementation)
+ *ptkImplementation = m_LiteWeightStgdb.m_MiniMd.getImplementationOfManifestResource(pRecord);
+ if (pdwOffset)
+ *pdwOffset = m_LiteWeightStgdb.m_MiniMd.getOffsetOfManifestResource(pRecord);
+ if (pdwResourceFlags)
+ *pdwResourceFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfManifestResource(pRecord);
+
+ return S_OK;
+} // MDInternalRO::GetManifestResourceProps
+
+//*****************************************************************************
+// Find the ExportedType given the name.
+//*****************************************************************************
+__checkReturn
+STDMETHODIMP MDInternalRO::FindExportedTypeByName( // S_OK or error
+ LPCSTR szNamespace, // [IN] Namespace of the ExportedType.
+ LPCSTR szName, // [IN] Name of the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] Token for the Enclosing Type.
+ mdExportedType *pmct) // [OUT] Put ExportedType token here.
+{
+ IMetaModelCommon *pCommon = static_cast<IMetaModelCommon*>(&m_LiteWeightStgdb.m_MiniMd);
+ return pCommon->CommonFindExportedType(szNamespace, szName, tkEnclosingType, pmct);
+} // MDInternalRO::FindExportedTypeByName
+
+//*****************************************************************************
+// Find the ManifestResource given the name.
+//*****************************************************************************
+__checkReturn
+STDMETHODIMP MDInternalRO::FindManifestResourceByName( // S_OK or error
+ LPCSTR szName, // [IN] Name of the resource.
+ mdManifestResource *pmmr) // [OUT] Put ManifestResource token here.
+{
+ _ASSERTE(szName && pmmr);
+
+ HRESULT hr;
+ ManifestResourceRec *pRecord;
+ ULONG cRecords; // Count of records.
+ LPCUTF8 szNameTmp = 0; // Name obtained from the database.
+ ULONG i;
+
+ cRecords = m_LiteWeightStgdb.m_MiniMd.getCountManifestResources();
+
+ // Search for the ExportedType.
+ for (i = 1; i <= cRecords; i++)
+ {
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetManifestResourceRecord(i, &pRecord));
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfManifestResource(pRecord, &szNameTmp));
+ if (! strcmp(szName, szNameTmp))
+ {
+ *pmmr = TokenFromRid(i, mdtManifestResource);
+ return S_OK;
+ }
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+} // MDInternalRO::FindManifestResourceByName
+
+//*****************************************************************************
+// Get the Assembly token from the given scope.
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::GetAssemblyFromScope( // S_OK or error
+ mdAssembly *ptkAssembly) // [OUT] Put token here.
+{
+ _ASSERTE(ptkAssembly);
+
+ if (m_LiteWeightStgdb.m_MiniMd.getCountAssemblys())
+ {
+ *ptkAssembly = TokenFromRid(1, mdtAssembly);
+ return S_OK;
+ }
+ else
+ return CLDB_E_RECORD_NOTFOUND;
+} // MDInternalRO::GetAssemblyFromScope
+
+//*******************************************************************************
+// return properties regarding a TypeSpec
+//*******************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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))
+ {
+ *ppvSig = NULL;
+ *pcbSig = 0;
+ return E_INVALIDARG;
+ }
+
+ TypeSpecRec *pRec;
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeSpecRecord(RidFromToken(typespec), &pRec));
+
+ if (pRec == NULL)
+ {
+ *ppvSig = NULL;
+ *pcbSig = 0;
+ return CLDB_E_FILE_CORRUPT;
+ }
+
+ IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfTypeSpec(pRec, ppvSig, pcbSig));
+
+ return hr;
+} // MDInternalRO::GetTypeSpecFromToken
+
+//*****************************************************************************
+// 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 MDInternalRO::GetVersionString(
+ LPCSTR * pVer) // [OUT] Put version string here.
+{
+ HRESULT hr = NOERROR;
+
+ if (m_LiteWeightStgdb.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_LiteWeightStgdb.m_pvMd)->pVersion);
+ }
+ else
+ { // No string.
+ *pVer = NULL;
+ }
+
+ return hr;
+} // MDInternalRO::GetVersionString
+
+//*****************************************************************************
+// convert a text signature to com format
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+} // MDInternalRO::ConvertTextSigToComSig
+
+
+//*****************************************************************************
+// determine if a token is valid or not
+//*****************************************************************************
+BOOL MDInternalRO::IsValidToken( // True or False.
+ mdToken tk) // [IN] Given token.
+{
+ RID rid = RidFromToken(tk);
+ if (rid == 0)
+ {
+ return FALSE;
+ }
+ switch (TypeFromToken(tk))
+ {
+ case mdtModule:
+ // can have only one module record
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountModules());
+ case mdtTypeRef:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountTypeRefs());
+ case mdtTypeDef:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountTypeDefs());
+ case mdtFieldDef:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountFields());
+ case mdtMethodDef:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountMethods());
+ case mdtParamDef:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountParams());
+ case mdtInterfaceImpl:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountInterfaceImpls());
+ case mdtMemberRef:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountMemberRefs());
+ case mdtCustomAttribute:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountCustomAttributes());
+ case mdtPermission:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountDeclSecuritys());
+ case mdtSignature:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountStandAloneSigs());
+ case mdtEvent:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountEvents());
+ case mdtProperty:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountPropertys());
+ case mdtModuleRef:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountModuleRefs());
+ case mdtTypeSpec:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountTypeSpecs());
+ case mdtAssembly:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountAssemblys());
+ case mdtAssemblyRef:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountAssemblyRefs());
+ case mdtFile:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountFiles());
+ case mdtExportedType:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountExportedTypes());
+ case mdtManifestResource:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountManifestResources());
+ case mdtMethodSpec:
+ return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountMethodSpecs());
+ case mdtString:
+ // need to check the user string heap
+ return m_LiteWeightStgdb.m_MiniMd.m_UserStringHeap.IsValidIndex(rid);
+ default:
+/* Don't Assert here, this will break verifier tests.
+ _ASSERTE(!"Unknown token kind!");
+*/
+ return FALSE;
+ }
+} // MDInternalRO::IsValidToken
+
+mdModule MDInternalRO::GetModuleFromScope(void)
+{
+ return TokenFromRid(1, mdtModule);
+} // MDInternalRO::GetModuleFromScope
+
+//*****************************************************************************
+// Fill a variant given a MDDefaultValue
+// This routine will create a bstr if the ELEMENT_TYPE of default value is STRING
+//*****************************************************************************
+__checkReturn
+HRESULT _FillVariant(
+ MDDefaultValue *pMDDefaultValue,
+ VARIANT *pvar)
+{
+ HRESULT hr = NOERROR;
+
+ _ASSERTE(pMDDefaultValue);
+
+ switch (pMDDefaultValue->m_bType)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ V_VT(pvar) = VT_BOOL;
+ V_BOOL(pvar) = pMDDefaultValue->m_bValue;
+ break;
+ case ELEMENT_TYPE_I1:
+ V_VT(pvar) = VT_I1;
+ V_I1(pvar) = pMDDefaultValue->m_cValue;
+ break;
+ case ELEMENT_TYPE_U1:
+ V_VT(pvar) = VT_UI1;
+ V_UI1(pvar) = pMDDefaultValue->m_byteValue;
+ break;
+ case ELEMENT_TYPE_I2:
+ V_VT(pvar) = VT_I2;
+ V_I2(pvar) = pMDDefaultValue->m_sValue;
+ break;
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR: // char is stored as UI2 internally
+ V_VT(pvar) = VT_UI2;
+ V_UI2(pvar) = pMDDefaultValue->m_usValue;
+ break;
+ case ELEMENT_TYPE_I4:
+ V_VT(pvar) = VT_I4;
+ V_I4(pvar) = pMDDefaultValue->m_lValue;
+ break;
+ case ELEMENT_TYPE_U4:
+ V_VT(pvar) = VT_UI4;
+ V_UI4(pvar) = pMDDefaultValue->m_ulValue;
+ break;
+ case ELEMENT_TYPE_R4:
+ V_VT(pvar) = VT_R4;
+ V_R4(pvar) = pMDDefaultValue->m_fltValue;
+ break;
+ case ELEMENT_TYPE_R8:
+ V_VT(pvar) = VT_R8;
+ V_R8(pvar) = pMDDefaultValue->m_dblValue;
+ break;
+ case ELEMENT_TYPE_STRING:
+ // allocated bstr here
+ V_BSTR(pvar) = ::SysAllocStringLen(pMDDefaultValue->m_wzValue, pMDDefaultValue->m_cbSize / sizeof(WCHAR));
+ if (V_BSTR(pvar) == NULL)
+ hr = E_OUTOFMEMORY;
+ V_VT(pvar) = VT_BSTR;
+ break;
+ case ELEMENT_TYPE_CLASS:
+ V_VT(pvar) = VT_UNKNOWN;
+ V_UNKNOWN(pvar) = pMDDefaultValue->m_unkValue;
+ break;
+ case ELEMENT_TYPE_I8:
+ V_VT(pvar) = VT_I8;
+ V_CY(pvar).int64 = pMDDefaultValue->m_llValue;
+ break;
+ case ELEMENT_TYPE_U8:
+ V_VT(pvar) = VT_UI8;
+ V_CY(pvar).int64 = pMDDefaultValue->m_ullValue;
+ break;
+ case ELEMENT_TYPE_VOID:
+ V_VT(pvar) = VT_EMPTY;
+ break;
+ default:
+ _ASSERTE(!"bad constant value type!");
+ }
+
+ return hr;
+} // _FillVariant
+
+
+//*****************************************************************************
+// Fill a variant given a MDDefaultValue
+// This routine will create a bstr if the ELEMENT_TYPE of default value is STRING
+//*****************************************************************************
+__checkReturn
+HRESULT _FillMDDefaultValue(
+ BYTE bType,
+ void const *pValue,
+ ULONG cbValue,
+ MDDefaultValue *pMDDefaultValue)
+{
+ HRESULT hr = NOERROR;
+
+ pMDDefaultValue->m_bType = bType;
+ pMDDefaultValue->m_cbSize = cbValue;
+ switch (bType)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ if (cbValue < 1)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_bValue = *((BYTE *) pValue);
+ break;
+ case ELEMENT_TYPE_I1:
+ if (cbValue < 1)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_cValue = *((CHAR *) pValue);
+ break;
+ case ELEMENT_TYPE_U1:
+ if (cbValue < 1)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_byteValue = *((BYTE *) pValue);
+ break;
+ case ELEMENT_TYPE_I2:
+ if (cbValue < 2)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_sValue = GET_UNALIGNED_VAL16(pValue);
+ break;
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+ if (cbValue < 2)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_usValue = GET_UNALIGNED_VAL16(pValue);
+ break;
+ case ELEMENT_TYPE_I4:
+ if (cbValue < 4)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_lValue = GET_UNALIGNED_VAL32(pValue);
+ break;
+ case ELEMENT_TYPE_U4:
+ if (cbValue < 4)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_ulValue = GET_UNALIGNED_VAL32(pValue);
+ break;
+ case ELEMENT_TYPE_R4:
+ {
+ if (cbValue < 4)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ __int32 Value = GET_UNALIGNED_VAL32(pValue);
+ pMDDefaultValue->m_fltValue = (float &)Value;
+ }
+ break;
+ case ELEMENT_TYPE_R8:
+ {
+ if (cbValue < 8)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ __int64 Value = GET_UNALIGNED_VAL64(pValue);
+ pMDDefaultValue->m_dblValue = (double &) Value;
+ }
+ break;
+ case ELEMENT_TYPE_STRING:
+ if (cbValue == 0)
+ pValue = NULL;
+
+#if BIGENDIAN
+ {
+ // We need to allocate and swap the string if we're on a big endian
+ pMDDefaultValue->m_wzValue = new WCHAR[(cbValue + 1) / sizeof (WCHAR)];
+ _ASSERTE(FALSE); // Nothing ever free's this newly allocated array. Inserting assert so that if we ever actually
+ // use this code path, we'll fix it then. (Don't want to fix something I can't test.)
+ IfNullGo(pMDDefaultValue->m_wzValue);
+ memcpy(const_cast<WCHAR *>(pMDDefaultValue->m_wzValue), pValue, cbValue);
+ _ASSERTE(cbValue % sizeof(WCHAR) == 0);
+ SwapStringLength(const_cast<WCHAR *>(pMDDefaultValue->m_wzValue), cbValue / sizeof(WCHAR));
+ }
+#else
+ pMDDefaultValue->m_wzValue = (LPWSTR) pValue;
+#endif
+ break;
+ case ELEMENT_TYPE_CLASS:
+ //
+ // There is only a 4-byte quantity in the MetaData, and it must always
+ // be zero. So, we load an INT32 and zero-extend it to be pointer-sized.
+ //
+ if (cbValue < 4)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_unkValue = (IUnknown *)(UINT_PTR)GET_UNALIGNED_VAL32(pValue);
+ if (pMDDefaultValue->m_unkValue != NULL)
+ {
+ _ASSERTE(!"Non-NULL objectref's are not supported as default values!");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ break;
+ case ELEMENT_TYPE_I8:
+ if (cbValue < 8)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_llValue = GET_UNALIGNED_VAL64(pValue);
+ break;
+ case ELEMENT_TYPE_U8:
+ if (cbValue < 8)
+ {
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ pMDDefaultValue->m_ullValue = GET_UNALIGNED_VAL64(pValue);
+ break;
+ case ELEMENT_TYPE_VOID:
+ break;
+ default:
+ _ASSERTE(!"BAD TYPE!");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ break;
+ }
+ErrExit:
+ return hr;
+} // _FillMDDefaultValue
+
+//*****************************************************************************
+// Given a scope, return the table size and table ptr for a given index
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::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;
+} // MDInternalRO::GetTableInfoWithIndex
+
+
+
+//*****************************************************************************
+// Given a delta metadata byte stream, apply the changes to the current metadata
+// object returning the resulting metadata object in ppv
+//*****************************************************************************
+__checkReturn
+HRESULT MDInternalRO::ApplyEditAndContinue(
+ void *pDeltaMD, // [IN] the delta metadata
+ ULONG cbDeltaMD, // [IN] length of pData
+ IMDInternalImport **ppv) // [OUT] the resulting metadata interface
+{
+ _ASSERTE(pDeltaMD);
+ _ASSERTE(ppv);
+
+ 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;
+}
+
+HRESULT MDInternalRO::GetRvaOffsetData(
+ DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table.
+ DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table.
+ DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table.
+ DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table.
+ DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table.
+ DWORD *pFieldRvaCount) // [OUT] Number of records in FieldRVA table.
+{
+ HRESULT hr = S_OK;
+ DWORD methodDefCount = *pMethodDefCount = m_LiteWeightStgdb.m_MiniMd.getCountMethods();
+ if (methodDefCount == 0)
+ *pFirstMethodRvaOffset = *pMethodDefRecordSize = 0;
+ else
+ {
+ MethodRec *pMethodRec;
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(1, &pMethodRec));
+
+ // RVA is the first column of the MethodDef table, so the address of MethodRec is also address of RVA column.
+ if ((const BYTE *)m_LiteWeightStgdb.m_pvMd > (const BYTE *)pMethodRec)
+ {
+ Debug_ReportError("Stream header is not within MetaData block.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ *pFirstMethodRvaOffset = (DWORD)((const BYTE *)pMethodRec - (const BYTE *)m_LiteWeightStgdb.m_pvMd);
+ *pMethodDefRecordSize = m_LiteWeightStgdb.m_MiniMd._CBREC(Method);
+ }
+
+ {
+ DWORD fieldRvaCount = *pFieldRvaCount = m_LiteWeightStgdb.m_MiniMd.getCountFieldRVAs();
+ if (fieldRvaCount == 0)
+ *pFirstFieldRvaOffset = *pFieldRvaRecordSize = 0;
+ else
+ {
+
+ // orig
+ // FieldRVARec *pFieldRVARec = m_LiteWeightStgdb.m_MiniMd.getFieldRVA(1);
+ FieldRVARec *pFieldRVARec;
+ IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetFieldRVARecord(1, &pFieldRVARec));
+
+//FieldRVARec *pFieldRVARec;
+//mdToken fakeTok = 1;
+//RidToToken(&fakeTok, mdtFieldDef);
+//GetFieldRVA(fakeTok, &pFieldRVARec);
+ // RVA is the first column of the FieldRVA table, so the address of FieldRVARec is also address of RVA column.
+ if ((const BYTE *)m_LiteWeightStgdb.m_pvMd > (const BYTE *)pFieldRVARec)
+ {
+ Debug_ReportError("Stream header is not within MetaData block.");
+ IfFailGo(CLDB_E_FILE_CORRUPT);
+ }
+ *pFirstFieldRvaOffset = (DWORD)((const BYTE *)pFieldRVARec - (const BYTE *)m_LiteWeightStgdb.m_pvMd);
+ *pFieldRvaRecordSize = m_LiteWeightStgdb.m_MiniMd._CBREC(FieldRVA);
+ }
+ }
+ hr = S_OK;
+
+ErrExit:
+ return hr;
+}
+
+#endif //FEATURE_METADATA_INTERNAL_APIS
diff --git a/src/md/runtime/mdinternalro.h b/src/md/runtime/mdinternalro.h
new file mode 100644
index 0000000000..92e5393ffa
--- /dev/null
+++ b/src/md/runtime/mdinternalro.h
@@ -0,0 +1,849 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MDInternalRO.h
+//
+
+//
+// Contains utility code for MD directory
+//
+//*****************************************************************************
+#ifndef __MDInternalRO__h__
+#define __MDInternalRO__h__
+
+#include "winmdinterfaces.h"
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+class MDInternalRO : public IMDInternalImport, IMDCommon
+{
+public:
+
+ MDInternalRO();
+ virtual ~MDInternalRO();
+ __checkReturn
+ HRESULT Init(LPVOID pData, ULONG cbData);
+
+ // *** IUnknown methods ***
+ __checkReturn
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
+ STDMETHODIMP_(ULONG) AddRef(void);
+ STDMETHODIMP_(ULONG) Release(void);
+
+ __checkReturn
+ STDMETHODIMP 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
+ DAC_UNEXPECTED();
+
+
+ __checkReturn
+ STDMETHODIMP 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
+
+
+
+ STDMETHODIMP_(IMetaModelCommon*) GetMetaModelCommon()
+ {
+ return static_cast<IMetaModelCommon*>(&m_LiteWeightStgdb.m_MiniMd);
+ }
+
+ STDMETHODIMP_(IMetaModelCommonRO*) GetMetaModelCommonRO()
+ {
+ if (m_LiteWeightStgdb.m_MiniMd.IsWritable())
+ {
+ _ASSERTE(!"IMetaModelCommonRO methods cannot be used because this importer is writable.");
+ return NULL;
+ }
+ return static_cast<IMetaModelCommonRO*>(&m_LiteWeightStgdb.m_MiniMd);
+ }
+
+ __checkReturn
+ STDMETHODIMP SetOptimizeAccessForSpeed(
+ BOOL fOptSpeed)
+ {
+ // The metadata cache of hot items is an optional working-set optimization
+ // that has a large speed cost relative to direct table lookup
+ if (fOptSpeed)
+ { // We want to disable usage of hot data (e.g. in ngen compilation process)
+ m_LiteWeightStgdb.m_MiniMd.DisableHotDataUsage();
+ }
+ return S_OK;
+ }
+
+ //*****************************************************************************
+ // return the count of entries of a given kind in a scope
+ // For example, pass in mdtMethodDef will tell you how many MethodDef
+ // contained in a scope
+ //*****************************************************************************
+ STDMETHODIMP_(ULONG) GetCountWithTokenKind(// return hresult
+ DWORD tkKind) // [IN] pass in the kind of token.
+ DAC_UNEXPECTED();
+
+ //*****************************************************************************
+ // enumerator for typedef
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP EnumTypeDefInit( // return hresult
+ HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data
+
+ STDMETHODIMP_(ULONG) EnumTypeDefGetCount(
+ HENUMInternal *phEnum); // [IN] the enumerator to retrieve information
+
+ STDMETHODIMP_(void) EnumTypeDefReset(
+ HENUMInternal *phEnum); // [IN] the enumerator to retrieve information
+
+ STDMETHODIMP_(bool) EnumTypeDefNext( // return hresult
+ HENUMInternal *phEnum, // [IN] input enum
+ mdTypeDef *ptd); // [OUT] return token
+
+ STDMETHODIMP_(void) EnumTypeDefClose(
+ HENUMInternal *phEnum); // [IN] the enumerator to retrieve information
+
+ //*****************************************************************************
+ // enumerator for MethodImpl
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP 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.
+
+ STDMETHODIMP_(ULONG) EnumMethodImplGetCount(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl); // [IN] MethodDecl enumerator.
+
+ STDMETHODIMP_(void) EnumMethodImplReset(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl); // [IN] MethodDecl enumerator.
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ STDMETHODIMP_(void) EnumMethodImplClose(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl); // [IN] MethodDecl enumerator.
+
+ //*****************************************
+ // Enumerator helpers for memberdef, memberref, interfaceimp,
+ // event, property, param, methodimpl
+ //*****************************************
+
+ __checkReturn
+ STDMETHODIMP EnumGlobalFunctionsInit( // return hresult
+ HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data
+
+ __checkReturn
+ STDMETHODIMP EnumGlobalFieldsInit( // return hresult
+ HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP EnumAllInit( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ HENUMInternal *phEnum); // [OUT] the enumerator to fill
+
+ STDMETHODIMP_(bool) EnumNext(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk); // [OUT] token to scope the search
+
+ STDMETHODIMP_(ULONG) EnumGetCount(
+ HENUMInternal *phEnum); // [IN] the enumerator to retrieve information
+
+ STDMETHODIMP_(void) EnumReset(
+ HENUMInternal *phEnum); // [IN] the enumerator to be reset
+
+ STDMETHODIMP_(void) EnumClose(
+ HENUMInternal *phEnum); // [IN] the enumerator to be closed
+
+ __checkReturn
+ STDMETHODIMP EnumPermissionSetsInit( // return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ CorDeclSecurity Action, // [IN] Action to scope the search
+ HENUMInternal *phEnum); // [OUT] the enumerator to fill
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP GetParentToken(
+ mdToken tkChild, // [IN] given child token
+ mdToken *ptkParent); // [OUT] returning parent
+
+ __checkReturn
+ STDMETHODIMP GetCustomAttributeProps(
+ mdCustomAttribute at, // [IN] The attribute.
+ mdToken *ptkType); // [OUT] Put attribute type here.
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP GetCustomAttributeByName( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData); // [OUT] Put size of data here.
+
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP GetScopeProps(
+ LPCSTR *pszName, // [OUT] scope name
+ GUID *pmvid); // [OUT] version id
+
+ // finding a particular method
+ __checkReturn
+ STDMETHODIMP FindMethodDef(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmd); // [OUT] matching memberdef
+
+ // return a iSeq's param given a MethodDef
+ __checkReturn
+ STDMETHODIMP 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.
+
+ //*****************************************
+ //
+ // GetName* functions
+ //
+ //*****************************************
+
+ // return the name and namespace of typedef
+ __checkReturn
+ STDMETHODIMP GetNameOfTypeDef(
+ mdTypeDef classdef, // given classdef
+ LPCSTR *pszname, // return class name(unqualified)
+ LPCSTR *psznamespace); // return the name space name
+
+ __checkReturn
+ STDMETHODIMP GetIsDualOfTypeDef(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pDual); // [OUT] return dual flag here.
+
+ __checkReturn
+ STDMETHODIMP GetIfaceTypeOfTypeDef(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pIface); // [OUT] 0=dual, 1=vtable, 2=dispinterface
+
+ // get the name of either methoddef
+ __checkReturn
+ STDMETHODIMP GetNameOfMethodDef( // return the name of the memberdef in UTF8
+ mdMethodDef md, // given memberdef
+ LPCSTR *pszName);
+
+ __checkReturn
+ STDMETHODIMP 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 *pszName);
+
+ // return the name of a FieldDef
+ __checkReturn
+ STDMETHODIMP GetNameOfFieldDef(
+ mdFieldDef fd, // given memberdef
+ LPCSTR *pszName);
+
+ // return the name of typeref
+ __checkReturn
+ STDMETHODIMP GetNameOfTypeRef(
+ mdTypeRef classref, // [IN] given typeref
+ LPCSTR *psznamespace, // [OUT] return typeref name
+ LPCSTR *pszname); // [OUT] return typeref namespace
+
+ // return the resolutionscope of typeref
+ __checkReturn
+ STDMETHODIMP GetResolutionScopeOfTypeRef(
+ mdTypeRef classref, // given classref
+ mdToken *ptkResolutionScope);
+
+ // return the typeref token given the name.
+ __checkReturn
+ STDMETHODIMP FindTypeRefByName(
+ 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.
+
+ // return the TypeDef properties
+ __checkReturn
+ STDMETHODIMP GetTypeDefProps( // return hresult
+ mdTypeDef classdef, // given classdef
+ DWORD *pdwAttr, // return flags on class, tdPublic, tdAbstract
+ mdToken *ptkExtends); // [OUT] Put base class TypeDef/TypeRef here.
+
+ // return the item's guid
+ __checkReturn
+ STDMETHODIMP GetItemGuid( // return hresult
+ mdToken tkObj, // [IN] given item.
+ CLSID *pGuid); // [OUT] Put guid here.
+
+ // get enclosing class of NestedClass.
+ __checkReturn
+ STDMETHODIMP GetNestedClassProps( // S_OK or error
+ mdTypeDef tkNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptkEnclosingClass); // [OUT] EnclosingClass token.
+
+ // Get count of Nested classes given the enclosing class.
+ __checkReturn
+ STDMETHODIMP GetCountNestedClasses( // return count of Nested classes.
+ mdTypeDef tkEnclosingClass, // [IN]Enclosing class.
+ ULONG *pcNestedClassesCount);
+
+ // Return array of Nested classes given the enclosing class.
+ __checkReturn
+ STDMETHODIMP 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);
+
+ // return the ModuleRef properties
+ __checkReturn
+ STDMETHODIMP GetModuleRefProps(
+ mdModuleRef mur, // [IN] moduleref token
+ LPCSTR *pszName); // [OUT] buffer to fill with the moduleref name
+
+ //*****************************************
+ //
+ // GetSig* functions
+ //
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetSigOfMethodDef(
+ mdMethodDef methoddef, // [IN] given memberdef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig);
+
+ __checkReturn
+ STDMETHODIMP GetSigOfFieldDef(
+ mdMethodDef methoddef, // [IN] given memberdef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig);
+
+ __checkReturn
+ STDMETHODIMP GetSigFromToken(
+ mdToken tk, // FieldDef, MethodDef, Signature or TypeSpec token
+ ULONG * pcbSig,
+ PCCOR_SIGNATURE * ppSig);
+
+
+
+ //*****************************************
+ // get method property
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetMethodDefProps(
+ mdMethodDef md, // The method for which to get props.
+ DWORD *pdwFlags);
+
+ __checkReturn
+ STDMETHODIMP_(ULONG) GetMethodDefSlot(
+ mdMethodDef mb); // The method for which to get props.
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetMethodImplProps(
+ mdMethodDef tk, // [IN] MethodDef
+ ULONG *pulCodeRVA, // [OUT] CodeRVA
+ DWORD *pdwImplFlags); // [OUT] Impl. Flags
+
+ //*****************************************************************************
+ // return the field RVA
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetFieldRVA(
+ mdToken fd, // [IN] FieldDef
+ ULONG *pulCodeRVA); // [OUT] CodeRVA
+
+ //*****************************************************************************
+ // return the field offset for a given field
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetFieldOffset(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulOffset); // [OUT] FieldOffset
+
+ //*****************************************
+ // get field property
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetFieldDefProps(
+ mdFieldDef fd, // [IN] given fielddef
+ DWORD *pdwFlags); // [OUT] return fdPublic, fdPrive, etc flags
+
+ //*****************************************************************************
+ // return default value of a token (could be paramdef, fielddef, or property)
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetDefaultValue(
+ mdToken tk, // [IN] given FieldDef, ParamDef, or Property
+ MDDefaultValue *pDefaultValue); // [OUT] default value to fill
+
+
+ //*****************************************
+ // get dispid of a MethodDef or a FieldDef
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetDispIdOfMemberDef( // return hresult
+ mdToken tk, // [IN] given methoddef or fielddef
+ ULONG *pDispid); // [OUT] Put the dispid here.
+
+ //*****************************************
+ // return TypeRef/TypeDef given an InterfaceImpl token
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetTypeOfInterfaceImpl( // return the TypeRef/typedef token for the interfaceimpl
+ mdInterfaceImpl iiImpl, // given a interfaceimpl
+ mdToken *ptkType);
+
+ //*****************************************
+ // return information about a generic method instantiation
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetMethodSpecProps(
+ 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
+
+ //*****************************************
+ // look up function for TypeDef
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP FindTypeDef(
+ 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
+
+ __checkReturn
+ STDMETHODIMP FindTypeDefByGUID(
+ REFGUID guid, // guid to look up
+ mdTypeDef *ptypedef); // return typedef
+
+
+
+ //*****************************************
+ // return name and sig of a memberref
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetNameAndSigOfMemberRef( // return name here
+ mdMemberRef memberref, // given memberref
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName);
+
+ //*****************************************************************************
+ // Given memberref, return the parent. It can be TypeRef, ModuleRef, MethodDef
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetParentOfMemberRef(
+ mdMemberRef memberref, // given memberref
+ mdToken *ptkParent); // return the parent token
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ //******************************************
+ // property info for method.
+ //******************************************
+ __checkReturn
+ STDMETHODIMP 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
+ DAC_UNEXPECTED();
+
+ //*****************************************
+ // class layout/sequence information
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetClassPackSize( // [OUT] return error if a class doesn't have packsize info
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwPackSize); // [OUT] return the pack size of the class. 1, 2, 4, 8 or 16
+
+ __checkReturn
+ STDMETHODIMP GetClassTotalSize( // [OUT] return error if a class doesn't have total size info
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwClassSize); // [OUT] return the total size of the class
+
+ __checkReturn
+ STDMETHODIMP GetClassLayoutInit(
+ mdTypeDef td, // [IN] give typedef
+ MD_CLASS_LAYOUT *pLayout); // [OUT] set up the status of query here
+
+ __checkReturn
+ STDMETHODIMP GetClassLayoutNext(
+ MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here
+ mdFieldDef *pfd, // [OUT] return the fielddef
+ ULONG *pulOffset); // [OUT] return the offset/ulSequence associate with it
+
+ //*****************************************
+ // marshal information of a field
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetFieldMarshal( // return error if no native type associate with the token
+ mdFieldDef fd, // [IN] given fielddef
+ PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature
+ ULONG *pcbNativeType); // [OUT] the count of bytes of *ppvNativeType
+
+
+ //*****************************************
+ // property APIs
+ //*****************************************
+ // find a property by name
+ __checkReturn
+ STDMETHODIMP FindProperty(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szPropName, // [IN] property name
+ mdProperty *pProp); // [OUT] return property token
+
+ __checkReturn
+ STDMETHODIMP GetPropertyProps(
+ mdProperty prop, // [IN] property token
+ LPCSTR *szProperty, // [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
+
+ //**********************************
+ // Event APIs
+ //**********************************
+ __checkReturn
+ STDMETHODIMP FindEvent(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szEventName, // [IN] event name
+ mdEvent *pEvent); // [OUT] return event token
+
+ __checkReturn
+ STDMETHODIMP 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
+
+ //**********************************
+ // Generics APIs
+ //**********************************
+ __checkReturn
+ STDMETHODIMP 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
+
+ __checkReturn
+ STDMETHODIMP 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
+
+
+ //**********************************
+ // find a particular associate of a property or an event
+ //**********************************
+ __checkReturn
+ STDMETHODIMP FindAssociate(
+ mdToken evprop, // [IN] given a property or event token
+ DWORD associate, // [IN] given a associate semantics(setter, getter, testdefault, reset, AddOn, RemoveOn, Fire)
+ mdMethodDef *pmd); // [OUT] return method def token
+
+ __checkReturn
+ STDMETHODIMP EnumAssociateInit(
+ mdToken evprop, // [IN] given a property or an event token
+ HENUMInternal *phEnum); // [OUT] cursor to hold the query result
+
+ __checkReturn
+ STDMETHODIMP GetAllAssociates(
+ HENUMInternal *phEnum, // [IN] query result form GetPropertyAssociateCounts
+ ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output
+ ULONG cAssociateRec); // [IN] size of the buffer
+
+
+ //**********************************
+ // Get info about a PermissionSet.
+ //**********************************
+ __checkReturn
+ STDMETHODIMP 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.
+
+ //****************************************
+ // Get the String given the String token.
+ // Returns a pointer to the string, or NULL in case of error.
+ //****************************************
+ __checkReturn
+ STDMETHODIMP GetUserString(
+ mdString stk, // [IN] the string token.
+ ULONG *pchString, // [OUT] count of characters in the string.
+ BOOL *pbIs80Plus, // [OUT] specifies where there are extended characters >= 0x80.
+ LPCWSTR *pwszUserString);
+
+ //*****************************************************************************
+ // p-invoke APIs.
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetPinvokeMap(
+ mdMethodDef tk, // [IN] FieldDef or MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ LPCSTR *pszImportName, // [OUT] Import name.
+ mdModuleRef *pmrImportDLL); // [OUT] ModuleRef token for the target DLL.
+
+ //*****************************************************************************
+ // Assembly MetaData APIs.
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP GetExportedTypeProps(
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ LPCSTR *pszNamespace, // [OUT] Buffer to fill with namespace.
+ 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.
+
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP 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.
+
+ __checkReturn
+ STDMETHODIMP FindManifestResourceByName(// S_OK or error
+ LPCSTR szName, // [IN] Name of the resource.
+ mdManifestResource *pmmr); // [OUT] Put ManifestResource token here.
+
+ __checkReturn
+ STDMETHODIMP GetAssemblyFromScope( // S_OK or error
+ mdAssembly *ptkAssembly); // [OUT] Put token here.
+
+ //***************************************************************************
+ // return properties regarding a TypeSpec
+ //***************************************************************************
+ __checkReturn
+ STDMETHODIMP 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.
+
+ //*****************************************************************************
+ // 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
+ STDMETHODIMP GetVersionString( // S_OK or error.
+ LPCSTR *pVer); // [OUT] Put version string here.
+
+
+ //*****************************************************************************
+ // helpers to convert a text signature to a com format
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP ConvertTextSigToComSig( // Return hresult.
+ BOOL fCreateTrIfNotFound, // [IN] create typeref if not found
+ LPCSTR pSignature, // [IN] class file format signature
+ CQuickBytes *pqbNewSig, // [OUT] place holder for COM+ signature
+ ULONG *pcbCount); // [OUT] the result size of signature
+
+ __checkReturn
+ STDMETHODIMP SetUserContextData( // S_OK or E_NOTIMPL
+ IUnknown *pIUnk) // The user context.
+ { return E_NOTIMPL; }
+
+ STDMETHODIMP_(BOOL) IsValidToken( // True or False.
+ mdToken tk); // [IN] Given token.
+
+ STDMETHODIMP_(IUnknown *) GetCachedPublicInterface(BOOL fWithLock) { return NULL;} // return the cached public interface
+ __checkReturn
+ STDMETHODIMP SetCachedPublicInterface(IUnknown *pUnk) { return E_FAIL;} ;// return hresult
+ STDMETHODIMP_(UTSemReadWrite*) GetReaderWriterLock() {return NULL;} // return the reader writer lock
+ __checkReturn
+ STDMETHODIMP SetReaderWriterLock(UTSemReadWrite *pSem) { return NOERROR; }
+ STDMETHODIMP_(mdModule) GetModuleFromScope(void);
+
+ // Find a paticular method and pass in the signature comparison routine. Very
+ // helpful when the passed in signature does not come from the same scope.
+ __checkReturn
+ STDMETHODIMP FindMethodDefUsingCompare(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ 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 *pmd); // [OUT] matching memberdef
+
+
+ //*****************************************************************************
+ // return the table pointer and size for a given table index
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetTableInfoWithIndex(
+ ULONG index, // [IN] pass in the index
+ void **pTable, // [OUT] pointer to table at index
+ void **pTableSize); // [OUT] size of table at index
+
+ __checkReturn
+ STDMETHODIMP ApplyEditAndContinue(
+ void *pData, // [IN] the delta metadata
+ ULONG cbData, // [IN] length of pData
+ IMDInternalImport **ppv); // [OUT] the resulting metadata interface
+
+ STDMETHODIMP GetRvaOffsetData(
+ DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table.
+ DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table.
+ DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table.
+ DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table.
+ DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table.
+ DWORD *pFieldRvaCount // [OUT] Number of records in FieldRVA table.
+ );
+
+ CLiteWeightStgdb<CMiniMd> m_LiteWeightStgdb;
+
+private:
+
+ struct CMethodSemanticsMap
+ {
+ mdToken m_mdMethod; // Method token.
+ RID m_ridSemantics; // RID of semantics record.
+ };
+ CMethodSemanticsMap *m_pMethodSemanticsMap; // Possible array of method semantics pointers, ordered by method token.
+
+#ifndef DACCESS_COMPILE
+ class CMethodSemanticsMapSorter : public CQuickSort<CMethodSemanticsMap>
+ {
+ public:
+ CMethodSemanticsMapSorter(CMethodSemanticsMap *pBase, int iCount) : CQuickSort<CMethodSemanticsMap>(pBase, iCount) {}
+ virtual int Compare(CMethodSemanticsMap *psFirst, CMethodSemanticsMap *psSecond);
+ };
+#endif //!DACCESS_COMPILE
+
+ class CMethodSemanticsMapSearcher : public CBinarySearch<CMethodSemanticsMap>
+ {
+ public:
+ CMethodSemanticsMapSearcher(const CMethodSemanticsMap *pBase, int iCount) : CBinarySearch<CMethodSemanticsMap>(pBase, iCount) {}
+ virtual int Compare(const CMethodSemanticsMap *psFirst, const CMethodSemanticsMap *psSecond);
+ };
+
+ static BOOL CompareSignatures(PCCOR_SIGNATURE pvFirstSigBlob, DWORD cbFirstSigBlob,
+ PCCOR_SIGNATURE pvSecondSigBlob, DWORD cbSecondSigBlob,
+ void* SigARguments);
+
+ mdTypeDef m_tdModule; // <Module> typedef value.
+ LONG m_cRefs; // Ref count.
+
+public:
+ STDMETHODIMP_(DWORD) GetMetadataStreamVersion()
+ {
+ return (DWORD)m_LiteWeightStgdb.m_MiniMd.m_Schema.m_minor |
+ ((DWORD)m_LiteWeightStgdb.m_MiniMd.m_Schema.m_major << 16);
+ };
+
+ STDMETHODIMP SetVerifiedByTrustedSource(// return hresult
+ BOOL fVerified)
+ {
+ m_LiteWeightStgdb.m_MiniMd.SetVerifiedByTrustedSource(fVerified);
+ return S_OK;
+ }
+}; // class MDInternalRO
+
+#endif //FEATURE_METADATA_INTERNAL_APIS
+
+#endif // __MDInternalRO__h__
diff --git a/src/md/runtime/metamodel.cpp b/src/md/runtime/metamodel.cpp
new file mode 100644
index 0000000000..293e5b6b5e
--- /dev/null
+++ b/src/md/runtime/metamodel.cpp
@@ -0,0 +1,1240 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MetaModel.cpp -- Base portion of compressed COM+ metadata.
+//
+
+//
+//*****************************************************************************
+#include "stdafx.h"
+
+#include <metamodel.h>
+#include <corerror.h>
+#include <posterror.h>
+
+//*****************************************************************************
+// meta-meta model.
+//*****************************************************************************
+
+//-----------------------------------------------------------------------------
+// Start of column definitions.
+//-----------------------------------------------------------------------------
+// Column type, offset, size.
+#define SCHEMA_TABLE_START(tbl) static CMiniColDef r##tbl##Cols[] = {
+#define SCHEMA_ITEM_NOFIXED()
+#define SCHEMA_ITEM_ENTRY(col,typ) {typ, 0,0},
+#define SCHEMA_ITEM_ENTRY2(col,typ,ofs,siz) {typ, ofs, siz},
+#define SCHEMA_ITEM(tbl,typ,col) SCHEMA_ITEM_ENTRY2(col, i##typ, offsetof(tbl##Rec,m_##col), sizeof(((tbl##Rec*)(0))->m_##col))
+#define SCHEMA_ITEM_RID(tbl,col,tbl2) SCHEMA_ITEM_ENTRY(col,TBL_##tbl2)
+#define SCHEMA_ITEM_STRING(tbl,col) SCHEMA_ITEM_ENTRY(col,iSTRING)
+#define SCHEMA_ITEM_GUID(tbl,col) SCHEMA_ITEM_ENTRY(col,iGUID)
+#define SCHEMA_ITEM_BLOB(tbl,col) SCHEMA_ITEM_ENTRY(col,iBLOB)
+#define SCHEMA_ITEM_CDTKN(tbl,col,tkns) SCHEMA_ITEM_ENTRY(col,iCodedToken+(CDTKN_##tkns))
+#define SCHEMA_TABLE_END(tbl) };
+//-----------------------------------------------------------------------------
+#include "metamodelcolumndefs.h"
+//-----------------------------------------------------------------------------
+#undef SCHEMA_TABLE_START
+#undef SCHEMA_ITEM_NOFIXED
+#undef SCHEMA_ITEM_ENTRY
+#undef SCHEMA_ITEM_ENTRY2
+#undef SCHEMA_ITEM
+#undef SCHEMA_ITEM_RID
+#undef SCHEMA_ITEM_STRING
+#undef SCHEMA_ITEM_GUID
+#undef SCHEMA_ITEM_BLOB
+#undef SCHEMA_ITEM_CDTKN
+#undef SCHEMA_TABLE_END
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Column names.
+#define SCHEMA_TABLE_START(tbl) static const char *r##tbl##ColNames[] = {
+#define SCHEMA_ITEM_NOFIXED()
+#define SCHEMA_ITEM_ENTRY(col,typ) #col,
+#define SCHEMA_ITEM_ENTRY2(col,typ,ofs,siz) #col,
+#define SCHEMA_ITEM(tbl,typ,col) SCHEMA_ITEM_ENTRY2(col, i##typ, offsetof(tbl##Rec,m_##col), sizeof(((tbl##Rec*)(0))->m_##col))
+#define SCHEMA_ITEM_RID(tbl,col,tbl2) SCHEMA_ITEM_ENTRY(col,TBL_##tbl2)
+#define SCHEMA_ITEM_STRING(tbl,col) SCHEMA_ITEM_ENTRY(col,iSTRING)
+#define SCHEMA_ITEM_GUID(tbl,col) SCHEMA_ITEM_ENTRY(col,iGUID)
+#define SCHEMA_ITEM_BLOB(tbl,col) SCHEMA_ITEM_ENTRY(col,iBLOB)
+#define SCHEMA_ITEM_CDTKN(tbl,col,tkns) SCHEMA_ITEM_ENTRY(col,iCodedToken+(CDTKN_##tkns))
+#define SCHEMA_TABLE_END(tbl) };
+//-----------------------------------------------------------------------------
+#include "metamodelcolumndefs.h"
+//-----------------------------------------------------------------------------
+#undef SCHEMA_TABLE_START
+#undef SCHEMA_ITEM_NOFIXED
+#undef SCHEMA_ITEM_ENTRY
+#undef SCHEMA_ITEM_ENTRY2
+#undef SCHEMA_ITEM
+#undef SCHEMA_ITEM_RID
+#undef SCHEMA_ITEM_STRING
+#undef SCHEMA_ITEM_GUID
+#undef SCHEMA_ITEM_BLOB
+#undef SCHEMA_ITEM_CDTKN
+#undef SCHEMA_TABLE_END
+
+//-----------------------------------------------------------------------------
+// End of column definitions.
+//-----------------------------------------------------------------------------
+
+// Define the array of Coded Token Definitions.
+#define MiniMdCodedToken(x) {lengthof(CMiniMdBase::mdt##x), CMiniMdBase::mdt##x, #x},
+const CCodedTokenDef g_CodedTokens [] = {
+ MiniMdCodedTokens()
+};
+#undef MiniMdCodedToken
+
+// Define the array of Table Definitions.
+#undef MiniMdTable
+#define MiniMdTable(x) { { r##x##Cols, lengthof(r##x##Cols), x##Rec::COL_KEY, 0 }, r##x##ColNames, #x},
+const CMiniTableDefEx g_Tables[TBL_COUNT] = {
+ MiniMdTables()
+};
+
+// Define a table descriptor for the obsolete v1.0 GenericParam table definition.
+const CMiniTableDefEx g_Table_GenericParamV1_1 = { { rGenericParamV1_1Cols, lengthof(rGenericParamV1_1Cols), GenericParamV1_1Rec::COL_KEY, 0 }, rGenericParamV1_1ColNames, "GenericParamV1_"};
+
+
+
+// Define the array of Ptr Tables. This is initialized to TBL_COUNT here.
+// The correct values will be set in the constructor for MiniMdRW.
+#undef MiniMdTable
+#define MiniMdTable(x) { TBL_COUNT, 0 },
+TblCol g_PtrTableIxs[TBL_COUNT] = {
+ MiniMdTables()
+};
+
+//*****************************************************************************
+// Initialize a new schema.
+//*****************************************************************************
+HRESULT
+CMiniMdSchema::InitNew(
+ MetadataVersion mdVersion)
+{
+ // Make sure the tables fit in the mask.
+ _ASSERTE((sizeof(m_maskvalid) * 8) > TBL_COUNT);
+
+ m_ulReserved = 0;
+
+ if(mdVersion == MDVersion1)
+ {
+ m_major = METAMODEL_MAJOR_VER_V1_0;
+ m_minor = METAMODEL_MINOR_VER_V1_0;
+ }
+ else if (mdVersion == MDVersion2)
+ {
+ m_major = METAMODEL_MAJOR_VER;
+ m_minor = METAMODEL_MINOR_VER;
+ }
+ else
+ {
+ return E_INVALIDARG;
+ }
+
+ m_heaps = 0;
+ m_rid = 0;
+ m_maskvalid = 0;
+ m_sorted = 0;
+ memset(m_cRecs, 0, sizeof(m_cRecs));
+ m_ulExtra = 0;
+
+ return S_OK;
+} // CMiniMdSchema::InitNew
+
+//*****************************************************************************
+// Compress a schema into a compressed version of the schema.
+//*****************************************************************************
+ULONG
+CMiniMdSchema::SaveTo(
+ void *pvData)
+{
+ ULONG ulData; // Bytes stored.
+ CMiniMdSchema *pDest = reinterpret_cast<CMiniMdSchema*>(pvData);
+ const unsigned __int64 one = 1;
+
+ // Make sure the tables fit in the mask.
+ _ASSERTE((sizeof(m_maskvalid) * 8) > TBL_COUNT);
+
+ // Set the flag for the extra data.
+#if defined(EXTRA_DATA)
+ if (m_ulExtra != 0)
+ {
+ m_heaps |= EXTRA_DATA;
+ }
+ else
+#endif // 0
+ {
+ m_heaps &= ~EXTRA_DATA;
+ }
+
+ // Minor version is preset when we instantiate the MiniMd.
+
+ // Make sure we're saving out a version that Beta1 version can read
+ _ASSERTE((m_major == METAMODEL_MAJOR_VER && m_minor == METAMODEL_MINOR_VER) ||
+ (m_major == METAMODEL_MAJOR_VER_B1 && m_minor == METAMODEL_MINOR_VER_B1) ||
+ (m_major == METAMODEL_MAJOR_VER_V1_0 && m_minor == METAMODEL_MINOR_VER_V1_0));
+
+ // Transfer the fixed fields.
+ *static_cast<CMiniMdSchemaBase*>(pDest) = *static_cast<CMiniMdSchemaBase*>(this);
+ static_cast<CMiniMdSchemaBase*>(pDest)->ConvertEndianness();
+ ulData = sizeof(CMiniMdSchemaBase);
+
+ // Transfer the variable fields.
+ m_maskvalid = 0;
+ for (int iSrc=0, iDst=0; iSrc<TBL_COUNT; ++iSrc)
+ {
+ if (m_cRecs[iSrc] != 0)
+ {
+ pDest->m_cRecs[iDst++] = VAL32(m_cRecs[iSrc]);
+ m_maskvalid |= (one << iSrc);
+ ulData += sizeof(m_cRecs[iSrc]);
+ }
+ }
+ // Refresh the mask.
+ pDest->m_maskvalid = VAL64(m_maskvalid);
+
+#if defined(EXTRA_DATA)
+ // Store the extra data.
+ if (m_ulExtra != 0)
+ {
+ *reinterpret_cast<ULONG*>(&pDest->m_cRecs[iDst]) = VAL32(m_ulExtra);
+ ulData += sizeof(ULONG);
+ }
+#endif // 0
+ return ulData;
+} // CMiniMdSchema::SaveTo
+
+//*****************************************************************************
+// Load a schema from a compressed version of the schema.
+// Returns count of bytes consumed. -1 if error.
+//*****************************************************************************
+ULONG
+CMiniMdSchema::LoadFrom(
+ const void *pvData, // Data to load from.
+ ULONG cbData) // Amount of data available.
+{
+ ULONG ulData; // Bytes consumed.
+
+ ulData = sizeof(CMiniMdSchemaBase);
+
+ // Be sure we can get the base part.
+ if (cbData < ulData)
+ return (ULONG)(-1);
+
+ // Transfer the fixed fields. The (void*) casts prevents the compiler
+ // from making bad assumptions about the alignment.
+ memcpy((void *)this, (void *)pvData, sizeof(CMiniMdSchemaBase));
+ static_cast<CMiniMdSchemaBase*>(this)->ConvertEndianness();
+
+ unsigned __int64 maskvalid = m_maskvalid;
+
+ // Transfer the variable fields.
+ memset(m_cRecs, 0, sizeof(m_cRecs));
+ int iDst;
+ for (iDst = 0; iDst < TBL_COUNT; ++iDst, maskvalid >>= 1)
+ {
+ if ((maskvalid & 1) != 0)
+ {
+ // Check integer overflow for: ulData + sizeof(UINT32)
+ ULONG ulDataTemp;
+ if (!ClrSafeInt<ULONG>::addition(ulData, sizeof(UINT32), ulDataTemp))
+ {
+ return (ULONG)(-1);
+ }
+ // Verify that the data is there before touching it.
+ if (cbData < (ulData + sizeof(UINT32)))
+ return (ULONG)(-1);
+
+ m_cRecs[iDst] = GET_UNALIGNED_VAL32((const BYTE *)pvData + ulData);
+ // It's safe to sum, because we checked integer overflow above
+ ulData += sizeof(UINT32);
+ }
+ }
+ // Also accumulate the sizes of any counters that we don't understand.
+ for (iDst = TBL_COUNT; (maskvalid != 0) && (iDst < ((int)sizeof(m_maskvalid) * 8)); ++iDst, maskvalid >>= 1)
+ {
+ if ((maskvalid & 1) != 0)
+ {
+ // Check integer overflow for: ulData += sizeof(UINT32);
+ if (!ClrSafeInt<ULONG>::addition(ulData, sizeof(UINT32), ulData))
+ {
+ return (ULONG)(-1);
+ }
+ // Did we go past end of buffer?
+ if (cbData < ulData)
+ {
+ return (ULONG)(-1);
+ }
+ }
+ }
+
+ // Retrieve the extra 4 bytes data.
+ if ((m_heaps & EXTRA_DATA) != 0)
+ {
+ // Check integer overflow for: ulData + sizeof(UINT32)
+ ULONG ulDataTemp;
+ if (!ClrSafeInt<ULONG>::addition(ulData, sizeof(UINT32), ulDataTemp))
+ {
+ return (ULONG)(-1);
+ }
+ // Verify that the 4 bytes data is there before touching it.
+ if (cbData < (ulData + sizeof(UINT32)))
+ return (ULONG)(-1);
+
+ m_ulExtra = GET_UNALIGNED_VAL32((const BYTE *)pvData + ulData);
+ // Check the size we used for buffer overflow verification above
+ ulData += sizeof(UINT32);
+ }
+
+ // Did we go past end of buffer?
+ if (cbData < ulData)
+ return (ULONG)(-1);
+
+ return ulData;
+} // CMiniMdSchema::LoadFrom
+
+
+const mdToken CMiniMdBase::mdtTypeDefOrRef[3] = {
+ mdtTypeDef,
+ mdtTypeRef,
+ mdtTypeSpec
+};
+
+// This array needs to be ordered the same as the source tables are processed (currently
+// {field, param, property}) for binary search.
+const mdToken CMiniMdBase::mdtHasConstant[3] = {
+ mdtFieldDef,
+ mdtParamDef,
+ mdtProperty
+};
+
+const mdToken CMiniMdBase::mdtHasCustomAttribute[24] = {
+ mdtMethodDef,
+ mdtFieldDef,
+ mdtTypeRef,
+ mdtTypeDef,
+ mdtParamDef,
+ mdtInterfaceImpl,
+ mdtMemberRef,
+ mdtModule,
+ mdtPermission,
+ mdtProperty,
+ mdtEvent,
+ mdtSignature,
+ mdtModuleRef,
+ mdtTypeSpec,
+ mdtAssembly,
+ mdtAssemblyRef,
+ mdtFile,
+ mdtExportedType,
+ mdtManifestResource,
+ mdtGenericParam,
+ mdtGenericParamConstraint,
+ mdtMethodSpec
+};
+
+const mdToken CMiniMdBase::mdtHasFieldMarshal[2] = {
+ mdtFieldDef,
+ mdtParamDef,
+};
+
+const mdToken CMiniMdBase::mdtHasDeclSecurity[3] = {
+ mdtTypeDef,
+ mdtMethodDef,
+ mdtAssembly
+};
+
+const mdToken CMiniMdBase::mdtMemberRefParent[5] = {
+ mdtTypeDef,
+ mdtTypeRef,
+ mdtModuleRef,
+ mdtMethodDef,
+ mdtTypeSpec
+};
+
+const mdToken CMiniMdBase::mdtHasSemantic[2] = {
+ mdtEvent,
+ mdtProperty,
+};
+
+const mdToken CMiniMdBase::mdtMethodDefOrRef[2] = {
+ mdtMethodDef,
+ mdtMemberRef
+};
+
+const mdToken CMiniMdBase::mdtMemberForwarded[2] = {
+ mdtFieldDef,
+ mdtMethodDef
+};
+
+const mdToken CMiniMdBase::mdtImplementation[3] = {
+ mdtFile,
+ mdtAssemblyRef,
+ mdtExportedType
+};
+
+const mdToken CMiniMdBase::mdtCustomAttributeType[5] = {
+ 0,
+ 0,
+ mdtMethodDef,
+ mdtMemberRef,
+ 0
+};
+
+const mdToken CMiniMdBase::mdtResolutionScope[4] = {
+ mdtModule,
+ mdtModuleRef,
+ mdtAssemblyRef,
+ mdtTypeRef
+};
+
+const mdToken CMiniMdBase::mdtTypeOrMethodDef[2] = {
+ mdtTypeDef,
+ mdtMethodDef
+};
+
+const int CMiniMdBase::m_cb[] = {0,1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5};
+
+//*****************************************************************************
+// Function to encode a token into fewer bits. Looks up token type in array of types.
+//*****************************************************************************
+//<TODO>@consider whether this could be a binary search.</TODO>
+ULONG
+CMiniMdBase::encodeToken(
+ RID rid, // Rid to encode.
+ mdToken typ, // Token type to encode.
+ const mdToken rTokens[], // Table of valid token.
+ ULONG32 cTokens) // Size of the table.
+{
+ mdToken tk = TypeFromToken(typ);
+ size_t ix;
+ for (ix = 0; ix < cTokens; ++ix)
+ {
+ if (rTokens[ix] == tk)
+ break;
+ }
+ _ASSERTE(ix < cTokens);
+ if (ix >= cTokens)
+ return mdTokenNil;
+ //<TODO>@FUTURE: make compile-time calculation</TODO>
+ return (ULONG)((rid << m_cb[cTokens]) | ix);
+} // CMiniMd::encodeToken
+
+
+//*****************************************************************************
+// Helpers for populating the hard-coded schema.
+//*****************************************************************************
+inline BYTE cbRID(ULONG ixMax) { return ixMax > USHRT_MAX ? (BYTE) sizeof(ULONG) : (BYTE) sizeof(USHORT); }
+
+#define _CBTKN(cRecs,tkns) cbRID(cRecs << m_cb[lengthof(tkns)])
+
+//*****************************************************************************
+// Constructor.
+//*****************************************************************************
+CMiniMdBase::CMiniMdBase()
+{
+#undef MiniMdTable
+#define MiniMdTable(tbl) \
+ m_TableDefs[TBL_##tbl] = g_Tables[TBL_##tbl].m_Def; \
+ m_TableDefs[TBL_##tbl].m_pColDefs = BYTEARRAY_TO_COLDES(s_##tbl##Col);
+ MiniMdTables()
+
+ m_TblCount = TBL_COUNT;
+ _ASSERTE(TBL_COUNT == TBL_COUNT_V2); // v2 counts.
+
+ m_fVerifiedByTrustedSource = FALSE;
+
+ // Validator depends on the Table Ids and the Token Ids being identical.
+ // Catch it if this ever breaks.
+ _ASSERTE((TypeFromToken(mdtModule) >> 24) == TBL_Module);
+ _ASSERTE((TypeFromToken(mdtTypeRef) >> 24) == TBL_TypeRef);
+ _ASSERTE((TypeFromToken(mdtTypeDef) >> 24) == TBL_TypeDef);
+ _ASSERTE((TypeFromToken(mdtFieldDef) >> 24) == TBL_Field);
+ _ASSERTE((TypeFromToken(mdtMethodDef) >> 24) == TBL_Method);
+ _ASSERTE((TypeFromToken(mdtParamDef) >> 24) == TBL_Param);
+ _ASSERTE((TypeFromToken(mdtInterfaceImpl) >> 24) == TBL_InterfaceImpl);
+ _ASSERTE((TypeFromToken(mdtMemberRef) >> 24) == TBL_MemberRef);
+ _ASSERTE((TypeFromToken(mdtCustomAttribute) >> 24) == TBL_CustomAttribute);
+ _ASSERTE((TypeFromToken(mdtPermission) >> 24) == TBL_DeclSecurity);
+ _ASSERTE((TypeFromToken(mdtSignature) >> 24) == TBL_StandAloneSig);
+ _ASSERTE((TypeFromToken(mdtEvent) >> 24) == TBL_Event);
+ _ASSERTE((TypeFromToken(mdtProperty) >> 24) == TBL_Property);
+ _ASSERTE((TypeFromToken(mdtModuleRef) >> 24) == TBL_ModuleRef);
+ _ASSERTE((TypeFromToken(mdtTypeSpec) >> 24) == TBL_TypeSpec);
+ _ASSERTE((TypeFromToken(mdtAssembly) >> 24) == TBL_Assembly);
+ _ASSERTE((TypeFromToken(mdtAssemblyRef) >> 24) == TBL_AssemblyRef);
+ _ASSERTE((TypeFromToken(mdtFile) >> 24) == TBL_File);
+ _ASSERTE((TypeFromToken(mdtExportedType) >> 24) == TBL_ExportedType);
+ _ASSERTE((TypeFromToken(mdtManifestResource) >> 24) == TBL_ManifestResource);
+ _ASSERTE((TypeFromToken(mdtGenericParam) >> 24) == TBL_GenericParam);
+ _ASSERTE((TypeFromToken(mdtMethodSpec) >> 24) == TBL_MethodSpec);
+ _ASSERTE((TypeFromToken(mdtGenericParamConstraint) >> 24) == TBL_GenericParamConstraint);
+} // CMiniMdBase::CMiniMdBase
+
+
+//*****************************************************************************
+// Destructor.
+//*****************************************************************************
+CMiniMdBase::~CMiniMdBase()
+{
+ for (ULONG i = 0; i < m_TblCount; i++)
+ {
+ if ((m_TableDefs[i].m_pColDefs != NULL) && UsesAllocatedMemory(m_TableDefs[i].m_pColDefs))
+ {
+ delete[] COLDES_TO_BYTEARRAY(m_TableDefs[i].m_pColDefs);
+ m_TableDefs[i].m_pColDefs = NULL;
+ }
+ }
+} // CMiniMdBase::~CMiniMdBase
+
+//*****************************************************************************
+// Build the schema based on the header data provided.
+// Handle all supported versions, and adjust data structures appropriately.
+//*****************************************************************************
+HRESULT
+CMiniMdBase::SchemaPopulate(
+ const void *pvData, // Pointer to the buffer.
+ ULONG cbData, // Size of the buffer.
+ ULONG *pcbUsed) // Put size of the header here.
+{
+ HRESULT hr;
+ ULONG cb; // Bytes read for header.
+ ULONG cbTables; // Bytes needed for tables.
+ ULONG cbTotal; // Bytes read for header + needed for tables.
+
+ // Uncompress the schema from the buffer into our structures.
+ cb = m_Schema.LoadFrom(pvData, cbData);
+
+ if ((cb > cbData) || (cb == (ULONG)(-1)))
+ {
+ Debug_ReportError("Schema is not in MetaData block.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ // 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))
+ {
+ // No it's not. Is this an older version that we support?
+
+#ifndef FEATURE_METADATA_STANDALONE_WINRT
+ // Is this v1.0?
+ if ((m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0) &&
+ (m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0))
+ {
+ // Older version has fewer tables.
+ m_TblCount = TBL_COUNT_V1;
+ }
+ else if ((m_Schema.m_major == METAMODEL_MAJOR_VER_B1) &&
+ (m_Schema.m_minor == METAMODEL_MINOR_VER_B1))
+ {
+ // 1.1 had a different type of GenericParam table
+ m_TableDefs[TBL_GenericParam] = g_Table_GenericParamV1_1.m_Def;
+ m_TableDefs[TBL_GenericParam].m_pColDefs = BYTEARRAY_TO_COLDES(s_GenericParamCol);
+ }
+ else
+#endif //!FEATURE_METADATA_STANDALONE_WINRT
+ { // 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);
+ }
+ }
+
+ // Populate the schema, based on the row counts and heap sizes.
+ IfFailRet(SchemaPopulate2(&cbTables));
+
+ // Check that header plus tables fits within the size given.
+ if (!ClrSafeInt<ULONG>::addition(cb, cbTables, cbTotal) || (cbTotal > cbData))
+ {
+ Debug_ReportError("Tables are not within MetaData block.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ *pcbUsed = cb;
+ return S_OK;
+} // CMiniMdBase::SchemaPopulate
+
+//*****************************************************************************
+// Initialize from another MD
+//*****************************************************************************
+HRESULT
+CMiniMdBase::SchemaPopulate(
+ const CMiniMdBase &that)
+{
+ HRESULT hr;
+ // Copy over the schema.
+ m_Schema = that.m_Schema;
+
+ // Adjust for prior versions.
+ if (m_Schema.m_major != METAMODEL_MAJOR_VER || m_Schema.m_minor != METAMODEL_MINOR_VER)
+ {
+ if ((m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0) && (m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0))
+ { // Older version has fewer tables.
+ m_TblCount = that.m_TblCount;
+ _ASSERTE(m_TblCount == TBL_COUNT_V1);
+ }
+ else if (m_Schema.m_major == METAMODEL_MAJOR_VER_B1 && m_Schema.m_minor == METAMODEL_MINOR_VER_B1)
+ {
+ // 1.1 had a different type of GenericParam table
+ m_TableDefs[TBL_GenericParam] = g_Table_GenericParamV1_1.m_Def;
+ m_TableDefs[TBL_GenericParam].m_pColDefs = BYTEARRAY_TO_COLDES(s_GenericParamCol);
+ }
+ // Is it a supported old version? This should never fail!
+ else
+ {
+ Debug_ReportError("Initializing on an unknown schema version");
+ return PostError(CLDB_E_FILE_OLDVER, m_Schema.m_major,m_Schema.m_minor);
+ }
+ }
+
+ IfFailRet(SchemaPopulate2(NULL));
+
+ return S_OK;
+} // CMiniMdBase::SchemaPopulate
+
+//*****************************************************************************
+// Iterate the tables, and fix the column sizes, based on size of data.
+//*****************************************************************************
+HRESULT CMiniMdBase::SchemaPopulate2(
+ ULONG *pcbTables, // [out, optional] Put size needed for tables here.
+ int bExtra) // Reserve an extra bit for rid columns?
+{
+ HRESULT hr; // A result.
+ ULONG cbTotal = 0; // Total size of all tables.
+
+ // 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;
+
+ // Make extra bits exactly zero or one bit.
+ if (bExtra)
+ bExtra = 1;
+
+ // Until ENC, make extra bits exactly zero.
+ bExtra = 0;
+
+ // For each table...
+ for (int ixTbl = 0; ixTbl < (int)m_TblCount; ++ixTbl)
+ {
+ IfFailRet(InitColsForTable(m_Schema, ixTbl, &m_TableDefs[ixTbl], bExtra, TRUE));
+
+ // Accumulate size of this table.
+ // Check integer overflow for table size: USHORT * ULONG: m_TableDefs[ixTbl].m_cbRec * GetCountRecs(ixTbl)
+ ULONG cbTable;
+ if (!ClrSafeInt<ULONG>::multiply(m_TableDefs[ixTbl].m_cbRec, GetCountRecs(ixTbl), cbTable))
+ {
+ Debug_ReportError("Table is too large - size overflow.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+ // Check integer overflow for all tables so far: cbTotal += cbTable
+ if (!ClrSafeInt<ULONG>::addition(cbTotal, cbTable, cbTotal))
+ {
+ Debug_ReportError("Tables are too large - size overflow.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+ }
+ // Check that unused table (e.g. generic tables in v1 format) are empty
+ for (ULONG ixTbl = m_TblCount; ixTbl < TBL_COUNT; ixTbl++)
+ {
+ // All unused tables have to be empty - malicious assemblies can have v1 format version, but can
+ // contain non-empty v2-only tables, this will catch it and refuse to load such assemblies
+ if (GetCountRecs(ixTbl) != 0)
+ {
+ Debug_ReportError("Invalid table present - 2.0 table in v1.x image.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+ }
+
+ // Let caller know sizes required.
+ if (pcbTables != NULL)
+ *pcbTables = cbTotal;
+
+ return S_OK;
+} // CMiniMdBase::SchemaPopulate2
+
+//*****************************************************************************
+// Get the template table definition for a given table.
+//*****************************************************************************
+const CMiniTableDef *
+CMiniMdBase::GetTableDefTemplate(
+ int ixTbl)
+{
+ const CMiniTableDef *pTemplate; // the return value.
+
+ // Return the table definition for the given table. Account for version of schema.
+ if ((m_Schema.m_major == METAMODEL_MAJOR_VER_B1) && (m_Schema.m_minor == METAMODEL_MINOR_VER_B1) && (ixTbl == TBL_GenericParam))
+ {
+ pTemplate = &g_Table_GenericParamV1_1.m_Def;
+ }
+ else
+ {
+ pTemplate = &g_Tables[ixTbl].m_Def;
+ }
+
+ return pTemplate;
+} // CMiniMdBase::GetTableDefTemplate
+
+//*****************************************************************************
+// Initialize the column defs for a table, based on their types and sizes.
+//*****************************************************************************
+HRESULT
+CMiniMdBase::InitColsForTable(
+ CMiniMdSchema &Schema, // Schema with sizes.
+ int ixTbl, // Index of table to init.
+ CMiniTableDef *pTable, // Table to init.
+ int bExtra, // Extra bits for rid column.
+ BOOL fUsePointers) // Should we have pTable point to it's Column Descriptors, or
+ // should we write the data into the structure
+{
+ const CMiniTableDef *pTemplate; // Template table definition.
+ CMiniColDef pCols[9]; // The col defs to init.
+ BYTE iOffset; // Running size of a record.
+ BYTE iSize; // Size of a field.
+ HRESULT hr = S_OK;
+
+ _ASSERTE((bExtra == 0) || (bExtra == 1));
+ _ASSERTE(NumItems(pCols) >= pTable->m_cCols);
+
+ bExtra = 0;//<TODO>@FUTURE: save in schema header. until then use 0.</TODO>
+
+ iOffset = 0;
+
+ pTemplate = GetTableDefTemplate(ixTbl);
+
+ PREFIX_ASSUME(pTemplate->m_pColDefs != NULL);
+
+ // For each column in the table...
+ for (ULONG ixCol = 0; ixCol < pTable->m_cCols; ++ixCol)
+ {
+ // Initialize from the template values (type, maybe offset, size).
+ pCols[ixCol] = pTemplate->m_pColDefs[ixCol];
+
+ // Is the field a RID into a table?
+ if (pCols[ixCol].m_Type <= iRidMax)
+ {
+ iSize = cbRID(Schema.m_cRecs[pCols[ixCol].m_Type] << bExtra);
+ }
+ else
+ // Is the field a coded token?
+ if (pCols[ixCol].m_Type <= iCodedTokenMax)
+ {
+ ULONG iCdTkn = pCols[ixCol].m_Type - iCodedToken;
+ ULONG cRecs = 0;
+
+ _ASSERTE(iCdTkn < lengthof(g_CodedTokens));
+ CCodedTokenDef const *pCTD = &g_CodedTokens[iCdTkn];
+
+ // Iterate the token list of this coded token.
+ for (ULONG ixToken=0; ixToken<pCTD->m_cTokens; ++ixToken)
+ { // Ignore string tokens.
+ if (pCTD->m_pTokens[ixToken] != mdtString)
+ {
+ // Get the table for the token.
+ ULONG nTokenTable = CMiniMdRW::GetTableForToken(pCTD->m_pTokens[ixToken]);
+ _ASSERTE(nTokenTable < TBL_COUNT);
+ // If largest token seen so far, remember it.
+ if (Schema.m_cRecs[nTokenTable] > cRecs)
+ cRecs = Schema.m_cRecs[nTokenTable];
+ }
+ }
+
+ iSize = cbRID(cRecs << (bExtra + m_cb[pCTD->m_cTokens]));
+
+ }
+ else
+ { // Fixed type.
+ switch (pCols[ixCol].m_Type)
+ {
+ case iBYTE:
+ iSize = 1;
+ _ASSERTE(pCols[ixCol].m_cbColumn == iSize);
+ _ASSERTE(pCols[ixCol].m_oColumn == iOffset);
+ break;
+ case iSHORT:
+ case iUSHORT:
+ iSize = 2;
+ _ASSERTE(pCols[ixCol].m_cbColumn == iSize);
+ _ASSERTE(pCols[ixCol].m_oColumn == iOffset);
+ break;
+ case iLONG:
+ case iULONG:
+ iSize = 4;
+ _ASSERTE(pCols[ixCol].m_cbColumn == iSize);
+ _ASSERTE(pCols[ixCol].m_oColumn == iOffset);
+ break;
+ case iSTRING:
+ iSize = (Schema.m_heaps & CMiniMdSchema::HEAP_STRING_4) ? 4 : 2;
+ break;
+ case iGUID:
+ iSize = (Schema.m_heaps & CMiniMdSchema::HEAP_GUID_4) ? 4 : 2;
+ break;
+ case iBLOB:
+ iSize = (Schema.m_heaps & CMiniMdSchema::HEAP_BLOB_4) ? 4 : 2;
+ break;
+ default:
+ _ASSERTE(!"Unexpected schema type");
+ iSize = 0;
+ break;
+ }
+ }
+
+ // Now save the size and offset.
+ pCols[ixCol].m_oColumn = iOffset;
+ pCols[ixCol].m_cbColumn = iSize;
+
+ // Align to 2 bytes.
+ iSize += iSize & 1;
+
+ iOffset += iSize;
+ }
+ // Record size of entire record.
+ pTable->m_cbRec = iOffset;
+
+ _ASSERTE(pTable->m_pColDefs != NULL);
+
+ // Can we write to the memory
+ if (!fUsePointers)
+ {
+ memcpy(pTable->m_pColDefs, pCols, sizeof(CMiniColDef)*pTable->m_cCols);
+ }
+ else
+ {
+ // We'll need to have pTable->m_pColDefs point to some data instead
+ hr = SetNewColumnDefinition(pTable, pCols, ixTbl);
+ }
+ // If no key, set to a distinct value.
+ if (pTable->m_iKey >= pTable->m_cCols)
+ pTable->m_iKey = (BYTE) -1;
+
+ return hr;
+} // CMiniMdBase::InitColsForTable
+
+//*****************************************************************************
+// Place a new Column Definition into the metadata.
+//*****************************************************************************
+HRESULT
+CMiniMdBase::SetNewColumnDefinition(
+ CMiniTableDef *pTable,
+ CMiniColDef *pCols,
+ DWORD ixTbl)
+{
+ // Look up the global cache to see if we can use a cached copy
+ if (UsesAllocatedMemory(pCols) ||
+ !FindSharedColDefs(pTable, pCols, ixTbl))
+ {
+ // See if we've already allocated memory for this item
+
+ if (!UsesAllocatedMemory(pTable->m_pColDefs))
+ {
+ // We don't have this column definition cached. Allocate new memory for it.
+ // Notice, we allocate one more byte than necessary, so we can 'mark' this chunk of memory
+ // as allocated so we can free it later.
+
+ BYTE *newMemory = new (nothrow) BYTE[(sizeof(CMiniColDef)*pTable->m_cCols)+1];
+
+ if (newMemory == NULL)
+ return E_OUTOFMEMORY;
+
+ // Mark the first byte in this as with the "allocated memory marker"
+ *newMemory = ALLOCATED_MEMORY_MARKER;
+
+ // Have the pointer point to the first Column Descriptor
+ pTable->m_pColDefs = BYTEARRAY_TO_COLDES(newMemory);
+ }
+
+ memcpy(pTable->m_pColDefs, pCols, sizeof(CMiniColDef)*pTable->m_cCols);
+ }
+
+ return S_OK;
+} // CMiniMdBase::SetNewColumnDefinition
+
+
+//*****************************************************************************
+// Get the count of records in a table. Virtual.
+//*****************************************************************************
+ULONG
+CMiniMdBase::GetCountRecs(
+ ULONG ixTbl)
+{
+ _ASSERTE(ixTbl < TBL_COUNT);
+ return m_Schema.m_cRecs[ixTbl];
+} // CMiniMdBase::GetCountRecs
+
+//*****************************************************************************
+// Search a table for multiple (adjacent) rows containing the given
+// key value. EG, InterfaceImpls all point back to the implementing class.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdBase::SearchTableForMultipleRows(
+ ULONG ixTbl, // Table to search.
+ CMiniColDef sColumn, // Sorted key column, containing search value.
+ ULONG ulTarget, // Target for search.
+ RID *pEnd, // [OPTIONAL, OUT]
+ RID *pFoundRid) // First RID found, or 0.
+{
+ HRESULT hr;
+ ULONG ridBegin; // RID of first entry.
+ ULONG ridEnd; // RID of first entry past last entry.
+
+ // Search for any entry in the table.
+ IfFailRet(vSearchTable(ixTbl, sColumn, ulTarget, &ridBegin));
+
+ // If nothing found, return invalid RID.
+ if (ridBegin == 0)
+ {
+ if (pEnd != NULL)
+ {
+ *pEnd = 0;
+ }
+ *pFoundRid = 0;
+ return S_OK;
+ }
+
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // If you change the rows touched while searching, please update
+ // CMiniMdRW::GetHotMetadataTokensSearchAware
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ // End will be at least one larger than found record.
+ ridEnd = ridBegin + 1;
+
+ // Search back to start of group.
+ for (;;)
+ {
+ void *pRow;
+ if (ridBegin <= 1)
+ {
+ break;
+ }
+ IfFailRet(vGetRow(ixTbl, ridBegin-1, &pRow));
+ if (getIX(pRow, sColumn) != ulTarget)
+ {
+ break;
+ }
+ --ridBegin;
+ }
+
+ // If desired, search forward to end of group.
+ if (pEnd != NULL)
+ {
+ for (;;)
+ {
+ void *pRow;
+ if (ridEnd > GetCountRecs(ixTbl))
+ {
+ break;
+ }
+ IfFailRet(vGetRow(ixTbl, ridEnd, &pRow));
+ if (getIX(pRow, sColumn) != ulTarget)
+ {
+ break;
+ }
+ ++ridEnd;
+ }
+ *pEnd = ridEnd;
+ }
+
+ *pFoundRid = ridBegin;
+ return S_OK;
+} // CMiniMdBase::SearchTableForMultipleRows
+
+
+#if BIGENDIAN
+// Endian Swaps the passed in blob representing a constant into the passed in StgPool
+HRESULT
+CMiniMdBase::SwapConstant(
+ const void *pBlobValue, // Original Value pointer
+ DWORD dwType, // Type of the constant
+ void *pConstant, // [Out] Location to store constant into
+ ULONG ValueLength) // [In] Length of constant
+{
+ HRESULT hr = NOERROR;
+
+ switch (dwType)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_VOID:
+ // Just return the value
+ *(BYTE *)pConstant = *(BYTE *)pBlobValue;
+ return NOERROR;
+
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+ _ASSERTE(ValueLength == 2);
+ *(SHORT *)pConstant = GET_UNALIGNED_VAL16(pBlobValue);
+ break;
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ _ASSERTE(ValueLength == 4);
+ *(__int32 *)pConstant = GET_UNALIGNED_VAL32(pBlobValue);
+ break;
+ case ELEMENT_TYPE_R4:
+ {
+ __int32 Value = GET_UNALIGNED_VAL32(pBlobValue);
+ *(float *)pConstant = (float &)Value;
+ }
+ break;
+
+ case ELEMENT_TYPE_R8:
+ {
+ __int64 Value = GET_UNALIGNED_VAL64(pBlobValue);
+ *(double *)pConstant = (double &) Value;
+ }
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ _ASSERTE(ValueLength == 8);
+ *(__int64 *)pConstant = GET_UNALIGNED_VAL64(pBlobValue);
+ break;
+ case ELEMENT_TYPE_STRING:
+ memcpy(pConstant, pBlobValue, ValueLength);
+ SwapStringLength((WCHAR *)pConstant, (ValueLength)/sizeof(WCHAR));
+ break;
+ default:
+ _ASSERTE(!"BAD TYPE!");
+ return E_INVALIDARG;
+ break;
+ }
+ return hr;
+} // CMiniMdBase::SwapConstant
+#endif //BIGENDIAN
+
+//*****************************************************************************
+// It is non-trivial to sort propertymap. VB is generating properties in
+// non-sorted order!!!
+//*****************************************************************************
+HRESULT
+CMiniMdBase::FindPropertyMapFor(
+ RID ridParent,
+ RID *pFoundRid)
+{
+ HRESULT hr;
+ ULONG i;
+ ULONG iCount;
+ void *pRec;
+ RID rid;
+
+ // If the table is sorted, use binary search. However we can only trust
+ // the sorted bit if we have verified it (see definition in MetaModel.h)
+ if (IsVerified() && m_Schema.IsSorted(TBL_PropertyMap))
+ {
+ return vSearchTable(TBL_PropertyMap,
+ _COLDEF(PropertyMap,Parent),
+ ridParent,
+ pFoundRid);
+ }
+ else
+ {
+ iCount = GetCountRecs(TBL_PropertyMap);
+
+ // loop through all LocalVar
+ for (i = 1; i <= iCount; i++)
+ {
+ IfFailRet(vGetRow(TBL_PropertyMap, i, &pRec));
+
+ // linear search for propertymap record
+ rid = getIX(pRec, _COLDEF(PropertyMap,Parent));
+ if (rid == ridParent)
+ {
+ *pFoundRid = i;
+ return S_OK;
+ }
+ }
+
+ *pFoundRid = 0;
+ return S_OK;
+ }
+
+} // CMiniMdBase::FindPropertyMapFor
+
+
+//*****************************************************************************
+// It is non-trivial to sort eventmap. VB is generating events in
+// non-sorted order!!!
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdBase::FindEventMapFor(
+ RID ridParent,
+ RID *pFoundRid)
+{
+ HRESULT hr;
+ ULONG i;
+ ULONG iCount;
+ void *pRec;
+ RID rid;
+
+ // If the table is sorted, use binary search. However we can only trust
+ // the sorted bit if we have verified it (see definition in MetaModel.h)
+ if (IsVerified() && m_Schema.IsSorted(TBL_EventMap))
+ {
+ return vSearchTable(TBL_EventMap,
+ _COLDEF(EventMap,Parent),
+ ridParent,
+ pFoundRid);
+ }
+ else
+ {
+ iCount = GetCountRecs(TBL_EventMap);
+
+ // loop through all LocalVar
+ for (i = 1; i <= iCount; i++)
+ {
+ IfFailRet(vGetRow(TBL_EventMap, i, &pRec));
+
+ // linear search for propertymap record
+ rid = getIX(pRec, _COLDEF(EventMap,Parent));
+ if (rid == ridParent)
+ {
+ *pFoundRid = i;
+ return S_OK;
+ }
+ }
+
+ *pFoundRid = 0;
+ return S_OK;
+ }
+} // CMiniMdBase::FindEventMapFor
+
+
+//*****************************************************************************
+// Search for a custom value with a given type.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdBase::FindCustomAttributeFor(
+ RID rid, // The object's rid.
+ mdToken tkObj, // The object's type.
+ mdToken tkType, // Type of custom value.
+ RID *pFoundRid) // RID of custom value, or 0.
+{
+ HRESULT hr;
+ int ixFound; // index of some custom value row.
+ ULONG ulTarget = encodeToken(rid,tkObj,mdtHasCustomAttribute,lengthof(mdtHasCustomAttribute)); // encoded token representing target.
+ ULONG ixCur; // Current row being examined.
+ mdToken tkFound; // Type of some custom value row.
+ void *pCur; // A custom value entry.
+
+ // Search for any entry in CustomAttribute table. Convert to RID.
+ IfFailRet(vSearchTable(TBL_CustomAttribute, _COLDEF(CustomAttribute,Parent), ulTarget, (RID *)&ixFound));
+ if (ixFound == 0)
+ {
+ *pFoundRid = 0;
+ return S_OK;
+ }
+
+ // Found an entry that matches the item. Could be anywhere in a range of
+ // custom values for the item, somewhat at random. Search for a match
+ // on name. On entry to the first loop, we know the object is the desired
+ // one, so the object test is at the bottom.
+ ixCur = ixFound;
+ IfFailRet(vGetRow(TBL_CustomAttribute, ixCur, &pCur));
+ for(;;)
+ {
+ // Test the type of the current row.
+ tkFound = getIX(pCur, _COLDEF(CustomAttribute,Type));
+ tkFound = decodeToken(tkFound, mdtCustomAttributeType, lengthof(mdtCustomAttributeType));
+ if (tkFound == tkType)
+ {
+ *pFoundRid = ixCur;
+ return S_OK;
+ }
+ // Was this the last row of the CustomAttribute table?
+ if (ixCur == GetCountRecs(TBL_CustomAttribute))
+ break;
+ // No match, more rows, try for the next row.
+ ++ixCur;
+ // Get the row and see if it is for the same object.
+ IfFailRet(vGetRow(TBL_CustomAttribute, ixCur, &pCur));
+ if (getIX(pCur, _COLDEF(CustomAttribute,Parent)) != ulTarget)
+ break;
+ }
+ // Didn't find the name looking up. Try looking down.
+ ixCur = ixFound - 1;
+ for(;;)
+ {
+ // Run out of table yet?
+ if (ixCur == 0)
+ break;
+ // Get the row and see if it is for the same object.
+ IfFailRet(vGetRow(TBL_CustomAttribute, ixCur, &pCur));
+ // still looking at the same object?
+ if (getIX(pCur, _COLDEF(CustomAttribute,Parent)) != ulTarget)
+ break;
+ // Test the type of the current row.
+ tkFound = getIX(pCur, _COLDEF(CustomAttribute,Type));
+ tkFound = decodeToken(tkFound, mdtCustomAttributeType, lengthof(mdtCustomAttributeType));
+ if (tkFound == tkType)
+ {
+ *pFoundRid = ixCur;
+ return S_OK;
+ }
+ // No match, try for the previous row.
+ --ixCur;
+ }
+ // Didn't find anything.
+ *pFoundRid = 0;
+ return S_OK;
+} // CMiniMdBase::FindCustomAttributeFor
+
+//*****************************************************************************
+// See if we can find a globally shared Column Def Array for this table
+//*****************************************************************************
+BOOL
+CMiniMdBase::FindSharedColDefs(
+ CMiniTableDef *pTable,
+ CMiniColDef *pColsToMatch,
+ DWORD ixTbl)
+{
+ // The majority of the time, m_pColDefs will point to the correct Column Definition Array.
+ if (!memcmp(pTable->m_pColDefs, pColsToMatch, sizeof(CMiniColDef)*(pTable->m_cCols)))
+ return TRUE;
+
+ else
+ {
+ // m_pColDefs points to a set of Column Def Arrays, with the byte previous to it the number
+ // of column descriptors that we have.
+ CMiniColDef *pListOfColumnDefs = BYTEARRAY_TO_COLDES(s_TableColumnDescriptors[ixTbl]);
+
+ BYTE nNumColDes = *(s_TableColumnDescriptors[ixTbl]);
+
+ // Start at '1' since we already compared the first set of column definitions above.
+ for (int i = 1; i < nNumColDes; i++)
+ {
+ pListOfColumnDefs += pTable->m_cCols;
+
+ if (!memcmp(pListOfColumnDefs, pColsToMatch, sizeof(CMiniColDef)*(pTable->m_cCols)))
+ {
+ pTable->m_pColDefs = pListOfColumnDefs;
+ return TRUE;
+ }
+ }
+ }
+
+ // We weren't able to find a shared column definition
+ return FALSE;
+}// CMiniMdBase::FindSharedColDefs
+
+//*****************************************************************************
+// Determines where the Table Def's Column Definitions used shared memory or
+// allocated memory
+//*****************************************************************************
+BOOL
+CMiniMdBase::UsesAllocatedMemory(
+ CMiniColDef *pCols)
+{
+ BYTE *pMem = COLDES_TO_BYTEARRAY(pCols);
+
+ // If the byte preceding this pointer is -1, then we allocated it and it must be freed
+ return (*pMem == ALLOCATED_MEMORY_MARKER);
+}// CMiniMdBase::UsesAllocatedMemory
diff --git a/src/md/runtime/metamodelcolumndefs.h b/src/md/runtime/metamodelcolumndefs.h
new file mode 100644
index 0000000000..311ec97078
--- /dev/null
+++ b/src/md/runtime/metamodelcolumndefs.h
@@ -0,0 +1,401 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MetaModelColumnDefs.h -- Table definitions for MetaData.
+//
+
+//
+//*****************************************************************************
+
+#if METAMODEL_MAJOR_VER != 2
+#if METAMODEL_MAJOR_VER != 1
+#error "METAMODEL_MAJOR_VER other than 1 or 2 is not implemented"
+#endif
+#endif
+ //
+ // These are used by #defining appropriately, then #including this file.
+ //
+ //-------------------------------------------------------------------------
+ //Module
+ SCHEMA_TABLE_START(Module)
+ SCHEMA_ITEM(Module, USHORT, Generation)
+ SCHEMA_ITEM_STRING(Module, Name)
+ SCHEMA_ITEM_GUID(Module, Mvid)
+ SCHEMA_ITEM_GUID(Module, EncId)
+ SCHEMA_ITEM_GUID(Module, EncBaseId)
+ SCHEMA_TABLE_END(Module)
+
+ //-------------------------------------------------------------------------
+ //TypeRef
+ SCHEMA_TABLE_START(TypeRef)
+ SCHEMA_ITEM_CDTKN(TypeRef, ResolutionScope, ResolutionScope)
+ SCHEMA_ITEM_STRING(TypeRef, Name)
+ SCHEMA_ITEM_STRING(TypeRef, Namespace)
+ SCHEMA_TABLE_END(TypeRef)
+
+ //-------------------------------------------------------------------------
+ // TypeDef
+ SCHEMA_TABLE_START(TypeDef)
+ SCHEMA_ITEM(TypeDef, ULONG, Flags)
+ SCHEMA_ITEM_STRING(TypeDef, Name)
+ SCHEMA_ITEM_STRING(TypeDef, Namespace)
+ SCHEMA_ITEM_CDTKN(TypeDef, Extends, TypeDefOrRef)
+ SCHEMA_ITEM_RID(TypeDef, FieldList, Field)
+ SCHEMA_ITEM_RID(TypeDef, MethodList, Method)
+ SCHEMA_TABLE_END(TypeDef)
+
+ //-------------------------------------------------------------------------
+ //FieldPtr
+ SCHEMA_TABLE_START(FieldPtr)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_RID(FieldPtr, Field, Field)
+ SCHEMA_TABLE_END(FieldPtr)
+
+ //-------------------------------------------------------------------------
+ //Field
+ SCHEMA_TABLE_START(Field)
+ SCHEMA_ITEM(Field, USHORT, Flags)
+ SCHEMA_ITEM_STRING(Field,Name)
+ SCHEMA_ITEM_BLOB(Field,Signature)
+ SCHEMA_TABLE_END(Field)
+
+ //-------------------------------------------------------------------------
+ //MethodPtr
+ SCHEMA_TABLE_START(MethodPtr)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_RID(MethodPtr, Method, Method)
+ SCHEMA_TABLE_END(MethodPtr)
+
+ //-------------------------------------------------------------------------
+ //Method
+ SCHEMA_TABLE_START(Method)
+ SCHEMA_ITEM(Method, ULONG, RVA)
+ SCHEMA_ITEM(Method, USHORT, ImplFlags)
+ SCHEMA_ITEM(Method, USHORT, Flags)
+ SCHEMA_ITEM_STRING(Method,Name)
+ SCHEMA_ITEM_BLOB(Method,Signature)
+ SCHEMA_ITEM_RID(Method,ParamList,Param)
+ SCHEMA_TABLE_END(Method)
+
+ //-------------------------------------------------------------------------
+ //ParamPtr
+ SCHEMA_TABLE_START(ParamPtr)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_RID(ParamPtr, Param, Param)
+ SCHEMA_TABLE_END(ParamPtr)
+
+ //-------------------------------------------------------------------------
+ // Param
+ SCHEMA_TABLE_START(Param)
+ SCHEMA_ITEM(Param, USHORT, Flags)
+ SCHEMA_ITEM(Param, USHORT, Sequence)
+ SCHEMA_ITEM_STRING(Param,Name)
+ SCHEMA_TABLE_END(Param)
+
+ //-------------------------------------------------------------------------
+ //InterfaceImpl
+ SCHEMA_TABLE_START(InterfaceImpl)
+ SCHEMA_ITEM_RID(InterfaceImpl,Class,TypeDef)
+ SCHEMA_ITEM_CDTKN(InterfaceImpl,Interface,TypeDefOrRef)
+ SCHEMA_TABLE_END(InterfaceImpl)
+
+ //-------------------------------------------------------------------------
+ //MemberRef
+ SCHEMA_TABLE_START(MemberRef)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_CDTKN(MemberRef,Class,MemberRefParent)
+ SCHEMA_ITEM_STRING(MemberRef,Name)
+ SCHEMA_ITEM_BLOB(MemberRef,Signature)
+ SCHEMA_TABLE_END(MemberRef)
+
+ //-------------------------------------------------------------------------
+ //Constant
+ SCHEMA_TABLE_START(Constant)
+ SCHEMA_ITEM(Constant, BYTE, Type)
+ SCHEMA_ITEM_CDTKN(Constant,Parent,HasConstant)
+ SCHEMA_ITEM_BLOB(Constant,Value)
+ SCHEMA_TABLE_END(Constant)
+
+ //-------------------------------------------------------------------------
+ //CustomAttribute
+ SCHEMA_TABLE_START(CustomAttribute)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_CDTKN(CustomAttribute,Parent,HasCustomAttribute)
+ SCHEMA_ITEM_CDTKN(CustomAttribute,Type,CustomAttributeType)
+ SCHEMA_ITEM_BLOB(CustomAttribute,Value)
+ SCHEMA_TABLE_END(CustomAttribute)
+
+ //-------------------------------------------------------------------------
+ //FieldMarshal
+ SCHEMA_TABLE_START(FieldMarshal)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_CDTKN(FieldMarshal,Parent,HasFieldMarshal)
+ SCHEMA_ITEM_BLOB(FieldMarshal,NativeType)
+ SCHEMA_TABLE_END(FieldMarshal)
+
+ //-------------------------------------------------------------------------
+ //DeclSecurity
+ SCHEMA_TABLE_START(DeclSecurity)
+ SCHEMA_ITEM(DeclSecurity, SHORT, Action)
+ SCHEMA_ITEM_CDTKN(DeclSecurity,Parent,HasDeclSecurity)
+ SCHEMA_ITEM_BLOB(DeclSecurity,PermissionSet)
+ SCHEMA_TABLE_END(DeclSecurity)
+
+ //-------------------------------------------------------------------------
+ //ClassLayout
+ SCHEMA_TABLE_START(ClassLayout)
+ SCHEMA_ITEM(ClassLayout, USHORT, PackingSize)
+ SCHEMA_ITEM(ClassLayout, ULONG, ClassSize)
+ SCHEMA_ITEM_RID(ClassLayout,Parent,TypeDef)
+ SCHEMA_TABLE_END(ClassLayout)
+
+ //-------------------------------------------------------------------------
+ //FieldLayout
+ SCHEMA_TABLE_START(FieldLayout)
+ SCHEMA_ITEM(FieldLayout, ULONG, OffSet)
+ SCHEMA_ITEM_RID(FieldLayout, Field, Field)
+ SCHEMA_TABLE_END(FieldLayout)
+
+ //-------------------------------------------------------------------------
+ //StandAloneSig
+ SCHEMA_TABLE_START(StandAloneSig)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_BLOB(StandAloneSig,Signature)
+ SCHEMA_TABLE_END(StandAloneSig)
+
+ //-------------------------------------------------------------------------
+ //EventMap
+ SCHEMA_TABLE_START(EventMap)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_RID(EventMap,Parent,TypeDef)
+ SCHEMA_ITEM_RID(EventMap,EventList,Event)
+ SCHEMA_TABLE_END(EventMap)
+
+ //-------------------------------------------------------------------------
+ //EventPtr
+ SCHEMA_TABLE_START(EventPtr)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_RID(EventPtr, Event, Event)
+ SCHEMA_TABLE_END(EventPtr)
+
+ //-------------------------------------------------------------------------
+ //Event
+ SCHEMA_TABLE_START(Event)
+ SCHEMA_ITEM(Event, USHORT, EventFlags)
+ SCHEMA_ITEM_STRING(Event,Name)
+ SCHEMA_ITEM_CDTKN(Event,EventType,TypeDefOrRef)
+ SCHEMA_TABLE_END(Event)
+
+ //-------------------------------------------------------------------------
+ //PropertyMap
+ SCHEMA_TABLE_START(PropertyMap)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_RID(PropertyMap,Parent,TypeDef)
+ SCHEMA_ITEM_RID(PropertyMap,PropertyList,Property)
+ SCHEMA_TABLE_END(PropertyMap)
+
+ //-------------------------------------------------------------------------
+ //PropertyPtr
+ SCHEMA_TABLE_START(PropertyPtr)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_RID(PropertyPtr, Property, Property)
+ SCHEMA_TABLE_END(PropertyPtr)
+
+ //-------------------------------------------------------------------------
+ //Property
+ SCHEMA_TABLE_START(Property)
+ SCHEMA_ITEM(Property, USHORT, PropFlags)
+ SCHEMA_ITEM_STRING(Property,Name)
+ SCHEMA_ITEM_BLOB(Property,Type)
+ SCHEMA_TABLE_END(Property)
+
+ //-------------------------------------------------------------------------
+ //MethodSemantics
+ SCHEMA_TABLE_START(MethodSemantics)
+ SCHEMA_ITEM(MethodSemantics, USHORT, Semantic)
+ SCHEMA_ITEM_RID(MethodSemantics,Method,Method)
+ SCHEMA_ITEM_CDTKN(MethodSemantics,Association,HasSemantic)
+ SCHEMA_TABLE_END(MethodSemantics)
+
+ //-------------------------------------------------------------------------
+ //MethodImpl
+ SCHEMA_TABLE_START(MethodImpl)
+ SCHEMA_ITEM_RID(MethodImpl,Class,TypeDef)
+ SCHEMA_ITEM_CDTKN(MethodImpl,MethodBody,MethodDefOrRef)
+ SCHEMA_ITEM_CDTKN(MethodImpl, MethodDeclaration, MethodDefOrRef)
+ SCHEMA_TABLE_END(MethodImpl)
+
+ //-------------------------------------------------------------------------
+ //ModuleRef
+ SCHEMA_TABLE_START(ModuleRef)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_STRING(ModuleRef, Name)
+ SCHEMA_TABLE_END(ModuleRef)
+
+ //-------------------------------------------------------------------------
+ // TypeSpec
+ SCHEMA_TABLE_START(TypeSpec)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_BLOB(TypeSpec,Signature)
+ SCHEMA_TABLE_END(TypeSpec)
+
+ //-------------------------------------------------------------------------
+ // ENCLog
+ SCHEMA_TABLE_START(ENCLog)
+ SCHEMA_ITEM(ENCLog, ULONG, Token)
+ SCHEMA_ITEM(ENCLog, ULONG, FuncCode)
+ SCHEMA_TABLE_END(ENCLog)
+
+ //-------------------------------------------------------------------------
+ // ImplMap
+ SCHEMA_TABLE_START(ImplMap)
+ SCHEMA_ITEM(ImplMap, USHORT, MappingFlags)
+ SCHEMA_ITEM_CDTKN(ImplMap, MemberForwarded, MemberForwarded)
+ SCHEMA_ITEM_STRING(ImplMap, ImportName)
+ SCHEMA_ITEM_RID(ImplMap, ImportScope, ModuleRef)
+ SCHEMA_TABLE_END(ImplMap)
+
+ //-------------------------------------------------------------------------
+ // ENCMap
+ SCHEMA_TABLE_START(ENCMap)
+ SCHEMA_ITEM(ENCMap, ULONG, Token)
+ SCHEMA_TABLE_END(ENCMap)
+
+ //-------------------------------------------------------------------------
+ // FieldRVA
+ SCHEMA_TABLE_START(FieldRVA)
+ SCHEMA_ITEM(FieldRVA, ULONG, RVA)
+ SCHEMA_ITEM_RID(FieldRVA, Field, Field)
+ SCHEMA_TABLE_END(FieldRVA)
+
+ //-------------------------------------------------------------------------
+ // Assembly
+ SCHEMA_TABLE_START(Assembly)
+ SCHEMA_ITEM(Assembly, ULONG, HashAlgId)
+ SCHEMA_ITEM(Assembly, USHORT, MajorVersion)
+ SCHEMA_ITEM(Assembly, USHORT, MinorVersion)
+ SCHEMA_ITEM(Assembly, USHORT, BuildNumber)
+ SCHEMA_ITEM(Assembly, USHORT, RevisionNumber)
+ SCHEMA_ITEM(Assembly, ULONG, Flags)
+ SCHEMA_ITEM_BLOB(Assembly, PublicKey)
+ SCHEMA_ITEM_STRING(Assembly, Name)
+ SCHEMA_ITEM_STRING(Assembly, Locale)
+ SCHEMA_TABLE_END(Assembly)
+
+ //-------------------------------------------------------------------------
+ // AssemblyProcessor
+ SCHEMA_TABLE_START(AssemblyProcessor)
+ SCHEMA_ITEM(AssemblyProcessor, ULONG, Processor)
+ SCHEMA_TABLE_END(AssemblyProcessor)
+
+ //-------------------------------------------------------------------------
+ // AssemblyOS
+ SCHEMA_TABLE_START(AssemblyOS)
+ SCHEMA_ITEM(AssemblyOS, ULONG, OSPlatformId)
+ SCHEMA_ITEM(AssemblyOS, ULONG, OSMajorVersion)
+ SCHEMA_ITEM(AssemblyOS, ULONG, OSMinorVersion)
+ SCHEMA_TABLE_END(AssemblyOS)
+
+ //-------------------------------------------------------------------------
+ // AssemblyRef
+ SCHEMA_TABLE_START(AssemblyRef)
+ SCHEMA_ITEM(AssemblyRef, USHORT, MajorVersion)
+ SCHEMA_ITEM(AssemblyRef, USHORT, MinorVersion)
+ SCHEMA_ITEM(AssemblyRef, USHORT, BuildNumber)
+ SCHEMA_ITEM(AssemblyRef, USHORT, RevisionNumber)
+ SCHEMA_ITEM(AssemblyRef, ULONG, Flags)
+ SCHEMA_ITEM_BLOB(AssemblyRef, PublicKeyOrToken)
+ SCHEMA_ITEM_STRING(AssemblyRef, Name)
+ SCHEMA_ITEM_STRING(AssemblyRef, Locale)
+ SCHEMA_ITEM_BLOB(AssemblyRef, HashValue)
+ SCHEMA_TABLE_END(AssemblyRef)
+
+ //-------------------------------------------------------------------------
+ // AssemblyRefProcessor
+ SCHEMA_TABLE_START(AssemblyRefProcessor)
+ SCHEMA_ITEM(AssemblyRefProcessor, ULONG, Processor)
+ SCHEMA_ITEM_RID(AssemblyRefProcessor, AssemblyRef, AssemblyRef)
+ SCHEMA_TABLE_END(AssemblyRefProcessor)
+
+ //-------------------------------------------------------------------------
+ // AssemblyRefOS
+ SCHEMA_TABLE_START(AssemblyRefOS)
+ SCHEMA_ITEM(AssemblyRefOS, ULONG, OSPlatformId)
+ SCHEMA_ITEM(AssemblyRefOS, ULONG, OSMajorVersion)
+ SCHEMA_ITEM(AssemblyRefOS, ULONG, OSMinorVersion)
+ SCHEMA_ITEM_RID(AssemblyRefOS, AssemblyRef, AssemblyRef)
+ SCHEMA_TABLE_END(AssemblyRefOS)
+
+ //-------------------------------------------------------------------------
+ // File
+ SCHEMA_TABLE_START(File)
+ SCHEMA_ITEM(File, ULONG, Flags)
+ SCHEMA_ITEM_STRING(File, Name)
+ SCHEMA_ITEM_BLOB(File, HashValue)
+ SCHEMA_TABLE_END(File)
+
+ //-------------------------------------------------------------------------
+ // ExportedType
+ SCHEMA_TABLE_START(ExportedType)
+ SCHEMA_ITEM(ExportedType, ULONG, Flags)
+ SCHEMA_ITEM(ExportedType, ULONG, TypeDefId)
+ SCHEMA_ITEM_STRING(ExportedType, TypeName)
+ SCHEMA_ITEM_STRING(ExportedType, TypeNamespace)
+ SCHEMA_ITEM_CDTKN(ExportedType, Implementation, Implementation)
+ SCHEMA_TABLE_END(ExportedType)
+
+ //-------------------------------------------------------------------------
+ // ManifestResource
+ SCHEMA_TABLE_START(ManifestResource)
+ SCHEMA_ITEM(ManifestResource, ULONG, Offset)
+ SCHEMA_ITEM(ManifestResource, ULONG, Flags)
+ SCHEMA_ITEM_STRING(ManifestResource, Name)
+ SCHEMA_ITEM_CDTKN(ManifestResource, Implementation, Implementation)
+ SCHEMA_TABLE_END(ManifestResource)
+
+ //-------------------------------------------------------------------------
+ // NestedClass
+ SCHEMA_TABLE_START(NestedClass)
+ SCHEMA_ITEM_RID(NestedClass, NestedClass, TypeDef)
+ SCHEMA_ITEM_RID(NestedClass, EnclosingClass, TypeDef)
+ SCHEMA_TABLE_END(NestedClass)
+
+
+ //-------------------------------------------------------------------------
+ // GenericParam
+ SCHEMA_TABLE_START(GenericParam)
+ SCHEMA_ITEM(GenericParam, USHORT, Number)
+ SCHEMA_ITEM(GenericParam, USHORT, Flags)
+ SCHEMA_ITEM_CDTKN(GenericParam, Owner, TypeOrMethodDef)
+ SCHEMA_ITEM_STRING(GenericParam, Name)
+ SCHEMA_TABLE_END(GenericParam)
+
+ //-------------------------------------------------------------------------
+ // Transitional table for Metadata v1.1 for GenericParam
+ SCHEMA_TABLE_START(GenericParamV1_1)
+ SCHEMA_ITEM(GenericParam, USHORT, Number)
+ SCHEMA_ITEM(GenericParam, USHORT, Flags)
+ SCHEMA_ITEM_CDTKN(GenericParam, Owner, TypeOrMethodDef)
+ SCHEMA_ITEM_STRING(GenericParam, Name)
+ SCHEMA_ITEM_CDTKN(GenericParam, Kind, TypeDefOrRef)
+ SCHEMA_TABLE_END(GenericParam)
+
+
+
+ //-------------------------------------------------------------------------
+ //MethodSpec
+ SCHEMA_TABLE_START(MethodSpec)
+ SCHEMA_ITEM_NOFIXED()
+ SCHEMA_ITEM_CDTKN(MethodSpec, Method, MethodDefOrRef)
+ SCHEMA_ITEM_BLOB(MethodSpec, Instantiation)
+ SCHEMA_TABLE_END(MethodSpec)
+
+ //-------------------------------------------------------------------------
+ // GenericParamConstraint
+ SCHEMA_TABLE_START(GenericParamConstraint)
+ SCHEMA_ITEM_RID(GenericParamConstraint, Owner, GenericParam)
+ SCHEMA_ITEM_CDTKN(GenericParamConstraint, Constraint, TypeDefOrRef)
+ SCHEMA_TABLE_END(GenericParamConstraint)
+
+// eof ------------------------------------------------------------------------
diff --git a/src/md/runtime/metamodelro.cpp b/src/md/runtime/metamodelro.cpp
new file mode 100644
index 0000000000..0feb153a2f
--- /dev/null
+++ b/src/md/runtime/metamodelro.cpp
@@ -0,0 +1,444 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MetaModelRO.cpp -- Read-only implementation of compressed COM+ metadata.
+//
+
+//
+//*****************************************************************************
+#include "stdafx.h"
+
+#include "metamodelro.h"
+#include <posterror.h>
+#include <corerror.h>
+#include "metadatatracker.h"
+
+//*****************************************************************************
+// Set the pointers to consecutive areas of a large buffer.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMd::InitializeTables(
+ MetaData::DataBlob tablesData)
+{
+ HRESULT hr;
+
+ for (int i = 0; i < TBL_COUNT; i++)
+ {
+ // This table data
+ MetaData::DataBlob tableData;
+
+ 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 large - size overflow.");
+ return CLDB_E_FILE_CORRUPT;
+ }
+ if (!tablesData.GetDataOfSize(cbTableSize.Value(), &tableData))
+ {
+ Debug_ReportError("Table is not within MetaData tables block.");
+ return CLDB_E_FILE_CORRUPT;
+ }
+ _ASSERTE(cbTableSize.Value() == tableData.GetSize());
+
+ METADATATRACKER_ONLY(MetaDataTracker::NoteSection(
+ i,
+ tableData.GetDataPointer(),
+ tableData.GetSize(),
+ m_TableDefs[i].m_cbRec));
+
+ IfFailRet(m_Tables[i].Initialize(
+ m_TableDefs[i].m_cbRec,
+ tableData,
+ FALSE)); // fCopyData
+ }
+
+ return S_OK;
+} // CMiniMd::SetTablePointers
+
+//*****************************************************************************
+// Given a buffer that contains a MiniMd, init to read it.
+//*****************************************************************************
+HRESULT
+CMiniMd::InitOnMem(
+ void *pvBuf, // The buffer.
+ ULONG ulBufLen) // Size of the buffer..
+{
+ HRESULT hr = S_OK;
+ ULONG cbData;
+ BYTE *pBuf = reinterpret_cast<BYTE*>(pvBuf);
+
+ // Uncompress the schema from the buffer into our structures.
+ IfFailGo(SchemaPopulate(pvBuf, ulBufLen, &cbData));
+ PREFAST_ASSUME(cbData <= ulBufLen);
+
+ // There shouldn't be any pointer tables.
+ if ((m_Schema.m_cRecs[TBL_MethodPtr] != 0) || (m_Schema.m_cRecs[TBL_FieldPtr] != 0))
+ {
+ Debug_ReportError("MethodPtr and FieldPtr tables are not allowed in Read-Only format.");
+ return PostError(CLDB_E_FILE_CORRUPT);
+ }
+
+ // initialize the pointers to the rest of the data.
+ IfFailGo(InitializeTables(MetaData::DataBlob(pBuf + Align4(cbData), ulBufLen-cbData)));
+
+ErrExit:
+ return hr;
+} // CMiniMd::InitOnMem
+
+//*****************************************************************************
+// Validate cross-stream consistency.
+//*****************************************************************************
+HRESULT
+CMiniMd::PostInit(
+ int iLevel)
+{
+ return S_OK;
+} // CMiniMd::PostInit
+
+//*****************************************************************************
+// converting a ANSI heap string to unicode string to an output buffer
+//*****************************************************************************
+HRESULT
+CMiniMd::Impl_GetStringW(
+ ULONG ix,
+ __inout_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 != NULL) && (cchBuffer != 0))
+ szOut[0] = W('\0');
+ if (pcchBuffer != NULL)
+ *pcchBuffer = 0;
+ goto ErrExit;
+ }
+ iSize = ::WszMultiByteToWideChar(CP_UTF8, 0, szString, -1, szOut, cchBuffer);
+ if (iSize == 0)
+ {
+ // 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 != NULL)
+ *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 != NULL)
+ *pcchBuffer = iSize;
+
+ErrExit:
+ return hr;
+} // CMiniMd::Impl_GetStringW
+
+
+//*****************************************************************************
+// 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
+CMiniMd::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;
+ _ASSERTE(nTableIndex < TBL_COUNT);
+ RID nLastRowIndex = m_Schema.m_cRecs[nTableIndex];
+
+ // Last rid in range from NEXT record, or count of table, if last record.
+ if (nRowIndex < nLastRowIndex)
+ {
+ BYTE *pRow;
+ IfFailRet(Impl_GetRow(nTableIndex, nRowIndex + 1, &pRow));
+ *pEndRid = getIX(pRow, def);
+ }
+ else // Convert count to 1-based rid.
+ {
+ if (nRowIndex != nLastRowIndex)
+ {
+ Debug_ReportError("Invalid table row index.");
+ IfFailRet(METADATA_E_INDEX_NOTFOUND);
+ }
+ _ASSERTE(nTargetTableIndex < TBL_COUNT);
+ *pEndRid = m_Schema.m_cRecs[nTargetTableIndex] + 1;
+ }
+
+ return S_OK;
+} // CMiniMd::Impl_GetEndRidForColumn
+
+
+//*****************************************************************************
+// return all found CAs in an enumerator
+//*****************************************************************************
+HRESULT
+CMiniMd::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.
+
+ _ASSERTE(phEnum != NULL);
+
+ memset(phEnum, 0, sizeof(HENUMInternal));
+
+ HENUMInternal::InitDynamicArrayEnum(phEnum);
+
+ phEnum->m_tkKind = mdtCustomAttribute;
+
+ // Get the list of custom values for the parent object.
+
+ IfFailGo(getCustomAttributeForToken(tkObj, &ridEnd, &ridStart));
+ if (ridStart == 0)
+ return S_FALSE;
+
+ // Look for one with the given name.
+ for (; ridStart < ridEnd; ++ridStart)
+ {
+ IfFailGoto(CompareCustomAttribute( tkObj, szName, ridStart), ErrExit);
+ 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;
+} // CMiniMd::CommonEnumCustomAttributeByName
+
+
+//*****************************************************************************
+// Search a table for the row containing the given key value.
+// EG. Constant table has pointer back to Param or Field.
+//
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMd::vSearchTable(
+ ULONG ixTbl, // Table to search.
+ CMiniColDef sColumn, // Sorted key column, containing search value.
+ ULONG ulTarget, // Target for search.
+ RID *pRid) // RID of matching row, or 0.
+{
+ HRESULT hr;
+ void *pRow = NULL; // Row from a table.
+ ULONG val; // Value from a row.
+ int lo, mid, hi; // binary search indices.
+
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // If you change the rows touched while searching, please update
+ // CMiniMdRW::GetHotMetadataTokensSearchAware
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ // 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_NoLogging(pRow, sColumn);
+ // If equal to the target, done.
+ if (val == ulTarget)
+ {
+ *pRid = mid;
+ METADATATRACKER_ONLY(MetaDataTracker::NoteSearch(pRow));
+ return S_OK;
+ }
+ // If middle item is too small, search the top half.
+ if (val < ulTarget)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+ // Didn't find anything that matched.
+ *pRid = 0;
+
+ METADATATRACKER_ONLY(MetaDataTracker::NoteSearch(pRow));
+ return S_OK;
+} // CMiniMd::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.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMd::vSearchTableNotGreater(
+ ULONG ixTbl, // Table to search.
+ CMiniColDef sColumn, // the column def containing search value
+ ULONG ulTarget, // target for search
+ RID *pRid) // RID of matching row, or 0
+{
+ HRESULT hr;
+ void *pRow = NULL; // 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);
+
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // If you change the rows touched while searching, please update
+ // CMiniMdRW::GetHotMetadataTokensSearchAware
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ // 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_NoLogging(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)
+ lo = mid + 1;
+ else // but if middle is to big, search bottom half.
+ hi = mid - 1;
+ }
+
+ METADATATRACKER_ONLY(MetaDataTracker::NoteSearch(pRow));
+
+ // 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)
+ {
+ while (val > ulTarget)
+ {
+ // If there is nothing else to look at, we won't find it.
+ if (--mid < 1)
+ break;
+ 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)
+ break;
+ mid++;
+ }
+ }
+
+ // Return the value that's just less than the target.
+ *pRid = mid;
+ return S_OK;
+} // CMiniMd::vSearchTableNotGreater
+
+//*****************************************************************************
+// return just the blob value of the first found CA matching the query.
+// returns S_FALSE if there is no match
+//*****************************************************************************
+HRESULT
+CMiniMd::CommonGetCustomAttributeByNameEx(
+ 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;
+
+ ULONG cbData;
+ CustomAttributeRec *pRec;
+
+ ULONG ridStart, ridEnd; // Loop start and endpoints.
+
+ // Get the list of custom values for the parent object.
+
+ IfFailGo(getCustomAttributeForToken(tkObj, &ridEnd, &ridStart));
+
+ hr = S_FALSE;
+ if (ridStart == 0)
+ {
+ goto ErrExit;
+ }
+
+ // Look for one with the given name.
+ for (; ridStart < ridEnd; ++ridStart)
+ {
+ IfFailGoto(CompareCustomAttribute( tkObj, szName, ridStart), ErrExit);
+ if (hr == S_OK)
+ {
+ if (ppData != NULL)
+ {
+ // now get the record out.
+ if (pcbData == NULL)
+ pcbData = &cbData;
+
+ IfFailGo(GetCustomAttributeRecord(ridStart, &pRec));
+ IfFailGo(getValueOfCustomAttribute(pRec, reinterpret_cast<const BYTE **>(ppData), pcbData));
+ if (ptkCA)
+ *ptkCA = TokenFromRid(mdtCustomAttribute, ridStart);
+ }
+ break;
+ }
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMd::CommonGetCustomAttributeByName
diff --git a/src/md/runtime/recordpool.cpp b/src/md/runtime/recordpool.cpp
new file mode 100644
index 0000000000..da4bcbc9e9
--- /dev/null
+++ b/src/md/runtime/recordpool.cpp
@@ -0,0 +1,388 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// RecordPool.cpp -- Implementation of record heaps.
+//
+
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include <recordpool.h>
+
+#define RECORDPOOL_GROW_FACTOR 8
+#define RECORDPOOL_GROW_MAX 2048
+#define RECORDPOOL_GROW_MINROWS 2
+#define RECORDPOOL_GROW_DEFAULTROWS 16
+
+HRESULT
+RecordPool::InitNew(
+ UINT32 cbRec, // Record size.
+ UINT32 cRecsInit) // Initial guess of count of record.
+{
+ HRESULT hr;
+ S_UINT32 cbGrow; // Initial grow size of the pool.
+
+ // Size of each record is fixed.
+ m_cbRec = cbRec;
+
+ if (cRecsInit > 0)
+ {
+ cbGrow = S_UINT32(cbRec) * S_UINT32(cRecsInit);
+ }
+ else
+ {
+ cbGrow = S_UINT32(cbRec) * S_UINT32(RECORDPOOL_GROW_DEFAULTROWS);
+ }
+ if (cbGrow.IsOverflow())
+ {
+ Debug_ReportInternalError("Growing record pool overflowed.");
+ return CLDB_E_INTERNALERROR;
+ }
+
+ m_ulGrowInc = cbGrow.Value();
+
+ IfFailRet(StgPool::InitNew());
+
+ // If there is an initial size for the record pool, grow to that now.
+ if (cRecsInit > 0)
+ {
+ if (!Grow(cbGrow.Value()))
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+} // RecordPool::InitNew
+
+//*****************************************************************************
+// Load a Record heap from persisted memory. If a copy of the data is made
+// (so that it may be updated), then a new hash table is generated which can
+// be used to elminate duplicates with new Records.
+//*****************************************************************************
+HRESULT
+RecordPool::InitOnMem(
+ ULONG cbRec, // Record size.
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ BOOL fReadOnly) // true if append is forbidden.
+{
+ HRESULT hr;
+ m_cbRec = cbRec;
+
+ // Let base class init our memory structure.
+ IfFailRet(StgPool::InitOnMem(pData, iSize, fReadOnly));
+
+ // For init on existing mem case.
+ if ((pData != NULL) && (iSize != 0))
+ {
+ // If we are doing an update in place don't make a copy
+ // If we cannot update, then we don't need a hash table.
+ if (fReadOnly)
+ return S_OK;
+
+ // Other wise copy the memory to do the update
+ IfFailRet(TakeOwnershipOfInitMem());
+ }
+
+ return S_OK;
+} // RecordPool::InitOnMem
+
+//*****************************************************************************
+// Allocate memory if we don't have any, or grow what we have. If successful,
+// then at least iRequired bytes will be allocated.
+//*****************************************************************************
+bool RecordPool::Grow( // true if successful.
+ ULONG iRequired) // Min required bytes to allocate.
+{
+ // Allocate the memory.
+ if (!StgPool::Grow(iRequired))
+ return false;
+
+ // Zero the new memory.
+ memset(GetNextLocation(), 0, GetCbSegAvailable());
+
+ return true;
+} // bool RecordProol::Grow()
+
+//*****************************************************************************
+// The Record will be added to the pool. The index of the Record in the pool
+// is returned in *piIndex. If the Record is already in the pool, then the
+// index will be to the existing copy of the Record.
+//*****************************************************************************
+HRESULT
+RecordPool::AddRecord(
+ BYTE **ppRecord,
+ UINT32 *pnIndex) // Return 1-based index of Record here.
+{
+ _ASSERTE(pnIndex != NULL);
+
+ // Space on heap for new Record?
+ if (m_cbRec > GetCbSegAvailable())
+ {
+ if (!Grow(m_cbRec))
+ {
+ *ppRecord = NULL;
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ // Records should be aligned on record boundaries.
+ _ASSERTE((GetNextOffset() % m_cbRec) == 0);
+
+ // Copy the Record to the heap.
+ *ppRecord = GetNextLocation();
+
+ // Give the 1-based index back to caller.
+ *pnIndex = (GetNextOffset() / m_cbRec) + 1;
+
+ // Update heap counters.
+ SegAllocate(m_cbRec);
+
+ return S_OK;
+} // RecordPool::AddRecord
+
+//*****************************************************************************
+// Insert a Record into the pool. The index of the Record before which to
+// insert is specified. Shifts all records down. Return a pointer to the
+// new record.
+//*****************************************************************************
+HRESULT
+RecordPool::InsertRecord(
+ UINT32 nIndex, // [IN] Insert record before this.
+ BYTE **ppRecord)
+{
+ HRESULT hr;
+ StgPoolSeg *pCurSeg; // Current segment.
+ StgPoolSeg *pPrevSeg; // Previous segment.
+ BYTE *pSegEnd; // Last record in a segment.
+ BYTE *pFrom; // A copy/move source.
+ ULONG cbMove; // Bytes to move.
+ BYTE *pNew; // New record.
+
+ // Notice the case of appending.
+ if (nIndex == (Count() + 1))
+ {
+ UINT32 nNewIndex_Ignore;
+ return AddRecord(ppRecord, &nNewIndex_Ignore);
+ }
+
+ // If past end or before beginning, invalid.
+ if ((nIndex > Count()) || (nIndex == 0))
+ {
+ Debug_ReportError("Invalid index passed for inserting record.");
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+
+ // This code works by allocating a new record at the end.
+ // The last record is moved to the new end record.
+ // Working backwards through the chained segments,
+ // shift the segment by one record, so the empty record
+ // is at the start of the segment instead of the end.
+ // copy the last record of the previous segment to the
+ // newly emptied first record of the current segment.
+ // When the segment containing the insert point is finally
+ // reached, its last record is empty (from above loop), so
+ // shift from the insertion point to the end-1 by one record.
+
+ // Current last record.
+ pCurSeg = m_pCurSeg;
+ IfFailRet(GetRecord(Count(), &pSegEnd));
+ _ASSERTE(hr == S_OK);
+
+ // Add an empty record to the end of the heap.
+ {
+ UINT32 nLastRecordIndex_Ignore;
+ IfFailRet(AddRecord(&pNew, &nLastRecordIndex_Ignore));
+ }
+
+ // Copy the current last record to the new record.
+ memcpy(pNew, pSegEnd, m_cbRec);
+
+ // While the insert location is prior to the current segment,
+ while (nIndex < GetIndexForRecord(pCurSeg->m_pSegData))
+ {
+ // Shift the segment up by one record.
+ cbMove = (ULONG)(pSegEnd - pCurSeg->m_pSegData);
+ memmove(pCurSeg->m_pSegData + m_cbRec, pCurSeg->m_pSegData, cbMove);
+
+ // Find the previous segment.
+ pPrevSeg = this;
+ while (pPrevSeg->m_pNextSeg != pCurSeg)
+ {
+ pPrevSeg = pPrevSeg->m_pNextSeg;
+ }
+
+ // Copy the final record of the previous segment to the start of this one.
+ pSegEnd = pPrevSeg->m_pSegData+pPrevSeg->m_cbSegNext-m_cbRec;
+ memcpy(pCurSeg->m_pSegData, pSegEnd, m_cbRec);
+
+ // Make the previous segment the current segment.
+ pCurSeg = pPrevSeg;
+ }
+
+ // Shift at the insert location, forward by one.
+ IfFailRet(GetRecord(nIndex, &pFrom));
+ _ASSERTE(hr == S_OK);
+
+ cbMove = (ULONG)(pSegEnd - pFrom);
+ memmove(pFrom + m_cbRec, pFrom, cbMove);
+
+ *ppRecord = pFrom;
+
+ return S_OK;
+} // RecordPool::InsertRecord
+
+//*****************************************************************************
+// Return a pointer to a Record given an index previously handed out by
+// AddRecord or FindRecord.
+//*****************************************************************************
+HRESULT
+RecordPool::GetRecord(
+ UINT32 nIndex, // 1-based index of Record in pool.
+ BYTE **ppRecord)
+{
+ MetaData::DataBlob record;
+
+ if (nIndex == 0)
+ {
+ Debug_ReportError("Invalid index 0 passed.");
+ *ppRecord = NULL;
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+
+ // Convert to 0-based internal form, defer to implementation.
+ HRESULT hr = GetData((nIndex - 1) * m_cbRec, &record);
+ if (FAILED(hr))
+ {
+ *ppRecord = NULL;
+ return hr;
+ }
+ _ASSERTE(record.ContainsData(m_cbRec));
+ *ppRecord = record.GetDataPointer();
+
+ return hr;
+} // RecordPool::GetRecord
+
+//*****************************************************************************
+// Return the first record in a pool, and set up a context for fast
+// iterating through the pool. Note that this scheme does pretty minimal
+// error checking.
+//*****************************************************************************
+void *RecordPool::GetFirstRecord( // Pointer to Record in pool.
+ void **pContext) // Store context here.
+{
+ StgPoolSeg **ppSeg = reinterpret_cast<StgPoolSeg**>(pContext);
+
+ *ppSeg = static_cast<StgPoolSeg*>(this);
+ return (*ppSeg)->m_pSegData;
+} // void *RecordPool::GetFirstRecord()
+
+//*****************************************************************************
+// Given a pointer to a record, return a pointer to the next record.
+// Note that this scheme does pretty minimal error checking. In particular,
+// this will let the caller walk off of the end of valid data in the last
+// segment.
+//*****************************************************************************
+void *RecordPool::GetNextRecord( // Pointer to Record in pool.
+ void *pRecord, // Current record.
+ void **pContext) // Stored context here.
+{
+ BYTE *pbRec = reinterpret_cast<BYTE*>(pRecord);
+ StgPoolSeg **ppSeg = reinterpret_cast<StgPoolSeg**>(pContext);
+
+ // Get the next record.
+ pbRec += m_cbRec;
+
+ // Is the next record outside of the current segment?
+ if (static_cast<ULONG>(pbRec - (*ppSeg)->m_pSegData) >= (*ppSeg)->m_cbSegSize)
+ {
+ // Better be exactly one past current segment.
+ _ASSERTE(static_cast<ULONG>(pbRec - (*ppSeg)->m_pSegData) == (*ppSeg)->m_cbSegSize);
+ // Switch the context pointer.
+ *ppSeg = (*ppSeg)->m_pNextSeg;
+ // Next record is start of next segment.
+ if (*ppSeg)
+ return (*ppSeg)->m_pSegData;
+ else
+ return 0;
+ }
+
+ return pbRec;
+} // void *RecordPool::GetNextRecord()
+
+//*****************************************************************************
+// Given a pointer to a record, determine the index corresponding to the
+// record.
+//*****************************************************************************
+ULONG RecordPool::GetIndexForRecord( // 1-based index of Record in pool.
+ const void *pvRecord) // Pointer to Record in pool.
+{
+ ULONG iPrev = 0; // cumulative index of previous segments.
+ const StgPoolSeg *pSeg = this;
+ const BYTE *pRecord = reinterpret_cast<const BYTE*>(pvRecord);
+ const BYTE *pSegData = NULL;
+ ULONG ulSegSize;
+ for (;;)
+ { // Does the current segment contain the record?
+ pSegData = pSeg->GetSegData();
+ ulSegSize = pSeg->GetSegSize();
+ if (pRecord >= pSegData && pRecord < pSegData + ulSegSize)
+ { // The pointer should be to the start of a record.
+ _ASSERTE(((pRecord - pSegData) % m_cbRec) == 0);
+ return (ULONG)(1 + iPrev + (pRecord - pSegData) / m_cbRec);
+ }
+ _ASSERTE((ulSegSize % m_cbRec) == 0);
+ iPrev += ulSegSize / m_cbRec;
+ pSeg = pSeg->GetNextSeg();
+ // If out of data, didn't find the record.
+ if (pSeg == 0)
+ return 0;
+ }
+} // ULONG RecordPool::GetIndexForRecord()
+
+//*****************************************************************************
+// Given a purported pointer to a record, determine if the pointer is valid.
+//*****************************************************************************
+int RecordPool::IsValidPointerForRecord(// true or false.
+ const void *pvRecord) // Pointer to Record in pool.
+{
+ const StgPoolSeg *pSeg;
+ const BYTE *pRecord = reinterpret_cast<const BYTE*>(pvRecord);
+ const BYTE *pSegData = NULL;
+ for (pSeg = this; (pSeg); pSeg = pSeg->GetNextSeg())
+ { // Does the current segment contain the record?
+ pSegData = pSeg->GetSegData();
+ if ((pRecord >= pSegData) && (pRecord < pSegData + pSeg->GetSegSize()))
+ { // The pointer should be to the start of a record.
+ return (((pRecord - pSegData) % m_cbRec) == 0);
+ }
+ _ASSERTE((pSeg->GetSegSize() % m_cbRec) == 0);
+ }
+ return 0;
+} // int RecordPool::IsValidPointerForRecord()
+
+//*****************************************************************************
+// Replace the contents of this pool with those from another pool. The other
+// pool loses ownership of the memory.
+//*****************************************************************************
+HRESULT RecordPool::ReplaceContents(
+ RecordPool *pOther) // The other record pool.
+{
+ // Release any memory currently held.
+ Uninit();
+
+ // Grab the new data.
+ *this = *pOther;
+
+ // If the other pool's curseg pointed to itself, make this pool point to itself.
+ if (pOther->m_pCurSeg == pOther)
+ m_pCurSeg = this;
+
+ // Fix the other pool so it won't free the memory that this one
+ // just hijacked.
+ pOther->m_pSegData = (BYTE*)m_zeros;
+ pOther->m_pNextSeg = 0;
+ pOther->Uninit();
+
+ return S_OK;
+} // HRESULT RecordPool::ReplaceContents()
diff --git a/src/md/runtime/stdafx.cpp b/src/md/runtime/stdafx.cpp
new file mode 100644
index 0000000000..ff341e19a7
--- /dev/null
+++ b/src/md/runtime/stdafx.cpp
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// stdafx.cpp
+//
+
+//
+// Precompiled headers.
+//
+//*****************************************************************************
+#include "stdafx.h"
diff --git a/src/md/runtime/stdafx.h b/src/md/runtime/stdafx.h
new file mode 100644
index 0000000000..061551a832
--- /dev/null
+++ b/src/md/runtime/stdafx.h
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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"
+
+#endif // __STDAFX_H_
diff --git a/src/md/runtime/wks/.gitmirror b/src/md/runtime/wks/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/runtime/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/runtime/wks/CMakeLists.txt b/src/md/runtime/wks/CMakeLists.txt
new file mode 100644
index 0000000000..9a1f72ed25
--- /dev/null
+++ b/src/md/runtime/wks/CMakeLists.txt
@@ -0,0 +1,5 @@
+include(../../md_wks.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDRUNTIME_SOURCES)
+add_library_clr(mdruntime_wks ${MDRUNTIME_SOURCES})
+
diff --git a/src/md/runtime/wks/MDRuntime.nativeproj b/src/md/runtime/wks/MDRuntime.nativeproj
new file mode 100644
index 0000000000..bc95bf65f2
--- /dev/null
+++ b/src/md/runtime/wks/MDRuntime.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>
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\Runtime\Runtime.settings.targets" />
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>MDRuntime</OutputName>
+ </PropertyGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/tables/.gitmirror b/src/md/tables/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/tables/.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/tables/export.h b/src/md/tables/export.h
new file mode 100644
index 0000000000..f8e0275820
--- /dev/null
+++ b/src/md/tables/export.h
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: export.h
+//
+
+//
+// Popular types defined in MetaData\Tables directory.
+// It's supposed to be included from other (MetaData) subcomponents, not from this directory.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "table.h"
diff --git a/src/md/tables/external.h b/src/md/tables/external.h
new file mode 100644
index 0000000000..998dfa9e66
--- /dev/null
+++ b/src/md/tables/external.h
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: external.h
+//
+
+//
+// External types used in MetaData\Storage subcomponent classes.
+// This file is used for precompiled headers, so it has to be included at the beginning of every .cpp in
+// this directory.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "../external.h"
+#include "../export.h"
+
+#include "../inc/recordpool.h"
+#include "../hotdata/export.h"
+#include "../hotdata/hotdataformat.h"
diff --git a/src/md/tables/table.h b/src/md/tables/table.h
new file mode 100644
index 0000000000..7cf79929c6
--- /dev/null
+++ b/src/md/tables/table.h
@@ -0,0 +1,243 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: Table.h
+//
+
+//
+// Class code:MetaData::Table represents a MetaData table.
+//
+// ======================================================================================
+
+#pragma once
+
+#include "external.h"
+
+namespace MetaData
+{
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents a read-only MetaData table (a continuous chunk of data).
+//
+class TableRO
+{
+ friend class TableRW;
+
+private:
+ //
+ // Private data
+ //
+
+ BYTE *m_pData;
+
+public:
+ //
+ // Initialization
+ //
+
+ __checkReturn
+ inline HRESULT Initialize(
+ __range(2, UINT32_MAX) UINT32 cbRecordSize,
+ DataBlob sourceData,
+ BOOL fCopyData)
+ {
+ _ASSERTE(!fCopyData);
+ _ASSERTE((cbRecordSize == 0) || (sourceData.GetSize() % cbRecordSize == 0));
+ m_pData = sourceData.GetDataPointer();
+ return S_OK;
+ }
+
+ // Destroys the table and all its allocated data. Can run on uninitialized table.
+ inline void Delete()
+ {
+ m_pData = NULL;
+ }
+
+public:
+ //
+ // Getters
+ //
+
+ __checkReturn
+ inline HRESULT GetRecord(
+ UINT32 nRowIndex,
+ __deref_out_opt BYTE **ppRecord,
+ UINT32 cbRecordSize,
+ UINT32 cRecordCount,
+#ifdef FEATURE_PREJIT
+ struct HotTablesDirectory *pHotTablesDirectory,
+#endif //FEATURE_PREJIT
+ UINT32 nTableIndex)
+ {
+ if ((nRowIndex == 0) || (nRowIndex > cRecordCount))
+ {
+ Debug_ReportError("Invalid record index.");
+ *ppRecord = NULL;
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+#ifdef FEATURE_PREJIT
+ if ((pHotTablesDirectory != NULL) && (pHotTablesDirectory->m_rgTableHeader_SignedOffset[nTableIndex] != 0))
+ {
+ HRESULT hr = HotTable::GetData(
+ nRowIndex,
+ ppRecord,
+ cbRecordSize,
+ HotTable::GetTableHeader(pHotTablesDirectory, nTableIndex));
+
+ if (hr == S_OK)
+ {
+ _ASSERTE(memcmp(
+ *ppRecord,
+ m_pData + (nRowIndex - 1) * cbRecordSize,
+ cbRecordSize) == 0);
+ return S_OK;
+ }
+ if (FAILED(hr))
+ {
+ *ppRecord = NULL;
+ return hr;
+ }
+ _ASSERTE(hr == S_FALSE);
+ }
+#endif //FEATURE_PREJIT
+ *ppRecord = m_pData + (nRowIndex - 1) * cbRecordSize;
+ return S_OK;
+ } // TableRO::GetRecord
+
+}; // class TableRO
+
+// --------------------------------------------------------------------------------------
+//
+// This class represents a read-write MetaData table.
+//
+class TableRW
+{
+private:
+ //
+ // Private data
+ //
+
+ // The storage of table records.
+ RecordPool m_RecordStorage;
+
+public:
+ //
+ // Initialization
+ //
+
+ // Initializes (empty) table of record size (cbRecordSize) with new allocated data for cRecordCount
+ // records.
+ __checkReturn
+ inline HRESULT InitializeEmpty_WithRecordCount(
+ __range(2, UINT32_MAX) UINT32 cbRecordSize,
+ __range(0, UINT32_MAX) UINT32 cRecordCount
+ COMMA_INDEBUG_MD( BOOL debug_fIsReadWrite))
+ {
+ return m_RecordStorage.InitNew(cbRecordSize, cRecordCount);
+ }
+
+ __checkReturn
+ inline HRESULT Initialize(
+ __range(2, UINT32_MAX) UINT32 cbRecordSize,
+ DataBlob sourceData,
+ BOOL fCopyData)
+ {
+ return m_RecordStorage.InitOnMem(cbRecordSize, sourceData.GetDataPointer(), sourceData.GetSize(), !fCopyData);
+ }
+
+ __checkReturn
+ inline HRESULT InitializeFromTable(
+ const TableRO *pSourceTable,
+ UINT32 cbRecordSize,
+ UINT32 cRecordCount,
+ BOOL fCopyData)
+ {
+ return m_RecordStorage.InitOnMem(cbRecordSize, pSourceTable->m_pData, cbRecordSize * cRecordCount, !fCopyData);
+ }
+ __checkReturn
+ inline HRESULT InitializeFromTable(
+ const TableRW *pSourceTable,
+ BOOL fCopyData)
+ {
+ _ASSERTE(fCopyData);
+ return m_RecordStorage.ReplaceContents(const_cast<RecordPool *>(&pSourceTable->m_RecordStorage));
+ }
+
+ // Destroys the table and all its allocated data. Can run on uninitialized table.
+ inline void Delete()
+ {
+ return m_RecordStorage.Uninit();
+ }
+
+public:
+ //
+ // Getters
+ //
+
+ inline UINT32 GetRecordCount() const
+ {
+ return const_cast<RecordPool &>(m_RecordStorage).Count();
+ }
+ inline HRESULT GetRecordsDataSize(UINT32 *pcbSize) const
+ {
+ return m_RecordStorage.GetSaveSize(pcbSize);
+ }
+
+ __checkReturn
+ inline HRESULT GetRecord(
+ UINT32 nIndex,
+ __deref_out_opt BYTE **ppRecord)
+ {
+ return m_RecordStorage.GetRecord(nIndex, ppRecord);
+ }
+
+ __checkReturn
+ inline HRESULT SaveToStream(
+ IStream *pStream) const
+ {
+ return const_cast<RecordPool &>(m_RecordStorage).PersistToStream(pStream);
+ }
+
+public:
+ //
+ // Setters
+ //
+
+ __checkReturn
+ inline HRESULT AddRecord(
+ __out BYTE **ppbRecord,
+ __out UINT32 *pnIndex)
+ {
+ return m_RecordStorage.AddRecord(ppbRecord, pnIndex);
+ }
+ __checkReturn
+ inline HRESULT InsertRecord(
+ UINT32 nIndex,
+ BYTE **ppbRecord)
+ {
+ return m_RecordStorage.InsertRecord(nIndex, ppbRecord);
+ }
+
+ // Makes table data writable, i.e. copies them into newly allocated chunk of memory. The caller
+ // guarantees that the table is not writable yet (otherwise this method asserts).
+ //
+ // Returns S_OK (even if the table is empty).
+ // Returns METADATA_E_INTERNAL_ERROR error code if the table has more segments.
+ __checkReturn
+ inline HRESULT MakeWritable()
+ {
+ return m_RecordStorage.ConvertToRW();
+ }
+
+#ifdef _DEBUG_METADATA
+ // Sets table information for debugging.
+ void Debug_SetTableInfo(const char *szTableName, UINT32 nTableIndex)
+ {
+ }
+#endif //_DEBUG_METADATA
+
+}; // class TableRW
+
+}; // namespace MetaData
diff --git a/src/md/winmd/.gitmirror b/src/md/winmd/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/winmd/.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/winmd/CMakeLists.txt b/src/md/winmd/CMakeLists.txt
new file mode 100644
index 0000000000..9fb7d839ac
--- /dev/null
+++ b/src/md/winmd/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(MDWINMD_SOURCES
+ adapter.cpp
+ winmdimport.cpp
+ winmdinternalimportro.cpp
+)
+
+convert_to_absolute_path(MDWINMD_SOURCES ${MDWINMD_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)
+ add_subdirectory(crossgen)
+endif(WIN32)
diff --git a/src/md/winmd/WinMD.settings.targets b/src/md/winmd/WinMD.settings.targets
new file mode 100644
index 0000000000..58fa8ee682
--- /dev/null
+++ b/src/md/winmd/WinMD.settings.targets
@@ -0,0 +1,49 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <!--
+ We build MetaData in several flavors:
+ - Full version (wks) - part of clr.dll/coreclr.dll.
+ - dac version - does not need Emit APIs.
+ - Standalone versions for:
+ * mscordbi.dll (hands the interfaces over to debugger client (e.g. VS) - does not need Emit APIs.
+ * CorDbg - does not need Emit APIs.
+ * WinRT
+ - Read-Only version (ships in Windows) - does not need Emit and Internal APIs.
+ - Read-Writer version (ships as private component of MidlRt.exe SDK tool) - does not need Internal APIs.
+ -->
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\MD.props" />
+
+ <PropertyGroup>
+ <MDWinMDSrcDirectory>$(ClrSrcDirectory)\MD\WinMD\</MDWinMDSrcDirectory>
+ <UserIncludes>
+ $(UserIncludes);
+ $(ClrSrcDirectory)\MD\inc;
+ $(ClrSrcDirectory)\vm;
+ $(ClrSrcDirectory)\strongname\inc
+ </UserIncludes>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE</ClAdditionalOptions>
+ <!--OK to delete NO_NTDLL for devdiv builds.-->
+ <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>$(MDWinMDSrcDirectory)\stdafx.cpp</PCHCompile>
+ <PCHObject>stdafx_winmd.obj</PCHObject>
+ <LinkUseCMT>false</LinkUseCMT>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="$(ClrSrcDirectory)inc\corguids.nativeproj">
+ <Comment>clrinternal.h</Comment>
+ </ProjectReference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <CppCompile Include="$(MDWinMDSrcDirectory)\Adapter.cpp" />
+ <CppCompile Include="$(MDWinMDSrcDirectory)\WinMDImport.cpp" />
+ <CppCompile Include="$(MDWinMDSrcDirectory)\WinMDInternalImportRO.cpp" />
+ </ItemGroup>
+</Project>
diff --git a/src/md/winmd/adapter.cpp b/src/md/winmd/adapter.cpp
new file mode 100644
index 0000000000..5b4d95cc7c
--- /dev/null
+++ b/src/md/winmd/adapter.cpp
@@ -0,0 +1,2745 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#include "stdafx.h"
+#include "sigparser.h"
+#include "sigbuilder.h"
+#include "inc/adapter.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);
+
+//----------------------------------------------------------------------------------------------------
+// The name prefixes used by WinMD to hide/unhide WinRT and CLR versions of RuntimeClasses.
+//----------------------------------------------------------------------------------------------------
+
+static const char s_szWinRTPrefix[] = "<WinRT>";
+//static const size_t s_ncWinRTPrefix = sizeof(s_szWinRTPrefix) - 1;
+
+static const char s_szCLRPrefix[] = "<CLR>";
+static const size_t s_ncCLRPrefix = sizeof(s_szCLRPrefix) - 1;
+
+// the public key token of the ecma key used by some framework assemblies (mscorlib, system, etc).
+// note that some framework assemblies use a different key: b03f5f7f11d50a3a
+static const BYTE s_pbFrameworkPublicKeyToken[] = {0xB7,0x7A,0x5C,0x56,0x19,0x34,0xE0,0x89};
+
+
+//-----------------------------------------------------------------------------------------------------
+// Returns:
+// S_OK: if WinMD adapter should be used.
+// S_FALSE: if not
+//-----------------------------------------------------------------------------------------------------
+HRESULT CheckIfWinMDAdapterNeeded(IMDCommon *pRawMDCommon)
+{
+ HRESULT hr;
+ _ASSERTE(pRawMDCommon != NULL);
+
+ LPCSTR szMetadataVersionString = NULL;
+
+#ifndef DACCESS_COMPILE
+#ifdef _DEBUG
+ //---------------------------------------------------------------------------------------------------------
+ // set COMPLUS_INTERNAL_MD_WinMD_AssertOnIllegalUsage=1
+ //
+ // to turn the WinMD adapter off universally.
+ //---------------------------------------------------------------------------------------------------------
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_WinMD_Disable))
+ {
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+#endif //_DEBUG
+#endif
+
+ //---------------------------------------------------------------------------------------------------------
+ // This is the real check: activate WinMD based on metadata version string.
+ //---------------------------------------------------------------------------------------------------------
+ static const LPCSTR g_szWindowsRuntimeVersion = "WindowsRuntime ";
+ IfFailGo(pRawMDCommon->GetVersionString(&szMetadataVersionString));
+ if (0 == strncmp(szMetadataVersionString, g_szWindowsRuntimeVersion, strlen(g_szWindowsRuntimeVersion)))
+ {
+ hr = S_OK;
+ goto ErrExit;
+ }
+ else
+ {
+ hr = S_FALSE;
+ goto ErrExit;
+ }
+
+ ErrExit:
+ return hr;
+}
+
+
+//------------------------------------------------------------------------------
+
+//
+// Factory for WinMDAdapters. Caller must use "delete" to destroy adapter when done.
+//
+/*static*/ HRESULT WinMDAdapter::Create(IMDCommon *pRawMDCommon, /*[out]*/ WinMDAdapter **ppAdapter)
+{
+ HRESULT hr;
+ LPWSTR wszCorVersion = NULL;
+ WinMDAdapter* pNewAdapter = NULL;
+
+ *ppAdapter = NULL;
+
+ pNewAdapter = new (nothrow) WinMDAdapter(pRawMDCommon);
+ if (!pNewAdapter)
+ {
+ IfFailGo(E_OUTOFMEMORY);
+ }
+
+ //------------------------------------------------------------------------------------------------
+ // Create a stored string to hold our phantom metadata version string.
+ //------------------------------------------------------------------------------------------------
+ LPCSTR szVersion;
+ IfFailGo(pRawMDCommon->GetVersionString(&szVersion));
+ const char *szClrPortion = strchr(szVersion, ';');
+ if (szClrPortion)
+ {
+ pNewAdapter->m_scenario = kWinMDExp;
+ szClrPortion++;
+
+ // skip the "CLR" prefix if present
+ if ((szClrPortion[0] == 'c' || szClrPortion[0] == 'C') &&
+ (szClrPortion[1] == 'l' || szClrPortion[1] == 'L') &&
+ (szClrPortion[2] == 'r' || szClrPortion[2] == 'R'))
+ {
+ szClrPortion += 3;
+ }
+ while (szClrPortion[0] == ' ')
+ {
+ szClrPortion++;
+ }
+
+ size_t ncClrPortion = strlen(szClrPortion);
+ pNewAdapter->m_pRedirectedVersionString = new (nothrow) char[ncClrPortion + 1];
+ IfNullGo(pNewAdapter->m_pRedirectedVersionString);
+ memcpy(pNewAdapter->m_pRedirectedVersionString, szClrPortion, ncClrPortion + 1);
+ }
+ else
+ {
+ pNewAdapter->m_scenario = kWinMDNormal;
+#ifndef DACCESS_COMPILE
+ WCHAR wszCorVersion[_MAX_PATH];
+ DWORD cchWszCorVersion = _countof(wszCorVersion);
+ IfFailGo(CLRRuntimeHostInternal_GetImageVersionString (wszCorVersion, &cchWszCorVersion));
+ MAKE_UTF8PTR_FROMWIDE_NOTHROW(szCorVersion, wszCorVersion);
+ IfNullGo(szCorVersion);
+ size_t nch = strlen(szCorVersion) + 1;
+ pNewAdapter->m_pRedirectedVersionString = new (nothrow) char[nch];
+ IfNullGo(pNewAdapter->m_pRedirectedVersionString);
+ memcpy(pNewAdapter->m_pRedirectedVersionString, szCorVersion, nch);
+#else
+ pNewAdapter->m_pRedirectedVersionString = new (nothrow) char[1];
+ pNewAdapter->m_pRedirectedVersionString[0] = 0;
+#endif
+ }
+
+
+ //------------------------------------------------------------------------------------------------
+ // Find an assemblyRef to mscorlib (required to exist in .winmd files precisely to make the adapter's job easier.
+ //------------------------------------------------------------------------------------------------
+ ULONG numAssemblyRefs = pNewAdapter->m_pRawMetaModelCommonRO->CommonGetRowCount(mdtAssemblyRef);
+ pNewAdapter->m_assemblyRefMscorlib = 0;
+ pNewAdapter->m_fReferencesMscorlibV4 = FALSE;
+ for (ULONG rid = 1; rid <= numAssemblyRefs; rid++)
+ {
+ mdAssemblyRef mdar = mdtAssemblyRef|rid;
+ LPCSTR arefName;
+ USHORT usMajorVersion;
+ IfFailGo(pNewAdapter->m_pRawMetaModelCommonRO->CommonGetAssemblyRefProps(mdar, &usMajorVersion, NULL, NULL, NULL, NULL, NULL, NULL, &arefName, NULL, NULL, NULL));
+
+ // We check for legacy Core library name since Windows.winmd references mscorlib and not System.Private.CoreLib
+ if (0 == strcmp(arefName, LegacyCoreLibName_A))
+ {
+ pNewAdapter->m_assemblyRefMscorlib = mdar;
+
+ if (usMajorVersion == 4)
+ {
+ // Older WinMDExp used to incorrectly generate an assemblyRef to 4.0.0.0 mscorlib.
+ // We use this flag to implement back-compat quirks.
+ pNewAdapter->m_fReferencesMscorlibV4 = TRUE;
+ }
+
+ break;
+ }
+ }
+ if (pNewAdapter->m_assemblyRefMscorlib == 0)
+ {
+ // .winmd files are required to have an assemblyRef to mscorlib.
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+
+
+ //------------------------------------------------------------------------------------------------
+ // All initialization tasks done.
+ //------------------------------------------------------------------------------------------------
+ *ppAdapter = pNewAdapter;
+ hr = S_OK;
+
+ ErrExit:
+ delete wszCorVersion;
+ if (FAILED(hr))
+ delete pNewAdapter;
+ return hr;
+}
+
+
+//------------------------------------------------------------------------------
+
+WinMDAdapter::WinMDAdapter(IMDCommon *pRawMDCommon)
+ : m_typeRefTreatmentMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtTypeRef), kTrNotYetInitialized)
+ , m_typeDefTreatmentMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtTypeDef), kTdNotYetInitialized)
+ , m_methodDefTreatmentMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtMethodDef), kMdNotYetInitialized)
+ , m_redirectedCABlobsMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtCustomAttribute), NULL)
+ , m_redirectedMethodDefSigMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtMethodDef), NULL)
+ , m_redirectedFieldDefSigMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtFieldDef), NULL)
+ , m_redirectedMemberRefSigMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtMemberRef), NULL)
+ , m_redirectedPropertySigMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtProperty), NULL)
+ , m_redirectedTypeSpecSigMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtTypeSpec), NULL)
+ , m_redirectedMethodSpecSigMemoTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtMethodSpec), NULL)
+ , m_mangledTypeNameTable(pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtTypeDef), NULL)
+ , m_extraAssemblyRefCount(-1)
+{
+ m_rawAssemblyRefCount = pRawMDCommon->GetMetaModelCommonRO()->CommonGetRowCount(mdtAssemblyRef);
+ m_pRedirectedVersionString = NULL;
+ m_pRawMetaModelCommonRO = pRawMDCommon->GetMetaModelCommonRO();
+}
+
+//------------------------------------------------------------------------------
+
+WinMDAdapter::~WinMDAdapter()
+{
+ delete m_pRedirectedVersionString;
+}
+
+
+//------------------------------------------------------------------------------
+// Hides projected jupiter struct helper class & interfaces
+
+struct HiddenWinRTTypeInfo
+{
+ LPCSTR szWinRTNamespace;
+ LPCSTR szWinRTName;
+};
+
+#define DEFINE_PROJECTED_JUPITER_STRUCT(szWinRTNamespace, szWinRTName, szClrNamespace, szClrName, nClrAssemblyIndex, nContractAssemblyIndex, WinRTRedirectedTypeIndex, ClrRedirectedTypeIndex, fieldSizes) \
+ { \
+ szWinRTNamespace, \
+ szWinRTName "Helper" \
+ },
+
+#define DEFINE_HIDDEN_WINRT_TYPE(szWinRTNamespace, szWinRTName) \
+ { \
+ szWinRTNamespace, \
+ szWinRTName \
+ },
+
+#define DEFINE_PROJECTED_TYPE(szWinRTNS, szWinRTName, szClrNS, szClrName, nClrAsmIdx, nContractAsmIdx, nWinRTIndex, nClrIndex, nWinMDTypeKind)
+
+static const HiddenWinRTTypeInfo g_rgHiddenWinRTTypes[] =
+{
+ #include "WinRTProjectedTypes.h"
+};
+
+#undef DEFINE_PROJECTED_JUPITER_STRUCT
+#undef DEFINE_HIDDEN_WINRT_TYPE
+#undef DEFINE_PROJECTED_TYPE
+
+// Whether the WinRT type should be hidden from managed code
+// Example: helper class for projected jupiter structs
+// (helper class interfaces are already private by default)
+BOOL WinMDAdapter::IsHiddenWinRTType(LPCSTR szWinRTNamespace, LPCSTR szWinRTName)
+{
+ _ASSERTE(szWinRTNamespace && szWinRTName);
+
+ for (UINT i = 0; i < _countof(g_rgHiddenWinRTTypes); i++)
+ {
+ if (0 == strcmp(szWinRTNamespace, g_rgHiddenWinRTTypes[i].szWinRTNamespace))
+ {
+ if (0 == strcmp(szWinRTName, g_rgHiddenWinRTTypes[i].szWinRTName))
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+//------------------------------------------------------------------------------
+
+struct RedirectedTypeInfo
+{
+ LPCSTR szWinRTNamespace;
+ LPCSTR szWinRTName;
+ LPCSTR szWinRTFullName;
+ LPCWSTR wszWinRTFullName;
+ LPCSTR szClrNamespace;
+ LPCSTR szClrName;
+ LPCSTR szClrFullName;
+ LPCWSTR wszClrFullName;
+ const WinMDAdapter::FrameworkAssemblyIndex nClrAssemblyIndex;
+ const WinMDAdapter::ContractAssemblyIndex nContractAssemblyIndex;
+ const WinMDAdapter::WinMDTypeKind nTypeKind;
+#ifdef _DEBUG
+ // Indexes for verification of constants RedirectedTypeIndex_*
+ const UINT dbg_nIndex1;
+ const UINT dbg_nIndex2;
+#endif
+};
+
+#define DEFINE_PROJECTED_TYPE(szWinRTNS, szWinRTName, szClrNS, szClrName, nClrAsmIdx, nContractAsmIdx, nWinRTIndex, nClrIndex, nWinMDTypeKind) \
+ { \
+ szWinRTNS, \
+ szWinRTName, \
+ szWinRTNS "." szWinRTName, \
+ L##szWinRTNS W(".") L##szWinRTName, \
+ szClrNS, \
+ szClrName, \
+ szClrNS "." szClrName, \
+ L##szClrNS W(".") L##szClrName, \
+ WinMDAdapter::FrameworkAssembly_ ## nClrAsmIdx, \
+ WinMDAdapter::ContractAssembly_ ## nContractAsmIdx, \
+ WinMDAdapter::WinMDTypeKind_ ## nWinMDTypeKind \
+ COMMA_INDEBUG(WinMDAdapter::RedirectedTypeIndex_ ## nWinRTIndex) \
+ COMMA_INDEBUG(WinMDAdapter::RedirectedTypeIndex_ ## nClrIndex) \
+ },
+
+static const RedirectedTypeInfo
+g_rgRedirectedTypes[WinMDAdapter::RedirectedTypeIndex_Count] =
+{
+#include "WinRTProjectedTypes.h"
+
+
+};
+#undef SCAT
+#undef DEFINE_PROJECTED_TYPE
+
+//------------------------------------------------------------------------------
+
+/*static*/ BOOL WinMDAdapter::ConvertWellKnownTypeNameFromWinRTToClr(LPCSTR *pszNamespace, LPCSTR *pszName)
+{
+ return ConvertWellKnownTypeNameFromWinRTToClr(pszNamespace, pszName, NULL);
+}
+
+
+// Maps well-known WinRT typenames to CLR typename. If the incoming name is not a well-known WinRT typename,
+// this function leaves *pszNamespace and *pszName alone and returns FALSE. Otherwise, it overwrites
+// them with pointers to const strings, returns the index into the rewrite table in *pIndex and returns TRUE.
+//
+// (Note: munged names from WinMDExp are not "well-known" names. Since the idea of munging them is to hide them,
+// this function will not transform such names. Not to mention, it would be hard to return such strings
+// in a function that no error return mechanism.)
+//
+/*static*/ BOOL WinMDAdapter::ConvertWellKnownTypeNameFromWinRTToClr(LPCSTR *pszNamespace, LPCSTR *pszName, UINT *pIndex)
+{
+ _ASSERTE(pszNamespace && pszName);
+ for (UINT i = 0; i < RedirectedTypeIndex_Count; i++)
+ {
+ _ASSERTE(g_rgRedirectedTypes[i].dbg_nIndex1 == i);
+ _ASSERTE(g_rgRedirectedTypes[i].dbg_nIndex2 == i);
+
+ if (0 == strcmp(*pszNamespace, g_rgRedirectedTypes[i].szWinRTNamespace))
+ {
+ if (0 == strcmp(*pszName, g_rgRedirectedTypes[i].szWinRTName))
+ {
+ *pszNamespace = g_rgRedirectedTypes[i].szClrNamespace;
+ *pszName = g_rgRedirectedTypes[i].szClrName;
+ if (pIndex)
+ *pIndex = i;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/*static*/ BOOL WinMDAdapter::ConvertWellKnownFullTypeNameFromWinRTToClr(LPCWSTR *pwszFullName, RedirectedTypeIndex *pIndex)
+{
+ _ASSERTE(pwszFullName);
+ for (UINT i = 0; i < RedirectedTypeIndex_Count; i++)
+ {
+ _ASSERTE(g_rgRedirectedTypes[i].dbg_nIndex1 == i);
+ _ASSERTE(g_rgRedirectedTypes[i].dbg_nIndex2 == i);
+
+ if (0 == wcscmp(*pwszFullName, g_rgRedirectedTypes[i].wszWinRTFullName))
+ {
+ *pwszFullName = g_rgRedirectedTypes[i].wszClrFullName;
+ if (pIndex)
+ *pIndex = static_cast<RedirectedTypeIndex>(i);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//------------------------------------------------------------------------------
+
+// Maps well-known CLR typenames to WinRT typename. If the incoming name is not a well-known CLR typename,
+// this function leaves *pszNamespace and *pszName alone and returns FALSE. Otherwise, it overwrites
+// them with pointers to const strings and returns TRUE.
+//
+// (Note: munged names from WinMDExp are not "well-known" names. Since the idea of munging them is to hide them,
+// this function will not transform such names. Not to mention, it would be hard to return such strings
+// in a function that no error return mechanism.)
+//
+/*static*/ BOOL WinMDAdapter::ConvertWellKnownTypeNameFromClrToWinRT(LPCSTR *pszFullName)
+{
+ _ASSERTE(pszFullName);
+
+ for (UINT i = 0; i < RedirectedTypeIndex_Count; i++)
+ {
+ if (0 == strcmp(g_rgRedirectedTypes[i].szClrFullName, *pszFullName))
+ {
+ *pszFullName = g_rgRedirectedTypes[i].szWinRTFullName;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*static*/ BOOL WinMDAdapter::ConvertWellKnownTypeNameFromClrToWinRT(LPCSTR *pszNamespace, LPCSTR *pszName)
+{
+ _ASSERTE(pszNamespace);
+ _ASSERTE(pszName);
+
+ for (UINT i = 0; i < RedirectedTypeIndex_Count; i++)
+ {
+ if (0 == strcmp(g_rgRedirectedTypes[i].szClrNamespace, *pszNamespace) &&
+ 0 == strcmp(g_rgRedirectedTypes[i].szClrName, *pszName))
+ {
+ *pszNamespace = g_rgRedirectedTypes[i].szWinRTNamespace;
+ *pszName = g_rgRedirectedTypes[i].szWinRTName;
+
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Returns names of redirected type 'index'.
+//
+//static
+void
+WinMDAdapter::GetRedirectedTypeInfo(
+ RedirectedTypeIndex index,
+ LPCSTR * pszClrNamespace,
+ LPCSTR * pszClrName,
+ LPCSTR * pszFullWinRTName,
+ FrameworkAssemblyIndex * pFrameworkAssemblyIdx,
+ ContractAssemblyIndex * pContractAssemblyIdx,
+ WinMDTypeKind * pWinMDTypeKind)
+{
+ _ASSERTE(index < RedirectedTypeIndex_Count);
+
+ if (pszClrName != nullptr)
+ {
+ *pszClrName = g_rgRedirectedTypes[index].szClrName;
+ }
+ if (pszClrNamespace != nullptr)
+ {
+ *pszClrNamespace = g_rgRedirectedTypes[index].szClrNamespace;
+ }
+ if (pszFullWinRTName != nullptr)
+ {
+ *pszFullWinRTName = g_rgRedirectedTypes[index].szWinRTFullName;
+ }
+ if (pFrameworkAssemblyIdx != nullptr)
+ {
+ *pFrameworkAssemblyIdx = g_rgRedirectedTypes[index].nClrAssemblyIndex;
+ }
+ if (pContractAssemblyIdx != nullptr)
+ {
+ *pContractAssemblyIdx = g_rgRedirectedTypes[index].nContractAssemblyIndex;
+ }
+ if (pWinMDTypeKind != nullptr)
+ {
+ *pWinMDTypeKind = g_rgRedirectedTypes[index].nTypeKind;
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Returns WinRT name of redirected type 'index'.
+//
+//static
+LPCWSTR
+WinMDAdapter::GetRedirectedTypeFullWinRTName(
+ RedirectedTypeIndex index)
+{
+ _ASSERTE(index < RedirectedTypeIndex_Count);
+
+ return g_rgRedirectedTypes[index].wszWinRTFullName;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Returns CLR name of redirected type 'index'.
+//
+//static
+LPCSTR
+WinMDAdapter::GetRedirectedTypeFullCLRName(
+ RedirectedTypeIndex index)
+{
+ _ASSERTE(index < RedirectedTypeIndex_Count);
+
+ return g_rgRedirectedTypes[index].szClrFullName;
+}
+
+//------------------------------------------------------------------------------
+//
+// Get TypeRefTreatment value for a TypeRef
+//
+HRESULT
+ WinMDAdapter::GetTypeDefTreatment(
+ mdTypeDef tkTypeDef,
+ ULONG * pTypeDefTreatment)
+{
+ HRESULT hr;
+
+ _ASSERTE(pTypeDefTreatment != NULL);
+ _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef);
+
+ ULONG typeDefIndex = RidFromToken(tkTypeDef) - 1;
+ ULONG treatment;
+ IfFailGo(m_typeDefTreatmentMemoTable.GetEntry(typeDefIndex, &treatment));
+ if (hr == S_FALSE)
+ {
+ LPCSTR szNamespace;
+ LPCSTR szName;
+ ULONG dwFlags;
+ mdToken extends;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetTypeDefProps(tkTypeDef, &szNamespace, &szName, &dwFlags, &extends, NULL));
+ treatment = kTdOther;
+ ULONG baseTypeRefTreatment = kTrNotYetInitialized;
+
+ // We only want to treat this type special if it has metadata that was encoded as WinRT metadata
+ if (IsTdWindowsRuntime(dwFlags))
+ {
+ // We need to know whether the typeDef is an Enum/Struct/Delegate/Attribute
+ if (TypeFromToken(extends) == mdtTypeRef)
+ {
+ IfFailGo(GetTypeRefTreatment(extends, &baseTypeRefTreatment));
+ }
+ // Force tdWindowsRuntime flag on (@todo: WinMDExp already does this on its own - if AppModel could do so, we could get rid of this code)
+ if (m_scenario == kWinMDNormal)
+ {
+ BOOL isAttribute = FALSE;
+ if (TypeFromToken(extends) == mdtTypeRef)
+ {
+ ULONG treatment;
+ IfFailGo(GetTypeRefTreatment(extends, &treatment));
+ if (treatment == kTrSystemAttribute)
+ {
+ isAttribute = TRUE;
+ }
+ }
+
+ if (!isAttribute)
+ {
+ treatment = kTdNormalNonAttribute;
+ }
+ else
+ {
+ treatment = kTdNormalAttribute;
+ }
+ }
+
+ // Windows.Foundation.WinMD: Hide any type defs that define well-known types that we'd redirect to mscorlib equivalents.
+ LPCSTR szTempNamespace = szNamespace;
+ LPCSTR szTempName = szName;
+ if ((treatment != kTdOther) && ConvertWellKnownTypeNameFromWinRTToClr(&szTempNamespace, &szTempName))
+ {
+ if (treatment == kTdNormalNonAttribute)
+ treatment = kTdRedirectedToCLRType;
+ else
+ treatment = kTdRedirectedToCLRAttribute;
+ }
+ else
+ {
+ // WinMDExp emits two versions of RuntimeClasses and Enums:
+ //
+ // public class Foo {} // the WinRT reference class
+ // internal class <CLR>Foo {} // the implementation class that we want WinRT consumers to ignore
+ //
+ // The adapter's job is to undo WinMDExp's transformations. I.e. turn the above into:
+ //
+ // internal class <WinRT>Foo {} // the WinRT reference class that we want CLR consumers to ignore
+ // public class Foo {} // the implementation class
+ //
+ // We only add the <WinRT> prefix here since the WinRT view is the only view that is marked tdWindowsRuntime
+ // De-mangling the CLR name is done below.
+ if (m_scenario == kWinMDExp && !IsTdNested(dwFlags) && IsTdPublic(dwFlags) && !IsTdInterface(dwFlags))
+ {
+ switch (baseTypeRefTreatment)
+ {
+ case kTrSystemDelegate:
+ case kTrSystemAttribute:
+ case kTrSystemValueType:
+ {
+ // Delegates, Attributes, and Structs have only one version
+ break;
+ }
+
+ case kTrSystemEnum:
+ {
+ if (m_fReferencesMscorlibV4 && !IsTdSpecialName(dwFlags))
+ {
+ // This is a back-compat quirk. Enums exported with an older WinMDExp have only one version
+ // not marked with tdSpecialName. These enums should *not* be mangled and flipped to private.
+ break;
+ }
+ // fall-thru
+ }
+ default:
+ {
+ // Prepend "<WinRT>" and flip the visibility to private
+ treatment = kTdPrefixWinRTName;
+ }
+ }
+ }
+ }
+
+ // Scan through Custom Attributes on type, looking for interesting bits. We only need to do this for RuntimeClasses. (The if check below is conservative.)
+ if (!IsTdInterface(dwFlags) && ((treatment == kTdPrefixWinRTName) || (treatment == kTdNormalNonAttribute)))
+ {
+ HRESULT hrCA;
+ hrCA = m_pRawMetaModelCommonRO->CommonGetCustomAttributeByNameEx(tkTypeDef, "Windows.UI.Xaml.TreatAsAbstractComposableClassAttribute", NULL, NULL, NULL);
+ if (hrCA == S_OK)
+ {
+ treatment |= kTdMarkAbstractFlag;
+ }
+ }
+
+ if ((treatment == kTdNormalNonAttribute || treatment == kTdNormalAttribute) && // native WinMD, not redirected
+ IsHiddenWinRTType(szTempNamespace, szTempName) // hidden type
+ )
+ {
+ // Hide those WinRT types. Examples of those WinRT types that we need to hide are Jupiter struct helpers
+ treatment |= kTdMarkInternalFlag;
+ }
+ }
+ else if (m_scenario == kWinMDExp && !IsTdNested(dwFlags))
+ {
+ // <CLR> implementation classes are not marked tdWindowsRuntime, but still need to be modified
+ // by the adapter. See if we have one of those here.
+ LPCSTR szUnmangledName;
+ IfFailGo(CheckIfClrImplementationType(szName, dwFlags, &szUnmangledName));
+ if (hr == S_OK)
+ {
+ treatment = kTdUnmangleWinRTName;
+ }
+ }
+
+ if (baseTypeRefTreatment == kTrSystemEnum)
+ {
+ // The typeDef is an Enum. We need to store the treatment.
+ treatment |= kTdEnum;
+ }
+
+ IfFailGo(m_typeDefTreatmentMemoTable.InitEntry(typeDefIndex, &treatment));
+ }
+ _ASSERTE(treatment != kTdNotYetInitialized);
+
+ *pTypeDefTreatment = treatment;
+ hr = S_OK;
+
+ErrExit:
+ return hr;
+} // WinMDAdapter::GetTypeDefTreatment
+
+//------------------------------------------------------------------------------
+// Returns renamed typedefs
+HRESULT WinMDAdapter::GetTypeDefProps(mdTypeDef tkTypeDef, // [IN] given typedef
+ LPCUTF8 *pszNamespace, // [OUT] return typedef namespace
+ LPCUTF8 *pszName, // [OUT] return typedef name
+ DWORD *pdwFlags, // [OUT] return typedef flags
+ mdToken *ptkExtends // [OUT] Put base class TypeDef/TypeRef here.
+ )
+{
+ HRESULT hr;
+
+ _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef);
+
+ LPCSTR szNamespace;
+ LPCSTR szName;
+ ULONG dwFlags;
+ mdToken extends;
+ ULONG treatment;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetTypeDefProps(tkTypeDef, &szNamespace, &szName, &dwFlags, &extends, NULL));
+ IfFailGo(GetTypeDefTreatment(tkTypeDef, &treatment));
+
+ switch (treatment & kTdTreatmentMask)
+ {
+ case kTdOther:
+ break;
+
+ case kTdNormalNonAttribute:
+ // Force tdWindowsRuntime flag on (@todo: WinMDExp already does this on its own - if AppModel could do so, we could get rid of this code)
+ dwFlags |= tdWindowsRuntime | tdImport;
+ break;
+
+ case kTdNormalAttribute:
+ // Attribute types don't really exist, so we don't want to allow derivation from them either
+ dwFlags |= tdWindowsRuntime | tdSealed;
+ break;
+
+ case kTdUnmangleWinRTName:
+ szName = szName + s_ncCLRPrefix;
+ dwFlags |= tdPublic;
+ dwFlags &= ~tdSpecialName;
+ break;
+
+ case kTdPrefixWinRTName:
+ {
+ // Prepend "<WinRT>" and flip the visibility to private
+ LPCSTR szPrefixedName;
+ ULONG index = RidFromToken(tkTypeDef) - 1;
+ IfFailGo(m_mangledTypeNameTable.GetEntry(index, &szPrefixedName));
+ if (hr == S_FALSE)
+ {
+ IfFailGo(CreatePrefixedName(s_szWinRTPrefix, szName, &szPrefixedName));
+ IfFailGo(m_mangledTypeNameTable.InitEntry(index, &szPrefixedName));
+ }
+ szName = szPrefixedName;
+ dwFlags &= ~tdPublic;
+ dwFlags |= tdImport;
+ break;
+ }
+
+ case kTdRedirectedToCLRType:
+ dwFlags &= ~tdPublic;
+ dwFlags |= tdImport;
+ break;
+
+ case kTdRedirectedToCLRAttribute:
+ dwFlags &= ~tdPublic;
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ if ((treatment & kTdMarkAbstractFlag) == kTdMarkAbstractFlag)
+ {
+ dwFlags |= tdAbstract;
+ }
+ if ((treatment & kTdMarkInternalFlag) == kTdMarkInternalFlag)
+ {
+ dwFlags &= ~tdPublic;
+ }
+
+ if (pszNamespace)
+ *pszNamespace = szNamespace;
+ if (pszName)
+ *pszName = szName;
+ if (pdwFlags)
+ *pdwFlags = dwFlags;
+ if (ptkExtends)
+ *ptkExtends = extends;
+
+ hr = S_OK;
+
+ ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+// Find TypeDef by name
+HRESULT WinMDAdapter::FindTypeDef(
+ LPCSTR szTypeDefNamespace, // [IN] Namespace for the TypeDef.
+ LPCSTR szTypeDefName, // [IN] Name of the TypeDef.
+ mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef of enclosing class.
+ mdTypeDef * ptkTypeDef // [OUT] return typedef
+)
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE((szTypeDefName != NULL) && (ptkTypeDef != NULL));
+ _ASSERTE((TypeFromToken(tkEnclosingClass) == mdtTypeRef) ||
+ (TypeFromToken(tkEnclosingClass) == mdtTypeDef) ||
+ IsNilToken(tkEnclosingClass));
+
+ // initialize the output parameter
+ *ptkTypeDef = mdTypeDefNil;
+
+ // Treat no namespace as empty string.
+ if (szTypeDefNamespace == NULL)
+ szTypeDefNamespace = "";
+
+ // Do a linear search
+ ULONG cTypeDefRecs = m_pRawMetaModelCommonRO->CommonGetRowCount(mdtTypeDef);
+
+ // Get TypeDef of the tkEnclosingClass passed in
+ if (TypeFromToken(tkEnclosingClass) == mdtTypeRef)
+ {
+ LPCUTF8 szNamespace;
+ LPCUTF8 szName;
+ mdToken tkResolutionScope;
+
+ IfFailRet(this->GetTypeRefProps(tkEnclosingClass, &szNamespace, &szName, &tkResolutionScope));
+ // Update tkEnclosingClass to TypeDef
+ IfFailRet(this->FindTypeDef(
+ szNamespace,
+ szName,
+ (TypeFromToken(tkResolutionScope) == mdtTypeRef) ? tkResolutionScope : mdTokenNil,
+ &tkEnclosingClass));
+ _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef);
+ }
+
+ // Search for the TypeDef
+ for (ULONG i = 1; i <= cTypeDefRecs; i++)
+ {
+ LPCUTF8 szNamespace;
+ LPCUTF8 szName;
+ ULONG dwFlags;
+
+ mdTypeDef tkTypeDefCandidate = TokenFromRid(i, mdtTypeDef);
+
+ IfFailRet(this->GetTypeDefProps(tkTypeDefCandidate, &szNamespace, &szName, &dwFlags, NULL));
+ if (!IsTdNested(dwFlags) && !IsNilToken(tkEnclosingClass))
+ {
+ // If the class is not Nested and EnclosingClass passed in is not nil
+ continue;
+ }
+ else if (IsTdNested(dwFlags) && IsNilToken(tkEnclosingClass))
+ {
+ // If the class is nested and EnclosingClass passed is nil
+ continue;
+ }
+ else if (!IsNilToken(tkEnclosingClass))
+ {
+ _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef);
+
+ mdTypeDef enclosingTypeDef;
+ IfFailRet(m_pRawMetaModelCommonRO->CommonGetEnclosingClassOfTypeDef(tkTypeDefCandidate, &enclosingTypeDef));
+ if (enclosingTypeDef != tkEnclosingClass)
+ {
+ // Type was not nested by tkEnclosingClass
+ continue;
+ }
+ }
+
+ if (strcmp(szTypeDefName, szName) == 0)
+ {
+ if (strcmp(szTypeDefNamespace, szNamespace) == 0)
+ {
+ *ptkTypeDef = TokenFromRid(i, mdtTypeDef);
+ return S_OK;
+ }
+ }
+ }
+ // Cannot find the TypeDef by name
+ return CLDB_E_RECORD_NOTFOUND;
+}
+
+//------------------------------------------------------------------------------
+//
+// Modifies TypeRef names and resolution scope.
+//
+HRESULT
+WinMDAdapter::GetTypeRefProps(
+ mdTypeRef tkTypeRef,
+ LPCSTR * pszNamespace,
+ LPCSTR * pszName,
+ mdToken * ptkResolutionScope)
+{
+
+ HRESULT hr;
+ ULONG treatment;
+ IfFailGo(GetTypeRefTreatment(tkTypeRef, &treatment));
+ _ASSERTE(treatment != kTrNotYetInitialized);
+
+ ULONG treatmentClass = treatment & kTrClassMask;
+ if (treatmentClass == kTrClassWellKnownRedirected)
+ {
+ ULONG nRewritePairIndex = treatment & ~kTrClassMask;
+ if (pszNamespace != NULL)
+ *pszNamespace = g_rgRedirectedTypes[nRewritePairIndex].szClrNamespace;
+ if (pszName != NULL)
+ *pszName = g_rgRedirectedTypes[nRewritePairIndex].szClrName;
+ if (ptkResolutionScope != NULL)
+ {
+ ContractAssemblyIndex assemblyIndex = g_rgRedirectedTypes[nRewritePairIndex].nContractAssemblyIndex;
+ _ASSERTE(assemblyIndex < ContractAssembly_Count);
+ *ptkResolutionScope = mdtAssemblyRef | (m_rawAssemblyRefCount + assemblyIndex + 1);
+ }
+ }
+ else
+ {
+ _ASSERTE(treatmentClass == kTrClassMisc);
+ switch (treatment)
+ {
+ case kTrNoRewriteNeeded:
+ case kTrSystemValueType:
+ case kTrSystemEnum:
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetTypeRefProps(tkTypeRef, pszNamespace, pszName, ptkResolutionScope));
+ break;
+
+ case kTrSystemDelegate:
+ if (pszNamespace != NULL) *pszNamespace = "System";
+ if (pszName != NULL) *pszName = "MulticastDelegate";
+ if (ptkResolutionScope) *ptkResolutionScope = mdtAssemblyRef | (m_rawAssemblyRefCount + ContractAssembly_SystemRuntime + 1);
+ break;
+
+ case kTrSystemAttribute:
+ if (pszNamespace != NULL) *pszNamespace = "System";
+ if (pszName != NULL) *pszName = "Attribute";
+ if (ptkResolutionScope != NULL) *ptkResolutionScope = mdtAssemblyRef | (m_rawAssemblyRefCount + ContractAssembly_SystemRuntime + 1);
+ break;
+
+ default:
+ _ASSERTE(!"Unknown treatment value.");
+ break;
+
+ }
+ }
+ hr = S_OK;
+
+ErrExit:
+ return hr;
+} // WinMDAdapter::GetTypeRefProps
+
+//------------------------------------------------------------------------------
+//
+// Get TypeRefTreatment value for a TypeRef
+//
+HRESULT
+WinMDAdapter::GetTypeRefTreatment(
+ mdTypeRef tkTypeRef,
+ ULONG * pTypeRefTreatment)
+{
+ HRESULT hr;
+
+ _ASSERTE(pTypeRefTreatment != NULL);
+ _ASSERTE(TypeFromToken(tkTypeRef) == mdtTypeRef);
+
+ ULONG typeRefIndex = RidFromToken(tkTypeRef) - 1;
+ ULONG treatment;
+ IfFailGo(m_typeRefTreatmentMemoTable.GetEntry(typeRefIndex, &treatment));
+ if (hr == S_FALSE)
+ {
+ LPCSTR szFromNamespace;
+ LPCSTR szFromName;
+ mdToken tkResolutionScope;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetTypeRefProps(tkTypeRef, &szFromNamespace, &szFromName, &tkResolutionScope));
+ treatment = kTrNoRewriteNeeded;
+ UINT redirIndex;
+ if (ConvertWellKnownTypeNameFromWinRTToClr(&szFromNamespace, &szFromName, &redirIndex))
+ {
+ treatment = kTrClassWellKnownRedirected | redirIndex;
+ }
+ else
+ {
+ if (0 == strcmp(szFromNamespace, "System"))
+ {
+ if (0 == strcmp(szFromName, "MulticastDelegate"))
+ {
+ treatment = kTrSystemDelegate;
+ }
+ else if (0 == strcmp(szFromName, "Attribute"))
+ {
+ treatment = kTrSystemAttribute;
+ }
+ else if (0 == strcmp(szFromName, "Enum"))
+ {
+ treatment = kTrSystemEnum;
+ }
+ else if (0 == strcmp(szFromName, "ValueType"))
+ {
+ treatment = kTrSystemValueType;
+ }
+ }
+ }
+
+ // Note that we intentionally do not mangle names of TypeRef's pointing to mangled TypeDef's
+ // in the same module. This allows WinMDExp to generate "conditional TypeRef's", i.e. references
+ // to the WinMD view or CLR view, depending on whether the adapter is on.
+
+ IfFailGo(m_typeRefTreatmentMemoTable.InitEntry(typeRefIndex, &treatment));
+ }
+ _ASSERTE(treatment != kTrNotYetInitialized);
+
+ *pTypeRefTreatment = treatment;
+ hr = S_OK;
+
+ErrExit:
+ return hr;
+} // WinMDAdapter::GetTypeRefTreatment
+
+
+//------------------------------------------------------------------------------
+//
+// Get TypeRef's index in array code:g_rgRedirectedTypes.
+// Returns S_OK if TypeRef is redirected and fills its index (*pIndex).
+// Returns S_FALSE if type is not well known redirected type (*pIndex is not initialized).
+
+//
+HRESULT
+WinMDAdapter::GetTypeRefRedirectedInfo(
+ mdTypeRef tkTypeRef,
+ RedirectedTypeIndex * pIndex)
+{
+ _ASSERTE(TypeFromToken(tkTypeRef) == mdtTypeRef);
+ _ASSERTE(pIndex != NULL);
+
+ HRESULT hr;
+ ULONG treatment;
+ IfFailGo(GetTypeRefTreatment(tkTypeRef, &treatment));
+
+ ULONG treatmentClass = treatment & kTrClassMask;
+ if (treatmentClass == kTrClassWellKnownRedirected)
+ {
+ *pIndex = (RedirectedTypeIndex)(treatment & ~kTrClassMask);
+ _ASSERTE(*pIndex < RedirectedTypeIndex_Count);
+ hr = S_OK;
+ }
+ else
+ {
+ // Do not initialize *pIndex
+ hr = S_FALSE;
+ }
+
+ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+//
+// Finds a typeref by its (transformed) name
+//
+HRESULT WinMDAdapter::FindTypeRef( // 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;
+
+ _ASSERTE(szName); // Crash on NULL pszName (just like the real metadata importer)
+ _ASSERTE(ptk); // Crash on NULL ptk (just like the real metadata importer)
+
+
+ // initialize the output parameter
+ *ptk = mdTypeRefNil;
+
+ // Treat no namespace as empty string.
+ if (!szNamespace)
+ szNamespace = "";
+
+ ULONG cTypeRefRecs = m_pRawMetaModelCommonRO->CommonGetRowCount(mdtTypeRef);
+
+ for (ULONG i = 1; i <= cTypeRefRecs; i++)
+ {
+ LPCUTF8 szNamespaceTmp;
+ LPCUTF8 szNameTmp;
+ mdToken tkRes;
+
+ mdTypeRef tr = TokenFromRid(i, mdtTypeRef);
+ IfFailGo(GetTypeRefProps(tr, &szNamespaceTmp, &szNameTmp, &tkRes));
+ if (IsNilToken(tkRes))
+ {
+ if (!IsNilToken(tkResolutionScope))
+ continue;
+ }
+ else if (tkRes != tkResolutionScope)
+ continue;
+
+ if (strcmp(szNamespace, szNamespaceTmp))
+ continue;
+
+ if (!strcmp(szNameTmp, szName))
+ {
+ *ptk = tr;
+ hr = S_OK;
+ goto ErrExit;
+ }
+ }
+
+ // cannot find the typedef
+ hr = CLDB_E_RECORD_NOTFOUND;
+ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+HRESULT WinMDAdapter::ModifyExportedTypeName(
+ mdExportedType tkExportedType, // [IN] exportedType token
+ LPCSTR *pszNamespace, // [IN,OUT,OPTIONAL] namespace to modify
+ LPCSTR *pszName // [IN,OUT,OPTIONAL] name to modify
+)
+{
+ _ASSERTE(TypeFromToken(tkExportedType) == mdtExportedType);
+
+ if (m_scenario == kWinMDExp)
+ {
+ if (pszName && 0 == strncmp(*pszName, s_szCLRPrefix, s_ncCLRPrefix))
+ {
+ (*pszName) += s_ncCLRPrefix;
+ }
+ }
+ return S_OK;
+}
+
+//------------------------------------------------------------------------------
+
+// We must optionaly add an assembly ref for System.Numerics.Vectors.dll since this assembly is not available
+// on downlevel platforms.
+//
+// This function assumes that System.Numerics.Vectors.dll is the last assembly that
+// we add so if we find a reference then we return ContractAssembly_Count otherwise we return
+// ContractAssembly_Count - 1.
+int WinMDAdapter::GetExtraAssemblyRefCount()
+{
+ HRESULT hr;
+
+ if (m_extraAssemblyRefCount == -1)
+ {
+ mdAssemblyRef tkSystemNumericsVectors = TokenFromRid(m_rawAssemblyRefCount + ContractAssembly_SystemNumericsVectors + 1, mdtAssemblyRef);
+ ULONG cTypeRefRecs = m_pRawMetaModelCommonRO->CommonGetRowCount(mdtTypeRef);
+ BOOL systemNumericsVectorsTypeFound = FALSE;
+
+ for (ULONG i = 1; i <= cTypeRefRecs; i++)
+ {
+ mdToken tkResolutionScope;
+ mdTypeRef tkTypeRef = TokenFromRid(i, mdtTypeRef);
+
+ // Get the resolution scope(AssemblyRef) token for the type. GetTypeRefProps does the type redirection.
+ IfFailGo(GetTypeRefProps(tkTypeRef, nullptr, nullptr, &tkResolutionScope));
+
+ if (tkResolutionScope == tkSystemNumericsVectors)
+ {
+ systemNumericsVectorsTypeFound = TRUE;
+ break;
+ }
+ }
+
+ if (systemNumericsVectorsTypeFound)
+ {
+ m_extraAssemblyRefCount = ContractAssembly_Count;
+ }
+ else
+ {
+ m_extraAssemblyRefCount = ContractAssembly_Count - 1;
+ }
+ }
+
+ErrExit:
+ if (m_extraAssemblyRefCount == -1)
+ {
+ // Setting m_extraAssemblyRefCount to ContractAssembly_Count so that this function returns a stable value and
+ // that if there is a System.Numerics type ref that it does not have a dangling assembly ref
+ m_extraAssemblyRefCount = ContractAssembly_Count;
+ }
+
+ return m_extraAssemblyRefCount;
+}
+
+//------------------------------------------------------------------------------
+
+/*static*/
+void WinMDAdapter::GetExtraAssemblyRefProps(FrameworkAssemblyIndex index,
+ LPCSTR* ppName,
+ AssemblyMetaDataInternal* pContext,
+ PCBYTE * ppPublicKeytoken,
+ DWORD* pTokenLength,
+ DWORD* pdwFlags)
+{
+ _ASSERTE(index >= 0 && index < FrameworkAssembly_Count);
+ _ASSERTE(index != FrameworkAssembly_Mscorlib);
+
+ if (ppName)
+ {
+ *ppName = GetExtraAssemblyRefNameFromIndex((FrameworkAssemblyIndex)index);
+ }
+
+ if (pContext)
+ {
+ ::memset(pContext, 0, sizeof(AssemblyMetaDataInternal));
+
+ pContext->usMajorVersion = VER_ASSEMBLYMAJORVERSION;
+ pContext->usMinorVersion = VER_ASSEMBLYMINORVERSION;
+ pContext->usBuildNumber = VER_ASSEMBLYBUILD;
+ pContext->usRevisionNumber = VER_ASSEMBLYBUILD_QFE;
+
+ pContext->szLocale = "";
+ }
+
+ if (ppPublicKeytoken)
+ {
+#ifdef FEATURE_CORECLR
+ if (index == FrameworkAssembly_Mscorlib)
+ {
+ *ppPublicKeytoken = g_rbTheSilverlightPlatformKeyToken;
+ *pTokenLength = sizeof(g_rbTheSilverlightPlatformKeyToken);
+ }
+ else
+#endif
+ {
+ if (index == FrameworkAssembly_SystemNumericsVectors || index == FrameworkAssembly_SystemRuntime || index == FrameworkAssembly_SystemObjectModel)
+ {
+ *ppPublicKeytoken = s_pbContractPublicKeyToken;
+ *pTokenLength = sizeof(s_pbContractPublicKeyToken);
+ }
+ else
+ {
+ *ppPublicKeytoken = s_pbFrameworkPublicKeyToken;
+ *pTokenLength = sizeof(s_pbFrameworkPublicKeyToken);
+ }
+ }
+ }
+
+ if (pdwFlags)
+ {
+ // ppPublicKeytoken contains the public key token, not the whole key.
+ *pdwFlags = 0;
+ }
+}
+
+//--------------------------------------------------------------------------------
+
+HRESULT WinMDAdapter::FindExportedType(
+ LPCUTF8 szNamespace, // [IN] expected namespace
+ LPCUTF8 szName, // [IN] expected name
+ mdToken tkEnclosingType, // [IN] expected tkEnclosingType
+ mdExportedType *ptkExportedType // [OUT] ExportedType token returned.
+)
+{
+ HRESULT hr;
+
+ _ASSERTE(szName && ptkExportedType);
+
+ // Set NULL namespace to empty string.
+ if (!szNamespace)
+ szNamespace = "";
+
+ // Set output to Nil.
+ *ptkExportedType = mdTokenNil;
+
+ ULONG ulCount = m_pRawMetaModelCommonRO->CommonGetRowCount(mdtExportedType);
+ while (ulCount)
+ {
+ mdExportedType tkCandidateExportedType = TokenFromRid(ulCount--, mdtExportedType);
+
+ LPCSTR szCandidateNamespace;
+ LPCSTR szCandidateName;
+ mdToken tkCandidateImpl;
+
+ IfFailRet(m_pRawMetaModelCommonRO->CommonGetExportedTypeProps(tkCandidateExportedType, &szCandidateNamespace, &szCandidateName, &tkCandidateImpl));
+ IfFailRet(this->ModifyExportedTypeName(tkCandidateExportedType, &szCandidateNamespace, &szCandidateName));
+ // Handle the case of nested vs. non-nested classes.
+ if (TypeFromToken(tkCandidateImpl) == mdtExportedType && !IsNilToken(tkCandidateImpl))
+ {
+ // Current ExportedType being looked at is a nested type, so
+ // comparing the implementation token.
+ if (tkCandidateImpl != tkEnclosingType)
+ continue;
+ }
+ else if (TypeFromToken(tkEnclosingType) == mdtExportedType &&
+ !IsNilToken(tkEnclosingType))
+ {
+ // ExportedType passed in is nested but the current ExportedType is not.
+ continue;
+ }
+
+ if (0 != strcmp(szName, szCandidateName))
+ continue;
+
+ if (0 != strcmp(szNamespace, szCandidateNamespace))
+ continue;
+
+ *ptkExportedType = tkCandidateExportedType;
+ return S_OK;
+ }
+ return CLDB_E_RECORD_NOTFOUND;
+}
+
+
+//------------------------------------------------------------------------------
+
+// Modifies methodDef flags and RVA
+HRESULT WinMDAdapter::ModifyMethodProps(mdMethodDef tkMethodDef, /*[in, out]*/ DWORD *pdwAttr, /* [in,out] */ DWORD *pdwImplFlags, /* [in,out] */ ULONG *pulRVA, /* [in, out] */ LPCSTR *pszName)
+{
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(tkMethodDef) == mdtMethodDef);
+
+ ULONG mdTreatment;
+ IfFailGo(GetMethodDefTreatment(tkMethodDef, &mdTreatment));
+
+ DWORD dwAttr = pdwAttr ? *pdwAttr: 0;
+ DWORD dwImplFlags = pdwImplFlags ? *pdwImplFlags : 0;
+ ULONG ulRVA = pulRVA ? *pulRVA : 0;
+
+ switch (mdTreatment & kMdTreatmentMask)
+ {
+ case kMdInterface:
+ // Method is declared on an interface
+ dwImplFlags |= miRuntime|miInternalCall;
+ break;
+
+ case kMdDelegate:
+ // Method is declared on a delegate
+ dwAttr &= ~mdMemberAccessMask;
+ dwAttr |= mdPublic;
+ ulRVA = 0;
+ dwImplFlags |= miRuntime;
+ break;
+
+ case kMdAttribute:
+ // Method is declared on an attribute
+ ulRVA = 0;
+ dwImplFlags |= miRuntime|miInternalCall;
+ break;
+
+ case kMdImplementation:
+ // CLR implementation class. Needs no adjustment.
+ break;
+
+ case kMdHiddenImpl:
+ dwAttr &= ~mdMemberAccessMask;
+ dwAttr |= mdPrivate;
+ // fall-through
+
+ case kMdOther:
+
+ // All other cases
+ ulRVA = 0;
+ dwImplFlags |= miRuntime|miInternalCall;
+
+ if (mdTreatment & kMdMarkAbstractFlag)
+ {
+ dwAttr |= mdAbstract;
+ }
+
+ if (mdTreatment & kMdMarkPublicFlag)
+ {
+ dwAttr &= ~mdMemberAccessMask;
+ dwAttr |= mdPublic;
+ }
+
+ break;
+
+ case kMdRenameToDisposeMethod:
+ ulRVA = 0;
+ dwImplFlags |= miRuntime|miInternalCall;
+ if(pszName)
+ {
+ *pszName = "Dispose";
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+
+ }
+
+ dwAttr |= mdHideBySig;
+
+ if (pdwAttr)
+ (*pdwAttr) = dwAttr;
+ if (pdwImplFlags)
+ (*pdwImplFlags) = dwImplFlags;
+ if (pulRVA)
+ *pulRVA = ulRVA;
+ hr = S_OK;
+
+ ErrExit:
+ return hr;
+}
+
+HRESULT WinMDAdapter::ModifyFieldProps (mdToken tkField, mdToken tkParent, LPCSTR szFieldName, DWORD *pdwFlags)
+{
+ _ASSERTE(szFieldName != NULL);
+
+ HRESULT hr;
+ if (pdwFlags && IsFdPrivate(*pdwFlags) && (0 == strcmp(szFieldName, "value__")))
+ {
+ ULONG treatment;
+ BOOL isEnum = FALSE;
+ if(TypeFromToken(tkParent) == mdtTypeDef)
+ {
+ IfFailGo(GetTypeDefTreatment(tkParent, &treatment));
+
+ if ((treatment & kTdEnum) == kTdEnum)
+ {
+ isEnum = TRUE;
+ }
+ }
+
+ if (isEnum)
+ {
+ // We have found the value__ field of the enum.
+ // We need to change its flags from private to public
+ *pdwFlags = (*pdwFlags & ~fdPrivate) | fdPublic;
+ }
+ }
+
+ hr = S_OK;
+
+ErrExit:
+ return hr;
+}
+
+
+HRESULT WinMDAdapter::ModifyFieldDefProps (mdFieldDef tkFielddDef, DWORD *pdwFlags)
+{
+ HRESULT hr;
+
+ if (pdwFlags)
+ {
+ mdTypeDef tkParent;
+ LPCSTR szName;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetFieldDefProps(tkFielddDef, &tkParent, &szName, pdwFlags));
+ IfFailGo(ModifyFieldProps(tkFielddDef, tkParent, szName, pdwFlags));
+ }
+
+ hr = S_OK;
+ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+HRESULT WinMDAdapter::ModifyMemberProps(mdToken tkMember, /*[in, out]*/ DWORD *pdwAttr, /* [in,out] */ DWORD *pdwImplFlags, /* [in,out] */ ULONG *pulRVA, LPCSTR *pszNewName)
+{
+ HRESULT hr;
+ switch(TypeFromToken(tkMember))
+ {
+ case mdtMethodDef: IfFailGo(ModifyMethodProps(tkMember, pdwAttr, pdwImplFlags, pulRVA, pszNewName));
+ break;
+
+ case mdtMemberRef:
+ {
+ //
+ // We need to rename the MemberRef for IClosable.Close as well
+ // so that the MethodImpl for Dispose method can correctly be shown as IDisposable.Dispose
+ // instead of IDisposable.Close
+ //
+ UINT nIndex = WinMDAdapter::RedirectedTypeIndex_Invalid;
+ if (pszNewName &&
+ CheckIfMethodImplImplementsARedirectedInterface(tkMember, &nIndex) == S_OK &&
+ nIndex == WinMDAdapter::RedirectedTypeIndex_Windows_Foundation_IClosable)
+ {
+ *pszNewName = "Dispose";
+ }
+ }
+ break;
+
+ case mdtFieldDef:
+ IfFailGo(ModifyFieldDefProps(tkMember, pdwAttr));
+ break;
+ }
+
+ hr = S_OK;
+ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+// Get MethodTreatment value for a methodDef
+HRESULT WinMDAdapter::GetMethodDefTreatment(mdMethodDef tkMethodDef, ULONG *ppMethodDefTreatment)
+{
+ HRESULT hr;
+
+ _ASSERTE(TypeFromToken(tkMethodDef) == mdtMethodDef);
+ ULONG index = RidFromToken(tkMethodDef) - 1;
+
+ // Thread-safety: No lock is needed to update this table as we're monotonically advancing a kMdNotYetInitialized to
+ // some other fixed byte. The work to decide this value is idempotent and side-effect free so
+ // there's no harm if two threads do it concurrently.
+ ULONG mdTreatment;
+ IfFailGo(m_methodDefTreatmentMemoTable.GetEntry(index, &mdTreatment));
+ if (hr == S_FALSE)
+ {
+ mdTypeDef declaringTypeDef;
+ IfFailGo(m_pRawMetaModelCommonRO->FindParentOfMethodHelper(tkMethodDef, &declaringTypeDef));
+ ULONG firstMethodRid;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetTypeDefProps(declaringTypeDef, NULL, NULL, NULL, NULL, &firstMethodRid));
+ IfFailGo(ComputeMethodDefTreatment(tkMethodDef, declaringTypeDef, &mdTreatment));
+ _ASSERTE(mdTreatment != kMdNotYetInitialized);
+ IfFailGo(m_methodDefTreatmentMemoTable.InitEntry(index, &mdTreatment));
+
+ // Since the mdTreatment is only a function of the declaring type in most cases, cache it for all the declared method since
+ // we took the time to look up the declaring type. Do enough validity checks to avoid corrupting the heap
+ // or table but otherwise, swallow validity errors as this is just an optimization.
+ // Methods on RuntimeClasses need to get treatment data per method.
+ if (((mdTreatment & kMdTreatmentMask) != kMdOther) &&
+ ((mdTreatment & kMdTreatmentMask) != kMdHiddenImpl) &&
+ ((mdTreatment & kMdTreatmentMask) != kMdRenameToDisposeMethod))
+ {
+ const ULONG methodDefCount = m_pRawMetaModelCommonRO->CommonGetRowCount(mdtMethodDef);
+ const ULONG startIndex = RidFromToken(firstMethodRid) - 1;
+ if (startIndex < methodDefCount)
+ {
+ const ULONG typeDefCount = m_pRawMetaModelCommonRO->CommonGetRowCount(mdtTypeDef);
+ if (RidFromToken(declaringTypeDef) < typeDefCount)
+ {
+ ULONG stopMethodRid;
+ if (S_OK == m_pRawMetaModelCommonRO->CommonGetTypeDefProps(declaringTypeDef + 1, NULL, NULL, NULL, NULL, &stopMethodRid))
+ {
+ ULONG stopIndex = RidFromToken(stopMethodRid) - 1;
+ if (startIndex < methodDefCount && stopIndex <= methodDefCount)
+ {
+ ULONG walkIndex = startIndex;
+ while (walkIndex < stopIndex)
+ {
+ IfFailGo(m_methodDefTreatmentMemoTable.InitEntry(walkIndex++, &mdTreatment));
+ }
+ }
+ }
+ }
+ else
+ {
+ // This was final typeDef - blast the mdTreatment into the rest of the table.
+ for (ULONG i = startIndex; i < methodDefCount; i++)
+ {
+ IfFailGo(m_methodDefTreatmentMemoTable.InitEntry(i, &mdTreatment));
+ }
+ }
+ }
+ }
+ }
+
+ *ppMethodDefTreatment = mdTreatment;
+ hr = S_OK;
+
+ ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+// Compute MethodTreatment value for a methodDef (unlike GetMethodDefTreatment, this
+// does not cache.)
+//
+HRESULT WinMDAdapter::ComputeMethodDefTreatment(mdMethodDef tkMethodDef, mdTypeDef tkDeclaringTypeDef, ULONG *ppMethodDefTreatment)
+{
+ HRESULT hr;
+
+ BYTE mdTreatment = kMdImplementation;
+
+ LPCSTR szDeclaringTypeName;
+ DWORD parentTdAttr;
+ mdToken extends;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetTypeDefProps(tkDeclaringTypeDef, NULL, &szDeclaringTypeName, &parentTdAttr, &extends, NULL));
+
+ // We only want to treat this method special if it has metadata exposed to WinRT
+ if (IsTdWindowsRuntime(parentTdAttr))
+ {
+ LPCSTR szUnmangledName;
+ IfFailGo(CheckIfClrImplementationType(szDeclaringTypeName, parentTdAttr, &szUnmangledName));
+ if (hr == S_OK)
+ {
+ mdTreatment = kMdImplementation;
+ }
+ else if (IsTdNested(parentTdAttr))
+ {
+ // nested types are implementation
+ mdTreatment = kMdImplementation;
+ }
+ else if (parentTdAttr & tdInterface)
+ {
+ // Method is declared on an interface.
+ mdTreatment = kMdInterface;
+ }
+ else if (m_scenario == kWinMDExp && (parentTdAttr & tdPublic) == 0)
+ {
+ // internal classes generated by WinMDExp are implementation
+ mdTreatment = kMdImplementation;
+ }
+ else
+ {
+ mdTreatment = kMdOther;
+
+ if (TypeFromToken(extends) == mdtTypeRef)
+ {
+ ULONG trTreatment;
+ IfFailGo(GetTypeRefTreatment(extends, &trTreatment));
+ if (trTreatment == kTrSystemDelegate)
+ {
+ // Method is declared on a delegate
+ mdTreatment = kMdDelegate;
+ }
+ else if (trTreatment == kTrSystemAttribute)
+ {
+ // Method is declared on a attribute
+ mdTreatment = kMdAttribute;
+ }
+ }
+ }
+ }
+ if (mdTreatment == kMdOther)
+ {
+ // we want to hide the method if it implements only redirected interfaces
+ // Also we want to check if the methodImpl is IClosable.Close then we will change the name.
+ bool fSeenRedirectedInterfaces = false;
+ bool fSeenNonRedirectedInterfaces = false;
+
+ bool isIClosableCloseMethod = false;
+
+ mdToken tkMethodImplFirst;
+ ULONG count;
+ mdTypeRef mtTypeRef;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetMethodImpls(tkDeclaringTypeDef, &tkMethodImplFirst, &count));
+ for (ULONG i = 0; i < count; i++)
+ {
+ mdToken tkMethodImpl = tkMethodImplFirst + i;
+ mdToken tkBody, tkDecl;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetMethodImplProps(tkMethodImpl, &tkBody, &tkDecl));
+
+ if (tkBody == tkMethodDef)
+ {
+ // See if this MethodImpl implements a redirected interface
+ UINT nIndex;
+ IfFailGo(CheckIfMethodImplImplementsARedirectedInterface(tkDecl, &nIndex));
+ if (hr == S_FALSE)
+ {
+ // Now we know this implements a non-redirected interface
+ // But we need to keep looking, just in case we got a MethodImpl that implements
+ // the IClosable.Close method and needs to be renamed
+ fSeenNonRedirectedInterfaces = true;
+ }
+ else if (SUCCEEDED(hr))
+ {
+ fSeenRedirectedInterfaces = true;
+ if (nIndex == WinMDAdapter::RedirectedTypeIndex_Windows_Foundation_IClosable)
+ {
+ // This method implements IClosable.Close
+ // Let's rename it to Dispose later
+ // Once we know this implements IClosable.Close, we are done looking as we know
+ // we won't hide it
+ isIClosableCloseMethod = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (isIClosableCloseMethod)
+ {
+ // Rename IClosable.Close to Dispose
+ mdTreatment = kMdRenameToDisposeMethod;
+ }
+ else if (fSeenRedirectedInterfaces && !fSeenNonRedirectedInterfaces)
+ {
+ // Only hide if all the interfaces implemented are redirected
+ mdTreatment = kMdHiddenImpl;
+ }
+ }
+
+ // If treatment is other, then this is a non-managed WinRT runtime class definition. Find out about various bits that we apply via attrubtes and name parsing.
+ if (mdTreatment == kMdOther)
+ {
+ // Scan through Custom Attributes on type, looking for interesting bits.
+ HRESULT hrCA;
+ hrCA = m_pRawMetaModelCommonRO->CommonGetCustomAttributeByNameEx(tkMethodDef, "Windows.UI.Xaml.TreatAsPublicMethodAttribute", NULL, NULL, NULL);
+ if (hrCA == S_OK)
+ {
+ mdTreatment |= kMdMarkPublicFlag;
+ }
+
+
+ hrCA = m_pRawMetaModelCommonRO->CommonGetCustomAttributeByNameEx(tkMethodDef, "Windows.UI.Xaml.TreatAsAbstractMethodAttribute", NULL, NULL, NULL);
+ if (hrCA == S_OK)
+ {
+ mdTreatment |= kMdMarkAbstractFlag;
+ }
+
+ LPCSTR szName;
+ DWORD dwFlags;
+ IfFailRet(m_pRawMetaModelCommonRO->CommonGetMethodDefProps(tkMethodDef, &szName, &dwFlags, NULL, NULL));
+ }
+ *ppMethodDefTreatment = mdTreatment;
+ hr = S_OK;
+
+ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+//
+// Finds a CA by its (transformed) name
+//
+HRESULT WinMDAdapter::GetCustomAttributeByName( // 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;
+ _ASSERTE(szName);
+
+ if (ConvertWellKnownTypeNameFromClrToWinRT(&szName))
+ {
+ mdCustomAttribute tkCA;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetCustomAttributeByNameEx(tkObj, szName, &tkCA, NULL, NULL));
+ if (hr == S_FALSE)
+ goto ErrExit;
+ if (ptkCA)
+ *ptkCA = tkCA;
+ IfFailGo(GetCustomAttributeBlob(tkCA, ppData, pcbData));
+ }
+ else
+ {
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetCustomAttributeByNameEx(tkObj, szName, ptkCA, ppData, pcbData));
+ }
+ ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+//
+// Modify CA blobs
+//
+HRESULT WinMDAdapter::GetCustomAttributeBlob(
+ mdCustomAttribute tkCA,
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) // [OUT] Put size of data here.
+{
+ HRESULT hr;
+
+ _ASSERTE(TypeFromToken(tkCA) == mdtCustomAttribute);
+ ULONG index = RidFromToken(tkCA) - 1;
+
+ // If someone already queried this CA, use the previous result.
+ CABlob * pCABlob;
+ IfFailGo(m_redirectedCABlobsMemoTable.GetEntry(index, &pCABlob));
+ if (hr == S_FALSE)
+ {
+ // No, we're the first. Initialize the entry (keeping in mind we may be racing with other threads.)
+ pCABlob = CABlob::NOREDIRECT;
+ mdToken tkOwner;
+ mdToken tkCtor;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetCustomAttributeProps(tkCA, &tkOwner, &tkCtor, NULL, NULL));
+ if (TypeFromToken(tkOwner) == mdtTypeDef) // AttributeUsageAttribute only goes on a typeDef, so if the owner isn't a typeDef, no point in going further.
+ {
+ if (TypeFromToken(tkCtor) == mdtMemberRef) // REX has promised to use a memberRef (not Def) here.
+ {
+ mdToken tkCtorType;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetMemberRefProps(tkCtor, &tkCtorType));
+ if (TypeFromToken(tkCtorType) == mdtTypeRef) // REX has promised to use a typeRef (not a Def, or heavens forbid, a Spec) here
+ {
+ RedirectedTypeIndex redirectedTypeIndex;
+ IfFailGo(GetTypeRefRedirectedInfo(tkCtorType, &redirectedTypeIndex));
+ _ASSERTE((hr == S_OK) || (hr == S_FALSE));
+
+ if ((hr == S_OK) && (redirectedTypeIndex == RedirectedTypeIndex_Windows_Foundation_Metadata_AttributeUsageAttribute))
+ {
+ // We found a Windows.Foundation.Metadata.AttributeUsageAttribute. The TypeRef redirection already makes this
+ // look like a System.AttributeUsageAttribute. Must munge the blob so that it matches the CLR expections.
+ BOOL allowMultiple;
+ DWORD clrTargetValue;
+ IfFailGo(TranslateWinMDAttributeUsageAttribute(tkOwner, &clrTargetValue, &allowMultiple));
+ if (hr == S_OK)
+ {
+ CABlob *pNewCABlob = NULL;
+ IfFailGo(CreateClrAttributeUsageAttributeCABlob(clrTargetValue, allowMultiple, &pNewCABlob));
+ pCABlob = pNewCABlob;
+ }
+ }
+ }
+ }
+ }
+
+ IfFailGo(m_redirectedCABlobsMemoTable.InitEntry(index, &pCABlob));
+ }
+
+ _ASSERTE(pCABlob != NULL);
+ const void *pData;
+ ULONG cbData;
+ if (pCABlob == CABlob::NOREDIRECT)
+ {
+ // The normal case: don't rewrite the blob.
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetCustomAttributeProps(tkCA, NULL, NULL, &pData, &cbData));
+ }
+ else
+ {
+ // The special cases: Blob was rewritten, return the rewritten blob.
+ pData = pCABlob->data;
+ cbData = pCABlob->cbBlob;
+ }
+ if (ppData)
+ *ppData = pData;
+ if (pcbData)
+ *pcbData = cbData;
+
+ hr = S_OK;
+ ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+// Note: This method will look in a cache for the reinterpreted signature, but does not add any values to
+// the cache or do any work on failure. If we can't find it then it returns S_FALSE.
+HRESULT WinMDAdapter::GetCachedSigForToken(
+ mdToken token, // [IN] given token
+ MemoTable<SigData*, SigData::Destroy> &memoTable, // [IN] the MemoTable to use
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig, // [OUT] new signature
+ BOOL *pfPassThrough // [OUT] did the cache say we don't need to reinterpret this sig?
+)
+{
+ _ASSERTE(pfPassThrough != NULL);
+
+ HRESULT hr;
+
+ ULONG index = RidFromToken(token) - 1;
+
+ // If someone already queried this method signature, use the previous result.
+ SigData *pSigData = NULL;
+ IfFailGo(memoTable.GetEntry(index, &pSigData));
+ if (hr == S_FALSE)
+ {
+ *pfPassThrough = FALSE;
+ return S_FALSE;
+ }
+
+ _ASSERTE(pSigData != NULL);
+
+ if (pSigData == SigData::NOREDIRECT)
+ {
+ // The normal case: don't rewrite the signature.
+ *pfPassThrough = TRUE;
+ return S_FALSE;
+ }
+ else
+ {
+ *pfPassThrough = FALSE;
+ // The signature was rewritten, return the rewritten sig.
+ if (ppSig)
+ *ppSig = (PCCOR_SIGNATURE) pSigData->data;
+ if (pcbSigBlob)
+ *pcbSigBlob = pSigData->cbSig;
+ }
+
+ hr = S_OK;
+
+ ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+// static
+HRESULT WinMDAdapter::InsertCachedSigForToken(
+ mdToken token, // [IN] given token
+ MemoTable<SigData*, SigData::Destroy> &memoTable, // [IN] the MemoTable to use
+ SigData **ppSigData // [IN, OUT] new signature or SigData::NOREDIRECT if the signature didn't need to be reparsed,
+) // will be updated with another (but identical) SigData* if this thread lost the race
+{
+ _ASSERTE(ppSigData != NULL && *ppSigData != NULL);
+
+ HRESULT hr;
+ ULONG index = RidFromToken(token) - 1;
+
+ IfFailGo(memoTable.InitEntry(index, ppSigData));
+ hr = S_OK;
+
+ ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+static HRESULT FinalizeSignatureRewrite(
+ SigBuilder & newSig,
+ BOOL fChangedSig,
+ WinMDAdapter::SigData **ppSigData
+ DEBUG_ARG(ULONG cbOrigSigBlob)
+ )
+{
+ // Make sure we didn't lose anything, since we wrote out the full signature.
+ ULONG cbNewSigLen;
+ BYTE * pNewSigBytes = (BYTE *) newSig.GetSignature(&cbNewSigLen);
+ _ASSERTE(cbNewSigLen == cbOrigSigBlob); // Didn't lose any data nor add anything
+
+ // Set the output SigData appropriately.
+ if (fChangedSig)
+ {
+ *ppSigData = WinMDAdapter::SigData::Create(cbNewSigLen, pNewSigBytes);
+
+ if (*ppSigData == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+ else
+ {
+ *ppSigData = WinMDAdapter::SigData::NOREDIRECT;
+ }
+ return S_OK;
+}
+
+//------------------------------------------------------------------------------
+
+// Purpose: Translate method signatures containing classes that we're projecting as value types, and vice versa.
+// Example: ELEMENT_TYPE_CLASS [IReference<T>] to ELEMENT_TYPE_VALUETYPE [Nullable<T>]
+// Example: ELEMENT_TYPE_VALUETYPE [Windows.Foundation.HResult] to ELEMENT_TYPE_CLASS [Exception]
+HRESULT WinMDAdapter::ReinterpretMethodSignature(
+ ULONG cbOrigSigBlob, // [IN] count of bytes in the original signature blob
+ PCCOR_SIGNATURE pOrigSig, // [IN] original signature
+ SigData **ppSigData // [OUT] new signature or SigData::NOREDIRECT
+)
+{
+ _ASSERTE(pOrigSig != NULL);
+ _ASSERTE(ppSigData != NULL);
+
+ HRESULT hr;
+
+ // @REVISIT_TODO: Need to allocate memory here. We cannot take a lock though (such as any lock needed by 'new'), or we can get
+ // into deadlocks from profilers that need to inspect metadata to walk the stack. Needs some help from some loader/ngen experts.
+
+ BOOL fChangedSig = FALSE;
+
+ // The following implements MethodDef signature parsing, per ECMA CLI spec, section 23.2.1.
+ SigParser sigParser(pOrigSig, cbOrigSigBlob);
+ SigBuilder newSig(cbOrigSigBlob); // We will not change the signature size, just modify it a bit (E_T_CLASS <-> E_T_VALUETYPE)
+
+ // Read calling convention info - Note: Calling convention is always one byte
+ ULONG callingConvention;
+ IfFailGo(sigParser.GetCallingConvInfo(&callingConvention));
+ _ASSERTE((callingConvention & 0xff) == callingConvention);
+ newSig.AppendByte((BYTE)callingConvention);
+
+ // If it is generic, read the generic parameter count
+ if ((callingConvention & CORINFO_CALLCONV_GENERIC) != 0)
+ {
+ ULONG genericArgsCount;
+ IfFailGo(sigParser.GetData(&genericArgsCount));
+ newSig.AppendData(genericArgsCount);
+ }
+
+ // Read number of locals / method parameters
+ ULONG cParameters;
+ IfFailGo(sigParser.GetData(&cParameters));
+ newSig.AppendData(cParameters);
+
+ if (callingConvention != CORINFO_CALLCONV_LOCAL_SIG)
+ {
+ // Read return type
+ IfFailGo(RewriteTypeInSignature(&sigParser, &newSig, &fChangedSig));
+ }
+
+ // Visit each local / parameter
+ for (ULONG i = 0; i < cParameters; i++)
+ {
+ IfFailGo(RewriteTypeInSignature(&sigParser, &newSig, &fChangedSig));
+ }
+
+ IfFailGo(FinalizeSignatureRewrite(newSig, fChangedSig, ppSigData DEBUG_ARG(cbOrigSigBlob)));
+ return S_OK;
+
+ErrExit:
+ Debug_ReportError("Couldn't parse a signature in WinMDAdapter::ReinterpretMethodSignature!");
+ return hr;
+} // WinMDAdapter::ReinterpretMethodSignature
+
+
+//------------------------------------------------------------------------------
+
+// Purpose: Translate FieldDef signatures containing classes that we're projecting as value types, and vice versa.
+// Example: ELEMENT_TYPE_VALUETYPE [Windows.Foundation.HResult] to ELEMENT_TYPE_CLASS [Exception]
+HRESULT WinMDAdapter::ReinterpretFieldSignature(
+ ULONG cbOrigSigBlob, // [IN] count of bytes in the original signature blob
+ PCCOR_SIGNATURE pOrigSig, // [IN] original signature
+ SigData **ppSigData // [OUT] new signature or SigData::NOREDIRECT
+)
+{
+ _ASSERTE(pOrigSig != NULL);
+ _ASSERTE(ppSigData != NULL);
+
+ HRESULT hr = S_OK;
+ BOOL fChangedSig = FALSE;
+
+ // The following implements FieldDef signature parsing, per ECMA CLI spec, section 23.2.4.
+ // Format is FIELD [custom modifiers]* Type
+ SigParser sigParser(pOrigSig, cbOrigSigBlob);
+ SigBuilder newSig(cbOrigSigBlob); // We will not change the signature size, just modify it a bit (E_T_CLASS <-> E_T_VALUETYPE)
+
+ // Read calling convention info - this should be IMAGE_CEE_CS_CALLCONV_FIELD.
+ ULONG callingConvention;
+ IfFailGo(sigParser.GetCallingConvInfo(&callingConvention));
+ _ASSERTE((callingConvention & 0xff) == callingConvention);
+ _ASSERTE(callingConvention == IMAGE_CEE_CS_CALLCONV_FIELD);
+ newSig.AppendByte((BYTE)callingConvention);
+
+ // Rewrite field type
+ IfFailGo(RewriteTypeInSignature(&sigParser, &newSig, &fChangedSig));
+
+ IfFailGo(FinalizeSignatureRewrite(newSig, fChangedSig, ppSigData DEBUG_ARG(cbOrigSigBlob)));
+ return S_OK;
+
+ErrExit:
+ Debug_ReportError("Couldn't parse a signature in WinMDAdapter::ReinterpretFieldSignature!");
+ return hr;
+} // WinMDAdapter::ReinterpretFieldSignature
+
+
+//------------------------------------------------------------------------------
+
+// Purpose: Translate TypeSpec signatures containing classes that we're projecting as value types, and vice versa.
+HRESULT WinMDAdapter::ReinterpretTypeSpecSignature(
+ ULONG cbOrigSigBlob, // [IN] count of bytes in the original signature blob
+ PCCOR_SIGNATURE pOrigSig, // [IN] original signature
+ SigData **ppSigData // [OUT] new signature or SigData::NOREDIRECT
+)
+{
+ _ASSERTE(pOrigSig != NULL);
+ _ASSERTE(ppSigData != NULL);
+
+ HRESULT hr = S_OK;
+ BOOL fChangedSig = FALSE;
+
+ // The following implements TypeSpec signature parsing, per ECMA CLI spec, section 23.2.14.
+ // Format is [custom modifiers]* Type
+ SigParser sigParser(pOrigSig, cbOrigSigBlob);
+ SigBuilder newSig(cbOrigSigBlob); // We will not change the signature size, just modify it a bit (E_T_CLASS <-> E_T_VALUETYPE)
+
+ // Rewrite the type
+ IfFailGo(RewriteTypeInSignature(&sigParser, &newSig, &fChangedSig));
+
+ IfFailGo(FinalizeSignatureRewrite(newSig, fChangedSig, ppSigData DEBUG_ARG(cbOrigSigBlob)));
+ return S_OK;
+
+ErrExit:
+ Debug_ReportError("Couldn't parse a signature in WinMDAdapter::ReinterpretTypeSpecSignature!");
+ return hr;
+} // WinMDAdapter::ReinterpretTypeSpecSignature
+
+
+//------------------------------------------------------------------------------
+
+// Purpose: Translate MethodSpec signatures containing classes that we're projecting as value types, and vice versa.
+HRESULT WinMDAdapter::ReinterpretMethodSpecSignature(
+ ULONG cbOrigSigBlob, // [IN] count of bytes in the original signature blob
+ PCCOR_SIGNATURE pOrigSig, // [IN] original signature
+ SigData **ppSigData // [OUT] new signature or SigData::NOREDIRECT
+)
+{
+ _ASSERTE(pOrigSig != NULL);
+ _ASSERTE(ppSigData != NULL);
+
+ HRESULT hr = S_OK;
+ BOOL fChangedSig = FALSE;
+
+ // The following implements MethodSpec signature parsing, per ECMA CLI spec, section 23.2.15.
+ // Format is GENERICINST GenArgCount Type+
+ SigParser sigParser(pOrigSig, cbOrigSigBlob);
+ SigBuilder newSig(cbOrigSigBlob); // We will not change the signature size, just modify it a bit (E_T_CLASS <-> E_T_VALUETYPE)
+
+ // Read calling convention info - this should be IMAGE_CEE_CS_CALLCONV_GENERICINST.
+ ULONG callingConvention;
+ IfFailGo(sigParser.GetCallingConvInfo(&callingConvention));
+ _ASSERTE((callingConvention & 0xff) == callingConvention);
+ _ASSERTE(callingConvention == IMAGE_CEE_CS_CALLCONV_GENERICINST);
+ newSig.AppendByte((BYTE)callingConvention);
+
+ // Read number of generic arguments
+ ULONG cArguments;
+ IfFailGo(sigParser.GetData(&cArguments));
+ newSig.AppendData(cArguments);
+
+ // Rewrite each argument
+ for (ULONG i = 0; i < cArguments; i++)
+ {
+ IfFailGo(RewriteTypeInSignature(&sigParser, &newSig, &fChangedSig));
+ }
+
+ IfFailGo(FinalizeSignatureRewrite(newSig, fChangedSig, ppSigData DEBUG_ARG(cbOrigSigBlob)));
+ return S_OK;
+
+ErrExit:
+ Debug_ReportError("Couldn't parse a signature in WinMDAdapter::ReinterpretMethodSpecSignature!");
+ return hr;
+} // WinMDAdapter::ReinterpretMethodSpecSignature
+
+
+//------------------------------------------------------------------------------
+//
+// We expose some WinRT types to managed as CLR types, while changing reference type <-> value type:
+// E_T_CLASS Windows.Foundation.IReference<T> ---> E_T_VALUETYPE System.Nullable<T>
+// E_T_CLASS Windows.Foundation.Collections.IKeyValuePair<U,V> ---> E_T_VALUETYPE System.Collections.Generic.KeyValuePair<U,V>
+// E_T_VALUETYPE Windows.UI.Xaml.Interop.TypeName ---> E_T_CLASS System.Type
+// E_T_VALUETYPE Windows.Foundation.HResult ---> E_T_CLASS System.Exception
+HRESULT WinMDAdapter::RewriteTypeInSignature(
+ SigParser * pSigParser,
+ SigBuilder * pSigBuilder,
+ BOOL * pfChangedSig)
+{
+ HRESULT hr;
+
+ BYTE elementType;
+ IfFailGo(pSigParser->GetByte(&elementType));
+
+ switch (elementType)
+ {
+ // Simple types with no additional data in the signature
+ case ELEMENT_TYPE_VOID:
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_TYPEDBYREF: // TYPEDREF (it takes no args) a typed reference to some other type
+ {
+ pSigBuilder->AppendByte(elementType);
+ break;
+ }
+
+ // Read a token
+ case ELEMENT_TYPE_CLASS:
+ {
+ mdToken token;
+ IfFailGo(pSigParser->GetToken(&token));
+
+ if (TypeFromToken(token) == mdtTypeRef)
+ {
+ RedirectedTypeIndex nRedirectedTypeIndex;
+ IfFailGo(this->GetTypeRefRedirectedInfo(token, &nRedirectedTypeIndex));
+ _ASSERTE((hr == S_OK) || (hr == S_FALSE));
+
+ if (hr == S_OK)
+ { // TypeRef is well known redirectetd type (with index in array code:g_rgRedirectedTypes)
+ if (nRedirectedTypeIndex == RedirectedTypeIndex_Windows_Foundation_IReference ||
+ nRedirectedTypeIndex == RedirectedTypeIndex_Windows_Foundation_Collections_IKeyValuePair)
+ { // The TypeRef name was changed to System.Nullable or System.Collections.Generic.KeyValuePair`2 (value type, not class)
+ elementType = ELEMENT_TYPE_VALUETYPE;
+ *pfChangedSig = TRUE;
+ }
+ }
+ // We do not want to return S_FALSE
+ hr = S_OK;
+ }
+
+ pSigBuilder->AppendByte(elementType);
+ pSigBuilder->AppendToken(token);
+
+ break;
+ }
+ case ELEMENT_TYPE_VALUETYPE:
+ {
+ mdToken token;
+ IfFailGo(pSigParser->GetToken(&token));
+
+ if (TypeFromToken(token) == mdtTypeRef)
+ {
+ RedirectedTypeIndex nRedirectedTypeIndex;
+ IfFailGo(this->GetTypeRefRedirectedInfo(token, &nRedirectedTypeIndex));
+ _ASSERTE((hr == S_OK) || (hr == S_FALSE));
+
+ if (hr == S_OK)
+ { // TypeRef is well known redirectetd type (with index in array code:g_rgRedirectedTypes)
+ if (nRedirectedTypeIndex == RedirectedTypeIndex_Windows_UI_Xaml_Interop_TypeName ||
+ nRedirectedTypeIndex == RedirectedTypeIndex_Windows_Foundation_HResult)
+ {
+ // TypeIdentifier and HResult are all value types in winmd and are mapped to reference
+ // types in CLR: Type, and Exception.
+ elementType = ELEMENT_TYPE_CLASS;
+ *pfChangedSig = TRUE;
+ }
+ }
+ // We do not want to return S_FALSE
+ hr = S_OK;
+ }
+
+ pSigBuilder->AppendByte(elementType);
+ pSigBuilder->AppendToken(token);
+
+ break;
+ }
+
+ // Read a type
+ case ELEMENT_TYPE_SZARRAY: // SZARRAY <type>
+ case ELEMENT_TYPE_PTR: // PTR <type>
+ case ELEMENT_TYPE_BYREF: // BYREF <type>
+ case ELEMENT_TYPE_SENTINEL: // sentinel for VARARGS ("..." in the parameter list), it behaves as prefix to next arg
+ {
+ pSigBuilder->AppendByte(elementType);
+
+ IfFailGo(RewriteTypeInSignature(pSigParser, pSigBuilder, pfChangedSig));
+ break;
+ }
+
+ // Read a token then a type. That type could be another custom modifier.
+ case ELEMENT_TYPE_CMOD_REQD: // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef> [followed by a type, or another custom modifier]
+ case ELEMENT_TYPE_CMOD_OPT: // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
+ {
+ pSigBuilder->AppendByte(elementType);
+
+ mdToken token;
+ IfFailGo(pSigParser->GetToken(&token));
+ pSigBuilder->AppendToken(token);
+
+ // Process next type or custom modifier
+ IfFailGo(RewriteTypeInSignature(pSigParser, pSigBuilder, pfChangedSig));
+ break;
+ }
+
+ // Read a number
+ case ELEMENT_TYPE_VAR: // a class type variable VAR <number>
+ case ELEMENT_TYPE_MVAR: // a method type variable MVAR <number>
+ {
+ pSigBuilder->AppendByte(elementType);
+
+ ULONG number;
+ IfFailGo(pSigParser->GetData(&number));
+ pSigBuilder->AppendData(number);
+ break;
+ }
+
+ case ELEMENT_TYPE_ARRAY: // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
+ {
+ pSigBuilder->AppendByte(elementType);
+
+ // Read array type
+ IfFailGo(RewriteTypeInSignature(pSigParser, pSigBuilder, pfChangedSig));
+
+ // Read rank
+ ULONG rank;
+ IfFailGo(pSigParser->GetData(&rank));
+ pSigBuilder->AppendData(rank);
+
+ // If rank is 0, then there's nothing else in the array signature
+ if (rank != 0)
+ {
+ // Read number of dimension sizes
+ ULONG cDimensionSizes;
+ IfFailGo(pSigParser->GetData(&cDimensionSizes));
+ pSigBuilder->AppendData(cDimensionSizes);
+
+ // Read all dimension sizes
+ for (ULONG i = 0; i < cDimensionSizes; i++)
+ {
+ ULONG dimensionSize;
+ IfFailGo(pSigParser->GetData(&dimensionSize));
+ pSigBuilder->AppendData(dimensionSize);
+ }
+
+ // Read number of lower bounds
+ ULONG cLowerBounds;
+ IfFailGo(pSigParser->GetData(&cLowerBounds));
+ pSigBuilder->AppendData(cLowerBounds);
+
+ // Read all lower bounds
+ for (ULONG i = 0; i < cLowerBounds; i++)
+ {
+ ULONG lowerBound;
+ IfFailGo(pSigParser->GetData(&lowerBound));
+ pSigBuilder->AppendData(lowerBound);
+ }
+ }
+ break;
+ }
+
+ case ELEMENT_TYPE_GENERICINST: // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
+ {
+ pSigBuilder->AppendByte(elementType);
+
+ // Read the generic type
+ IfFailGo(RewriteTypeInSignature(pSigParser, pSigBuilder, pfChangedSig));
+
+ // Read arg count
+ ULONG cGenericTypeArguments;
+ IfFailGo(pSigParser->GetData(&cGenericTypeArguments));
+ pSigBuilder->AppendData(cGenericTypeArguments);
+
+ // Read each type argument
+ for (ULONG i = 0; i < cGenericTypeArguments; i++)
+ {
+ IfFailGo(RewriteTypeInSignature(pSigParser, pSigBuilder, pfChangedSig));
+ }
+ break;
+ }
+
+ case ELEMENT_TYPE_FNPTR: // FNPTR <complete sig for the function including calling convention>
+ /*
+ // FNPTR is not supported in C#/VB, thefore this is not a main scenario.
+ // This implementation was late during .NET 4.5, but may be useful in future releases when we decide to support more languages for managed WinMD implementation.
+ {
+ pSigBuilder->AppendByte(elementType);
+
+ // Read calling convention
+ DWORD callingConvention;
+ IfFailGo(pSigParser->GetData(&callingConvention));
+ pSigBuilder->AppendData(callingConvention);
+
+ // Read arg count
+ ULONG cArgs;
+ IfFailGo(pSigParser->GetData(&cArgs));
+ pSigBuilder->AppendData(cArgs);
+
+ // Read return argument
+ IfFailGo(RewriteTypeInSignature(pSigParser, pSigBuilder, pfChangedSig));
+
+ // Read each argument
+ for (ULONG i = 0; i < cArgs; i++)
+ {
+ IfFailGo(RewriteTypeInSignature(pSigParser, pSigBuilder, pfChangedSig));
+ }
+ break;
+ }
+ */
+ Debug_ReportError("ELEMENT_TYPE_FNPTR signature parsing in WinMD Adapter is NYI.");
+ IfFailGo(E_FAIL);
+
+ case ELEMENT_TYPE_END:
+ case ELEMENT_TYPE_INTERNAL: // INTERNAL <typehandle> (Only in ngen images, but not reachable from MetaData - no sig rewriting)
+ case ELEMENT_TYPE_PINNED: // PINNED <type>, used only in LocalSig (no sig rewriting)
+ Debug_ReportError("Unexpected CorElementType in a signature. Sig parsing failing.");
+ IfFailGo(E_FAIL);
+
+ default:
+ Debug_ReportError("Unknown CorElementType.");
+ IfFailGo(E_FAIL);
+ }
+ _ASSERTE(hr == S_OK);
+ return hr;
+
+ErrExit:
+ Debug_ReportError("Sig parsing failed.");
+ return hr;
+} // WinMDAdapter::RewriteTypeInSignature
+
+//------------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------------
+// Windows.Foundation.Metadata.AttributeTarget and System.AttributeTarget enum
+// define different bits for everything (@todo: Be nice to change that before we ship.)
+// Do the conversion here.
+//---------------------------------------------------------------------------------
+static DWORD ConvertToClrAttributeTarget(DWORD winRTTarget)
+{
+ struct AttributeTargetsPair
+ {
+ DWORD WinRTValue;
+ DWORD ClrValue;
+ };
+
+ static const AttributeTargetsPair s_attributeTargetPairs[] =
+ {
+#define DEFINE_PROJECTED_TYPE(a,b,c,d,e,f,g,h,i)
+#define DEFINE_PROJECTED_ATTRIBUTETARGETS_VALUE(winrt, clr) { winrt, clr },
+#include "WinRTProjectedTypes.h"
+#undef DEFINE_PROJECTED_ATTRIBUTETARGETS_VALUES
+#undef DEFINE_PROJECTED_TYPE
+ };
+
+ if (winRTTarget == 0xffffffff /* Windows.Foundation.Metadata.AttributeTargets.All */)
+ return 0x00007fff; /* System.AttributeTargets.All */
+ DWORD clrTarget = 0;
+ for (UINT i = 0; i < sizeof(s_attributeTargetPairs)/sizeof(*s_attributeTargetPairs); i++)
+ {
+ if (winRTTarget & s_attributeTargetPairs[i].WinRTValue)
+ {
+ clrTarget |= s_attributeTargetPairs[i].ClrValue;
+ }
+ }
+ return clrTarget;
+}
+
+
+//------------------------------------------------------------------------------
+
+
+// Search a typeDef for WF.AttributeUsageAttribute and WF.AllowMultipleAttribute, and compute
+//
+// *pClrTargetValue - the equivalent System.AttributeTargets enum value
+// *pAllowMultiple - where multiple instances of the CA are allowed.
+//
+// Returns:
+// S_OK: a WF.AttributeUsageAttribute CA exists (this is the one that we will rewrite)
+// S_FALSE: no WF.AttributeUsageAttribute CA exists
+//
+HRESULT WinMDAdapter::TranslateWinMDAttributeUsageAttribute(mdTypeDef tkTypeDefOfCA, /*[out]*/ DWORD *pClrTargetValue, /*[out]*/ BOOL *pAllowMultiple)
+{
+ HRESULT hr;
+ _ASSERTE(TypeFromToken(tkTypeDefOfCA) == mdtTypeDef);
+ _ASSERTE(pClrTargetValue != NULL);
+ _ASSERTE(pAllowMultiple != NULL);
+
+ const BYTE *pbWFUsageBlob;
+ ULONG cbWFUsageBlob;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetCustomAttributeByName(tkTypeDefOfCA, "Windows.Foundation.Metadata.AttributeUsageAttribute", (const void **)&pbWFUsageBlob, &cbWFUsageBlob));
+ if (hr == S_FALSE)
+ goto ErrExit;
+ // Expected blob format:
+ // 01 00 - Fixed prolog for CA's
+ // xx xx xx xx - The Windows.Foundation.Metadata.AttributeTarget value
+ // 00 00 - Indicates 0 name/value pairs following.
+ if (cbWFUsageBlob != 2 + sizeof(DWORD) + 2)
+ {
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+ DWORD wfTargetValue = *(DWORD*)(pbWFUsageBlob + 2);
+ *pClrTargetValue = ConvertToClrAttributeTarget(wfTargetValue);
+
+ // add AttributeTargets.Method, AttributeTargets.Constructor , AttributeTargets.Property, and AttributeTargets.Event if this is the VersionAttribute
+ LPCSTR szNamespace;
+ LPCSTR szName;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetTypeDefProps(tkTypeDefOfCA, &szNamespace, &szName, NULL, NULL, NULL));
+
+ if ((strcmp(szName, "VersionAttribute") == 0 || strcmp(szName, "DeprecatedAttribute") == 0) && strcmp(szNamespace, "Windows.Foundation.Metadata") == 0)
+ {
+ *pClrTargetValue |= 0x2E0;
+ }
+
+ const BYTE *pbWFAllowMultipleBlob;
+ ULONG cbWFAllowMultipleBlob;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetCustomAttributeByName(tkTypeDefOfCA, "Windows.Foundation.Metadata.AllowMultipleAttribute", (const void **)&pbWFAllowMultipleBlob, &cbWFAllowMultipleBlob));
+ *pAllowMultiple = (hr == S_OK);
+ hr = S_OK;
+
+ ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+/*static*/ HRESULT WinMDAdapter::CreateClrAttributeUsageAttributeCABlob(DWORD clrTargetValue, BOOL allowMultiple, CABlob **ppCABlob)
+{
+ // Emit the blob format corresponding to:
+ // [System.AttributeUsage(System.AttributeTargets.xx, AllowMultiple=yy)]
+ //
+ // 01 00 - Fixed prolog for CA's
+ // xx xx xx xx - The System.AttributeTarget value
+ // 01 00 - Indicates 1 name/value pair following.
+ // 54 - SERIALIZATION_TYPE_PROPERTY
+ // 02 - ELEMENT_TYPE_BOOLEAN
+ // 0d - strlen("AllowMultiple") - 1
+ // 41 6c ... 65 - "A" "l" "l" "o" "w" "M" "u" "l" "t" "i" "p" "l" "e"
+ // yy - The boolean selection for "AllowMultiple"
+
+ BYTE blob[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x54, 0x02, 0x0D, 0x41, 0x6C, 0x6C, 0x6F, 0x77, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x6C, 0x65, 0x00 };
+ blob[sizeof(blob) - 1] = allowMultiple ? 1 : 0;
+ *((DWORD*)(blob+2)) = clrTargetValue;
+ CABlob *pCABlob;
+ IfNullRet(pCABlob = CABlob::Create(blob, sizeof(blob)));
+ *ppCABlob = pCABlob;
+ return S_OK;
+}
+
+
+//------------------------------------------------------------------------------
+
+// Gets Guid from Windows.Foundation.Metadata.Guid
+HRESULT WinMDAdapter::GetItemGuid(mdToken tkObj, CLSID *pGuid)
+{
+ HRESULT hr;
+ _ASSERTE(pGuid);
+ *pGuid = GUID_NULL;
+ const BYTE *pBlob;
+ ULONG cbBlob;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetCustomAttributeByName(tkObj, "Windows.Foundation.Metadata.GuidAttribute", (const void**)&pBlob, &cbBlob));
+ if (hr == S_OK)
+ {
+ if (cbBlob == 2 + sizeof(GUID) + 2)
+ {
+ memcpy(pGuid, pBlob + 2, sizeof(GUID));
+ hr = S_OK;
+ goto ErrExit;
+ }
+ }
+ hr = S_FALSE;
+ ErrExit:
+ return hr;
+
+}
+
+
+//------------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+// Gets filtered methodImpl list and adds it to an existing DynamicArray enum.
+//
+// This is used to hide implementations of methods on redirected interfaces after
+// we've replaced them with their CLR counterparts in the interface implementation list.
+//
+// Each filtered methodImpl adds two elements to the enum: the body and decl values
+// in that order.
+//----------------------------------------------------------------------------
+HRESULT WinMDAdapter::AddMethodImplsToEnum(mdTypeDef tkTypeDef, HENUMInternal *henum)
+{
+ _ASSERTE(henum != NULL);
+ _ASSERTE(henum->m_EnumType == MDDynamicArrayEnum);
+ _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef);
+
+ HRESULT hr;
+
+ mdToken tkMethodImplFirst;
+ ULONG count;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetMethodImpls(tkTypeDef, &tkMethodImplFirst, &count));
+ for (ULONG i = 0; i < count; i++)
+ {
+ mdToken tkMethodImpl = tkMethodImplFirst + i;
+ mdToken tkBody, tkDecl;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetMethodImplProps(tkMethodImpl, &tkBody, &tkDecl));
+ UINT nIndex;
+ IfFailGo(CheckIfMethodImplImplementsARedirectedInterface(tkDecl, &nIndex));
+ if (hr == S_FALSE ||
+ (SUCCEEDED(hr) && nIndex == WinMDAdapter::RedirectedTypeIndex_Windows_Foundation_IClosable))
+ {
+ // Keep MethodImpl for IClosable methods and non-redirected interfaces
+ IfFailGo(HENUMInternal::AddElementToEnum(henum, tkBody));
+ IfFailGo(HENUMInternal::AddElementToEnum(henum, tkDecl));
+ }
+ }
+
+ hr = S_OK;
+ ErrExit:
+ return hr;
+}
+
+//------------------------------------------------------------------------------
+
+// S_OK if this is a CLR implementation type that was mangled and hidden by WinMDExp
+//
+// Logically, this function takes a mdTypeDef, but since the caller has already extracted the
+// row data for other purposes, we'll take the row data.
+HRESULT WinMDAdapter::CheckIfClrImplementationType(LPCSTR szName, DWORD dwAttr, LPCSTR *pszUnmangledName)
+{
+ if (m_scenario != kWinMDExp)
+ return S_FALSE; // Input file not produced by WinMDExp
+ if (IsTdNested(dwAttr))
+ return S_FALSE; // Type is nested in another type
+ if ((dwAttr & (tdPublic|tdSpecialName)) != tdSpecialName)
+ return S_FALSE; // Type public or not SpecialName
+ if (0 != strncmp(szName, s_szCLRPrefix, s_ncCLRPrefix))
+ return S_FALSE; // Name does not begin with "<CLR>"
+
+ // Ran out of reasons.
+ *pszUnmangledName = szName + s_ncCLRPrefix;
+ return S_OK;
+}
+
+
+//------------------------------------------------------------------------------
+
+//-------------------------------------------------------------------------------------
+// Returns: S_OK if tkDecl of a methodImpl is a reference to a method on a redirected interface.
+//-------------------------------------------------------------------------------------
+HRESULT WinMDAdapter::CheckIfMethodImplImplementsARedirectedInterface(mdToken tkDecl, UINT *pIndex)
+{
+
+ HRESULT hr;
+ if (TypeFromToken(tkDecl) != mdtMemberRef)
+ return S_FALSE; // REX will always use memberRef and typeRefs to refer to redirected interfaces, even if in same module.
+
+ mdToken tkParent;
+ IfFailRet(m_pRawMetaModelCommonRO->CommonGetMemberRefProps(tkDecl, &tkParent));
+
+ mdTypeRef tkTypeRef;
+ if (TypeFromToken(tkParent) == mdtTypeRef)
+ {
+ tkTypeRef = tkParent;
+ }
+ else if (TypeFromToken(tkParent) == mdtTypeSpec)
+ {
+ PCCOR_SIGNATURE pvSig;
+ ULONG cbSig;
+ IfFailRet(m_pRawMetaModelCommonRO->CommonGetTypeSpecProps(tkParent, &pvSig, &cbSig));
+ static const BYTE expectedSigStart[] = {ELEMENT_TYPE_GENERICINST, ELEMENT_TYPE_CLASS};
+ if (cbSig < sizeof(expectedSigStart) + 1)
+ return S_FALSE;
+ if (0 != memcmp(pvSig, expectedSigStart, sizeof(expectedSigStart)))
+ return S_FALSE;
+ const BYTE *pCodedToken = pvSig + sizeof(expectedSigStart);
+ if (cbSig < sizeof(expectedSigStart) + CorSigUncompressedDataSize(pCodedToken))
+ return S_FALSE;
+ mdToken genericType = CorSigUncompressToken(/*modifies*/pCodedToken);
+ if (TypeFromToken(genericType) != mdtTypeRef)
+ return S_FALSE;
+ tkTypeRef = genericType;
+ }
+ else
+ {
+ return S_FALSE;
+ }
+
+ ULONG treatment;
+ IfFailRet(GetTypeRefTreatment(tkTypeRef, &treatment));
+ if ((treatment & kTrClassMask) != kTrClassWellKnownRedirected)
+ return S_FALSE;
+
+ if (pIndex)
+ *pIndex = (treatment & ~kTrClassMask);
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+/*static*/ HRESULT WinMDAdapter::CreatePrefixedName(LPCSTR szPrefix, LPCSTR szName, LPCSTR *ppOut)
+{
+ // This can cause allocations (thus entering the host) during a profiler stackwalk.
+ // But we're ok since we're not supporting SQL/F1 profiling with WinMDs. FUTURE:
+ // Would be nice to eliminate allocations on stack walks regardless.
+ PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonUnsupportedForSQLF1Profiling);
+
+ size_t ncPrefix = strlen(szPrefix);
+ size_t ncName = strlen(szName);
+ if (ncPrefix + ncName < ncPrefix || ncPrefix + ncName + 1 < ncPrefix + ncName)
+ return E_OUTOFMEMORY;
+ LPSTR szResult = new (nothrow) char[ncPrefix + ncName + 1];
+ IfNullRet(szResult);
+ memcpy(szResult, szPrefix, ncPrefix);
+ memcpy(szResult + ncPrefix, szName, ncName);
+ szResult[ncPrefix + ncName] = '\0';
+ *ppOut = szResult;
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+// Sentinel value in m_redirectedCABlobsMemoTable table. Means "do no blob rewriting. Return the one from the underlying importer."
+/*static*/ WinMDAdapter::CABlob * const WinMDAdapter::CABlob::NOREDIRECT = ((WinMDAdapter::CABlob *)(0x1));
+
+//-----------------------------------------------------------------------------------------------------
+
+/*static*/ WinMDAdapter::CABlob* WinMDAdapter::CABlob::Create(const BYTE *pBlob, ULONG cbBlob)
+{
+ // This can cause allocations (thus entering the host) during a profiler stackwalk.
+ // But we're ok since we're not supporting SQL/F1 profiling with WinMDs. FUTURE:
+ // Would be nice to eliminate allocations on stack walks regardless.
+ PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonUnsupportedForSQLF1Profiling);
+
+ size_t cbAlloc = sizeof(CABlob) + cbBlob; // This overestimates the needed size a bit - no biggie
+ if (cbAlloc < sizeof(CABlob))
+ return NULL;
+
+ CABlob *pNewCABlob = (CABlob*)(new (nothrow) BYTE[cbAlloc]);
+ if (!pNewCABlob)
+ return NULL;
+
+ pNewCABlob->cbBlob = cbBlob;
+ memcpy(pNewCABlob->data, pBlob, cbBlob);
+ return pNewCABlob;
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+/*static*/ void WinMDAdapter::CABlob::Destroy(WinMDAdapter::CABlob *pCABlob)
+{
+ if (pCABlob != CABlob::NOREDIRECT)
+ delete [] (BYTE*)pCABlob;
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+// Sentinel value in m_redirectedMethodSigMemoTable or m_redirectedFieldMemoTable.
+// Means "do no signature rewriting. Return the one from the underlying importer."
+/*static*/ WinMDAdapter::SigData * const WinMDAdapter::SigData::NOREDIRECT = ((WinMDAdapter::SigData *)(0x1));
+
+//-----------------------------------------------------------------------------------------------------
+
+/*static*/ WinMDAdapter::SigData* WinMDAdapter::SigData::Create(ULONG cbSig, PCCOR_SIGNATURE pSig)
+{
+ // This can cause allocations (thus entering the host) during a profiler stackwalk.
+ // But we're ok since we're not supporting SQL/F1 profiling with WinMDs. FUTURE:
+ // Would be nice to eliminate allocations on stack walks regardless.
+ PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonUnsupportedForSQLF1Profiling);
+
+ _ASSERTE(pSig != NULL);
+ size_t cbAlloc = sizeof(SigData) + cbSig; // This overestimates the needed size a bit - no biggie
+ if (cbAlloc < sizeof(SigData))
+ return NULL;
+
+ SigData *pNewSigData = (SigData*)(new (nothrow) BYTE[cbAlloc]);
+ if (!pNewSigData)
+ return NULL;
+
+ pNewSigData->cbSig = cbSig;
+ memcpy(pNewSigData->data, pSig, cbSig);
+ return pNewSigData;
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+/*static*/ void WinMDAdapter::SigData::Destroy(WinMDAdapter::SigData *pSigData)
+{
+ if (pSigData != SigData::NOREDIRECT)
+ delete [] (BYTE*)pSigData;
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------------------------------
+// S_OK if pUnknown is really a WinMD wrapper. This is just a polite way of asking "is it bad to
+// to static cast pUnknown to RegMeta/MDInternalRO."
+//-----------------------------------------------------------------------------------------------------
+HRESULT CheckIfImportingWinMD(IUnknown *pUnknown)
+{
+ IUnknown *pIgnore = NULL;
+ HRESULT hr = pUnknown->QueryInterface(IID_IWinMDImport, (void**)&pIgnore);
+ if (hr == S_OK)
+ {
+ pIgnore->Release();
+ }
+ if (hr == E_NOINTERFACE)
+ {
+ hr = S_FALSE;
+ }
+ return hr;
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+// E_NOTIMPL if pUnknown is really a WinMD wrapper.
+//-----------------------------------------------------------------------------------------------------
+HRESULT VerifyNotWinMDHelper(IUnknown *pUnknown
+#ifdef _DEBUG
+ ,LPCSTR assertMsg
+ ,LPCSTR file
+ ,int line
+#endif //_DEBUG
+ )
+{
+ HRESULT hr = CheckIfImportingWinMD(pUnknown);
+ if (FAILED(hr))
+ return hr;
+ if (hr == S_FALSE)
+ {
+ return S_OK;
+ }
+#ifdef _DEBUG
+#ifndef DACCESS_COMPILE
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_WinMD_AssertOnIllegalUsage))
+ {
+ DbgAssertDialog(file, line, assertMsg);
+ }
+#endif
+#endif
+ return E_NOTIMPL;
+}
+
+
+
diff --git a/src/md/winmd/crossgen/.gitmirror b/src/md/winmd/crossgen/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/winmd/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/winmd/crossgen/CMakeLists.txt b/src/md/winmd/crossgen/CMakeLists.txt
new file mode 100644
index 0000000000..30859c30ae
--- /dev/null
+++ b/src/md/winmd/crossgen/CMakeLists.txt
@@ -0,0 +1,5 @@
+include(${CLR_DIR}/crossgen.cmake)
+include(../../md_wks.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDWINMD_SOURCES)
+add_library_clr(mdwinmd_crossgen ${MDWINMD_SOURCES})
diff --git a/src/md/winmd/crossgen/MDWinMD_crossgen.nativeproj b/src/md/winmd/crossgen/MDWinMD_crossgen.nativeproj
new file mode 100644
index 0000000000..88520924f6
--- /dev/null
+++ b/src/md/winmd/crossgen/MDWinMD_crossgen.nativeproj
@@ -0,0 +1,14 @@
+<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>
+ <OutputName>mdwinmd_crossgen</OutputName>
+ </PropertyGroup>
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\WinMD\WinMD.settings.targets" />
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/winmd/dac/.gitmirror b/src/md/winmd/dac/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/winmd/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/winmd/dac/CMakeLists.txt b/src/md/winmd/dac/CMakeLists.txt
new file mode 100644
index 0000000000..aca5cb581d
--- /dev/null
+++ b/src/md/winmd/dac/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+include(${CLR_DIR}/dac.cmake)
+include(../../md_dbi.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDWINMD_SOURCES)
+add_library_clr(mdwinmd_dac ${MDWINMD_SOURCES})
diff --git a/src/md/winmd/dac/dirs.proj b/src/md/winmd/dac/dirs.proj
new file mode 100644
index 0000000000..c73e2390db
--- /dev/null
+++ b/src/md/winmd/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\mdwinmd_dac.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/winmd/dbi/.gitmirror b/src/md/winmd/dbi/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/winmd/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/winmd/dbi/CMakeLists.txt b/src/md/winmd/dbi/CMakeLists.txt
new file mode 100644
index 0000000000..89a39d3d9b
--- /dev/null
+++ b/src/md/winmd/dbi/CMakeLists.txt
@@ -0,0 +1,4 @@
+include(../../md_dbi.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDWINMD_SOURCES)
+add_library_clr(mdwinmd_dbi ${MDWINMD_SOURCES}) \ No newline at end of file
diff --git a/src/md/winmd/dbi/MDWinMD-dbi.props b/src/md/winmd/dbi/MDWinMD-dbi.props
new file mode 100644
index 0000000000..829e1d7c6d
--- /dev/null
+++ b/src/md/winmd/dbi/MDWinMD-dbi.props
@@ -0,0 +1,9 @@
+<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>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\WinMD\WinMD.settings.targets" />
+
+</Project>
diff --git a/src/md/winmd/dbi/MDWinMD_dbi.nativeproj b/src/md/winmd/dbi/MDWinMD_dbi.nativeproj
new file mode 100644
index 0000000000..3223689415
--- /dev/null
+++ b/src/md/winmd/dbi/MDWinMD_dbi.nativeproj
@@ -0,0 +1,17 @@
+<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>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\WinMD\WinMD.settings.targets" />
+
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>MDWinMD_dbi</OutputName>
+ </PropertyGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/md/winmd/dbi/dirs.proj b/src/md/winmd/dbi/dirs.proj
new file mode 100644
index 0000000000..88863b561f
--- /dev/null
+++ b/src/md/winmd/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 Condition="'$(FeatureDbiDebugging)'=='true'" Include="HostLocal\MDWinMD-dbi.nativeproj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/winmd/dirs.proj b/src/md/winmd/dirs.proj
new file mode 100644
index 0000000000..7788116cc7
--- /dev/null
+++ b/src/md/winmd/dirs.proj
@@ -0,0 +1,21 @@
+<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\mdwinmd_wks.nativeproj" />
+ <ProjectFile Include="dbi\dirs.proj" />
+ <ProjectFile Include="dac\dirs.proj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/md/winmd/inc/.gitmirror b/src/md/winmd/inc/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/winmd/inc/.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/winmd/inc/adapter.h b/src/md/winmd/inc/adapter.h
new file mode 100644
index 0000000000..e69b620938
--- /dev/null
+++ b/src/md/winmd/inc/adapter.h
@@ -0,0 +1,951 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#ifndef __MDWinMDAdapter__h__
+#define __MDWinMDAdapter__h__
+
+#include "memotable.h"
+#include "../../inc/winmdinterfaces.h"
+#include "thekey.h"
+#include "ecmakey.h"
+
+// Instantiation of template in holder.h
+template void DoNothing<ULONG>(ULONG);
+
+class SigBuilder;
+class SigParser;
+
+typedef const BYTE * PCBYTE;
+static const BYTE s_pbContractPublicKeyToken[] = {0xB0,0x3F,0x5F,0x7F,0x11,0xD5,0x0A,0x3A};
+static const BYTE s_pbContractPublicKey[] = {0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x07, 0xD1, 0xFA, 0x57, 0xC4, 0xAE, 0xD9, 0xF0, 0xA3, 0x2E, 0x84, 0xAA, 0x0F, 0xAE, 0xFD, 0x0D, 0xE9, 0xE8, 0xFD, 0x6A, 0xEC, 0x8F, 0x87, 0xFB, 0x03, 0x76, 0x6C, 0x83, 0x4C, 0x99, 0x92, 0x1E, 0xB2, 0x3B, 0xE7, 0x9A, 0xD9, 0xD5, 0xDC, 0xC1, 0xDD, 0x9A, 0xD2, 0x36, 0x13, 0x21, 0x02, 0x90, 0x0B, 0x72, 0x3C, 0xF9, 0x80, 0x95, 0x7F, 0xC4, 0xE1, 0x77, 0x10, 0x8F, 0xC6, 0x07, 0x77, 0x4F, 0x29, 0xE8, 0x32, 0x0E, 0x92, 0xEA, 0x05, 0xEC, 0xE4, 0xE8, 0x21, 0xC0, 0xA5, 0xEF, 0xE8, 0xF1, 0x64, 0x5C, 0x4C, 0x0C, 0x93, 0xC1, 0xAB, 0x99, 0x28, 0x5D, 0x62, 0x2C, 0xAA, 0x65, 0x2C, 0x1D, 0xFA, 0xD6, 0x3D, 0x74, 0x5D, 0x6F, 0x2D, 0xE5, 0xF1, 0x7E, 0x5E, 0xAF, 0x0F, 0xC4, 0x96, 0x3D, 0x26, 0x1C, 0x8A, 0x12, 0x43, 0x65, 0x18, 0x20, 0x6D, 0xC0, 0x93, 0x34, 0x4D, 0x5A, 0xD2, 0x93};
+
+class DECLSPEC_UUID("996AA908-5606-476d-9985-48607B2DA076") IWinMDImport : public IUnknown
+{
+public :
+ STDMETHOD(IsScenarioWinMDExp)(BOOL *pbResult) = 0;
+ STDMETHOD(IsRuntimeClassImplementation)(mdTypeDef tkTypeDef, BOOL *pbResult) = 0;
+};
+
+//========================================================================================
+// Only IWinMDImport and IWinMDImportInternalRO QI successfully for this guid - for cases where we need to
+// tell the difference between the classic MD importers and the WinMD wrappers.
+//========================================================================================
+// {996AA908-5606-476d-9985-48607B2DA076}
+extern const IID DECLSPEC_SELECTANY IID_IWinMDImport = __uuidof(IWinMDImport);
+
+//========================================================================================
+// Popup an assert box if COMPLUS_MD_WinMD_AssertOnIllegalUsage=1
+//========================================================================================
+#if defined(_DEBUG) && !defined(DACCESS_COMPILE)
+#define WINMD_COMPAT_ASSERT(assertMsg) if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_WinMD_AssertOnIllegalUsage)) DbgAssertDialog(__FILE__, __LINE__, assertMsg)
+#else
+#define WINMD_COMPAT_ASSERT(assertMsg)
+#endif
+
+
+//========================================================================================
+// class WinMDAdapter
+//
+// This object performs typeref redirection and any other chores involved in
+// masquerading WinMD files as .NET assemblies.
+//
+// WinMDAdapters act as internal helper objects to WinMDImport and WinMDInternalImportRO
+// and factor out as much common code as is practical given that these two importers
+// expose wildly different interfaces.
+//
+// The main input to this object is a standard .NET metadata importer (the "raw" importer.) Since
+// the two importers have no common public interface, we use the internal (non-COM) IMetaModelCommon
+// interface that both imports have internally and expose through the private IMDCommon
+// interface.
+//
+// Methods on this class follow the IMDInternalImport philosophy (i.e. validation? what validation?,
+// return strings as direct UTF8 pointers to internal strings allocated for the lifetime
+// of WinMDAdapter.) When used by the public IMetaDataImport adapter, it's important that the caller
+// validate parameters before invoking WinMDAdapter.
+//========================================================================================
+class WinMDAdapter
+{
+public:
+#define DEFINE_PROJECTED_TYPE(szWinRTNS, szWinRTName, szClrNS, szClrName, ClrAsmIdx, nContractAsmIdx, WinRTIndex, ClrIndex, TypeKind) \
+ RedirectedTypeIndex_ ## WinRTIndex, \
+ RedirectedTypeIndex_ ## ClrIndex = RedirectedTypeIndex_ ## WinRTIndex, \
+
+ // Indexes of well-known redirected types into array code:g_rgRedirectedTypes
+ enum RedirectedTypeIndex
+ {
+#include "WinRTProjectedTypes.h"
+ RedirectedTypeIndex_Count,
+ RedirectedTypeIndex_Invalid = -1,
+ };
+#undef DEFINE_PROJECTED_TYPE
+
+ enum FrameworkAssemblyIndex
+ {
+ FrameworkAssembly_Mscorlib,
+ FrameworkAssembly_SystemObjectModel,
+ FrameworkAssembly_SystemRuntime,
+ FrameworkAssembly_SystemRuntimeWindowsRuntime,
+ FrameworkAssembly_SystemRuntimeWindowsRuntimeUIXaml,
+ FrameworkAssembly_SystemNumericsVectors,
+
+ FrameworkAssembly_Count,
+ };
+
+ // If new contract assemblies need to be added, they must be appended to the end of the following list.
+ // Also, don't remove or change any existing assemblies in this list.
+ // Not following these rules will break existing MDIL images generated in the Store.
+ enum ContractAssemblyIndex
+ {
+ ContractAssembly_SystemRuntime,
+ ContractAssembly_SystemRuntimeInteropServicesWindowsRuntime,
+ ContractAssembly_SystemObjectModel,
+ ContractAssembly_SystemRuntimeWindowsRuntime,
+ ContractAssembly_SystemRuntimeWindowsRuntimeUIXaml,
+ ContractAssembly_SystemNumericsVectors, // GetExtraAssemblyRefCount assumes SystemNumericsVectors is the last assembly.
+ // If you add an assembly you must update GetActualExtraAssemblyRefCount.
+
+ ContractAssembly_Count,
+ };
+
+
+ enum WinMDTypeKind
+ {
+ WinMDTypeKind_Attribute,
+ WinMDTypeKind_Enum,
+ WinMDTypeKind_Delegate,
+ WinMDTypeKind_Interface,
+ WinMDTypeKind_PDelegate,
+ WinMDTypeKind_PInterface,
+ WinMDTypeKind_Struct,
+ WinMDTypeKind_Runtimeclass,
+ };
+
+ int GetExtraAssemblyRefCount();
+
+ // Factory and destructor
+ static HRESULT Create(IMDCommon *pRawMDCommon, /*[out]*/ WinMDAdapter **ppAdapter);
+ ~WinMDAdapter();
+
+ // Map a well-known WinRT typename to CLR typename
+ static BOOL ConvertWellKnownTypeNameFromWinRTToClr(LPCSTR *pszNamespace, LPCSTR *pszName);
+
+ // Map a well-known WinRT full typename to CLR full typename
+ static BOOL ConvertWellKnownFullTypeNameFromWinRTToClr(LPCWSTR *pszFullName, RedirectedTypeIndex *pIndex);
+
+ // Map a well-known CLR typename to WinRT typename
+ static BOOL ConvertWellKnownTypeNameFromClrToWinRT(LPCSTR *pszFullName);
+
+ // Map a well-known CLR typename to WinRT typename
+ static BOOL WinMDAdapter::ConvertWellKnownTypeNameFromClrToWinRT(LPCSTR *pszNamespace, LPCSTR *pszName);
+
+ // Returns names of redirected type 'index'.
+ static void GetRedirectedTypeInfo(
+ RedirectedTypeIndex index,
+ LPCSTR * pszClrNamespace,
+ LPCSTR * pszClrName,
+ LPCSTR * pszFullWinRTName,
+ FrameworkAssemblyIndex * pFrameworkAssemblyIdx,
+ ContractAssemblyIndex * pContractAssemblyIdx,
+ WinMDTypeKind * pWinMDTypeKind);
+
+ // Returns name of redirected type 'index'.
+ static LPCWSTR GetRedirectedTypeFullWinRTName(RedirectedTypeIndex index);
+ static LPCSTR GetRedirectedTypeFullCLRName(RedirectedTypeIndex index);
+
+ // Returns renamed typedefs
+ HRESULT GetTypeDefProps(
+ mdTypeDef typeDef, // [IN] given typedef
+ LPCUTF8 *pszNameSpace, // [OUT] return typedef namespace
+ LPCUTF8 *pszName, // [OUT] return typedef name
+ DWORD *pdwFlags, // [OUT] return typedef flags
+ mdToken *ptkExtends // [OUT] Put base class TypeDef/TypeRef here.
+ );
+
+ // Find TypeDef by name
+ HRESULT FindTypeDef(
+ LPCSTR szTypeDefNamespace, // [IN] Namespace for the TypeDef.
+ LPCSTR szTypeDefName, // [IN] Name of the TypeDef.
+ mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef of enclosing class.
+ mdTypeDef * ptkTypeDef // [OUT] return typedef
+ );
+
+ // Returns redirected typerefs
+ HRESULT GetTypeRefProps(
+ mdTypeRef typeref, // [IN] given typeref
+ LPCSTR *psznamespace, // [OUT] return typeref namespace
+ LPCSTR *pszname, // [OUT] return typeref name
+ mdToken *ptkResolutionScope // [OUT] return typeref resolutionscope
+ );
+
+ // Find TypeRef by name
+ HRESULT FindTypeRef(
+ LPCSTR szNamespace, // [IN] Namespace for the TypeRef (NULL for standalone names)
+ LPCSTR szName, // [IN] Name of the TypeRef.
+ mdToken tkResolutionScope, // [IN] Resolution Scope fo the TypeRef.
+ mdTypeRef *ptk // [OUT] TypeRef token returned.
+ );
+
+ // Modify an ExportedType name
+ HRESULT ModifyExportedTypeName(
+ mdExportedType tkExportedType, // [IN] exportedType token
+ LPCSTR *pszNamespace, // [IN,OUT,OPTIONAL] namespace to modify
+ LPCSTR *pszName // [IN,OUT,OPTIONAL] name to modify
+ );
+
+ // Find ExportedType by name
+ HRESULT FindExportedType(
+ LPCUTF8 szNamespace, // [IN] expected namespace
+ LPCUTF8 szName, // [IN] expected name
+ mdToken tkEnclosingType, // [IN] expected tkEnclosingType
+ mdExportedType *ptkExportedType // [OUT] ExportedType token returned.
+ );
+
+ // Returns rewritten metadata version string
+ HRESULT GetVersionString(
+ LPCSTR *pszVersion // [OUT] return metadata version string
+ )
+ {
+ _ASSERTE(pszVersion != NULL);
+ *pszVersion = m_pRedirectedVersionString;
+ return S_OK;
+ }
+
+ void ModifyAssemblyRefProps(
+ mdAssemblyRef mdar,
+ const void **ppbPublicKeyOrToken,
+ ULONG *pcbPublicKeyOrToken,
+ LPCSTR *pszName,
+ USHORT *pusMajorVersion,
+ USHORT *pusMinorVersion,
+ USHORT *pusBuildNumber,
+ USHORT *pusRevisionNumber,
+ const void **ppbHashValue,
+ ULONG *pcbHashValue)
+ {
+ _ASSERTE(TypeFromToken(mdar) == mdtAssemblyRef);
+
+ // The version of the mscorlib should be 4.0.0.0
+ if (m_assemblyRefMscorlib == mdar)
+ {
+ if (pusMajorVersion != nullptr)
+ *pusMajorVersion = VER_ASSEMBLYMAJORVERSION;
+ if (pusMinorVersion != nullptr)
+ *pusMinorVersion = VER_ASSEMBLYMINORVERSION;
+ if (pusBuildNumber != nullptr)
+ *pusBuildNumber = VER_ASSEMBLYBUILD;
+ if (pusRevisionNumber != nullptr)
+ *pusRevisionNumber = VER_ASSEMBLYBUILD_QFE;
+
+#ifdef FEATURE_CORECLR
+ // Under CoreCLR, we replace the ECMA key in the mscorlib assembly ref with the CoreCLR platform public key token
+ if (ppbPublicKeyOrToken != nullptr)
+ {
+ *ppbPublicKeyOrToken = g_rbTheSilverlightPlatformKeyToken;
+ *pcbPublicKeyOrToken = _countof(g_rbTheSilverlightPlatformKeyToken);
+ }
+#endif
+ }
+ else if (RidFromToken(mdar) > m_rawAssemblyRefCount)
+ {
+ // This is one of the assemblies that we inject
+ UINT index = RidFromToken(mdar) - m_rawAssemblyRefCount - 1;
+
+ if (ppbPublicKeyOrToken != nullptr)
+ {
+ if (index != ContractAssemblyIndex::ContractAssembly_SystemRuntimeWindowsRuntime &&
+ index != ContractAssemblyIndex::ContractAssembly_SystemRuntimeWindowsRuntimeUIXaml)
+ {
+ // The assembly ref is a contract/facade assembly. System.Runtime.WindowsRuntime and
+ // System.Runtime.WindowsRuntime.UI.Xaml are special cased because the contract and the implementation
+ // assembly share the same identity and use mscorlib's public key/token that ppbPublicKeyOrToken
+ // alredy contains since the raw GetAssemblyRefProps was called with mscorlib's token before this
+ // function was called.
+ if (*pcbPublicKeyOrToken == sizeof(s_pbContractPublicKeyToken))
+ *ppbPublicKeyOrToken = s_pbContractPublicKeyToken;
+ else if (*pcbPublicKeyOrToken == sizeof(s_pbContractPublicKey))
+ *ppbPublicKeyOrToken = s_pbContractPublicKey;
+ }
+#ifdef FEATURE_WINDOWSPHONE
+ // System.Runtime.WindowsRuntime uses the ECMA key on Windows Phone.
+ // The WinRT adapter's policy of using mscorlib's assembly references for all the additional
+ // assembly references doesn't work here since mscorlib uses the Silverlight Platform key.
+ else
+ {
+ if (*pcbPublicKeyOrToken == sizeof(g_rbNeutralPublicKeyToken))
+ *ppbPublicKeyOrToken = g_rbNeutralPublicKeyToken;
+ else if (*pcbPublicKeyOrToken == sizeof(g_rbNeutralPublicKey))
+ *ppbPublicKeyOrToken = g_rbNeutralPublicKey;
+ }
+#endif
+ }
+
+ if (pszName != nullptr)
+ *pszName = GetExtraAssemblyRefName(mdar);
+
+ if (pusMajorVersion != nullptr)
+ *pusMajorVersion = VER_ASSEMBLYMAJORVERSION;
+ if (pusMinorVersion != nullptr)
+ *pusMinorVersion = VER_ASSEMBLYMINORVERSION;
+ if (pusBuildNumber != nullptr)
+ *pusBuildNumber = VER_ASSEMBLYBUILD;
+ if (pusRevisionNumber != nullptr)
+ *pusRevisionNumber = VER_ASSEMBLYBUILD_QFE;
+
+ if (ppbHashValue)
+ *ppbHashValue = NULL;
+
+ if (pcbHashValue)
+ *pcbHashValue = 0;
+ }
+ }
+
+ // Modifes the FieldDefProps.
+ HRESULT ModifyFieldDefProps (mdFieldDef tkFielddDef, DWORD *pdwFlags);
+
+ // Modifies FieldProps
+ HRESULT ModifyFieldProps (mdToken tkField, mdToken tkParent, LPCSTR szFieldName, DWORD *pdwFlags);
+
+ // Modifies methodDef flags and RVA
+ HRESULT ModifyMethodProps(mdMethodDef tkMethodDef, /*[in, out]*/ DWORD *pdwAttr, /* [in,out] */ DWORD *pdwImplFlags, /* [in,out] */ ULONG *pulRVA, LPCSTR *pszName);
+
+ // Modifies member flags and RVA
+ HRESULT ModifyMemberProps(mdToken tkMember, /*[in, out]*/ DWORD *pdwAttr, /* [in,out] */ DWORD *pdwImplFlags, /* [in,out] */ ULONG *pulRVA, LPCSTR *pszNewName);
+
+ // Modifies CA's
+ HRESULT GetCustomAttributeByName( // 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.
+
+ // Modify CA blobs
+ HRESULT GetCustomAttributeBlob(
+ mdCustomAttribute tkCA,
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData); // [OUT] Put size of data here.
+
+ // Gets the GUID used for COM interop purposes.
+ HRESULT GetItemGuid(mdToken tkObj, CLSID *pGuid);
+
+ // Gets filtered methodImpl list
+ HRESULT AddMethodImplsToEnum(mdTypeDef tkTypeDef, HENUMInternal *henum);
+
+ //-----------------------------------------------------------------------------------
+ // For each token, we cache the signature that the adapter reports to callers.
+ //-----------------------------------------------------------------------------------
+ struct SigData
+ {
+ ULONG cbSig; // Length of sig in bytes
+ BYTE data[1]; // Signature
+
+ static SigData* Create(ULONG cbSig, PCCOR_SIGNATURE pSig);
+ static void Destroy(SigData *pSigData);
+
+ // Sentinel value meaning we did not need to rewrite the signature; use the underlying importer's signature
+ static SigData* const NOREDIRECT;
+ };
+
+ // Gets a method/field/TypeSpec/MethodSpec signature, with appropriate WinMD changes.
+ HRESULT ReinterpretMethodSignature (ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData);
+ HRESULT ReinterpretFieldSignature (ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData);
+ HRESULT ReinterpretTypeSpecSignature (ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData);
+ HRESULT ReinterpretMethodSpecSignature (ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData);
+
+ template<mdToken TOKENTYPE>
+ HRESULT ReinterpretSignature(
+ ULONG cbOrigSigBlob, // [IN] count of bytes in original signature blob
+ PCCOR_SIGNATURE pOrigSig, // [IN] original signature
+ SigData **ppSigData // [OUT] new signature or SigData::NOREDIRECT
+ )
+ {
+ UNREACHABLE_MSG("You should create a specialized version of ReinterpretSignature for this token type");
+ }
+
+ // Explicit specializations of ReinterpretSignature for all supported token types
+ template<> // mdMethodDef
+ HRESULT ReinterpretSignature<mdtMethodDef>(ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData)
+ {
+ return ReinterpretMethodSignature(cbOrigSigBlob, pOrigSig, ppSigData);
+ }
+
+ template<> // mdFieldDef
+ HRESULT ReinterpretSignature<mdtFieldDef>(ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData)
+ {
+ return ReinterpretFieldSignature(cbOrigSigBlob, pOrigSig, ppSigData);
+ }
+
+ template<> // mdMemberRef
+ HRESULT ReinterpretSignature<mdtMemberRef>(ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData)
+ {
+ if (cbOrigSigBlob == 0)
+ {
+ *ppSigData = SigData::NOREDIRECT;
+
+ return META_E_BAD_SIGNATURE;
+ }
+
+ // MemberRef references either a field or a method
+ return (*pOrigSig == IMAGE_CEE_CS_CALLCONV_FIELD) ?
+ ReinterpretFieldSignature(cbOrigSigBlob, pOrigSig, ppSigData) :
+ ReinterpretMethodSignature(cbOrigSigBlob, pOrigSig, ppSigData);
+ }
+
+ template<> // mdProperty
+ HRESULT ReinterpretSignature<mdtProperty>(ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData)
+ {
+ // Per ECMA CLI spec, section 23.2.5 PropertySig is just an ordinary method (getter) signature
+ return ReinterpretMethodSignature(cbOrigSigBlob, pOrigSig, ppSigData);
+ }
+
+ template<> // mdTypeSpec
+ HRESULT ReinterpretSignature<mdtTypeSpec>(ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData)
+ {
+ return ReinterpretTypeSpecSignature(cbOrigSigBlob, pOrigSig, ppSigData);
+ }
+
+ template<> // mdMethodSpec
+ HRESULT ReinterpretSignature<mdtMethodSpec>(ULONG cbOrigSigBlob, PCCOR_SIGNATURE pOrigSig, SigData **ppSigData)
+ {
+ return ReinterpretMethodSpecSignature(cbOrigSigBlob, pOrigSig, ppSigData);
+ }
+
+ // Note: This method will look in a cache for the reinterpreted signature, but does not add any values to
+ // the cache or do any work on failure. If we can't find it then it returns S_FALSE.
+ static HRESULT GetCachedSigForToken(
+ mdToken token, // [IN] given token
+ MemoTable<SigData*, SigData::Destroy> &memoTable, // [IN] the MemoTable to use
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig, // [OUT] new signature
+ BOOL *pfPassThrough // [OUT] did the cache say we don't need to reinterpret this sig?
+ );
+
+ static HRESULT InsertCachedSigForToken(
+ mdToken token, // [IN] given token
+ MemoTable<SigData*, SigData::Destroy> &memoTable, // [IN] the MemoTable to use
+ SigData **ppSigData // [IN, OUT] new signature or SigData::NOREDIRECT if the signature didn't need to be reparsed,
+ ); // will be updated with another (but identical) SigData* if this thread lost the race
+
+ template<typename T, mdToken TOKENTYPE>
+ HRESULT GetOriginalSigForToken(
+ T *pImport,
+ mdToken token, // [IN] Token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to signature.
+ ULONG *pcbSig // [OUT] return size of signature.
+ )
+ {
+ UNREACHABLE_MSG("You should create a specialized version of GetOriginalSigForToken for this interface/token type");
+ }
+
+ template<mdToken TOKENTYPE>
+ MemoTable<SigData*, SigData::Destroy> &GetSignatureMemoTable()
+ {
+ UNREACHABLE_MSG("You should create a specialized version of GetSignatureMemoTable for this token type");
+ }
+
+ // Explicit specializations of GetOriginalSigForToken for all supported token types (IMetaDataImport2)
+ template<> // mdMethodDef
+ HRESULT GetOriginalSigForToken<IMetaDataImport2, mdtMethodDef>(IMetaDataImport2 *pImport, mdMethodDef tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetMethodProps(tk, NULL, NULL, 0, NULL, NULL, ppvSig, pcbSig, NULL, NULL);
+ }
+
+ template<> // mdFieldDef
+ HRESULT GetOriginalSigForToken<IMetaDataImport2, mdtFieldDef>(IMetaDataImport2 *pImport, mdFieldDef tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetFieldProps(tk, NULL, NULL, 0, NULL, NULL, ppvSig, pcbSig, NULL, NULL, NULL);
+ }
+
+ template<> // mdMemberRef
+ HRESULT GetOriginalSigForToken<IMetaDataImport2, mdtMemberRef>(IMetaDataImport2 *pImport, mdMemberRef tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetMemberRefProps(tk, NULL, NULL, 0, NULL, ppvSig, pcbSig);
+ }
+
+ template<> // mdProperty
+ HRESULT GetOriginalSigForToken<IMetaDataImport2, mdtProperty>(IMetaDataImport2 *pImport, mdProperty tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetPropertyProps(tk, NULL, NULL, 0, NULL, NULL, ppvSig, pcbSig, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL);
+ }
+
+ template<> // mdTypeSpec
+ HRESULT GetOriginalSigForToken<IMetaDataImport2, mdtTypeSpec>(IMetaDataImport2 *pImport, mdTypeSpec tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetTypeSpecFromToken(tk, ppvSig, pcbSig);
+ }
+
+ template<> // mdMethodSpec
+ HRESULT GetOriginalSigForToken<IMetaDataImport2, mdtMethodSpec>(IMetaDataImport2 *pImport, mdMethodSpec tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetMethodSpecProps(tk, NULL, ppvSig, pcbSig);
+ }
+
+ // Explicit specializations of GetOriginalSigForToken for all supported token types (IMDInternalImport)
+ template<> // mdMethodDef
+ HRESULT GetOriginalSigForToken<IMDInternalImport, mdtMethodDef>(IMDInternalImport *pImport, mdMethodDef tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetSigOfMethodDef(tk, pcbSig, ppvSig);
+ }
+
+ template<> // mdFieldDef
+ HRESULT GetOriginalSigForToken<IMDInternalImport, mdtFieldDef>(IMDInternalImport *pImport, mdFieldDef tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetSigOfFieldDef(tk, pcbSig, ppvSig);
+ }
+
+ template<> // mdMemberRef
+ HRESULT GetOriginalSigForToken<IMDInternalImport, mdtMemberRef>(IMDInternalImport *pImport, mdMemberRef tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ LPCSTR szMemberRefName;
+ return pImport->GetNameAndSigOfMemberRef(tk, ppvSig, pcbSig, &szMemberRefName);
+ }
+
+ template<> // mdProperty
+ HRESULT GetOriginalSigForToken<IMDInternalImport, mdtProperty>(IMDInternalImport *pImport, mdProperty tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetPropertyProps(tk, NULL, NULL, ppvSig, pcbSig);
+ }
+
+ template<> // mdTypeSpec
+ HRESULT GetOriginalSigForToken<IMDInternalImport, mdtTypeSpec>(IMDInternalImport *pImport, mdTypeSpec tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetSigFromToken(tk, pcbSig, ppvSig);
+ }
+
+ template<> // mdMethodSpec
+ HRESULT GetOriginalSigForToken<IMDInternalImport, mdtMethodSpec>(IMDInternalImport *pImport, mdMethodSpec tk, PCCOR_SIGNATURE *ppvSig, ULONG *pcbSig)
+ {
+ return pImport->GetMethodSpecProps(tk, NULL, ppvSig, pcbSig);
+ }
+
+
+ // Returns signature for the given MethodDef, FieldDef, MemberRef, standalone Signature, Property, TypeSpec, or MethodSpec token.
+ // Performs cache lookup, computes the new reinterpreted signature if needed, and inserts it into the cache. Be sure to instantiate
+ // the method with right template arguments. T is the underlying metadata interface that should be used to retrieve the original
+ // signature if not passed in ppOrigSig/pcbOrigSigBlob. TOKENTYPE an mdt* constants corresponding to the type of the token.
+ template<typename T, mdToken TOKENTYPE>
+ HRESULT GetSignatureForToken(
+ mdToken token,
+ PCCOR_SIGNATURE *ppOrigSig, // [IN] pointer to count of bytes in the original signature blob, NULL if we need to retrieve from GetOriginalSigForToken
+ ULONG *pcbOrigSigBlob, // [IN] pointer to original signature, NULL if we need to retrieve from GetOriginalSigForToken
+ PCCOR_SIGNATURE *ppSig, // [OUT] new signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ T *pImport)
+ {
+ _ASSERTE(TypeFromToken(token) == TOKENTYPE);
+ if ((ppSig == NULL) && (pcbSigBlob == NULL))
+ {
+ return S_OK;
+ }
+
+ // When loading NGen images we go through code paths that expect no faults and no
+ // throws. We will need to take a look at how we use the winmd metadata with ngen,
+ // potentially storing the post-mangled metadata in the NI because as the adapter grows
+ // we'll see more of these.
+ CONTRACT_VIOLATION(ThrowsViolation | FaultViolation);
+
+ HRESULT hr = S_OK;
+ ULONG cbOrigSigBlob = (ULONG)(-1);
+ PCCOR_SIGNATURE pOrigSig = NULL;
+ BOOL fPassThrough = FALSE;
+
+ MemoTable<SigData*, SigData::Destroy> &memoTable = GetSignatureMemoTable<TOKENTYPE>();
+
+ // Get from cache
+ IfFailRet(GetCachedSigForToken(token, memoTable, pcbSigBlob, ppSig, &fPassThrough));
+ if (hr == S_FALSE)
+ {
+ // We do not want to leak S_FALSE from this function
+ hr = S_OK;
+
+ // Original signature has already been provided?
+ if ((pcbOrigSigBlob == NULL) || (ppOrigSig == NULL))
+ {
+ // Not provided, we need to get one by ourselves
+ IfFailRet((GetOriginalSigForToken<T, TOKENTYPE>(pImport, token, &pOrigSig, &cbOrigSigBlob)));
+ }
+ else
+ {
+ // Provided, use that
+ pOrigSig = *ppOrigSig;
+ cbOrigSigBlob = *pcbOrigSigBlob;
+ }
+
+ if (fPassThrough) // We cached that we don't need to reinterpret anything.
+ {
+ if (ppSig != NULL)
+ *ppSig = pOrigSig;
+ if (pcbSigBlob != NULL)
+ *pcbSigBlob = cbOrigSigBlob;
+ }
+ else
+ {
+ SigData *pSigData;
+ IfFailRet(ReinterpretSignature<TOKENTYPE>(cbOrigSigBlob, pOrigSig, &pSigData));
+ IfFailRet(InsertCachedSigForToken(token, memoTable, &pSigData));
+
+ fPassThrough = (pSigData == SigData::NOREDIRECT);
+
+ if (ppSig != NULL)
+ *ppSig = (fPassThrough ? pOrigSig : (PCCOR_SIGNATURE)pSigData->data);
+ if (pcbSigBlob != NULL)
+ *pcbSigBlob = (fPassThrough ? cbOrigSigBlob : pSigData->cbSig);
+ }
+ }
+ else
+ {
+ _ASSERTE(!fPassThrough);
+ // Already wrote to our output parameters.
+ }
+ // We should return error (via IfFailRet macro) or S_OK here
+ _ASSERTE(hr == S_OK);
+
+ return hr;
+ }
+
+ //
+ // Support for extra assembly refs inserted into this assembly
+ //
+ ULONG GetRawAssemblyRefCount() { return m_rawAssemblyRefCount; }
+
+ mdAssemblyRef GetAssemblyRefMscorlib() { return m_assemblyRefMscorlib; }
+
+ LPCSTR GetExtraAssemblyRefName(mdAssemblyRef mda)
+ {
+ UINT index = RidFromToken(mda) - m_rawAssemblyRefCount - 1;
+ return WinMDAdapter::GetExtraAssemblyRefNameFromIndex((ContractAssemblyIndex)index);
+ }
+
+
+ static LPCSTR GetExtraAssemblyRefNameFromIndex(FrameworkAssemblyIndex index)
+ {
+ _ASSERTE(index >= 0 && index < FrameworkAssembly_Count);
+ _ASSERTE(index != FrameworkAssembly_Mscorlib);
+ switch(index)
+ {
+ case FrameworkAssembly_SystemObjectModel:
+ return "System.ObjectModel";
+ case FrameworkAssembly_SystemRuntime:
+ return "System.Runtime";
+ case FrameworkAssembly_SystemRuntimeWindowsRuntime:
+ return "System.Runtime.WindowsRuntime";
+ case FrameworkAssembly_SystemRuntimeWindowsRuntimeUIXaml:
+ return "System.Runtime.WindowsRuntime.UI.Xaml";
+ case FrameworkAssembly_SystemNumericsVectors:
+#ifdef FEATURE_CORECLR
+ return "System.Numerics.Vectors";
+#else
+ return "System.Numerics";
+#endif
+ default:
+ _ASSERTE(!"Invalid AssemblyRef token!");
+ return NULL;
+ }
+ }
+
+ static LPCSTR GetExtraAssemblyRefNameFromIndex(ContractAssemblyIndex index)
+ {
+ _ASSERTE(index >= 0 && index < ContractAssembly_Count);
+ switch(index)
+ {
+ case ContractAssembly_SystemRuntime:
+ return "System.Runtime";
+ case ContractAssembly_SystemRuntimeInteropServicesWindowsRuntime:
+ return "System.Runtime.InteropServices.WindowsRuntime";
+ case ContractAssembly_SystemObjectModel:
+ return "System.ObjectModel";
+ case ContractAssembly_SystemRuntimeWindowsRuntime:
+ return "System.Runtime.WindowsRuntime";
+ case ContractAssembly_SystemRuntimeWindowsRuntimeUIXaml:
+ return "System.Runtime.WindowsRuntime.UI.Xaml";
+ case ContractAssembly_SystemNumericsVectors:
+ return "System.Numerics.Vectors";
+ default:
+ _ASSERTE(!"Invalid AssemblyRef token!");
+ return NULL;
+ }
+ }
+
+ LPCWSTR GetExtraAssemblyRefNameW(mdAssemblyRef mda)
+ {
+ UINT index = RidFromToken(mda) - m_rawAssemblyRefCount - 1;
+ return WinMDAdapter::GetExtraAssemblyRefNameFromIndexW((ContractAssemblyIndex)index);
+ }
+
+ static LPCWSTR GetExtraAssemblyRefNameFromIndexW(ContractAssemblyIndex index)
+ {
+ _ASSERTE(index >= 0 && index < ContractAssembly_Count);
+ switch(index)
+ {
+ case ContractAssembly_SystemRuntime:
+ return W("System.Runtime");
+ case ContractAssembly_SystemRuntimeInteropServicesWindowsRuntime:
+ return W("System.Runtime.InteropServices.WindowsRuntime");
+ case ContractAssembly_SystemObjectModel:
+ return W("System.ObjectModel");
+ case ContractAssembly_SystemRuntimeWindowsRuntime:
+ return W("System.Runtime.WindowsRuntime");
+ case ContractAssembly_SystemRuntimeWindowsRuntimeUIXaml:
+ return W("System.Runtime.WindowsRuntime.UI.Xaml");
+ case ContractAssembly_SystemNumericsVectors:
+ return W("System.Numerics.Vectors");
+ default:
+ _ASSERTE(!"Invalid AssemblyRef token!");
+ return NULL;
+ }
+ }
+
+ BOOL IsValidAssemblyRefToken(mdAssemblyRef tk)
+ {
+ _ASSERTE(TypeFromToken(tk) == mdtAssemblyRef);
+
+ RID rid = RidFromToken(tk);
+ if (rid > 0 &&
+ rid <= m_rawAssemblyRefCount + GetExtraAssemblyRefCount())
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ static void GetExtraAssemblyRefProps(FrameworkAssemblyIndex index,
+ LPCSTR* ppName,
+ AssemblyMetaDataInternal* pContext,
+ PCBYTE * ppPublicKeytoken,
+ DWORD* pTokenLength,
+ DWORD* pdwFlags);
+
+ BOOL IsScenarioWinMDExp()
+ {
+ return (m_scenario == kWinMDExp);
+ }
+
+ HRESULT IsRuntimeClassImplementation(mdTypeDef tkTypeDef, BOOL *pbResult)
+ {
+ _ASSERTE(pbResult != NULL);
+
+ ULONG typeDefTreatment;
+ HRESULT hr = GetTypeDefTreatment(tkTypeDef, &typeDefTreatment);
+
+ if (SUCCEEDED(hr))
+ {
+ // kTdUnmangleWinRTName treatment means it is a <CLR> implementation class
+ *pbResult = (typeDefTreatment == kTdUnmangleWinRTName);
+ }
+
+ return hr;
+ }
+
+private:
+ struct CABlob;
+
+private:
+
+ WinMDAdapter(IMDCommon * pRawMDCommon);
+
+ // S_OK if this is a CLR implementation type that was mangled and hidden by WinMDExp
+ HRESULT CheckIfClrImplementationType(LPCSTR szName, DWORD dwAttr, LPCSTR *pszUnmangledName);
+
+ // Get TypeRefTreatment value for a typeRef
+ HRESULT GetTypeRefTreatment(mdTypeRef typeRef, ULONG *ppTypeRefTreatment);
+
+ // Get TypeRef's index in array code:g_rgRedirectedTypes or return S_FALSE.
+ HRESULT GetTypeRefRedirectedInfo(
+ mdTypeRef tkTypeRef,
+ RedirectedTypeIndex * pIndex);
+
+ // Get TypeDefTreatment value for a typeDef
+ HRESULT GetTypeDefTreatment(mdTypeDef typeDef, ULONG *ppTypeRefTreatment);
+
+ // Get MethodTreatment value for a methodDef
+ HRESULT GetMethodDefTreatment(mdMethodDef methodDef, ULONG *ppMethodDefTreatment);
+
+ // Compute MethodTreatment value for a methodDef (unlike GetMethodDefTreatment, this
+ // does not cache.)
+ HRESULT ComputeMethodDefTreatment(mdMethodDef tkMethodDef, mdTypeDef tkDeclaringTypeDef, ULONG *ppMethodDefTreatment);
+
+ HRESULT CheckIfMethodImplImplementsARedirectedInterface(mdToken tkDecl, UINT *pIndex);
+
+ HRESULT TranslateWinMDAttributeUsageAttribute(mdTypeDef tkTypeDefOfCA, DWORD *pClrTargetValue, BOOL *pAllowMultiple);
+
+ static HRESULT CreateClrAttributeUsageAttributeCABlob(DWORD clrTargetValue, BOOL allowMultiple, CABlob **ppCABlob);
+
+ // Whether the WinRT type should be hidden from managed code
+ // Example: helper class/interface for projected jupiter structs
+ static BOOL IsHiddenWinRTType(LPCSTR szWinRTNamespace, LPCSTR szWinRTName);
+
+ // Map a WinRT typename to CLR typename
+ static BOOL ConvertWellKnownTypeNameFromWinRTToClr(LPCSTR *pszNamespace, LPCSTR *pszName, UINT *pIndex);
+
+ static HRESULT CreatePrefixedName(LPCSTR szPrefix, LPCSTR szName, LPCSTR *ppOut);
+
+ template <typename T> static void Delete(T* ptr)
+ {
+ delete [] ptr;
+ }
+
+ HRESULT RewriteTypeInSignature(SigParser * pSigParser, SigBuilder * pSigBuilder, BOOL * pfChangedSig);
+
+ private:
+ //-----------------------------------------------------------------------------------
+ // Pointer to the raw view of the metadata.
+ //-----------------------------------------------------------------------------------
+ IMetaModelCommonRO *m_pRawMetaModelCommonRO;
+
+
+ private:
+ //-----------------------------------------------------------------------------------
+ // Stores whether the file is a pure .winmd file or one that combines WinRT and CLR code.
+ //-----------------------------------------------------------------------------------
+ enum WinMDScenario
+ {
+ kWinMDNormal = 1, // File is normal Windows .winmd file (Version string = "Windows Runtime nnn")
+ kWinMDExp = 2, // File is output of winmdexp (Version string = "Windows Runtime nnn;<dotnetVersion>")
+ };
+
+ WinMDScenario m_scenario;
+
+
+ private:
+
+ //-----------------------------------------------------------------------------------
+ // Every WinMD file is required to have an assemblyRef to mscorlib - this field caches that assemblyRef
+ //-----------------------------------------------------------------------------------
+ mdAssemblyRef m_assemblyRefMscorlib;
+ BOOL m_fReferencesMscorlibV4; // m_assemblyRefMscorlib is a version=4.0.0.0 AssemblyRef
+ ULONG m_rawAssemblyRefCount; // the raw assembly ref count not including the extra ones.
+ LONG m_extraAssemblyRefCount; // the assembly ref count to return from IMetaDataAssemblyImport::EnumAssemblyRefs
+
+
+ private:
+ //-----------------------------------------------------------------------------------
+ // For each typeref token, we cache an enum that determines how the adapter treats it.
+ //-----------------------------------------------------------------------------------
+ enum TypeRefTreatment
+ {
+ // The upper 8 bits determine how to interpret the lower 24-bits:
+ kTrClassMask = 0xff000000,
+
+ // Lower 24-bits represent fixed values (defined in rest of enum)
+ kTrClassMisc = 0x00000000,
+
+ // TypeRef is one of a small # of hard-coded Windows.Foundation types that we redirect to mscorlib counterparts.
+ // Lower 24-bits is index into typeref redirection table.
+ kTrClassWellKnownRedirected = 0x01000000,
+
+ kTrNotYetInitialized = kTrClassMisc|0x000000, // Entry has not yet been initialized.
+ kTrNoRewriteNeeded = kTrClassMisc|0x000001, // Do not mangle the name.
+ kTrSystemDelegate = kTrClassMisc|0x000002, // Fast-recognition code for System.Delegate
+ kTrSystemAttribute = kTrClassMisc|0x000003, // Fast-recognition code for System.Attribute
+ kTrSystemEnum = kTrClassMisc|0x000004, // Fast-recognition code for System.Enum
+ kTrSystemValueType = kTrClassMisc|0x000005, // Fast-recognition code for System.ValueType
+ };
+ MemoTable<ULONG, DoNothing<ULONG> > m_typeRefTreatmentMemoTable; // Holds index into typeRef rename array or member of TypeRefTreatment enum
+
+ private:
+ //-----------------------------------------------------------------------------------
+ // For each typedef token, we cache an enum that determines how the adapter treats it.
+ //-----------------------------------------------------------------------------------
+ enum TypeDefTreatment
+ {
+ kTdNotYetInitialized = 0x00,
+
+ kTdTreatmentMask = 0x0f,
+ kTdOther = 0x01, // Anything not covered below, and not affected by the treatment flags
+ kTdNormalNonAttribute = 0x02, // non-attribute TypeDef from non-managed winmd assembly
+ kTdNormalAttribute = 0x03, // Attribute TypeDef from non-managed winmd assembly
+ kTdUnmangleWinRTName = 0x04, // Type in managed winmd that should be renamed from <CLR>Foo to Foo
+ kTdPrefixWinRTName = 0x05, // Type in managed winmd that should be renamed from Foo to <WinRT>Foo
+ kTdRedirectedToCLRType = 0x06, // Type is redirected to a CLR type implementation.
+ kTdRedirectedToCLRAttribute = 0x07, // Type is redirected to a CLR type implementation that is an attribute.
+
+ kTdMarkAbstractFlag = 0x10, // Type should be marked as abstract
+ kTdMarkInternalFlag = 0x20, // Type should be hidden from managed code - examples are struct helpers
+ // for redirected Jupiter structs
+ kTdEnum = 0x40, // Type is an enum from managed\non-managed winmd assembly.
+ };
+ MemoTable<ULONG, DoNothing<ULONG> > m_typeDefTreatmentMemoTable; // Holds member of TypeDefTreatment enum
+
+ private:
+ //-----------------------------------------------------------------------------------
+ // For each methoddef token, we cache an enum that determines how the adapter treats it.
+ //-----------------------------------------------------------------------------------
+ enum MethodDefTreatment
+ {
+ kMdNotYetInitialized = 0x00,
+ kMdTreatmentMask = 0x0f, // Mask of various options
+ kMdOther = 0x01, // Anything not covered below
+ kMdDelegate = 0x02, // Declared by delegate
+ kMdAttribute = 0x03, // Declared by an attribute
+ kMdInterface = 0x04, // Is member of interface
+ kMdImplementation = 0x05, // CLR implementation of RuntimeClass
+ kMdHiddenImpl = 0x06, // Implements a redirected (hidden) interface
+ kMdtUnusedFlag = 0x07, // UnUnsed flag.
+ kMdRenameToDisposeMethod = 0x08, // Rename IClosable.Close to Dispose
+
+ kMdMarkAbstractFlag = 0x10, // Method should be marked as abstract
+ kMdMarkPublicFlag = 0x20, // Method visibility should be marked as public
+ };
+ MemoTable<ULONG, DoNothing<ULONG> > m_methodDefTreatmentMemoTable; // Holds member of MethodDefTreatment enum
+
+ private:
+ //-----------------------------------------------------------------------------------
+ // The version string we report to callers.
+ //-----------------------------------------------------------------------------------
+ LPSTR m_pRedirectedVersionString;
+
+ private:
+ //-----------------------------------------------------------------------------------
+ // For each customattribute token, we cache the blob that the adapter reports to callers.
+ // The special pointer value CABlob::NOREDIRECT indicates that the raw blob is to be
+ // passed unchanged.
+ //-----------------------------------------------------------------------------------
+
+ // Represents a custom attribute blob
+ struct CABlob
+ {
+ ULONG cbBlob; // Length of blob in bytes.
+ BYTE data[1]; // Start of variable-length blob (cbBlob indicates length in bytes.)
+
+ static CABlob* Create(const BYTE *pBlob, ULONG cbBlob);
+ static void Destroy(CABlob *pCABlob);
+
+ // Sentinel value in m_redirectedCABlobsMemoTable table. Means "do no blob rewriting. Return the one from the underlying importer."
+ static CABlob* const NOREDIRECT;
+ };
+
+ MemoTable<CABlob*, CABlob::Destroy> m_redirectedCABlobsMemoTable; // Array of rewritten CA blobs
+
+ private:
+ //-----------------------------------------------------------------------------------
+ // For each token, we cache the signature that the adapter reports to callers.
+ //-----------------------------------------------------------------------------------
+ MemoTable<SigData*, SigData::Destroy> m_redirectedMethodDefSigMemoTable; // Array of rewritten MethodDef signatures
+ MemoTable<SigData*, SigData::Destroy> m_redirectedFieldDefSigMemoTable; // Array of rewritten FieldDef signatures
+ MemoTable<SigData*, SigData::Destroy> m_redirectedMemberRefSigMemoTable; // Array of rewritten MemberRef signatures
+ MemoTable<SigData*, SigData::Destroy> m_redirectedPropertySigMemoTable; // Array of rewritten Property signatures
+ MemoTable<SigData*, SigData::Destroy> m_redirectedTypeSpecSigMemoTable; // Array of rewritten TypeSpec signatures
+ MemoTable<SigData*, SigData::Destroy> m_redirectedMethodSpecSigMemoTable; // Array of rewritten MethodSpec signatures
+
+ // Explicit specializations of GetSignatureMemoTable for all supported token types
+ template<> MemoTable<SigData*, SigData::Destroy> &GetSignatureMemoTable<mdtMethodDef>() { return m_redirectedMethodDefSigMemoTable; }
+ template<> MemoTable<SigData*, SigData::Destroy> &GetSignatureMemoTable<mdtFieldDef>() { return m_redirectedFieldDefSigMemoTable; }
+ template<> MemoTable<SigData*, SigData::Destroy> &GetSignatureMemoTable<mdtMemberRef>() { return m_redirectedMemberRefSigMemoTable; }
+ template<> MemoTable<SigData*, SigData::Destroy> &GetSignatureMemoTable<mdtProperty>() { return m_redirectedPropertySigMemoTable; }
+ template<> MemoTable<SigData*, SigData::Destroy> &GetSignatureMemoTable<mdtTypeSpec>() { return m_redirectedTypeSpecSigMemoTable; }
+ template<> MemoTable<SigData*, SigData::Destroy> &GetSignatureMemoTable<mdtMethodSpec>() { return m_redirectedMethodSpecSigMemoTable; }
+
+ private:
+ //-----------------------------------------------------------------------------------
+ // For each typedef whose name we mangle, we cache the mangled name that we report to callers. (The "name"
+ // is the "name" half of the namespace/name pair. We don't mangle namespaces.)
+ //
+ // Currently, the adapter used the tdAttr value to determine whether a typedef name needs
+ // be mangled at all - thus, we don't have a sentinel for "don't mangle."
+ //-----------------------------------------------------------------------------------
+ MemoTable<LPCSTR, Delete<const char> > m_mangledTypeNameTable; // Array of mangled typedef names
+};
+
+
+#endif // __MDWinMDAdapter__h__
diff --git a/src/md/winmd/inc/memotable.h b/src/md/winmd/inc/memotable.h
new file mode 100644
index 0000000000..df6823d184
--- /dev/null
+++ b/src/md/winmd/inc/memotable.h
@@ -0,0 +1,205 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#ifndef __MDMemoTable__h__
+#define __MDMemoTable__h__
+
+
+//========================================================================================
+// A MemoTable is a 0-based N element array designed for safe multithreaded access
+// and single-initialization rules (e.g. multiple threads can race to initialize an entry
+// but only the first one actually modifies the table.)
+//
+// Template parameters:
+//
+// ELEMTYPE - Type of the data stored in the array. This type must be small enough
+// to be supported by InterlockedCompareExchange.
+//
+// INITVALUE - Sentinel that indicates that InitEntry() has not been called for that entry yet.
+// This is the "initial value" for all elements of the array.
+//
+// ELEMDELETER - (set to NULL if ELEMTYPE does not represent allocated storage.)
+// A function that deallocates the memory referenced by a pointer-typed ELEMTYPE.
+// MemoTable will never pass INITVALUE to this function.
+//
+// Restriction: if ELEMDELETER != NULL, ELEMTYPE must be a C++ pointer type
+// (annoying template weirdness with InterlockedCompareExchangeT that
+// life is too short to disentangle.)
+//========================================================================================
+template <typename ELEMTYPE, void (*ELEMDELETER)(ELEMTYPE)>
+class MemoTable
+{
+public:
+ MemoTable(ULONG numElems, ELEMTYPE initValue);
+ ~MemoTable();
+ HRESULT GetEntry(ULONG index, /* [out] */ ELEMTYPE *pElem);
+ HRESULT InitEntry(ULONG index, /* [in,out] */ ELEMTYPE *pElem);
+
+private:
+ MemoTable(const MemoTable &other);
+ MemoTable& operator=(const MemoTable &other);
+
+ const ULONG m_numElems;
+ const ELEMTYPE m_initValue;
+ ELEMTYPE *m_pTable; // Lazily allocated 0-based array with m_numElems elements.
+};
+
+
+
+
+//==============================================================================
+// MemoTable::MemoTable()
+//==============================================================================
+template <typename ELEMTYPE, void (*ELEMDELETER)(ELEMTYPE)>
+MemoTable<ELEMTYPE, ELEMDELETER>::MemoTable(ULONG numElems, ELEMTYPE initValue) :
+ m_numElems(numElems),
+ m_initValue(initValue)
+{
+ m_pTable = NULL;
+}
+
+
+
+//==============================================================================
+// MemoTable::~MemoTable()
+//==============================================================================
+template <typename ELEMTYPE, void (*ELEMDELETER)(ELEMTYPE)>
+MemoTable<ELEMTYPE, ELEMDELETER>::~MemoTable()
+{
+ if (m_pTable != NULL)
+ {
+ if (ELEMDELETER != NULL)
+ {
+ for (ULONG index = 0; index < m_numElems; index++)
+ {
+ if (m_pTable[index] != m_initValue)
+ {
+ ELEMDELETER(m_pTable[index]);
+ }
+ }
+ }
+ delete [] m_pTable;
+ }
+}
+
+//==============================================================================
+// HRESULT MemoTable::GetEntry(ULONG index, [out] ELEMTYPE *pElem)
+//
+// Retrieves the element at the specified index.
+//
+// Returns:
+// S_OK if returned element is not INITVALUE
+// S_FALSE if returned element is INITVALUE
+// CDLB_E_INDEX_NOT_FOUND if index is out of range.
+//==============================================================================
+template <typename ELEMTYPE, void (*ELEMDELETER)(ELEMTYPE)>
+HRESULT MemoTable<ELEMTYPE, ELEMDELETER>::GetEntry(ULONG index, /* [out] */ ELEMTYPE *pElem)
+{
+ _ASSERTE(pElem);
+ if (index >= m_numElems)
+ {
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+ if (m_pTable == NULL)
+ {
+ *pElem = m_initValue;
+ return S_FALSE;
+ }
+ ELEMTYPE elem = m_pTable[index];
+ *pElem = elem;
+ return (elem == m_initValue) ? S_FALSE : S_OK;
+}
+
+//==============================================================================
+// HRESULT MemoTable::InitEntry(ULONG index, [in, out] ELEMTYPE *pElem)
+//
+// Initalizes the elment at the specified index. It is illegal to attempt
+// to initialize to INITVALUE. For scalar tables, it is illegal to attempt
+// to overwrite a previously initialized entry with a different value.
+// For pointer tables, the entry will be initialized using InterlockedCompareExchangeT
+// and your new value thrown away via ELEMDELETER if you lose the race to initialize.
+//
+// Returns:
+// *pElem overwritten with the actual value written into the table
+// (for pointer tables, this may not be the original pointer you passed
+// due to races.)
+//
+// S_OK is the only success value.
+//==============================================================================
+template <typename ELEMTYPE, void (*ELEMDELETER)(ELEMTYPE)>
+HRESULT MemoTable<ELEMTYPE, ELEMDELETER>::InitEntry(ULONG index, /* [in,out] */ ELEMTYPE *pElem)
+{
+ // This can cause allocations (thus entering the host) during a profiler stackwalk.
+ // But we're ok since we're not supporting SQL/F1 profiling with WinMDs. FUTURE:
+ // Would be nice to eliminate allocations on stack walks regardless.
+ PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonUnsupportedForSQLF1Profiling);
+
+ HRESULT hr = S_OK;
+ _ASSERTE(pElem);
+ ELEMTYPE incomingElem = *pElem;
+
+ if (index >= m_numElems)
+ {
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+ _ASSERTE(incomingElem != m_initValue);
+
+ // If this is first call to InitEntry(), must initialize the table itself.
+ if (m_pTable == NULL)
+ {
+ NewHolder<ELEMTYPE> pNewTable = NULL;
+
+ if ((((size_t)(-1)) / sizeof(ELEMTYPE)) < m_numElems)
+ {
+ IfFailGo(E_OUTOFMEMORY);
+ }
+
+ // When loading NGen images we go through code paths that expect no faults and no
+ // throws. We will need to take a look at how we use the winmd metadata with ngen,
+ // potentially storing the post-mangled metadata in the NI because as the adapter grows
+ // we'll see more of these.
+ CONTRACT_VIOLATION(FaultViolation);
+ pNewTable = new (nothrow) ELEMTYPE[m_numElems];
+ IfNullGo(pNewTable);
+
+ for (ULONG walk = 0; walk < m_numElems; walk++)
+ {
+ pNewTable[walk] = m_initValue;
+ }
+ if (InterlockedCompareExchangeT<ELEMTYPE *>(&m_pTable, pNewTable, NULL) == NULL)
+ { // We won the initialization race
+ pNewTable.SuppressRelease();
+ }
+ }
+
+
+ //-------------------------------------------------------------------------
+ // Cannot fail after this point, or we may delete *pElem after entering it into table.
+ //-------------------------------------------------------------------------
+ hr = S_OK;
+ if (ELEMDELETER == NULL)
+ {
+ _ASSERTE(m_pTable[index] == m_initValue || m_pTable[index] == incomingElem);
+ m_pTable[index] = incomingElem;
+ }
+ else
+ {
+ ELEMTYPE winner = InterlockedCompareExchangeT((ELEMTYPE volatile *)&m_pTable[index], incomingElem, m_initValue);
+ if (winner != m_initValue)
+ {
+ ELEMDELETER(incomingElem); // Lost the race
+ *pElem = winner;
+ }
+ }
+ _ASSERTE(*pElem != m_initValue);
+
+ErrExit:
+ if (ELEMDELETER != NULL && FAILED(hr))
+ {
+ ELEMDELETER(*pElem);
+ *pElem = m_initValue;
+ }
+ return hr;
+}
+
+#endif // __MDMemoTable__h__
diff --git a/src/md/winmd/stdafx.cpp b/src/md/winmd/stdafx.cpp
new file mode 100644
index 0000000000..ff341e19a7
--- /dev/null
+++ b/src/md/winmd/stdafx.cpp
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// stdafx.cpp
+//
+
+//
+// Precompiled headers.
+//
+//*****************************************************************************
+#include "stdafx.h"
diff --git a/src/md/winmd/stdafx.h b/src/md/winmd/stdafx.h
new file mode 100644
index 0000000000..7b662bcf38
--- /dev/null
+++ b/src/md/winmd/stdafx.h
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// 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 <ndpversion.h>
+#include "nsutilpriv.h"
+
+#include "utsem.h"
+
+#endif // __STDAFX_H_
diff --git a/src/md/winmd/winmdimport.cpp b/src/md/winmd/winmdimport.cpp
new file mode 100644
index 0000000000..fe80bf0b04
--- /dev/null
+++ b/src/md/winmd/winmdimport.cpp
@@ -0,0 +1,2132 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+
+#include "stdafx.h"
+#include "winmdinterfaces.h"
+#include "metadataexports.h"
+#include "inc/adapter.h"
+#include "strsafe.h"
+
+//========================================================================================
+// This metadata importer is exposed publically via CoCreateInstance(CLSID_CorMetaDataDispenser...).
+// when the target is a .winmd file. It applies a small number of on-the-fly
+// conversions to make the .winmd file look like a regular .NET assembly.
+//
+// Despite what the MSDN docs say, this importer only supports the reader interfaces and
+// a subset of them at that. Supporting the whole set would not be practical with an
+// on-the-fly translation strategy.
+//========================================================================================
+class WinMDImport : public IMetaDataImport2
+ , IMetaDataAssemblyImport
+ , IMetaDataWinMDImport
+ , IMetaDataValidate
+ , IMDCommon
+ , IMetaModelCommon
+ , IWinMDImport
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+ , IGetIMDInternalImport
+#endif //FEATURE_METADATA_INTERNAL_APIS
+{
+ public:
+ //=========================================================
+ // Factory
+ //=========================================================
+ static HRESULT Create(IMDCommon *pRawMDCommon, WinMDImport **ppWinMDImport)
+ {
+ HRESULT hr;
+ *ppWinMDImport = NULL;
+
+ WinMDImport *pNewWinMDImport = new (nothrow) WinMDImport(pRawMDCommon);
+
+ IfFailGo(pNewWinMDImport->m_pRawMDCommon->QueryInterface(IID_IMetaDataImport2, (void**)&pNewWinMDImport->m_pRawImport));
+ IfFailGo(pNewWinMDImport->m_pRawImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&pNewWinMDImport->m_pRawAssemblyImport));
+
+ if (FAILED(pNewWinMDImport->m_pRawImport->QueryInterface(IID_IMetaDataValidate, (void**)&pNewWinMDImport->m_pRawValidate)))
+ {
+ pNewWinMDImport->m_pRawValidate = nullptr;
+ }
+
+ pNewWinMDImport->m_pRawMetaModelCommonRO = pRawMDCommon->GetMetaModelCommonRO();
+
+ IfFailGo(WinMDAdapter::Create(pRawMDCommon, &pNewWinMDImport->m_pWinMDAdapter));
+
+ (*ppWinMDImport = pNewWinMDImport)->AddRef();
+ hr = S_OK;
+
+ ErrExit:
+ if (pNewWinMDImport)
+ pNewWinMDImport->Release();
+ return hr;
+ }
+
+
+ private:
+ //=========================================================
+ // Ctors, Dtors
+ //=========================================================
+ WinMDImport(IMDCommon * pRawMDCommon)
+ {
+ m_cRef = 1;
+ m_pRawImport = NULL;
+ m_pWinMDAdapter = NULL;
+ m_pRawAssemblyImport = NULL;
+ m_pFreeThreadedMarshaler = NULL;
+ (m_pRawMDCommon = pRawMDCommon)->AddRef();
+ }
+
+ //---------------------------------------------------------
+ ~WinMDImport()
+ {
+ if (m_pRawMDCommon)
+ m_pRawMDCommon->Release();
+ if (m_pRawImport)
+ m_pRawImport->Release();
+ if (m_pRawAssemblyImport)
+ m_pRawAssemblyImport->Release();
+ if (m_pRawValidate)
+ m_pRawValidate->Release();
+ if (m_pFreeThreadedMarshaler)
+ m_pFreeThreadedMarshaler->Release();
+ delete m_pWinMDAdapter;
+ }
+
+ //---------------------------------------------------------
+ BOOL IsValidNonNilToken(mdToken token, DWORD tkKind)
+ {
+ DWORD tkKindActual = TypeFromToken(token);
+ DWORD rid = RidFromToken(token);
+
+ if (tkKindActual != tkKind)
+ return FALSE;
+
+ if (rid == 0)
+ return FALSE;
+
+ ULONG ulRowCount = m_pRawMetaModelCommonRO->CommonGetRowCount(tkKind);
+ if (tkKind == mdtAssemblyRef)
+ return rid <= ulRowCount + m_pWinMDAdapter->GetExtraAssemblyRefCount();
+ else
+ return rid <= ulRowCount;
+ }
+
+ public:
+ //=========================================================
+ // IUnknown methods
+ //=========================================================
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk)
+ {
+ HRESULT hr;
+
+ *ppUnk = 0;
+ if (riid == IID_IUnknown || riid == IID_IWinMDImport)
+ *ppUnk = (IWinMDImport*)this;
+ else if (riid == IID_IMetaDataImport || riid == IID_IMetaDataImport2)
+ *ppUnk = (IMetaDataImport*)this;
+ else if (riid == IID_IMetaDataWinMDImport)
+ *ppUnk = (IMetaDataWinMDImport*)this;
+ else if (riid == IID_IMetaDataAssemblyImport)
+ *ppUnk = (IMetaDataAssemblyImport*)this;
+ else if (riid == IID_IMDCommon)
+ *ppUnk = (IMDCommon*)this;
+ else if (riid == IID_IMetaDataValidate)
+ {
+ if (m_pRawValidate == NULL)
+ {
+ return E_NOINTERFACE;
+ }
+
+ *ppUnk = (IMetaDataValidate*)this;
+ }
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+ else if (riid == IID_IGetIMDInternalImport)
+ *ppUnk = (IGetIMDInternalImport*)this;
+#endif // FEATURE_METADATA_INTERNAL_APIS
+ else if (riid == IID_IMarshal)
+ {
+ if (m_pFreeThreadedMarshaler == NULL)
+ {
+ ReleaseHolder<IUnknown> pFreeThreadedMarshaler = NULL;
+ IfFailRet(CoCreateFreeThreadedMarshaler((IUnknown *)(IMetaDataImport *)this, &pFreeThreadedMarshaler));
+ if (InterlockedCompareExchangeT<IUnknown *>(&m_pFreeThreadedMarshaler, pFreeThreadedMarshaler, NULL) == NULL)
+ { // We won the initialization race
+ pFreeThreadedMarshaler.SuppressRelease();
+ }
+ }
+ return m_pFreeThreadedMarshaler->QueryInterface(riid, ppUnk);
+ }
+ else
+ {
+#ifndef DACCESS_COMPILE
+#ifdef _DEBUG
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_WinMD_AssertOnIllegalUsage))
+ {
+ if (riid == IID_IMetaDataTables)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataTables) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataTables2)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataTables2) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataInfo)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataInfo) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataEmit)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataEmit) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataEmit2)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataEmit2) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataAssemblyEmit)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataAssemblyEmit) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataValidate)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataValidate) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataFilter)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataFilter) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataHelper)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataHelper) returning E_NOINTERFACE");
+ else if (riid == IID_IMDInternalEmit)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMDInternalEmit) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataEmitHelper)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataEmitHelper) returning E_NOINTERFACE");
+ else if (riid == IID_IMetaDataCorProfileData)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMetaDataCorProfileData) returning E_NOINTERFACE");
+ else if (riid == IID_IMDInternalMetadataReorderingOptions)
+ _ASSERTE(!"WinMDImport::QueryInterface(IID_IMDInternalMetadataReorderingOptions) returning E_NOINTERFACE");
+ else
+ _ASSERTE(!"WinMDImport::QueryInterface() returning E_NOINTERFACE");
+ }
+#endif //_DEBUG
+#endif
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+ //---------------------------------------------------------
+ STDMETHODIMP_(ULONG) AddRef(void)
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+ //---------------------------------------------------------
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (!cRef)
+ delete this;
+ return cRef;
+ }
+
+ public:
+ //=========================================================
+ // IWinMDImport methods
+ //=========================================================
+ STDMETHODIMP IsScenarioWinMDExp(BOOL *pbResult)
+ {
+ if (pbResult == NULL)
+ return E_POINTER;
+
+ *pbResult = m_pWinMDAdapter->IsScenarioWinMDExp();
+ return S_OK;
+ }
+
+ STDMETHODIMP IsRuntimeClassImplementation(mdTypeDef tkTypeDef, BOOL *pbResult)
+ {
+ if (pbResult == NULL)
+ return E_POINTER;
+
+ return m_pWinMDAdapter->IsRuntimeClassImplementation(tkTypeDef, pbResult);
+ }
+
+ //=========================================================
+ // IMetaDataImport methods
+ //=========================================================
+ STDMETHODIMP_(void) CloseEnum(HCORENUM hEnum)
+ {
+ m_pRawImport->CloseEnum(hEnum);
+ }
+
+ STDMETHODIMP CountEnum(HCORENUM hEnum, ULONG *pulCount)
+ {
+ _ASSERTE(pulCount != NULL); // Crash on NULL pulCount (just like real RegMeta)
+ if (hEnum != NULL)
+ {
+ HENUMInternal *henuminternal = static_cast<HENUMInternal*>(hEnum);
+ // Special case: We hijack MethodImpl enum's
+ if (henuminternal->m_tkKind == (TBL_MethodImpl << 24))
+ {
+ *pulCount = henuminternal->m_ulCount / 2; // MethodImpl enum's are weird - their entries are body/decl pairs so the internal count is twice the number the caller wants to see.
+ return S_OK;
+ }
+ }
+
+ return m_pRawImport->CountEnum(hEnum, pulCount);
+ }
+
+ STDMETHODIMP ResetEnum(HCORENUM hEnum, ULONG ulPos)
+ {
+ return m_pRawImport->ResetEnum(hEnum, ulPos);
+ }
+
+ STDMETHODIMP EnumTypeDefs(HCORENUM *phEnum, mdTypeDef rTypeDefs[],
+ ULONG cMax, ULONG *pcTypeDefs)
+ {
+ return m_pRawImport->EnumTypeDefs(phEnum, rTypeDefs, cMax, pcTypeDefs);
+ }
+
+ STDMETHODIMP EnumInterfaceImpls(HCORENUM *phEnum, mdTypeDef td,
+ mdInterfaceImpl rImpls[], ULONG cMax,
+ ULONG* pcImpls)
+ {
+ return m_pRawImport->EnumInterfaceImpls(phEnum, td, rImpls, cMax, pcImpls);
+ }
+
+ STDMETHODIMP EnumTypeRefs(HCORENUM *phEnum, mdTypeRef rTypeRefs[],
+ ULONG cMax, ULONG* pcTypeRefs)
+ {
+ // No trick needed: a previous call to EnumTypeRefs must have already taken care of the
+ // extra type refs.
+ return m_pRawImport->EnumTypeRefs(phEnum, rTypeRefs, cMax, pcTypeRefs);
+ }
+
+ STDMETHODIMP FindTypeDefByName( // S_OK or error.
+ LPCWSTR wszTypeDef, // [IN] Name of the Type.
+ mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef for Enclosing class.
+ mdTypeDef *ptd) // [OUT] Put the TypeDef token here.
+ {
+ if (wszTypeDef == NULL) // E_INVALIDARG on NULL szTypeDef (just like real RegMeta)
+ return E_INVALIDARG;
+ _ASSERTE(ptd != NULL); // AV on NULL ptd (just like real RegMeta)
+ *ptd = mdTypeDefNil;
+
+ LPUTF8 szFullName;
+ LPCUTF8 szNamespace;
+ LPCUTF8 szName;
+ // Convert the name to UTF8.
+ UTF8STR(wszTypeDef, szFullName);
+ ns::SplitInline(szFullName, szNamespace, szName);
+
+ return m_pWinMDAdapter->FindTypeDef(szNamespace, szName, tkEnclosingClass, ptd);
+ }
+
+
+ STDMETHODIMP GetScopeProps( // S_OK or error.
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR szName, // [OUT] Put the name here.
+ ULONG cchName, // [IN] Size of name buffer in wide chars.
+ ULONG *pchName, // [OUT] Put size of name (wide chars) here.
+ GUID *pmvid) // [OUT, OPTIONAL] Put MVID here.
+ {
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return m_pRawImport->GetScopeProps(szName, cchName, pchName, pmvid);
+ }
+
+
+ STDMETHODIMP GetModuleFromScope( // S_OK.
+ mdModule *pmd) // [OUT] Put mdModule token here.
+ {
+ return m_pRawImport->GetModuleFromScope(pmd);
+ }
+
+
+ STDMETHODIMP GetTypeDefProps( // S_OK or error.
+ mdTypeDef td, // [IN] TypeDef token for inquiry.
+ __out_ecount_part_opt(cchTypeDef, *pchTypeDef)
+ LPWSTR szTypeDef, // [OUT] Put name here.
+ ULONG cchTypeDef, // [IN] size of name buffer in wide chars.
+ ULONG *pchTypeDef, // [OUT] put size of name (wide chars) here.
+ DWORD *pdwTypeDefFlags, // [OUT] Put flags here.
+ mdToken *ptkExtends) // [OUT] Put base class TypeDef/TypeRef here.
+ {
+ HRESULT hr;
+ LPCSTR szNamespace;
+ LPCSTR szName;
+ if (!IsValidNonNilToken(td, mdtTypeDef))
+ {
+ // Idiosyncractic edge cases - delegate straight through to inherit the correct idiosyncractic edge results
+ return m_pRawImport->GetTypeDefProps(td, szTypeDef, cchTypeDef, pchTypeDef, pdwTypeDefFlags, ptkExtends);
+ }
+ IfFailRet(m_pWinMDAdapter->GetTypeDefProps(td, &szNamespace, &szName, pdwTypeDefFlags, ptkExtends));
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return DeliverUtf8NamespaceAndName(szNamespace, szName, szTypeDef, cchTypeDef, pchTypeDef);
+ }
+
+
+ STDMETHODIMP GetInterfaceImplProps( // S_OK or error.
+ mdInterfaceImpl iiImpl, // [IN] InterfaceImpl token.
+ mdTypeDef *pClass, // [OUT] Put implementing class token here.
+ mdToken *ptkIface) // [OUT] Put implemented interface token here.
+ {
+ return m_pRawImport->GetInterfaceImplProps(iiImpl, pClass, ptkIface);
+ }
+
+
+ STDMETHODIMP GetTypeRefProps( // S_OK or error.
+ mdTypeRef tr, // [IN] TypeRef token.
+ mdToken *ptkResolutionScope, // [OUT] Resolution scope, ModuleRef or AssemblyRef.
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR szName, // [OUT] Name of the TypeRef.
+ ULONG cchName, // [IN] Size of buffer.
+ ULONG *pchName) // [OUT] Size of Name.
+ {
+ HRESULT hr;
+ if (!IsValidNonNilToken(tr, mdtTypeRef))
+ {
+ // Idiosyncratic edge cases - delegate straight through to inherit the correct idiosyncratic edge result
+ return m_pRawImport->GetTypeRefProps(tr, ptkResolutionScope, szName, cchName, pchName);
+ }
+
+ LPCUTF8 szUtf8Namespace;
+ LPCUTF8 szUtf8Name;
+ mdToken tkResolutionScope;
+ IfFailRet(CommonGetTypeRefProps(tr, &szUtf8Namespace, &szUtf8Name, &tkResolutionScope));
+ if (ptkResolutionScope != NULL)
+ {
+ *ptkResolutionScope = tkResolutionScope;
+ }
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return DeliverUtf8NamespaceAndName(szUtf8Namespace, szUtf8Name, szName, cchName, pchName);
+ }
+
+
+ STDMETHODIMP ResolveTypeRef(mdTypeRef tr, REFIID riid, IUnknown **ppIScope, mdTypeDef *ptd)
+ {
+ WINMD_COMPAT_ASSERT("IMetaDataImport::ResolveTypeRef() not supported on .winmd files.");
+ return E_NOTIMPL;
+ }
+
+
+ STDMETHODIMP EnumMembers( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumMembers(phEnum, cl, rMembers, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP EnumMembersWithName( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdToken rMembers[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumMembersWithName(phEnum, cl, szName, rMembers, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP EnumMethods( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdMethodDef rMethods[], // [OUT] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumMethods(phEnum, cl, rMethods, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP EnumMethodsWithName( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdMethodDef rMethods[], // [OU] Put MethodDefs here.
+ ULONG cMax, // [IN] Max MethodDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumMethodsWithName(phEnum, cl, szName, rMethods, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP EnumFields( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ mdFieldDef rFields[], // [OUT] Put FieldDefs here.
+ ULONG cMax, // [IN] Max FieldDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumFields(phEnum, cl, rFields, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP EnumFieldsWithName( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef cl, // [IN] TypeDef to scope the enumeration.
+ LPCWSTR szName, // [IN] Limit results to those with this name.
+ mdFieldDef rFields[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumFieldsWithName(phEnum, cl, szName, rFields, cMax, pcTokens);
+ }
+
+
+
+ STDMETHODIMP EnumParams( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdParamDef rParams[], // [OUT] Put ParamDefs here.
+ ULONG cMax, // [IN] Max ParamDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumParams(phEnum, mb, rParams, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP EnumMemberRefs( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tkParent, // [IN] Parent token to scope the enumeration.
+ mdMemberRef rMemberRefs[], // [OUT] Put MemberRefs here.
+ ULONG cMax, // [IN] Max MemberRefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumMemberRefs(phEnum, tkParent, rMemberRefs, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP EnumMethodImpls( // S_OK, S_FALSE, or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdToken rMethodBody[], // [OUT] Put Method Body tokens here.
+ mdToken rMethodDecl[], // [OUT] Put Method Declaration tokens here.
+ ULONG cMax, // [IN] Max tokens to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ // Note: This wrapper emulates the underlying RegMeta's parameter validation semantics:
+ // Pass a bad phEnum: Instant AV
+ // Pass out of range typedef: returns S_FALSE (i.e. generates empty list)
+ // Pass non-typedef: returns S_FALSE with assert
+ // Pass bad output ptr for bodies or decls, AV
+ // Pass bad output ptr for pcTokens, AV (but NULL is allowed.)
+
+ HRESULT hr;
+ HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum);
+ HENUMInternal *pEnum = *ppmdEnum;
+
+ if ( pEnum == 0 )
+ {
+ // Create the enumerator, DynamicArrayEnum does not use the token type.
+ IfFailGo( HENUMInternal::CreateDynamicArrayEnum( (TBL_MethodImpl << 24), &pEnum) );
+ IfFailGo( m_pWinMDAdapter->AddMethodImplsToEnum(td, pEnum));
+ // set the output parameter
+ *ppmdEnum = pEnum;
+ }
+
+ // fill the output token buffer
+ hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethodBody, rMethodDecl, pcTokens);
+
+ ErrExit:
+ HENUMInternal::DestroyEnumIfEmpty(ppmdEnum);
+ return hr;
+ }
+
+
+ STDMETHODIMP EnumPermissionSets( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] if !NIL, token to scope the enumeration.
+ DWORD dwActions, // [IN] if !0, return only these actions.
+ mdPermission rPermission[], // [OUT] Put Permissions here.
+ ULONG cMax, // [IN] Max Permissions to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumPermissionSets(phEnum, tk, dwActions, rPermission, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP FindMember(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdToken *pmb) // [OUT] matching memberdef
+ {
+ HRESULT hr = FindMethod(
+ td,
+ szName,
+ pvSigBlob,
+ cbSigBlob,
+ pmb);
+
+ if (hr == CLDB_E_RECORD_NOTFOUND)
+ {
+ // now try field table
+ hr = FindField(
+ td,
+ szName,
+ pvSigBlob,
+ cbSigBlob,
+ pmb);
+ }
+
+ return hr;
+ }
+
+
+ STDMETHODIMP FindMethod(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmb) // [OUT] matching memberdef
+ {
+ if (pvSigBlob == NULL || cbSigBlob == 0)
+ {
+ // if signature matching is not needed, we can delegate to the underlying implementation
+ return m_pRawImport->FindMethod(td, szName, pvSigBlob, cbSigBlob, pmb);
+ }
+
+ // The following code emulates RegMeta::FindMethod. We cannot call the underlying
+ // implementation because we need to compare pvSigBlob to reinterpreted signatures.
+ HRESULT hr = S_OK;
+ HCORENUM hEnum = NULL;
+
+ CQuickBytes qbSig; // holds non-varargs signature
+ CQuickArray<WCHAR> rName;
+
+ if (szName == NULL || pmb == NULL)
+ IfFailGo(E_INVALIDARG);
+
+ *pmb = mdMethodDefNil;
+
+ // check to see if this is a vararg signature
+ PCCOR_SIGNATURE pvSigTemp = pvSigBlob;
+ if (isCallConv(CorSigUncompressCallingConv(pvSigTemp), IMAGE_CEE_CS_CALLCONV_VARARG))
+ {
+ // Get the fixed part of VARARG signature
+ IfFailGo(_GetFixedSigOfVarArg(pvSigBlob, cbSigBlob, &qbSig, &cbSigBlob));
+ pvSigBlob = (PCCOR_SIGNATURE) qbSig.Ptr();
+ }
+
+ // now iterate all methods in td and compare name and signature
+ IfNullGo(rName.AllocNoThrow(wcslen(szName) + 1));
+
+ mdMethodDef md;
+ ULONG count;
+ while ((hr = EnumMethods(&hEnum, td, &md, 1, &count)) == S_OK)
+ {
+ PCCOR_SIGNATURE pvMethodSigBlob;
+ ULONG cbMethodSigBlob;
+ ULONG chMethodName;
+ DWORD dwMethodAttr;
+
+ IfFailGo(GetMethodProps(md, NULL, rName.Ptr(), (ULONG)rName.Size(), &chMethodName, &dwMethodAttr, &pvMethodSigBlob, &cbMethodSigBlob, NULL, NULL));
+
+ if (chMethodName == rName.Size() && wcscmp(szName, rName.Ptr()) == 0)
+ {
+ // we have a name match, check signature
+ if (cbSigBlob != cbMethodSigBlob || memcmp(pvSigBlob, pvMethodSigBlob, cbSigBlob) != 0)
+ continue;
+
+ // ignore PrivateScope methods
+ if (IsMdPrivateScope(dwMethodAttr))
+ continue;
+
+ // we found the method
+ *pmb = md;
+ goto ErrExit;
+ }
+ }
+ IfFailGo(hr);
+ hr = CLDB_E_RECORD_NOTFOUND;
+
+ ErrExit:
+ if (hEnum != NULL)
+ CloseEnum(hEnum);
+
+ return hr;
+ }
+
+
+ STDMETHODIMP FindField(
+ mdTypeDef td, // [IN] given typedef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdFieldDef *pmb) // [OUT] matching memberdef
+ {
+ if (pvSigBlob == NULL || cbSigBlob == 0)
+ {
+ // if signature matching is not needed, we can delegate to the underlying implementation
+ return m_pRawImport->FindField(td, szName, pvSigBlob, cbSigBlob, pmb);
+ }
+
+ // The following code emulates RegMeta::FindField. We cannot call the underlying
+ // implementation because we need to compare pvSigBlob to reinterpreted signatures.
+ HRESULT hr = S_OK;
+ HCORENUM hEnum = NULL;
+
+ CQuickArray<WCHAR> rName;
+
+ if (szName == NULL || pmb == NULL)
+ IfFailGo(E_INVALIDARG);
+
+ *pmb = mdFieldDefNil;
+
+ // now iterate all fields in td and compare name and signature
+ IfNullGo(rName.AllocNoThrow(wcslen(szName) + 1));
+
+ mdFieldDef fd;
+ ULONG count;
+ while ((hr = EnumFields(&hEnum, td, &fd, 1, &count)) == S_OK)
+ {
+ PCCOR_SIGNATURE pvFieldSigBlob;
+ ULONG cbFieldSigBlob;
+ ULONG chFieldName;
+ DWORD dwFieldAttr;
+
+ IfFailGo(GetFieldProps(fd, NULL, rName.Ptr(), (ULONG)rName.Size(), &chFieldName, &dwFieldAttr, &pvFieldSigBlob, &cbFieldSigBlob, NULL, NULL, NULL));
+
+ if (chFieldName == rName.Size() && wcscmp(szName, rName.Ptr()) == 0)
+ {
+ // we have a name match, check signature
+ if (cbSigBlob != cbFieldSigBlob || memcmp(pvSigBlob, pvFieldSigBlob, cbSigBlob) != 0)
+ continue;
+
+ // ignore PrivateScope fields
+ if (IsFdPrivateScope(dwFieldAttr))
+ continue;
+
+ // we found the field
+ *pmb = fd;
+ goto ErrExit;
+ }
+ }
+ IfFailGo(hr);
+ hr = CLDB_E_RECORD_NOTFOUND;
+
+ ErrExit:
+ if (hEnum != NULL)
+ CloseEnum(hEnum);
+
+ return hr;
+ }
+
+
+ STDMETHODIMP FindMemberRef(
+ mdTypeRef td, // [IN] given typeRef
+ LPCWSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMemberRef *pmr) // [OUT] matching memberref
+ {
+ if (pvSigBlob == NULL || cbSigBlob == 0)
+ {
+ // if signature matching is not needed, we can delegate to the underlying implementation
+ return m_pRawImport->FindMemberRef(td, szName, pvSigBlob, cbSigBlob, pmr);
+ }
+
+ // The following code emulates RegMeta::FindMemberRef. We cannot call the underlying
+ // implementation because we need to compare pvSigBlob to reinterpreted signatures.
+ HRESULT hr = S_OK;
+ HCORENUM hEnum = NULL;
+
+ CQuickArray<WCHAR> rName;
+
+ if (szName == NULL || pmr == NULL)
+ IfFailGo(CLDB_E_RECORD_NOTFOUND);
+
+ *pmr = mdMemberRefNil;
+
+ // now iterate all members in td and compare name and signature
+ IfNullGo(rName.AllocNoThrow(wcslen(szName) + 1));
+
+ mdMemberRef rd;
+ ULONG count;
+ while ((hr = EnumMemberRefs(&hEnum, td, &rd, 1, &count)) == S_OK)
+ {
+ PCCOR_SIGNATURE pvMemberSigBlob;
+ ULONG cbMemberSigBlob;
+ ULONG chMemberName;
+
+ IfFailGo(GetMemberRefProps(rd, NULL, rName.Ptr(), (ULONG)rName.Size(), &chMemberName, &pvMemberSigBlob, &cbMemberSigBlob));
+
+ if (chMemberName == rName.Size() && wcscmp(szName, rName.Ptr()) == 0)
+ {
+ // we have a name match, check signature
+ if (cbSigBlob != cbMemberSigBlob || memcmp(pvSigBlob, pvMemberSigBlob, cbSigBlob) != 0)
+ continue;
+
+ // we found the member
+ *pmr = rd;
+ goto ErrExit;
+ }
+ }
+ IfFailGo(hr);
+ hr = CLDB_E_RECORD_NOTFOUND;
+
+ ErrExit:
+ if (hEnum != NULL)
+ CloseEnum(hEnum);
+
+ return hr;
+ }
+
+
+ STDMETHODIMP GetMethodProps(
+ mdMethodDef mb, // The method for which to get props.
+ mdTypeDef *pClass, // Put method's class here.
+ __out_ecount_part_opt(cchMethod, *pchMethod)
+ LPWSTR szMethod, // Put method's name here.
+ ULONG cchMethod, // Size of szMethod buffer in wide chars.
+ ULONG *pchMethod, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG *pulCodeRVA, // [OUT] codeRVA
+ DWORD *pdwImplFlags) // [OUT] Impl. Flags
+ {
+ HRESULT hr;
+ HRESULT hrNameTruncation;
+ if (!IsValidNonNilToken(mb, mdtMethodDef))
+ {
+ // Idiosyncratic edge cases - delegate straight through to inherit the correct idiosyncratic edge result
+ return m_pRawImport->GetMethodProps(mb, pClass, szMethod, cchMethod, pchMethod, pdwAttr, ppvSigBlob, pcbSigBlob, pulCodeRVA, pdwImplFlags);
+ }
+
+ ULONG cbOrigSigBlob = (ULONG)(-1);
+ PCCOR_SIGNATURE pOrigSig = NULL;
+ IfFailRet(hrNameTruncation = m_pRawImport->GetMethodProps(mb, pClass, szMethod, cchMethod, pchMethod, pdwAttr, &pOrigSig, &cbOrigSigBlob, pulCodeRVA, pdwImplFlags));
+
+ IfFailRet((m_pWinMDAdapter->GetSignatureForToken<IMetaDataImport2, mdtMethodDef>(
+ mb,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSigBlob, // pcbOrigSig
+ ppvSigBlob,
+ pcbSigBlob,
+ m_pRawImport)));
+
+ LPCSTR szNewName = NULL;
+ IfFailRet(m_pWinMDAdapter->ModifyMethodProps(mb, pdwAttr, pdwImplFlags, pulCodeRVA, &szNewName));
+ if (szNewName != NULL)
+ {
+ // We want to return name truncation status from the method that really fills the output buffer, rewrite the previous value
+ IfFailRet(hrNameTruncation = DeliverUtf8String(szNewName, szMethod, cchMethod, pchMethod));
+ }
+
+ // Return the success code from name filling (S_OK or CLDB_S_TRUNCATION)
+ return hrNameTruncation;
+ }
+
+
+ STDMETHODIMP GetMemberRefProps( // S_OK or error.
+ mdMemberRef mr, // [IN] given memberref
+ mdToken *ptk, // [OUT] Put classref or classdef here.
+ __out_ecount_part_opt(cchMember, *pchMember)
+ LPWSTR szMember, // [OUT] buffer to fill for member's name
+ ULONG cchMember, // [IN] the count of char of szMember
+ ULONG *pchMember, // [OUT] actual count of char in member name
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to meta data blob value
+ ULONG *pbSig) // [OUT] actual size of signature blob
+ {
+ HRESULT hr = S_OK;
+ HRESULT hrNameTruncation;
+
+ ULONG cbOrigSigBlob = (ULONG)(-1);
+ PCCOR_SIGNATURE pOrigSig = NULL;
+ IfFailRet(hrNameTruncation = m_pRawImport->GetMemberRefProps(mr, ptk, szMember, cchMember, pchMember, &pOrigSig, &cbOrigSigBlob));
+ LPCSTR szNewName = NULL;
+ IfFailRet(m_pWinMDAdapter->ModifyMemberProps(mr, NULL, NULL, NULL, &szNewName));
+ IfFailRet((m_pWinMDAdapter->GetSignatureForToken<IMetaDataImport2, mdtMemberRef>(
+ mr,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSigBlob, // pcbOrigSig
+ ppvSigBlob,
+ pbSig,
+ m_pRawImport)));
+ if (szNewName != NULL)
+ {
+ // We want to return name truncation status from the method that really fills the output buffer, rewrite the previous value
+ IfFailRet(hrNameTruncation = DeliverUtf8String(szNewName, szMember, cchMember, pchMember));
+ }
+ // Return the success code from name filling (S_OK or CLDB_S_TRUNCATION)
+ return hrNameTruncation;
+ }
+
+
+ STDMETHODIMP EnumProperties( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdProperty rProperties[], // [OUT] Put Properties here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcProperties) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumProperties(phEnum, td, rProperties, cMax, pcProperties);
+ }
+
+
+ STDMETHODIMP EnumEvents( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdTypeDef td, // [IN] TypeDef to scope the enumeration.
+ mdEvent rEvents[], // [OUT] Put events here.
+ ULONG cMax, // [IN] Max events to put.
+ ULONG *pcEvents) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumEvents(phEnum, td, rEvents, cMax, pcEvents);
+ }
+
+
+ STDMETHODIMP GetEventProps( // S_OK, S_FALSE, or error.
+ mdEvent ev, // [IN] event token
+ mdTypeDef *pClass, // [OUT] typedef containing the event declarion.
+ LPCWSTR szEvent, // [OUT] Event name
+ ULONG cchEvent, // [IN] the count of wchar of szEvent
+ ULONG *pchEvent, // [OUT] actual count of wchar for event's name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType, // [OUT] EventType class
+ mdMethodDef *pmdAddOn, // [OUT] AddOn method of the event
+ mdMethodDef *pmdRemoveOn, // [OUT] RemoveOn method of the event
+ mdMethodDef *pmdFire, // [OUT] Fire method of the event
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the event
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod) // [OUT] total number of other method of this event
+ {
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return m_pRawImport->GetEventProps(ev, pClass, szEvent, cchEvent, pchEvent, pdwEventFlags, ptkEventType, pmdAddOn, pmdRemoveOn, pmdFire, rmdOtherMethod, cMax, pcOtherMethod);
+ }
+
+
+ STDMETHODIMP EnumMethodSemantics( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdMethodDef mb, // [IN] MethodDef to scope the enumeration.
+ mdToken rEventProp[], // [OUT] Put Event/Property here.
+ ULONG cMax, // [IN] Max properties to put.
+ ULONG *pcEventProp) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumMethodSemantics(phEnum, mb, rEventProp, cMax, pcEventProp);
+ }
+
+ STDMETHODIMP GetMethodSemantics( // S_OK, S_FALSE, or error.
+ mdMethodDef mb, // [IN] method token
+ mdToken tkEventProp, // [IN] event/property token.
+ DWORD *pdwSemanticsFlags) // [OUT] the role flags for the method/propevent pair
+ {
+ return m_pRawImport->GetMethodSemantics(mb, tkEventProp, pdwSemanticsFlags);
+ }
+
+
+ STDMETHODIMP GetClassLayout(
+ mdTypeDef td, // [IN] give typedef
+ DWORD *pdwPackSize, // [OUT] 1, 2, 4, 8, or 16
+ COR_FIELD_OFFSET rFieldOffset[], // [OUT] field offset array
+ ULONG cMax, // [IN] size of the array
+ ULONG *pcFieldOffset, // [OUT] needed array size
+ ULONG *pulClassSize) // [OUT] the size of the class
+ {
+ return m_pRawImport->GetClassLayout(td, pdwPackSize, rFieldOffset, cMax, pcFieldOffset, pulClassSize);
+ }
+
+
+ STDMETHODIMP GetFieldMarshal(
+ mdToken tk, // [IN] given a field's memberdef
+ PCCOR_SIGNATURE *ppvNativeType, // [OUT] native type of this field
+ ULONG *pcbNativeType) // [OUT] the count of bytes of *ppvNativeType
+ {
+ return m_pRawImport->GetFieldMarshal(tk, ppvNativeType, pcbNativeType);
+ }
+
+
+ STDMETHODIMP GetRVA( // S_OK or error.
+ mdToken tk, // Member for which to set offset
+ ULONG *pulCodeRVA, // The offset
+ DWORD *pdwImplFlags) // the implementation flags
+ {
+ HRESULT hr;
+ if (!IsValidNonNilToken(tk, mdtMethodDef))
+ {
+ // Idiosyncratic edge cases - delegate straight through to inherit the correct idiosyncratic edge result
+ return m_pRawImport->GetRVA(tk, pulCodeRVA, pdwImplFlags);
+ }
+
+ IfFailRet(m_pRawImport->GetRVA(tk, pulCodeRVA, pdwImplFlags));
+ IfFailRet(m_pWinMDAdapter->ModifyMethodProps(tk, NULL, pdwImplFlags, pulCodeRVA, NULL));
+ return hr;
+ }
+
+
+ STDMETHODIMP 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.
+ {
+ return m_pRawImport->GetPermissionSetProps(pm, pdwAction, ppvPermission, pcbPermission);
+ }
+
+
+ STDMETHODIMP GetSigFromToken( // S_OK or error.
+ mdSignature mdSig, // [IN] Signature token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token.
+ ULONG *pcbSig) // [OUT] return size of signature.
+ {
+ // mdSignature is not part of public WinMD surface, so it does not need signature rewriting
+ return m_pRawImport->GetSigFromToken(mdSig, ppvSig, pcbSig);
+ }
+
+ STDMETHODIMP GetModuleRefProps( // S_OK or error.
+ mdModuleRef mur, // [IN] moduleref token.
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR szName, // [OUT] buffer to fill with the moduleref name.
+ ULONG cchName, // [IN] size of szName in wide characters.
+ ULONG *pchName) // [OUT] actual count of characters in the name.
+ {
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return m_pRawImport->GetModuleRefProps(mur, szName, cchName, pchName);
+ }
+
+
+ STDMETHODIMP EnumModuleRefs( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdModuleRef rModuleRefs[], // [OUT] put modulerefs here.
+ ULONG cmax, // [IN] max memberrefs to put.
+ ULONG *pcModuleRefs) // [OUT] put # put here.
+ {
+ return m_pRawImport->EnumModuleRefs(phEnum, rModuleRefs, cmax, pcModuleRefs);
+ }
+
+
+ STDMETHODIMP GetTypeSpecFromToken( // S_OK or error.
+ mdTypeSpec typespec, // [IN] TypeSpec token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to TypeSpec signature
+ ULONG *pcbSig) // [OUT] return size of signature.
+ {
+ return m_pWinMDAdapter->GetSignatureForToken<IMetaDataImport2, mdtTypeSpec>(
+ typespec,
+ NULL, // ppOrigSig
+ NULL, // pcbOrigSig
+ ppvSig,
+ pcbSig,
+ m_pRawImport);
+ }
+
+
+ STDMETHODIMP GetNameFromToken( // Not Recommended! May be removed!
+ mdToken tk, // [IN] Token to get name from. Must have a name.
+ MDUTF8CSTR *pszUtf8NamePtr) // [OUT] Return pointer to UTF8 name in heap.
+ {
+ HRESULT hr;
+
+ if (!IsValidNonNilToken(tk, mdtTypeRef))
+ {
+ // Handle corner error case
+ IfFailGo(m_pRawImport->GetNameFromToken(tk, pszUtf8NamePtr));
+ }
+ else
+ {
+ IfFailGo(m_pWinMDAdapter->GetTypeRefProps(tk, NULL, pszUtf8NamePtr, NULL));
+ }
+
+ ErrExit:
+ return hr;
+ }
+
+
+ STDMETHODIMP EnumUnresolvedMethods( // S_OK, S_FALSE, or error.
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken rMethods[], // [OUT] Put MemberDefs here.
+ ULONG cMax, // [IN] Max MemberDefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumUnresolvedMethods(phEnum, rMethods, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP GetUserString( // S_OK or error.
+ mdString stk, // [IN] String token.
+ __out_ecount_part_opt(cchString, *pchString)
+ LPWSTR szString, // [OUT] Copy of string.
+ ULONG cchString, // [IN] Max chars of room in szString.
+ ULONG *pchString) // [OUT] How many chars in actual string.
+ {
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return m_pRawImport->GetUserString(stk, szString, cchString, pchString);
+ }
+
+
+ STDMETHODIMP GetPinvokeMap( // S_OK or error.
+ mdToken tk, // [IN] FieldDef or MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ __out_ecount_part_opt(cchImportName, *pchImportName)
+ LPWSTR szImportName, // [OUT] Import name.
+ ULONG cchImportName, // [IN] Size of the name buffer.
+ ULONG *pchImportName, // [OUT] Actual number of characters stored.
+ mdModuleRef *pmrImportDLL) // [OUT] ModuleRef token for the target DLL.
+ {
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return m_pRawImport->GetPinvokeMap(tk, pdwMappingFlags, szImportName, cchImportName, pchImportName, pmrImportDLL);
+ }
+
+
+ STDMETHODIMP EnumSignatures( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdSignature rSignatures[], // [OUT] put signatures here.
+ ULONG cmax, // [IN] max signatures to put.
+ ULONG *pcSignatures) // [OUT] put # put here.
+ {
+ return m_pRawImport->EnumSignatures(phEnum, rSignatures, cmax, pcSignatures);
+ }
+
+
+ STDMETHODIMP EnumTypeSpecs( // S_OK or error.
+ HCORENUM *phEnum, // [IN|OUT] pointer to the enum.
+ mdTypeSpec rTypeSpecs[], // [OUT] put TypeSpecs here.
+ ULONG cmax, // [IN] max TypeSpecs to put.
+ ULONG *pcTypeSpecs) // [OUT] put # put here.
+ {
+ return m_pRawImport->EnumTypeSpecs(phEnum, rTypeSpecs, cmax, pcTypeSpecs);
+ }
+
+
+ STDMETHODIMP EnumUserStrings( // S_OK or error.
+ HCORENUM *phEnum, // [IN/OUT] pointer to the enum.
+ mdString rStrings[], // [OUT] put Strings here.
+ ULONG cmax, // [IN] max Strings to put.
+ ULONG *pcStrings) // [OUT] put # put here.
+ {
+ return m_pRawImport->EnumUserStrings(phEnum, rStrings, cmax, pcStrings);
+ }
+
+
+ STDMETHODIMP GetParamForMethodIndex( // S_OK or error.
+ mdMethodDef md, // [IN] Method token.
+ ULONG ulParamSeq, // [IN] Parameter sequence.
+ mdParamDef *ppd) // [IN] Put Param token here.
+ {
+ return m_pRawImport->GetParamForMethodIndex(md, ulParamSeq, ppd);
+ }
+
+
+ STDMETHODIMP EnumCustomAttributes( // S_OK or error.
+ HCORENUM *phEnum, // [IN, OUT] COR enumerator.
+ mdToken tk, // [IN] Token to scope the enumeration, 0 for all.
+ mdToken tkType, // [IN] Type of interest, 0 for all.
+ mdCustomAttribute rCustomAttributes[], // [OUT] Put custom attribute tokens here.
+ ULONG cMax, // [IN] Size of rCustomAttributes.
+ ULONG *pcCustomAttributes) // [OUT, OPTIONAL] Put count of token values here.
+ {
+ return m_pRawImport->EnumCustomAttributes(phEnum, tk, tkType, rCustomAttributes, cMax, pcCustomAttributes);
+ }
+
+
+ STDMETHODIMP GetCustomAttributeProps( // S_OK or error.
+ mdCustomAttribute cv, // [IN] CustomAttribute token.
+ mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here.
+ mdToken *ptkType, // [OUT, OPTIONAL] Put AttrType token here.
+ void const **ppBlob, // [OUT, OPTIONAL] Put pointer to data here.
+ ULONG *pcbSize) // [OUT, OPTIONAL] Put size of date here.
+ {
+ HRESULT hr;
+ if (!IsValidNonNilToken(cv, mdtCustomAttribute))
+ {
+ // Idiosyncratic edge cases - delegate straight through to inherit the correct idiosyncratic edge result
+ return m_pRawImport->GetCustomAttributeProps(cv, ptkObj, ptkType, ppBlob, pcbSize);
+ }
+ IfFailRet(m_pRawImport->GetCustomAttributeProps(cv, ptkObj, ptkType, NULL, NULL));
+ IfFailRet(m_pWinMDAdapter->GetCustomAttributeBlob(cv, ppBlob, pcbSize));
+ return hr;
+ }
+
+
+ STDMETHODIMP FindTypeRef(
+ mdToken tkResolutionScope, // [IN] ModuleRef, AssemblyRef or TypeRef.
+ LPCWSTR wzTypeName, // [IN] TypeRef Name.
+ mdTypeRef *ptr) // [OUT] matching TypeRef.
+ {
+
+ HRESULT hr;
+ LPUTF8 szFullName;
+ LPCUTF8 szNamespace;
+ LPCUTF8 szName;
+
+ _ASSERTE(wzTypeName && ptr);
+ *ptr = mdTypeRefNil; // AV if caller passes NULL: just like the real RegMeta.
+
+ // Convert the name to UTF8.
+ PREFIX_ASSUME(wzTypeName != NULL); // caller might pass NULL, but they'll AV (just like the real RegMeta)
+ UTF8STR(wzTypeName, szFullName);
+ ns::SplitInline(szFullName, szNamespace, szName);
+ hr = m_pWinMDAdapter->FindTypeRef(szNamespace, szName, tkResolutionScope, ptr);
+
+ return hr;
+ }
+
+
+ STDMETHODIMP GetMemberProps(
+ mdToken mb, // The member for which to get props.
+ mdTypeDef *pClass, // Put member's class here.
+ __out_ecount_part_opt(cchMember, *pchMember)
+ LPWSTR szMember, // Put member's name here.
+ ULONG cchMember, // Size of szMember buffer in wide chars.
+ ULONG *pchMember, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ ULONG *pulCodeRVA, // [OUT] codeRVA
+ DWORD *pdwImplFlags, // [OUT] Impl. Flags
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppValue, // [OUT] constant value
+ ULONG *pcchValue) // [OUT] size of constant string in chars, 0 for non-strings.
+ {
+ HRESULT hr;
+ HRESULT hrNameTruncation;
+ if (!IsValidNonNilToken(mb, mdtMethodDef) && !IsValidNonNilToken(mb, mdtFieldDef))
+ {
+ // Idiosyncratic edge cases - delegate straight through to inherit the correct idiosyncratic edge result
+ return m_pRawImport->GetMemberProps(mb, pClass, szMember, cchMember, pchMember, pdwAttr, ppvSigBlob, pcbSigBlob, pulCodeRVA, pdwImplFlags, pdwCPlusTypeFlag, ppValue, pcchValue);
+ }
+
+ PCCOR_SIGNATURE pOrigSig;
+ ULONG cbOrigSig;
+
+ IfFailRet(hrNameTruncation = m_pRawImport->GetMemberProps(mb, pClass, szMember, cchMember, pchMember, pdwAttr, &pOrigSig, &cbOrigSig, pulCodeRVA, pdwImplFlags, pdwCPlusTypeFlag, ppValue, pcchValue));
+
+ LPCSTR szNewName = NULL;
+ IfFailRet(m_pWinMDAdapter->ModifyMemberProps(mb, pdwAttr, pdwImplFlags, pulCodeRVA, &szNewName));
+
+ if (IsValidNonNilToken(mb, mdtMethodDef))
+ {
+ IfFailRet((m_pWinMDAdapter->GetSignatureForToken<IMetaDataImport2, mdtMethodDef>(
+ mb,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSig, // pcbOrigSig
+ ppvSigBlob,
+ pcbSigBlob,
+ m_pRawImport)));
+ }
+ else if (IsValidNonNilToken(mb, mdtFieldDef))
+ {
+ IfFailRet((m_pWinMDAdapter->GetSignatureForToken<IMetaDataImport2, mdtFieldDef>(
+ mb,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSig, // pcbOrigSig
+ ppvSigBlob,
+ pcbSigBlob,
+ m_pRawImport)));
+ }
+ else
+ {
+ if (ppvSigBlob != NULL)
+ *ppvSigBlob = pOrigSig;
+
+ if (pcbSigBlob != NULL)
+ *pcbSigBlob = cbOrigSig;
+ }
+
+ if (szNewName != NULL)
+ {
+ // We want to return name truncation status from the method that really fills the output buffer, rewrite the previous value
+ IfFailRet(hrNameTruncation = DeliverUtf8String(szNewName, szMember, cchMember, pchMember));
+ }
+
+ // Return the success code from name filling (S_OK or CLDB_S_TRUNCATION)
+ return hrNameTruncation;
+ }
+
+
+ STDMETHODIMP GetFieldProps(
+ mdFieldDef mb, // The field for which to get props.
+ mdTypeDef *pClass, // Put field's class here.
+ __out_ecount_part_opt(cchField, *pchField)
+ LPWSTR szField, // Put field's name here.
+ ULONG cchField, // Size of szField buffer in wide chars.
+ ULONG *pchField, // Put actual size here
+ DWORD *pdwAttr, // Put flags here.
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob, // [OUT] actual size of signature blob
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppValue, // [OUT] constant value
+ ULONG *pcchValue) // [OUT] size of constant string in chars, 0 for non-strings.
+ {
+ HRESULT hr;
+ HRESULT hrNameTruncation;
+
+ PCCOR_SIGNATURE pOrigSig;
+ ULONG cbOrigSig;
+ IfFailRet(hrNameTruncation = m_pRawImport->GetFieldProps(mb, pClass, szField, cchField, pchField, pdwAttr, &pOrigSig, &cbOrigSig, pdwCPlusTypeFlag, ppValue, pcchValue));
+
+ IfFailRet(m_pWinMDAdapter->ModifyFieldDefProps(mb, pdwAttr));
+ IfFailRet((m_pWinMDAdapter->GetSignatureForToken<IMetaDataImport2, mdtFieldDef>(
+ mb,
+ &pOrigSig,
+ &cbOrigSig,
+ ppvSigBlob,
+ pcbSigBlob,
+ m_pRawImport)));
+
+ // Return the success code from name filling (S_OK or CLDB_S_TRUNCATION)
+ return hrNameTruncation;
+ }
+
+
+ STDMETHODIMP GetPropertyProps( // S_OK, S_FALSE, or error.
+ mdProperty prop, // [IN] property token
+ mdTypeDef *pClass, // [OUT] typedef containing the property declarion.
+ LPCWSTR szProperty, // [OUT] Property name
+ ULONG cchProperty, // [IN] the count of wchar of szProperty
+ ULONG *pchProperty, // [OUT] actual count of wchar for property name
+ DWORD *pdwPropFlags, // [OUT] property flags.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob
+ ULONG *pbSig, // [OUT] count of bytes in *ppvSig
+ DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_*
+ UVCP_CONSTANT *ppDefaultValue, // [OUT] constant value
+ ULONG *pcchDefaultValue, // [OUT] size of constant string in chars, 0 for non-strings.
+ mdMethodDef *pmdSetter, // [OUT] setter method of the property
+ mdMethodDef *pmdGetter, // [OUT] getter method of the property
+ mdMethodDef rmdOtherMethod[], // [OUT] other method of the property
+ ULONG cMax, // [IN] size of rmdOtherMethod
+ ULONG *pcOtherMethod) // [OUT] total number of other method of this property
+ {
+ HRESULT hr = S_OK;
+ HRESULT hrNameTruncation;
+
+ ULONG cbOrigSigBlob = (ULONG)(-1);
+ PCCOR_SIGNATURE pOrigSig = NULL;
+ IfFailRet(hrNameTruncation = m_pRawImport->GetPropertyProps(prop, pClass, szProperty, cchProperty, pchProperty, pdwPropFlags, &pOrigSig, &cbOrigSigBlob, pdwCPlusTypeFlag, ppDefaultValue, pcchDefaultValue, pmdSetter, pmdGetter, rmdOtherMethod, cMax, pcOtherMethod));
+
+ IfFailRet((m_pWinMDAdapter->GetSignatureForToken<IMetaDataImport2, mdtProperty>(
+ prop,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSigBlob, // pcbOrigSig
+ ppvSig,
+ pbSig,
+ m_pRawImport)));
+ // Return the success code from name filling (S_OK or CLDB_S_TRUNCATION)
+ return hrNameTruncation;
+ }
+
+
+ STDMETHODIMP GetParamProps( // S_OK or error.
+ mdParamDef tk, // [IN]The Parameter.
+ mdMethodDef *pmd, // [OUT] Parent Method token.
+ ULONG *pulSequence, // [OUT] Parameter sequence.
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR szName, // [OUT] Put name here.
+ ULONG cchName, // [OUT] Size of name buffer.
+ ULONG *pchName, // [OUT] Put actual size of name here.
+ DWORD *pdwAttr, // [OUT] Put flags here.
+ DWORD *pdwCPlusTypeFlag, // [OUT] Flag for value type. selected ELEMENT_TYPE_*.
+ UVCP_CONSTANT *ppValue, // [OUT] Constant value.
+ ULONG *pcchValue) // [OUT] size of constant string in chars, 0 for non-strings.
+ {
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return m_pRawImport->GetParamProps(tk, pmd, pulSequence, szName, cchName, pchName, pdwAttr, pdwCPlusTypeFlag, ppValue, pcchValue);
+ }
+
+
+ STDMETHODIMP GetCustomAttributeByName( // S_OK or error.
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCWSTR wszName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) // [OUT] Put size of data here.
+ {
+ HRESULT hr;
+ if (wszName == NULL)
+ {
+ hr = S_FALSE; // Questionable response but maintains compatibility with RegMeta
+ }
+ else
+ {
+ MAKE_UTF8PTR_FROMWIDE_NOTHROW(szName, wszName);
+ IfNullGo(szName);
+ IfFailGo(CommonGetCustomAttributeByName(tkObj, szName, ppData, pcbData));
+ }
+ ErrExit:
+ return hr;
+ }
+
+
+ STDMETHODIMP_(BOOL) IsValidToken( // True or False.
+ mdToken tk) // [IN] Given token.
+ {
+ mdToken tokenType = TypeFromToken(tk);
+ if (tokenType == mdtAssemblyRef)
+ return m_pWinMDAdapter->IsValidAssemblyRefToken(tk);
+
+ return m_pRawImport->IsValidToken(tk);
+ }
+
+
+ STDMETHODIMP GetNestedClassProps( // S_OK or error.
+ mdTypeDef tdNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptdEnclosingClass) // [OUT] EnclosingClass token.
+ {
+ return m_pRawImport->GetNestedClassProps(tdNestedClass, ptdEnclosingClass);
+ }
+
+
+ STDMETHODIMP GetNativeCallConvFromSig( // S_OK or error.
+ void const *pvSig, // [IN] Pointer to signature.
+ ULONG cbSig, // [IN] Count of signature bytes.
+ ULONG *pCallConv) // [OUT] Put calling conv here (see CorPinvokemap).
+ {
+ return m_pRawImport->GetNativeCallConvFromSig(pvSig, cbSig, pCallConv);
+ }
+
+
+ STDMETHODIMP IsGlobal( // S_OK or error.
+ mdToken pd, // [IN] Type, Field, or Method token.
+ int *pbGlobal) // [OUT] Put 1 if global, 0 otherwise.
+ {
+ return m_pRawImport->IsGlobal(pd, pbGlobal);
+ }
+
+ public:
+
+ // ========================================================
+ // IMetaDataWinMDImport methods
+ // ========================================================
+
+ // This method returns the RAW view of the metadata. Essentially removing the Adapter's projection support for the typeRef.
+ STDMETHODIMP GetUntransformedTypeRefProps(
+ mdTypeRef tr, // [IN] TypeRef token.
+ mdToken *ptkResolutionScope, // [OUT] Resolution scope, ModuleRef or AssemblyRef.
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR szName, // [OUT] Unprojected name of the TypeRef.
+ ULONG cchName, // [IN] Size of buffer.
+ ULONG *pchName) // [OUT] Size of Name.
+ {
+ // By-pass the call to the raw importer, removing the adapter layer.
+ return m_pRawImport->GetTypeRefProps(tr, ptkResolutionScope, szName, cchName, pchName);
+ }
+
+ //=========================================================
+ // IMetaDataImport2 methods
+ //=========================================================
+ STDMETHODIMP EnumGenericParams(
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] TypeDef or MethodDef whose generic parameters are requested
+ mdGenericParam rGenericParams[], // [OUT] Put GenericParams here.
+ ULONG cMax, // [IN] Max GenericParams to put.
+ ULONG *pcGenericParams) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumGenericParams(phEnum, tk, rGenericParams, cMax, pcGenericParams);
+ }
+
+
+ STDMETHODIMP GetGenericParamProps( // S_OK or error.
+ mdGenericParam gp, // [IN] GenericParam
+ ULONG *pulParamSeq, // [OUT] Index of the type parameter
+ DWORD *pdwParamFlags, // [OUT] Flags, for future use (e.g. variance)
+ mdToken *ptOwner, // [OUT] Owner (TypeDef or MethodDef)
+ DWORD *reserved, // [OUT] For future use (e.g. non-type parameters)
+ __out_ecount_part_opt(cchName, *pchName)
+ LPWSTR wzname, // [OUT] Put name here
+ ULONG cchName, // [IN] Size of buffer
+ ULONG *pchName) // [OUT] Put size of name (wide chars) here.
+ {
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return m_pRawImport->GetGenericParamProps(gp, pulParamSeq, pdwParamFlags, ptOwner, reserved, wzname, cchName, pchName);
+ }
+
+
+ STDMETHODIMP GetMethodSpecProps(
+ 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 = S_OK;
+
+ ULONG cbOrigSigBlob = (ULONG)(-1);
+ PCCOR_SIGNATURE pOrigSig = NULL;
+ IfFailRet(m_pRawImport->GetMethodSpecProps(mi, tkParent, &pOrigSig, &cbOrigSigBlob));
+
+ return m_pWinMDAdapter->GetSignatureForToken<IMetaDataImport2, mdtMethodSpec>(
+ mi,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSigBlob, // pcbOrigSig
+ ppvSigBlob,
+ pcbSigBlob,
+ m_pRawImport);
+ }
+
+
+ STDMETHODIMP EnumGenericParamConstraints(
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdGenericParam tk, // [IN] GenericParam whose constraints are requested
+ mdGenericParamConstraint rGenericParamConstraints[], // [OUT] Put GenericParamConstraints here.
+ ULONG cMax, // [IN] Max GenericParamConstraints to put.
+ ULONG *pcGenericParamConstraints) // [OUT] Put # put here.
+ {
+ return m_pRawImport->EnumGenericParamConstraints(phEnum, tk, rGenericParamConstraints, cMax, pcGenericParamConstraints);
+ }
+
+
+ STDMETHODIMP GetGenericParamConstraintProps( // S_OK or error.
+ mdGenericParamConstraint gpc, // [IN] GenericParamConstraint
+ mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained
+ mdToken *ptkConstraintType) // [OUT] TypeDef/Ref/Spec constraint
+ {
+ return m_pRawImport->GetGenericParamConstraintProps(gpc, ptGenericParam, ptkConstraintType);
+ }
+
+
+ STDMETHODIMP GetPEKind( // S_OK or error.
+ DWORD* pdwPEKind, // [OUT] The kind of PE (0 - not a PE)
+ DWORD* pdwMachine) // [OUT] Machine as defined in NT header
+ {
+ return m_pRawImport->GetPEKind(pdwPEKind, pdwMachine);
+ }
+
+
+ STDMETHODIMP GetVersionString( // S_OK or error.
+ __out_ecount_part_opt(ccBufSize, *pccBufSize)
+ LPWSTR pwzBuf, // [OUT] Put version string here.
+ DWORD ccBufSize, // [IN] size of the buffer, in wide chars
+ DWORD *pccBufSize) // [OUT] Size of the version string, wide chars, including terminating nul.
+ {
+ HRESULT hr;
+ LPCSTR szVersion;
+ IfFailRet(GetVersionString(&szVersion));
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return DeliverUtf8String(szVersion, pwzBuf, ccBufSize, pccBufSize);
+ }
+
+
+ STDMETHODIMP EnumMethodSpecs(
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdToken tk, // [IN] MethodDef or MemberRef whose MethodSpecs are requested
+ mdMethodSpec rMethodSpecs[], // [OUT] Put MethodSpecs here.
+ ULONG cMax, // [IN] Max tokens to put.
+ ULONG *pcMethodSpecs) // [OUT] Put actual count here.
+ {
+ return m_pRawImport->EnumMethodSpecs(phEnum, tk, rMethodSpecs, cMax, pcMethodSpecs);
+ }
+
+
+ public:
+ //=========================================================
+ // IMetaDataAssemblyImport methods
+ //=========================================================
+
+ STDMETHODIMP GetAssemblyProps( // S_OK or error.
+ 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.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with assembly's simply name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ DWORD *pdwAssemblyFlags) // [OUT] Flags.
+ {
+ return m_pRawAssemblyImport->GetAssemblyProps(mda, ppbPublicKey, pcbPublicKey, pulHashAlgId, szName, cchName, pchName, pMetaData, pdwAssemblyFlags);
+ }
+
+
+ STDMETHODIMP GetAssemblyRefProps( // S_OK or error.
+ 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.
+ __out_ecount_part_opt(cchName, *pchName)LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData.
+ const void **ppbHashValue, // [OUT] Hash blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ DWORD *pdwAssemblyRefFlags) // [OUT] Flags.
+ {
+ HRESULT hr;
+ HRESULT hrNameTruncation;
+
+ if (!IsValidNonNilToken(mdar, mdtAssemblyRef))
+ {
+ // Idiosyncratic edge cases - delegate straight through to inherit the correct idiosyncratic edge result
+ return m_pRawAssemblyImport->GetAssemblyRefProps(mdar, ppbPublicKeyOrToken, pcbPublicKeyOrToken, szName, cchName, pchName, pMetaData, ppbHashValue, pcbHashValue, pdwAssemblyRefFlags);
+ }
+
+ mdAssemblyRef md = mdar;
+ if (RidFromToken(md) > m_pWinMDAdapter->GetRawAssemblyRefCount())
+ {
+ // The extra framework assemblies we add references to should all have the
+ // same verion, key, culture, etc as those of mscorlib.
+ // So we retrieve the mscorlib properties and change the name.
+ md = m_pWinMDAdapter->GetAssemblyRefMscorlib();
+ }
+
+ IfFailRet(hrNameTruncation = m_pRawAssemblyImport->GetAssemblyRefProps(md, ppbPublicKeyOrToken, pcbPublicKeyOrToken, szName, cchName, pchName, pMetaData, ppbHashValue, pcbHashValue, pdwAssemblyRefFlags));
+
+ LPCSTR szNewName = nullptr;
+ USHORT *pusMajorVersion = nullptr;
+ USHORT *pusMinorVersion = nullptr;
+ USHORT *pusBuildNumber = nullptr;
+ USHORT *pusRevisionNumber = nullptr;
+
+ if (pMetaData != nullptr)
+ {
+ pusMajorVersion = &pMetaData->usMajorVersion;
+ pusMinorVersion = &pMetaData->usMinorVersion;
+ pusBuildNumber = &pMetaData->usBuildNumber;
+ pusRevisionNumber = &pMetaData->usRevisionNumber;
+ }
+
+ m_pWinMDAdapter->ModifyAssemblyRefProps(
+ mdar,
+ ppbPublicKeyOrToken,
+ pcbPublicKeyOrToken,
+ &szNewName,
+ pusMajorVersion,
+ pusMinorVersion,
+ pusBuildNumber,
+ pusRevisionNumber,
+ ppbHashValue,
+ pcbHashValue);
+
+ if (szNewName != nullptr)
+ {
+ IfFailRet(hrNameTruncation = DeliverUtf8String(szNewName, szName, cchName, pchName));
+ }
+
+ // Return the success code from name filling (S_OK or CLDB_S_TRUNCATION)
+ return hrNameTruncation;
+ }
+
+
+ STDMETHODIMP GetFileProps( // S_OK or error.
+ mdFile mdf, // [IN] The File for which to get the properties.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in 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.
+ {
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return m_pRawAssemblyImport->GetFileProps(mdf, szName, cchName, pchName, ppbHashValue, pcbHashValue, pdwFileFlags);
+ }
+
+
+ STDMETHODIMP GetExportedTypeProps( // S_OK or error.
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef or mdExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags) // [OUT] Flags.
+ {
+ HRESULT hr;
+ LPCSTR szUtf8Namespace;
+ LPCSTR szUtf8Name;
+ if (!IsValidNonNilToken(mdct, mdtExportedType))
+ {
+ // Idiosyncractic edge cases - delegate straight through to inherit the correct idiosyncractic edge results
+ return m_pRawAssemblyImport->GetExportedTypeProps(mdct, szName, cchName, pchName, ptkImplementation, ptkTypeDef, pdwExportedTypeFlags);
+ }
+ IfFailRet(m_pRawAssemblyImport->GetExportedTypeProps(mdct, NULL, 0, NULL, NULL, ptkTypeDef, pdwExportedTypeFlags));
+ IfFailRet(this->CommonGetExportedTypeProps(mdct, &szUtf8Namespace, &szUtf8Name, ptkImplementation));
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return DeliverUtf8NamespaceAndName(szUtf8Namespace, szUtf8Name, szName, cchName, pchName);
+ }
+
+
+ STDMETHODIMP GetManifestResourceProps( // S_OK or error.
+ mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties.
+ __out_ecount_part_opt(cchName, *pchName)LPWSTR szName, // [OUT] Buffer to fill with name.
+ ULONG cchName, // [IN] Size of buffer in wide chars.
+ ULONG *pchName, // [OUT] Actual # of wide chars in name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ManifestResource.
+ DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file.
+ DWORD *pdwResourceFlags) // [OUT] Flags.
+ {
+ // Returns error code from name filling (may be CLDB_S_TRUNCTATION)
+ return m_pRawAssemblyImport->GetManifestResourceProps(mdmr, szName, cchName, pchName, ptkImplementation, pdwOffset, pdwResourceFlags);
+ }
+
+
+ STDMETHODIMP EnumAssemblyRefs( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdAssemblyRef rAssemblyRefs[], // [OUT] Put AssemblyRefs here.
+ ULONG cMax, // [IN] Max AssemblyRefs to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ if (*phEnum != NULL)
+ {
+ // No trick needed: a previous call to EnumAssemblyRefs must have already taken care of the
+ // extra assembly refs.
+ return m_pRawAssemblyImport->EnumAssemblyRefs(phEnum, rAssemblyRefs, cMax, pcTokens);
+ }
+ else
+ {
+ // if *phEnum is NULL, we need create the HENUMInternal, adjust the assembly ref count,
+ // and enumerate the number of assembly refs requested. This is done in three steps:
+ HRESULT hr;
+
+ // Step 1: Call EnumAssemblyRefs with an empty buffer to create the HENUMInternal
+ IfFailGo(m_pRawAssemblyImport->EnumAssemblyRefs(phEnum, NULL, 0, NULL));
+
+ // Step 2: Increment the cound to include the extra assembly refs
+ HENUMInternal *phInternalEnum = static_cast<HENUMInternal*>(*phEnum);
+
+ _ASSERTE(phInternalEnum->m_EnumType == MDSimpleEnum);
+
+ _ASSERTE( phInternalEnum->m_ulCount == m_pWinMDAdapter->GetRawAssemblyRefCount());
+ int n = m_pWinMDAdapter->GetExtraAssemblyRefCount();
+ phInternalEnum->m_ulCount += n;
+ phInternalEnum->u.m_ulEnd += n;
+
+ // Step 3: Call EnumAssemblyRefs again and pass in the modifed HENUMInternal and the real buffer
+ IfFailGo(m_pRawAssemblyImport->EnumAssemblyRefs(phEnum, rAssemblyRefs, cMax, pcTokens));
+
+ErrExit:
+ return hr;
+ }
+ }
+
+
+ STDMETHODIMP EnumFiles( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdFile rFiles[], // [OUT] Put Files here.
+ ULONG cMax, // [IN] Max Files to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawAssemblyImport->EnumFiles(phEnum, rFiles, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP EnumExportedTypes( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdExportedType rExportedTypes[], // [OUT] Put ExportedTypes here.
+ ULONG cMax, // [IN] Max ExportedTypes to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawAssemblyImport->EnumExportedTypes(phEnum, rExportedTypes, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP EnumManifestResources( // S_OK or error
+ HCORENUM *phEnum, // [IN|OUT] Pointer to the enum.
+ mdManifestResource rManifestResources[], // [OUT] Put ManifestResources here.
+ ULONG cMax, // [IN] Max Resources to put.
+ ULONG *pcTokens) // [OUT] Put # put here.
+ {
+ return m_pRawAssemblyImport->EnumManifestResources(phEnum, rManifestResources, cMax, pcTokens);
+ }
+
+
+ STDMETHODIMP GetAssemblyFromScope( // S_OK or error
+ mdAssembly *ptkAssembly) // [OUT] Put token here.
+ {
+ return m_pRawAssemblyImport->GetAssemblyFromScope(ptkAssembly);
+ }
+
+
+ STDMETHODIMP FindExportedTypeByName( // S_OK or error
+ LPCWSTR wszName, // [IN] Name of the ExportedType.
+ mdToken mdtExportedType, // [IN] ExportedType for the enclosing class.
+ mdExportedType *ptkExportedType) // [OUT] Put the ExportedType token here.
+ {
+ if (wszName == NULL)
+ return E_INVALIDARG;
+
+ LPUTF8 szFullName;
+ LPCUTF8 szNamespace;
+ LPCUTF8 szName;
+ // Convert the name to UTF8.
+ UTF8STR(wszName, szFullName);
+ ns::SplitInline(szFullName, szNamespace, szName);
+ return this->CommonFindExportedType(szNamespace, szName, mdtExportedType, ptkExportedType);
+ }
+
+
+ STDMETHODIMP FindManifestResourceByName( // S_OK or error
+ LPCWSTR szName, // [IN] Name of the ManifestResource.
+ mdManifestResource *ptkManifestResource) // [OUT] Put the ManifestResource token here.
+ {
+ return m_pRawAssemblyImport->FindManifestResourceByName(szName, ptkManifestResource);
+ }
+
+
+
+ STDMETHODIMP FindAssembliesByName( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
+ IUnknown *ppIUnk[], // [OUT] put IMetaDataAssemblyImport pointers here
+ ULONG cMax, // [IN] The max number to put
+ ULONG *pcAssemblies) // [OUT] The number of assemblies returned.
+ {
+ return m_pRawAssemblyImport->FindAssembliesByName(szAppBase, szPrivateBin, szAssemblyName, ppIUnk, cMax, pcAssemblies);
+ }
+
+ //=========================================================
+ // IMetaDataValidate
+ //=========================================================
+ STDMETHODIMP ValidatorInit( // S_OK or error.
+ DWORD dwModuleType, // [IN] Specifies the type of the module.
+ IUnknown *pUnk) // [IN] Validation error handler.
+ {
+ if (m_pRawValidate == nullptr)
+ return E_NOTIMPL;
+
+ return m_pRawValidate->ValidatorInit(dwModuleType, pUnk);
+ }
+
+ STDMETHODIMP ValidateMetaData() // S_OK or error.
+ {
+ if (m_pRawValidate == nullptr)
+ return E_NOTIMPL;
+
+ return m_pRawValidate->ValidateMetaData();
+ }
+
+ //=========================================================
+ // IMDCommon methods
+ //=========================================================
+ STDMETHODIMP_(IMetaModelCommon*) GetMetaModelCommon()
+ {
+ return this;
+ }
+
+ STDMETHODIMP_(IMetaModelCommonRO*) GetMetaModelCommonRO()
+ {
+ _ASSERTE(!"WinMDImport does not support GetMetaModelCommonRO(). The most likely cause of this assert is that you're trying to wrap a WinMD adapter around another WinMD adapter.");
+ return NULL;
+ }
+
+ STDMETHODIMP GetVersionString(LPCSTR *pszVersionString)
+ {
+ return m_pWinMDAdapter->GetVersionString(pszVersionString);
+ }
+
+
+ //=========================================================
+ // IMetaModelCommon methods
+ //=========================================================
+ __checkReturn
+ virtual HRESULT CommonGetScopeProps(
+ LPCUTF8 *pszName,
+ GUID *pMvid)
+ {
+ return m_pRawMetaModelCommonRO->CommonGetScopeProps(pszName, pMvid);
+ }
+
+ __checkReturn
+ virtual HRESULT CommonGetTypeRefProps(
+ mdTypeRef tr,
+ LPCUTF8 *pszNamespace,
+ LPCUTF8 *pszName,
+ mdToken *ptkResolution)
+ {
+ return m_pWinMDAdapter->GetTypeRefProps(tr, pszNamespace, pszName, ptkResolution);
+ }
+
+
+ __checkReturn
+ virtual HRESULT CommonGetTypeDefProps(
+ mdTypeDef td,
+ LPCUTF8 *pszNameSpace,
+ LPCUTF8 *pszName,
+ DWORD *pdwFlags,
+ mdToken *pdwExtends,
+ ULONG *pMethodList)
+ {
+ HRESULT hr;
+ IfFailGo(m_pRawMetaModelCommonRO->CommonGetTypeDefProps(td, NULL, NULL, NULL, NULL, pMethodList));
+ IfFailGo(m_pWinMDAdapter->GetTypeDefProps(td, pszNameSpace, pszName, pdwFlags, pdwExtends));
+ ErrExit:
+ return hr;
+ }
+
+
+ __checkReturn
+ virtual HRESULT CommonGetTypeSpecProps(
+ mdTypeSpec ts,
+ PCCOR_SIGNATURE *ppvSig,
+ ULONG *pcbSig)
+ {
+ return m_pWinMDAdapter->GetSignatureForToken<IMetaDataImport2, mdtTypeSpec>(
+ ts,
+ NULL, // ppOrigSig
+ NULL, // pcbOrigSig
+ ppvSig,
+ pcbSig,
+ m_pRawImport);
+ }
+
+
+ __checkReturn
+ virtual HRESULT CommonGetEnclosingClassOfTypeDef(
+ mdTypeDef td,
+ mdTypeDef *ptkEnclosingTypeDef)
+ {
+ return m_pRawMetaModelCommonRO->CommonGetEnclosingClassOfTypeDef(td, ptkEnclosingTypeDef);
+ }
+
+
+ __checkReturn
+ virtual HRESULT CommonGetAssemblyProps(
+ USHORT *pusMajorVersion,
+ USHORT *pusMinorVersion,
+ USHORT *pusBuildNumber,
+ USHORT *pusRevisionNumber,
+ DWORD *pdwFlags,
+ const void **ppbPublicKey,
+ ULONG *pcbPublicKey,
+ LPCUTF8 *pszName,
+ LPCUTF8 *pszLocale)
+ {
+ return m_pRawMetaModelCommonRO->CommonGetAssemblyProps(pusMajorVersion, pusMinorVersion, pusBuildNumber, pusRevisionNumber, pdwFlags, ppbPublicKey, pcbPublicKey, pszName, pszLocale);
+ }
+
+
+ __checkReturn
+ virtual HRESULT CommonGetAssemblyRefProps(
+ mdAssemblyRef tkAssemRef,
+ USHORT *pusMajorVersion,
+ USHORT *pusMinorVersion,
+ USHORT *pusBuildNumber,
+ USHORT *pusRevisionNumber,
+ DWORD *pdwFlags,
+ const void **ppbPublicKeyOrToken,
+ ULONG *pcbPublicKeyOrToken,
+ LPCUTF8 *pszName,
+ LPCUTF8 *pszLocale,
+ const void **ppbHashValue,
+ ULONG *pcbHashValue)
+ {
+ HRESULT hr;
+
+ mdAssemblyRef md = tkAssemRef;
+ if (RidFromToken(md) > m_pWinMDAdapter->GetRawAssemblyRefCount())
+ {
+ // The extra framework assemblies we add references to should all have the
+ // same verion, key, culture, etc as those of mscorlib.
+ // So we retrieve the mscorlib properties and change the name.
+ md = m_pWinMDAdapter->GetAssemblyRefMscorlib();
+ }
+
+ IfFailRet(m_pRawMetaModelCommonRO->CommonGetAssemblyRefProps(
+ md,
+ pusMajorVersion,
+ pusMinorVersion,
+ pusBuildNumber,
+ pusRevisionNumber,
+ pdwFlags,
+ ppbPublicKeyOrToken,
+ pcbPublicKeyOrToken,
+ pszName,
+ pszLocale,
+ ppbHashValue,
+ pcbHashValue));
+
+ m_pWinMDAdapter->ModifyAssemblyRefProps(
+ tkAssemRef,
+ ppbPublicKeyOrToken,
+ pcbPublicKeyOrToken,
+ pszName,
+ pusMajorVersion,
+ pusMinorVersion,
+ pusBuildNumber,
+ pusRevisionNumber,
+ ppbHashValue,
+ pcbHashValue);
+
+ return hr;
+ }
+
+
+ __checkReturn
+ virtual HRESULT CommonGetModuleRefProps(
+ mdModuleRef tkModuleRef,
+ LPCUTF8 *pszName)
+ {
+ return m_pRawMetaModelCommonRO->CommonGetModuleRefProps(tkModuleRef, pszName);
+ }
+
+
+ __checkReturn
+ virtual HRESULT CommonFindExportedType(
+ LPCUTF8 szNamespace,
+ LPCUTF8 szName,
+ mdToken tkEnclosingType,
+ mdExportedType *ptkExportedType)
+ {
+ return m_pWinMDAdapter->FindExportedType(szNamespace, szName, tkEnclosingType, ptkExportedType);
+ }
+
+
+ __checkReturn
+ virtual HRESULT CommonGetExportedTypeProps(
+ mdToken tkExportedType,
+ LPCUTF8 *pszNamespace,
+ LPCUTF8 *pszName,
+ mdToken *ptkImpl)
+ {
+ HRESULT hr;
+ IfFailRet(m_pRawMetaModelCommonRO->CommonGetExportedTypeProps(tkExportedType, pszNamespace, pszName, ptkImpl));
+ IfFailRet(m_pWinMDAdapter->ModifyExportedTypeName(tkExportedType, pszNamespace, pszName));
+ return hr;
+ }
+
+
+ virtual int CommonIsRo()
+ {
+ return m_pRawMetaModelCommonRO->CommonIsRo();
+ }
+
+
+ __checkReturn
+ virtual HRESULT 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.
+ {
+ return m_pWinMDAdapter->GetCustomAttributeByName(tkObj, szName, ptkCA, ppData, pcbData);
+ }
+
+
+ __checkReturn
+ virtual HRESULT FindParentOfMethodHelper(mdMethodDef md, mdTypeDef *ptd)
+ {
+ return m_pRawMetaModelCommonRO->FindParentOfMethodHelper(md, ptd);
+ }
+
+
+
+ public:
+ //=========================================================
+ // IGetIMDInternalImport methods
+ //=========================================================
+ STDMETHODIMP GetIMDInternalImport(IMDInternalImport ** ppIMDInternalImport)
+ {
+ HRESULT hr = S_OK;
+ ReleaseHolder<IMDCommon> pMDCommon;
+ ReleaseHolder<IUnknown> pRawMDInternalImport;
+
+ *ppIMDInternalImport = NULL;
+ // Get the raw IMDInternalImport
+ IfFailGo(GetMDInternalInterfaceFromPublic(m_pRawImport, IID_IMDInternalImport, (LPVOID*)(&pRawMDInternalImport)));
+
+ // Create an adapter around the internal raw interface
+ IfFailGo(pRawMDInternalImport->QueryInterface(IID_IMDCommon, (LPVOID*)(&pMDCommon)));
+ IfFailGo(CreateWinMDInternalImportRO(pMDCommon, IID_IMDInternalImport, (void**)ppIMDInternalImport));
+
+ ErrExit:
+ return hr;
+ }
+
+
+ //=========================================================
+ // Private methods
+ //=========================================================
+
+ //------------------------------------------------------------------------------------------------------
+ // Deliver a result string (Unicode) to a caller's sized output buffer using the standard convention
+ // followed by all metadata api.
+ //------------------------------------------------------------------------------------------------------
+ static HRESULT DeliverUnicodeString(LPCWSTR wszResult, __out_ecount_part(cchCallerBuffer, *pchSizeNeeded) LPWSTR wszCallerBuffer, ULONG cchCallerBuffer, ULONG *pchSizeNeeded)
+ {
+ ULONG cchActual = (ULONG)(wcslen(wszResult) + 1);
+ if (pchSizeNeeded)
+ {
+ *pchSizeNeeded = cchActual;
+ }
+ if (wszCallerBuffer == NULL || cchCallerBuffer < cchActual)
+ {
+ if (wszCallerBuffer != NULL)
+ {
+ memcpy(wszCallerBuffer, wszResult, cchCallerBuffer * sizeof(WCHAR)); // If buffer too small, return truncated result to be compatible with metadata api conventions
+ if (cchCallerBuffer > 0)
+ { // null-terminate the truncated output string
+ wszCallerBuffer[cchCallerBuffer - 1] = W('\0');
+ }
+ }
+ return CLDB_S_TRUNCATION;
+ }
+ else
+ {
+ memcpy(wszCallerBuffer, wszResult, cchActual * sizeof(WCHAR));
+ return S_OK;
+ }
+ }
+
+ //------------------------------------------------------------------------------------------------------
+ // Deliver a result string (Utf8) to a caller's sized output buffer using the standard convention
+ // followed by all metadata api.
+ //------------------------------------------------------------------------------------------------------
+ static HRESULT DeliverUtf8String(LPCSTR szUtf8Result, __out_ecount_part(cchCallerBuffer, *pchSizeNeeded) LPWSTR wszCallerBuffer, ULONG cchCallerBuffer, ULONG *pchSizeNeeded)
+ {
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzResult, szUtf8Result);
+ if (wzResult == NULL)
+ return E_OUTOFMEMORY;
+ return DeliverUnicodeString(wzResult, wszCallerBuffer, cchCallerBuffer, pchSizeNeeded);
+ }
+
+ //------------------------------------------------------------------------------------------------------
+ // Combine a result namespace/name string pair (Utf8) to a Unicode fullname and deliver to a caller's
+ // sized output buffer using the standard convention followed by all metadata api.
+ //------------------------------------------------------------------------------------------------------
+ static HRESULT DeliverUtf8NamespaceAndName(LPCSTR szUtf8Namespace, LPCSTR szUtf8Name, __out_ecount_part(cchCallerBuffer, *pchSizeNeeded) LPWSTR wszCallerBuffer, ULONG cchCallerBuffer, ULONG *pchSizeNeeded)
+ {
+ HRESULT hr;
+ if (wszCallerBuffer != NULL || pchSizeNeeded != NULL)
+ {
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzNamespace, szUtf8Namespace);
+ IfNullRet(wzNamespace);
+ MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzName, szUtf8Name);
+ IfNullRet(wzName);
+
+ BOOL fTruncation = FALSE;
+ if (wszCallerBuffer != NULL)
+ {
+ fTruncation = !(ns::MakePath(wszCallerBuffer, cchCallerBuffer, wzNamespace, wzName));
+ if (fTruncation && (cchCallerBuffer > 0))
+ { // null-terminate the truncated output string
+ wszCallerBuffer[cchCallerBuffer - 1] = W('\0');
+ }
+ }
+ if (pchSizeNeeded != NULL)
+ {
+ if (fTruncation || (wszCallerBuffer == NULL))
+ {
+ *pchSizeNeeded = ns::GetFullLength(wzNamespace, wzName);
+ }
+ else
+ {
+ *pchSizeNeeded = (ULONG)(wcslen(wszCallerBuffer) + 1);
+ }
+ }
+ hr = fTruncation ? CLDB_S_TRUNCATION : S_OK;
+ }
+ else
+ {
+ hr = S_OK; // Caller did not request name back.
+ }
+ return hr;
+ }
+
+ private:
+ //=========================================================
+ // Private instance data
+ //=========================================================
+ IMDCommon *m_pRawMDCommon;
+ IMetaDataImport2 *m_pRawImport;
+ IMetaDataAssemblyImport *m_pRawAssemblyImport;
+ IMetaDataValidate *m_pRawValidate;
+ IMetaModelCommonRO *m_pRawMetaModelCommonRO;
+ IUnknown *m_pFreeThreadedMarshaler;
+ WinMDAdapter *m_pWinMDAdapter;
+ LONG m_cRef;
+
+}; // class WinMDImport
+
+
+
+//========================================================================================
+// Entrypoint called by IMetaDataDispenser::OpenScope()
+//========================================================================================
+HRESULT CreateWinMDImport(IMDCommon * pRawMDCommon, REFIID riid, /*[out]*/ void **ppWinMDImport)
+{
+ HRESULT hr;
+ *ppWinMDImport = NULL;
+ WinMDImport *pWinMDImport = NULL;
+ IfFailGo(WinMDImport::Create(pRawMDCommon, &pWinMDImport));
+ IfFailGo(pWinMDImport->QueryInterface(riid, ppWinMDImport));
+ hr = S_OK;
+ ErrExit:
+ if (pWinMDImport)
+ pWinMDImport->Release();
+ return hr;
+}
diff --git a/src/md/winmd/winmdinternalimportro.cpp b/src/md/winmd/winmdinternalimportro.cpp
new file mode 100644
index 0000000000..70a54b88b3
--- /dev/null
+++ b/src/md/winmd/winmdinternalimportro.cpp
@@ -0,0 +1,1804 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+
+#include "stdafx.h"
+#include "winmdinterfaces.h"
+#include "inc/adapter.h"
+
+
+#ifdef FEATURE_METADATA_INTERNAL_APIS
+
+
+__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
+
+
+//========================================================================================
+// This metadata importer is used internally by the runtime and exposes an
+// IMDInternalImport* on .winmd files. It applies a small number of on-the-fly
+// conversions to make the .winmd file look like a regular .NET assembly.
+//
+// All those places in src\vm where the runtime calls an IMDInternalImport*
+// pointer, it may now be talking to a WinMDInternalImportRO. Ideally, the
+// runtime will never know the difference (but this being an internal interface,
+// we may tolerate the occasional leakiness in the name of expediency.)
+//========================================================================================
+class WinMDInternalImportRO : public IMDInternalImport, IWinMDImport, IMetaModelCommon
+{
+ public:
+ //=========================================================
+ // Factory
+ //=========================================================
+ static HRESULT Create(IMDCommon *pRawMDCommon, WinMDInternalImportRO **ppWinMDInternalImportRO)
+ {
+ HRESULT hr;
+ *ppWinMDInternalImportRO = NULL;
+ WinMDInternalImportRO *pNewWinMDInternalImport = new (nothrow) WinMDInternalImportRO(pRawMDCommon);
+ IfFailGo(pRawMDCommon->QueryInterface(IID_IMDInternalImport, (void**)&(pNewWinMDInternalImport->m_pRawInternalImport)));
+ IfFailGo(WinMDAdapter::Create(pNewWinMDInternalImport->m_pRawMDCommon, &(pNewWinMDInternalImport->m_pWinMDAdapter)));
+ (*ppWinMDInternalImportRO = pNewWinMDInternalImport)->AddRef();
+ hr = S_OK;
+
+ ErrExit:
+ if (pNewWinMDInternalImport)
+ pNewWinMDInternalImport->Release();
+ return hr;
+ }
+
+ private:
+ //=========================================================
+ // Ctors, Dtors
+ //=========================================================
+ WinMDInternalImportRO(IMDCommon * pRawMDCommon)
+ : m_cRef(1)
+ , m_pWinMDAdapter(NULL)
+ , m_pRawInternalImport(NULL)
+ , m_pRawMDCommon(pRawMDCommon)
+ , m_pRawMetaModelCommon(pRawMDCommon->GetMetaModelCommon())
+ {
+ m_pRawMDCommon->AddRef();
+ }
+
+ //---------------------------------------------------------
+ ~WinMDInternalImportRO()
+ {
+ m_pRawMDCommon->Release();
+ m_pRawInternalImport->Release();
+ delete m_pWinMDAdapter;
+ }
+
+ public:
+ //=========================================================
+ // IUnknown methods
+ //=========================================================
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk)
+ {
+ *ppUnk = 0;
+ if (riid == IID_IUnknown || riid == IID_IWinMDImport)
+ *ppUnk = (IWinMDImport *) this;
+ else if (riid == IID_IMDInternalImport)
+ *ppUnk = (IMDInternalImport *) this;
+ else
+ {
+#ifndef DACCESS_COMPILE
+#ifdef _DEBUG
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_WinMD_AssertOnIllegalUsage))
+ {
+ if (riid == IID_IMDInternalImportENC)
+ _ASSERTE(!"WinMDInternalImportRO::QueryInterface(IID_IMDInternalImportENC) returning E_NOINTERFACE");
+ else if (riid == IID_IMarshal)
+ _ASSERTE(!"WinMDInternalImportRO::QueryInterface(IID_IMarshal) returning E_NOINTERFACE");
+ else
+ _ASSERTE(!"WinMDInternalImportRO::QueryInterface() returning E_NOINTERFACE");
+ }
+#endif //_DEBUG
+#endif
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+ //---------------------------------------------------------
+ STDMETHODIMP_(ULONG) AddRef(void)
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+ //---------------------------------------------------------
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (!cRef)
+ delete this;
+ return cRef;
+ }
+
+ public:
+ //=========================================================
+ // IWinMDImport methods
+ //=========================================================
+ STDMETHODIMP IsScenarioWinMDExp(BOOL *pbResult)
+ {
+ if (pbResult == NULL)
+ return E_POINTER;
+
+ *pbResult = m_pWinMDAdapter->IsScenarioWinMDExp();
+ return S_OK;
+ }
+
+ STDMETHODIMP IsRuntimeClassImplementation(mdTypeDef tkTypeDef, BOOL *pbResult)
+ {
+ if (pbResult == NULL)
+ return E_POINTER;
+
+ return m_pWinMDAdapter->IsRuntimeClassImplementation(tkTypeDef, pbResult);
+ }
+
+ //=========================================================
+ // IMDInternalImport methods
+ //=========================================================
+ //*****************************************************************************
+ // return the count of entries of a given kind in a scope
+ // For example, pass in mdtMethodDef will tell you how many MethodDef
+ // contained in a scope
+ //*****************************************************************************
+ STDMETHODIMP_(ULONG) GetCountWithTokenKind(// return hresult
+ DWORD tkKind) // [IN] pass in the kind of token.
+ {
+ if (tkKind == mdtAssemblyRef)
+ {
+ return m_pRawInternalImport->GetCountWithTokenKind(tkKind) + m_pWinMDAdapter->GetExtraAssemblyRefCount();
+ }
+ else
+ {
+ return m_pRawInternalImport->GetCountWithTokenKind(tkKind);
+ }
+ }
+
+ //*****************************************************************************
+ // enumerator for typedef
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP EnumTypeDefInit( // return hresult
+ HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data
+ {
+ return m_pRawInternalImport->EnumTypeDefInit(phEnum);
+ }
+
+ STDMETHODIMP_(ULONG) EnumTypeDefGetCount(
+ HENUMInternal *phEnum) // [IN] the enumerator to retrieve information
+ {
+ return m_pRawInternalImport->EnumTypeDefGetCount(phEnum);
+ }
+
+ STDMETHODIMP_(void) EnumTypeDefReset(
+ HENUMInternal *phEnum) // [IN] the enumerator to retrieve information
+ {
+ return m_pRawInternalImport->EnumTypeDefReset(phEnum);
+ }
+
+ STDMETHODIMP_(bool) EnumTypeDefNext( // return hresult
+ HENUMInternal *phEnum, // [IN] input enum
+ mdTypeDef *ptd) // [OUT] return token
+ {
+ return m_pRawInternalImport->EnumTypeDefNext(phEnum, ptd);
+ }
+
+ STDMETHODIMP_(void) EnumTypeDefClose(
+ HENUMInternal *phEnum) // [IN] the enumerator to retrieve information
+ {
+ return m_pRawInternalImport->EnumTypeDefClose(phEnum);
+ }
+
+ //*****************************************************************************
+ // enumerator for MethodImpl
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP 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 *) // [OUT] used only on RW imports
+ {
+ HRESULT hr;
+ HENUMInternal::InitDynamicArrayEnum(phEnumBody);
+ phEnumBody->m_tkKind = TBL_MethodImpl << 24;
+ IfFailGo(m_pWinMDAdapter->AddMethodImplsToEnum(td, phEnumBody));
+ hr = S_OK;
+
+ ErrExit:
+ if (FAILED(hr))
+ {
+ HENUMInternal::ClearEnum(phEnumBody);
+ INDEBUG(memset(phEnumBody, 0xcc, sizeof(HENUMInternal)));
+ }
+ return hr;
+ }
+
+ STDMETHODIMP_(ULONG) EnumMethodImplGetCount(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *) // [IN] used only on RW imports
+
+ {
+ return phEnumBody->m_ulCount / 2;
+ }
+
+ STDMETHODIMP_(void) EnumMethodImplReset(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *) // [IN] used only on RW imports
+ {
+ phEnumBody->u.m_ulCur = phEnumBody->u.m_ulStart;
+ return;
+ }
+
+ __checkReturn
+ STDMETHODIMP EnumMethodImplNext( // return hresult (S_OK = TRUE, S_FALSE = FALSE or error code)
+ HENUMInternal *phEnumBody, // [IN] input enum for MethodBody
+ HENUMInternal *, // [IN] used only on RW imports
+ mdToken *ptkBody, // [OUT] return token for MethodBody
+ mdToken *ptkDecl) // [OUT] return token for MethodDecl
+ {
+ _ASSERTE(ptkBody && ptkDecl);
+ return HENUMInternal::EnumWithCount(phEnumBody, 1, ptkBody, ptkDecl, NULL);
+ }
+
+ STDMETHODIMP_(void) EnumMethodImplClose(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *) // [IN] used only on RW imports
+ {
+ HENUMInternal::ClearEnum(phEnumBody);
+ }
+
+ //*****************************************
+ // Enumerator helpers for memberdef, memberref, interfaceimp,
+ // event, property, exception, param
+ //*****************************************
+
+ __checkReturn
+ STDMETHODIMP EnumGlobalFunctionsInit( // return hresult
+ HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data
+ {
+ return m_pRawInternalImport->EnumGlobalFunctionsInit(phEnum);
+ }
+
+ __checkReturn
+ STDMETHODIMP EnumGlobalFieldsInit( // return hresult
+ HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data
+ {
+ return m_pRawInternalImport->EnumGlobalFieldsInit(phEnum);
+ }
+
+ __checkReturn
+ STDMETHODIMP 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
+ {
+ if (tkKind == (TBL_MethodImpl << 24))
+ return EnumMethodImplInit(tkParent, phEnum, NULL);
+
+ HRESULT hr;
+ IfFailGo(m_pRawInternalImport->EnumInit(tkKind, tkParent, phEnum));
+
+ _ASSERTE(phEnum->m_EnumType == MDSimpleEnum);
+
+ if (tkKind == mdtAssemblyRef)
+ {
+ _ASSERTE( phEnum->m_ulCount == m_pWinMDAdapter->GetRawAssemblyRefCount());
+ int n = m_pWinMDAdapter->GetExtraAssemblyRefCount();
+ phEnum->m_ulCount += n;
+ phEnum->u.m_ulEnd += n;
+ }
+
+
+ErrExit:
+ return hr;
+ }
+
+ __checkReturn
+ STDMETHODIMP 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;
+ IfFailGo(m_pRawInternalImport->EnumAllInit(tkKind, phEnum));
+
+ _ASSERTE(phEnum->m_EnumType == MDSimpleEnum);
+
+ if (tkKind == mdtAssemblyRef)
+ {
+ _ASSERTE( phEnum->m_ulCount == m_pWinMDAdapter->GetRawAssemblyRefCount());
+ int n = m_pWinMDAdapter->GetExtraAssemblyRefCount();
+ phEnum->m_ulCount += n;
+ phEnum->u.m_ulEnd += n;
+ }
+
+ErrExit:
+ return hr;
+ }
+
+ STDMETHODIMP_(bool) EnumNext(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk) // [OUT] token to scope the search
+ {
+ return m_pRawInternalImport->EnumNext(phEnum, ptk);
+ }
+
+ STDMETHODIMP_(ULONG) EnumGetCount(
+ HENUMInternal *phEnum) // [IN] the enumerator to retrieve information
+ {
+ return m_pRawInternalImport->EnumGetCount(phEnum);
+ }
+
+ STDMETHODIMP_(void) EnumReset(
+ HENUMInternal *phEnum) // [IN] the enumerator to be reset
+ {
+ return m_pRawInternalImport->EnumReset(phEnum);
+ }
+
+ STDMETHODIMP_(void) EnumClose(
+ HENUMInternal *phEnum) // [IN] the enumerator to be closed
+ {
+ return m_pRawInternalImport->EnumClose(phEnum);
+ }
+
+ //*****************************************
+ // Enumerator helpers for declsecurity.
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP EnumPermissionSetsInit( // return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ CorDeclSecurity Action, // [IN] Action to scope the search
+ HENUMInternal *phEnum) // [OUT] the enumerator to fill
+ {
+ return m_pRawInternalImport->EnumPermissionSetsInit(tkParent, Action, phEnum);
+ }
+
+ //*****************************************
+ // Enumerator helpers for CustomAttribute
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP 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
+ {
+ WinMDAdapter::ConvertWellKnownTypeNameFromClrToWinRT(&szName);
+ return m_pRawInternalImport->EnumCustomAttributeByNameInit(tkParent, szName, phEnum);
+ }
+
+ //*****************************************
+ // 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
+ STDMETHODIMP GetParentToken(
+ mdToken tkChild, // [IN] given child token
+ mdToken *ptkParent) // [OUT] returning parent
+ {
+ return m_pRawInternalImport->GetParentToken(tkChild, ptkParent);
+ }
+
+ //*****************************************
+ // Custom value helpers
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetCustomAttributeProps( // S_OK or error.
+ mdCustomAttribute at, // [IN] The attribute.
+ mdToken *ptkType) // [OUT] Put attribute type here.
+ {
+ return m_pRawInternalImport->GetCustomAttributeProps(at, ptkType);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetCustomAttributeAsBlob(
+ mdCustomAttribute cv, // [IN] given custom value token
+ void const **ppBlob, // [OUT] return the pointer to internal blob
+ ULONG *pcbSize) // [OUT] return the size of the blob
+ {
+ return m_pWinMDAdapter->GetCustomAttributeBlob(cv, ppBlob, pcbSize);
+ }
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP GetScopeProps(
+ LPCSTR *pszName, // [OUT] scope name
+ GUID *pmvid) // [OUT] version id
+ {
+ return m_pRawInternalImport->GetScopeProps(pszName, pmvid);
+ }
+
+ // The default signature comparison function.
+ static BOOL CompareSignatures(PCCOR_SIGNATURE pvFirstSigBlob, // First signature
+ DWORD cbFirstSigBlob, //
+ PCCOR_SIGNATURE pvSecondSigBlob, // Second signature
+ DWORD cbSecondSigBlob, //
+ void * SigArguments) // No additional arguments required
+ {
+ if (cbFirstSigBlob != cbSecondSigBlob || memcmp(pvFirstSigBlob, pvSecondSigBlob, cbSecondSigBlob))
+ return FALSE;
+ else
+ return TRUE;
+ }
+
+ // finding a particular method
+ __checkReturn
+ STDMETHODIMP FindMethodDef(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmd) // [OUT] matching memberdef
+ {
+ return FindMethodDefUsingCompare(classdef,
+ szName,
+ pvSigBlob,
+ cbSigBlob,
+ CompareSignatures,
+ NULL,
+ pmd);
+ }
+
+ // return a iSeq's param given a MethodDef
+ __checkReturn
+ STDMETHODIMP 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.
+ {
+ return m_pRawInternalImport->FindParamOfMethod(md, iSeq, pparamdef);
+ }
+
+ //*****************************************
+ //
+ // GetName* functions
+ //
+ //*****************************************
+
+ // return the name and namespace of typedef
+ __checkReturn
+ STDMETHODIMP GetNameOfTypeDef(
+ mdTypeDef classdef, // given classdef
+ LPCSTR *pszname, // return class name(unqualified)
+ LPCSTR *psznamespace) // return the name space name
+ {
+ if (TypeFromToken(classdef) != mdtTypeDef)
+ return CLDB_E_INTERNALERROR;
+
+ HRESULT hr;
+ IfFailRet(m_pWinMDAdapter->GetTypeDefProps(classdef, psznamespace, pszname, NULL, NULL));
+ return hr;
+ }
+
+ __checkReturn
+ STDMETHODIMP GetIsDualOfTypeDef(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pDual) // [OUT] return dual flag here.
+ {
+ return m_pRawInternalImport->GetIsDualOfTypeDef(classdef, pDual);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetIfaceTypeOfTypeDef(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pIface) // [OUT] 0=dual, 1=vtable, 2=dispinterface
+ {
+ return m_pRawInternalImport->GetIfaceTypeOfTypeDef(classdef, pIface);
+ }
+
+ // get the name of either methoddef
+ __checkReturn
+ STDMETHODIMP GetNameOfMethodDef( // return the name of the memberdef in UTF8
+ mdMethodDef md, // given memberdef
+ LPCSTR *pszName)
+ {
+ HRESULT hr = S_OK;
+ IfFailRet(m_pRawInternalImport->GetNameOfMethodDef(md, pszName));
+ return m_pWinMDAdapter->ModifyMethodProps(md, NULL, NULL, NULL, pszName);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetNameAndSigOfMethodDef(
+ mdMethodDef methoddef, // [IN] given memberdef
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of CLR signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName)
+ {
+ PCCOR_SIGNATURE pOrigSig;
+ ULONG cbOrigSig;
+ HRESULT hr = S_OK;
+ IfFailRet(m_pRawInternalImport->GetNameAndSigOfMethodDef(methoddef, &pOrigSig, &cbOrigSig, pszName));
+ IfFailRet(m_pWinMDAdapter->ModifyMethodProps(methoddef, NULL, NULL, NULL, pszName));
+
+ return m_pWinMDAdapter->GetSignatureForToken<IMDInternalImport, mdtMethodDef>(
+ methoddef,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSig, // pcbOrigSig
+ ppvSigBlob,
+ pcbSigBlob,
+ m_pRawInternalImport);
+ }
+
+ // return the name of a FieldDef
+ __checkReturn
+ STDMETHODIMP GetNameOfFieldDef(
+ mdFieldDef fd, // given memberdef
+ LPCSTR *pszName)
+ {
+ return m_pRawInternalImport->GetNameOfFieldDef(fd, pszName);
+ }
+
+ // return the name of typeref
+ __checkReturn
+ STDMETHODIMP GetNameOfTypeRef(
+ mdTypeRef classref, // [IN] given typeref
+ LPCSTR *psznamespace, // [OUT] return typeref name
+ LPCSTR *pszname) // [OUT] return typeref namespace
+ {
+ mdToken resolutionScope;
+ return m_pWinMDAdapter->GetTypeRefProps(classref, psznamespace, pszname, &resolutionScope);
+ }
+
+ // return the resolutionscope of typeref
+ __checkReturn
+ STDMETHODIMP GetResolutionScopeOfTypeRef(
+ mdTypeRef classref, // given classref
+ mdToken *ptkResolutionScope)
+ {
+ LPCSTR sznamespace;
+ LPCSTR szname;
+ return m_pWinMDAdapter->GetTypeRefProps(classref, &sznamespace, &szname, ptkResolutionScope);
+ }
+
+ // Find the type token given the name.
+ __checkReturn
+ STDMETHODIMP FindTypeRefByName(
+ 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.
+ {
+ return m_pWinMDAdapter->FindTypeRef(szNamespace, szName, tkResolutionScope, ptk);
+ }
+
+ // return the TypeDef properties
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP GetTypeDefProps(
+ mdTypeDef classdef, // given classdef
+ DWORD *pdwAttr, // return flags on class, tdPublic, tdAbstract
+ mdToken *ptkExtends) // [OUT] Put base class TypeDef/TypeRef here
+ {
+ HRESULT hr;
+ IfFailGo(m_pWinMDAdapter->GetTypeDefProps(classdef, NULL, NULL, pdwAttr, ptkExtends));
+ ErrExit:
+ return hr;
+ }
+
+ // return the item's guid
+ __checkReturn
+ STDMETHODIMP GetItemGuid(
+ mdToken tkObj, // [IN] given item.
+ CLSID *pGuid) // [out[ put guid here.
+ {
+ HRESULT hr;
+ IfFailGo(m_pWinMDAdapter->GetItemGuid(tkObj, pGuid));
+
+ if (hr == S_FALSE)
+ {
+ // if this is not a WinRT type, also look for System.Guid by falling back to the raw internal MD import
+ if (TypeFromToken(tkObj) == mdtTypeDef)
+ {
+ DWORD dwAttr;
+ IfFailGo(m_pWinMDAdapter->GetTypeDefProps(tkObj, NULL, NULL, &dwAttr, NULL));
+
+ if (!IsTdWindowsRuntime(dwAttr))
+ {
+ IfFailGo(m_pRawInternalImport->GetItemGuid(tkObj, pGuid));
+ }
+ else
+ {
+ // reset the return value to S_FALSE if the type is WinRT
+ hr = S_FALSE;
+ }
+ }
+ }
+
+ ErrExit:
+ return hr;
+ }
+
+ // Get enclosing class of the NestedClass.
+ __checkReturn
+ STDMETHODIMP GetNestedClassProps( // S_OK or error
+ mdTypeDef tkNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptkEnclosingClass) // [OUT] EnclosingClass token.
+ {
+ return m_pRawInternalImport->GetNestedClassProps(tkNestedClass, ptkEnclosingClass);
+ }
+
+ // Get count of Nested classes given the enclosing class.
+ __checkReturn
+ STDMETHODIMP GetCountNestedClasses( // return count of Nested classes.
+ mdTypeDef tkEnclosingClass, // Enclosing class.
+ ULONG *pcNestedClassesCount)
+ {
+ return m_pRawInternalImport->GetCountNestedClasses(tkEnclosingClass, pcNestedClassesCount);
+ }
+
+ // Return array of Nested classes given the enclosing class.
+ __checkReturn
+ STDMETHODIMP 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)
+ {
+ return m_pRawInternalImport->GetNestedClasses(tkEnclosingClass, rNestedClasses, ulNestedClasses, pcNestedClasses);
+ }
+
+ // return the ModuleRef properties
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP GetModuleRefProps(
+ mdModuleRef mur, // [IN] moduleref token
+ LPCSTR *pszName) // [OUT] buffer to fill with the moduleref name
+ {
+ return m_pRawInternalImport->GetModuleRefProps(mur, pszName);
+ }
+
+ //*****************************************
+ //
+ // GetSig* functions
+ //
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetSigOfMethodDef(
+ mdMethodDef methoddef, // [IN] given memberdef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig)
+ {
+ return m_pWinMDAdapter->GetSignatureForToken<IMDInternalImport, mdtMethodDef>(
+ methoddef,
+ NULL,
+ NULL,
+ ppSig,
+ pcbSigBlob,
+ m_pRawInternalImport);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetSigOfFieldDef(
+ mdFieldDef fielddef, // [IN] given fielddef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig)
+ {
+ return m_pWinMDAdapter->GetSignatureForToken<IMDInternalImport, mdtFieldDef>(
+ fielddef,
+ NULL,
+ NULL,
+ ppSig,
+ pcbSigBlob,
+ m_pRawInternalImport);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetSigFromToken(
+ mdToken tk, // FieldDef, MethodDef, Signature or TypeSpec token
+ ULONG * pcbSig,
+ PCCOR_SIGNATURE * ppSig)
+ {
+ if (TypeFromToken(tk) == mdtMethodDef)
+ {
+ return GetSigOfMethodDef(tk, pcbSig, ppSig);
+ }
+ else if (TypeFromToken(tk) == mdtFieldDef)
+ {
+ return GetSigOfFieldDef(tk, pcbSig, ppSig);
+ }
+ else if (TypeFromToken(tk) == mdtTypeSpec)
+ {
+ return GetTypeSpecFromToken(tk, ppSig, pcbSig);
+ }
+ // Note: mdtSignature is not part of public WinMD surface, so it does not need signature rewriting - just call the underlying "raw" implementation
+ return m_pRawInternalImport->GetSigFromToken(tk, pcbSig, ppSig);
+ }
+
+
+
+ //*****************************************
+ // get method property
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetMethodDefProps(
+ mdMethodDef md, // The method for which to get props.
+ DWORD *pdwFlags)
+ {
+ HRESULT hr;
+ IfFailGo(m_pRawInternalImport->GetMethodDefProps(md, pdwFlags));
+ IfFailGo(m_pWinMDAdapter->ModifyMethodProps(md, pdwFlags, NULL, NULL, NULL));
+ ErrExit:
+ return hr;
+ }
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP GetMethodImplProps(
+ mdToken tk, // [IN] MethodDef
+ ULONG *pulCodeRVA, // [OUT] CodeRVA
+ DWORD *pdwImplFlags) // [OUT] Impl. Flags
+ {
+ HRESULT hr;
+ IfFailGo(m_pRawInternalImport->GetMethodImplProps(tk, pulCodeRVA, pdwImplFlags));
+ IfFailGo(m_pWinMDAdapter->ModifyMethodProps(tk, NULL, pdwImplFlags, pulCodeRVA, NULL));
+ ErrExit:
+ return hr;
+ }
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetFieldRVA(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulCodeRVA) // [OUT] CodeRVA
+ {
+ return m_pRawInternalImport->GetFieldRVA(fd, pulCodeRVA);
+ }
+
+ //*****************************************
+ // get field property
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetFieldDefProps(
+ mdFieldDef fd, // [IN] given fielddef
+ DWORD *pdwFlags) // [OUT] return fdPublic, fdPrive, etc flags
+ {
+ HRESULT hr;
+ IfFailGo(m_pRawInternalImport->GetFieldDefProps(fd, pdwFlags));
+ IfFailGo(m_pWinMDAdapter->ModifyFieldDefProps(fd, pdwFlags));
+ErrExit:
+ return hr;
+ }
+
+ //*****************************************************************************
+ // return default value of a token(could be paramdef, fielddef, or property
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetDefaultValue(
+ mdToken tk, // [IN] given FieldDef, ParamDef, or Property
+ MDDefaultValue *pDefaultValue) // [OUT] default value to fill
+ {
+ return m_pRawInternalImport->GetDefaultValue(tk, pDefaultValue);
+ }
+
+
+ //*****************************************
+ // get dispid of a MethodDef or a FieldDef
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetDispIdOfMemberDef( // return hresult
+ mdToken tk, // [IN] given methoddef or fielddef
+ ULONG *pDispid) // [OUT] Put the dispid here.
+ {
+ return m_pRawInternalImport->GetDispIdOfMemberDef(tk, pDispid);
+ }
+
+ //*****************************************
+ // return TypeRef/TypeDef given an InterfaceImpl token
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetTypeOfInterfaceImpl( // return the TypeRef/typedef token for the interfaceimpl
+ mdInterfaceImpl iiImpl, // given a interfaceimpl
+ mdToken *ptkType)
+ {
+ return m_pRawInternalImport->GetTypeOfInterfaceImpl(iiImpl, ptkType);
+ }
+
+ //*****************************************
+ // look up function for TypeDef
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP FindTypeDef(
+ LPCSTR szNamespace, // [IN] Namespace for the TypeDef.
+ LPCSTR szName, // [IN] Name of the TypeDef.
+ mdToken tkEnclosingClass, // [IN] TypeRef/TypeDef Token for the enclosing class.
+ mdTypeDef *ptypedef) // [IN] return typedef
+ {
+ return m_pWinMDAdapter->FindTypeDef(szNamespace, szName, tkEnclosingClass, ptypedef);
+ }
+
+ //*****************************************
+ // return name and sig of a memberref
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetNameAndSigOfMemberRef( // return name here
+ mdMemberRef memberref, // given memberref
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of CLR signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName)
+ {
+ HRESULT hr = S_OK;
+ PCCOR_SIGNATURE pOrigSig;
+ ULONG cbOrigSig;
+
+ IfFailRet(m_pRawInternalImport->GetNameAndSigOfMemberRef(memberref, &pOrigSig, &cbOrigSig, pszName));
+ IfFailRet(m_pWinMDAdapter->ModifyMemberProps(memberref, NULL, NULL, NULL, pszName));
+
+ return m_pWinMDAdapter->GetSignatureForToken<IMDInternalImport, mdtMemberRef>(
+ memberref,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSig, // pcbOrigSig
+ ppvSigBlob,
+ pcbSigBlob,
+ m_pRawInternalImport);
+ }
+
+ //*****************************************************************************
+ // Given memberref, return the parent. It can be TypeRef, ModuleRef, MethodDef
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetParentOfMemberRef(
+ mdMemberRef memberref, // given memberref
+ mdToken *ptkParent) // return the parent token
+ {
+ return m_pRawInternalImport->GetParentOfMemberRef(memberref, ptkParent);
+ }
+
+ __checkReturn
+ STDMETHODIMP 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
+ {
+ return m_pRawInternalImport->GetParamDefProps(paramdef, pusSequence, pdwAttr, pszName);
+ }
+
+ __checkReturn
+ STDMETHODIMP 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
+ {
+ return m_pRawInternalImport->GetPropertyInfoForMethodDef(md, ppd, pName, pSemantic);
+ }
+
+ //*****************************************
+ // class layout/sequence information
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetClassPackSize( // return error if class doesn't have packsize
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwPackSize) // [OUT] 1, 2, 4, 8, or 16
+ {
+ return m_pRawInternalImport->GetClassPackSize(td, pdwPackSize);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetClassTotalSize( // return error if class doesn't have total size info
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwClassSize) // [OUT] return the total size of the class
+ {
+ return m_pRawInternalImport->GetClassTotalSize(td, pdwClassSize);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetClassLayoutInit(
+ mdTypeDef td, // [IN] give typedef
+ MD_CLASS_LAYOUT *pLayout) // [OUT] set up the status of query here
+ {
+ return m_pRawInternalImport->GetClassLayoutInit(td, pLayout);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetClassLayoutNext(
+ MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here
+ mdFieldDef *pfd, // [OUT] return the fielddef
+ ULONG *pulOffset) // [OUT] return the offset/ulSequence associate with it
+ {
+ return m_pRawInternalImport->GetClassLayoutNext(pLayout, pfd, pulOffset);
+ }
+
+ //*****************************************
+ // marshal information of a field
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetFieldMarshal( // return error if no native type associate with the token
+ mdFieldDef fd, // [IN] given fielddef
+ PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature
+ ULONG *pcbNativeType) // [OUT] the count of bytes of *ppvNativeType
+ {
+ return m_pRawInternalImport->GetFieldMarshal(fd, pSigNativeType, pcbNativeType);
+ }
+
+
+ //*****************************************
+ // property APIs
+ //*****************************************
+ // find a property by name
+ __checkReturn
+ STDMETHODIMP FindProperty(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szPropName, // [IN] property name
+ mdProperty *pProp) // [OUT] return property token
+ {
+ return m_pRawInternalImport->FindProperty(td, szPropName, pProp);
+ }
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP GetPropertyProps(
+ mdProperty prop, // [IN] property token
+ LPCSTR *szProperty, // [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;
+ PCCOR_SIGNATURE pOrigSig;
+ ULONG cbOrigSig;
+
+ IfFailRet(m_pRawInternalImport->GetPropertyProps(prop, szProperty, pdwPropFlags, &pOrigSig, &cbOrigSig));
+
+ return m_pWinMDAdapter->GetSignatureForToken<IMDInternalImport, mdtProperty>(
+ prop,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSig, // pcbOrigSig
+ ppvSig,
+ pcbSig,
+ m_pRawInternalImport);
+ }
+
+ //**********************************
+ // Event APIs
+ //**********************************
+ __checkReturn
+ STDMETHODIMP FindEvent(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szEventName, // [IN] event name
+ mdEvent *pEvent) // [OUT] return event token
+ {
+ return m_pRawInternalImport->FindEvent(td, szEventName, pEvent);
+ }
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP GetEventProps(
+ mdEvent ev, // [IN] event token
+ LPCSTR *pszEvent, // [OUT] Event name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType) // [OUT] EventType class
+ {
+ return m_pRawInternalImport->GetEventProps(ev, pszEvent, pdwEventFlags, ptkEventType);
+ }
+
+
+ //**********************************
+ // find a particular associate of a property or an event
+ //**********************************
+ __checkReturn
+ STDMETHODIMP FindAssociate(
+ mdToken evprop, // [IN] given a property or event token
+ DWORD associate, // [IN] given a associate semantics(setter, getter, testdefault, reset, AddOn, RemoveOn, Fire)
+ mdMethodDef *pmd) // [OUT] return method def token
+ {
+ return m_pRawInternalImport->FindAssociate(evprop, associate, pmd);
+ }
+
+ // Note, void function in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP EnumAssociateInit(
+ mdToken evprop, // [IN] given a property or an event token
+ HENUMInternal *phEnum) // [OUT] cursor to hold the query result
+ {
+ return m_pRawInternalImport->EnumAssociateInit(evprop, phEnum);
+ }
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP GetAllAssociates(
+ HENUMInternal *phEnum, // [IN] query result form GetPropertyAssociateCounts
+ ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output
+ ULONG cAssociateRec) // [IN] size of the buffer
+ {
+ return m_pRawInternalImport->GetAllAssociates(phEnum, pAssociateRec, cAssociateRec);
+ }
+
+
+ //**********************************
+ // Get info about a PermissionSet.
+ //**********************************
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP 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.
+ {
+ return m_pRawInternalImport->GetPermissionSetProps(pm, pdwAction, ppvPermission, pcbPermission);
+ }
+
+ //****************************************
+ // Get the String given the String token.
+ // Returns a pointer to the string, or NULL in case of error.
+ //****************************************
+ __checkReturn
+ STDMETHODIMP GetUserString(
+ mdString stk, // [IN] the string token.
+ ULONG *pchString, // [OUT] count of characters in the string.
+ BOOL *pbIs80Plus, // [OUT] specifies where there are extended characters >= 0x80.
+ LPCWSTR *pwszUserString)
+ {
+ return m_pRawInternalImport->GetUserString(stk, pchString, pbIs80Plus, pwszUserString);
+ }
+
+ //*****************************************************************************
+ // p-invoke APIs.
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP GetPinvokeMap(
+ mdToken tk, // [IN] FieldDef, MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ LPCSTR *pszImportName, // [OUT] Import name.
+ mdModuleRef *pmrImportDLL) // [OUT] ModuleRef token for the target DLL.
+ {
+ return m_pRawInternalImport->GetPinvokeMap(tk, pdwMappingFlags, pszImportName, pmrImportDLL);
+ }
+
+ //*****************************************************************************
+ // helpers to convert a text signature to a com format
+ //*****************************************************************************
+ __checkReturn
+ STDMETHODIMP ConvertTextSigToComSig( // Return hresult.
+ BOOL fCreateTrIfNotFound, // [IN] create typeref if not found
+ LPCSTR pSignature, // [IN] class file format signature
+ CQuickBytes *pqbNewSig, // [OUT] place holder for CLR signature
+ ULONG *pcbCount) // [OUT] the result size of signature
+ {
+ return m_pRawInternalImport->ConvertTextSigToComSig(fCreateTrIfNotFound, pSignature, pqbNewSig, pcbCount);
+ }
+
+ //*****************************************************************************
+ // Assembly MetaData APIs.
+ //*****************************************************************************
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP 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.
+ {
+ return m_pRawInternalImport->GetAssemblyProps(mda, ppbPublicKey, pcbPublicKey, pulHashAlgId, pszName, pMetaData, pdwAssemblyFlags);
+ }
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP 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.
+ {
+ HRESULT hr;
+ mdAssemblyRef md = mdar;
+ if (RidFromToken(md) > m_pWinMDAdapter->GetRawAssemblyRefCount())
+ {
+ // The extra framework assemblies we add references to should all have the
+ // same verion, key, culture, etc as those of mscorlib.
+ // So we retrieve the mscorlib properties and change the name.
+ md = m_pWinMDAdapter->GetAssemblyRefMscorlib();
+ }
+
+ IfFailRet(m_pRawInternalImport->GetAssemblyRefProps(md, ppbPublicKeyOrToken, pcbPublicKeyOrToken, pszName, pMetaData, ppbHashValue, pcbHashValue, pdwAssemblyRefFlags));
+
+ USHORT *pusMajorVersion = nullptr;
+ USHORT *pusMinorVersion = nullptr;
+ USHORT *pusBuildNumber = nullptr;
+ USHORT *pusRevisionNumber = nullptr;
+
+ if (pMetaData != nullptr)
+ {
+ pusMajorVersion = &pMetaData->usMajorVersion;
+ pusMinorVersion = &pMetaData->usMinorVersion;
+ pusBuildNumber = &pMetaData->usBuildNumber;
+ pusRevisionNumber = &pMetaData->usRevisionNumber;
+ }
+
+ m_pWinMDAdapter->ModifyAssemblyRefProps(
+ mdar,
+ ppbPublicKeyOrToken,
+ pcbPublicKeyOrToken,
+ pszName,
+ pusMajorVersion,
+ pusMinorVersion,
+ pusBuildNumber,
+ pusRevisionNumber,
+ ppbHashValue,
+ pcbHashValue);
+
+ return hr;
+ }
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP 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.
+ {
+ return m_pRawInternalImport->GetFileProps(mdf, pszName, ppbHashValue, pcbHashValue, pdwFileFlags);
+ }
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP GetExportedTypeProps(
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ LPCSTR *pszNamespace, // [OUT] Namespace.
+ LPCSTR *pszName, // [OUT] Name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags) // [OUT] Flags.
+ {
+ HRESULT hr;
+ IfFailRet(m_pRawInternalImport->GetExportedTypeProps(mdct, pszNamespace, pszName, ptkImplementation, ptkTypeDef, pdwExportedTypeFlags));
+ IfFailRet(m_pWinMDAdapter->ModifyExportedTypeName(mdct, pszNamespace, pszName));
+ return hr;
+ }
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHODIMP 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.
+ {
+ return m_pRawInternalImport->GetManifestResourceProps(mdmr, pszName, ptkImplementation, pdwOffset, pdwResourceFlags);
+ }
+
+ __checkReturn
+ STDMETHODIMP FindExportedTypeByName( // S_OK or error
+ LPCSTR szNamespace, // [IN] Namespace of the ExportedType.
+ LPCSTR szName, // [IN] Name of the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] ExportedType for the enclosing class.
+ mdExportedType *pmct) // [OUT] Put ExportedType token here.
+ {
+ return m_pWinMDAdapter->FindExportedType(szNamespace, szName, tkEnclosingType, pmct);
+ }
+
+ __checkReturn
+ STDMETHODIMP FindManifestResourceByName( // S_OK or error
+ LPCSTR szName, // [IN] Name of the ManifestResource.
+ mdManifestResource *pmmr) // [OUT] Put ManifestResource token here.
+ {
+ return m_pRawInternalImport->FindManifestResourceByName(szName, pmmr);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetAssemblyFromScope( // S_OK or error
+ mdAssembly *ptkAssembly) // [OUT] Put token here.
+ {
+ return m_pRawInternalImport->GetAssemblyFromScope(ptkAssembly);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetCustomAttributeByName( // S_OK or error
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) // [OUT] Put size of data here.
+ {
+ return m_pWinMDAdapter->GetCustomAttributeByName(tkObj, szName, NULL, ppData, pcbData);
+ }
+
+ // Note: The return type of this method was void in v1
+ __checkReturn
+ STDMETHODIMP 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.
+ {
+ return m_pWinMDAdapter->GetSignatureForToken<IMDInternalImport, mdtTypeSpec>(
+ typespec,
+ NULL, // ppOrigSig
+ NULL, // pcbOrigSig
+ ppvSig,
+ pcbSig,
+ m_pRawInternalImport);
+ }
+
+ __checkReturn
+ STDMETHODIMP SetUserContextData( // S_OK or E_NOTIMPL
+ IUnknown *pIUnk) // The user context.
+ {
+ return m_pRawInternalImport->SetUserContextData(pIUnk);
+ }
+
+ __checkReturn
+ STDMETHODIMP_(BOOL) IsValidToken( // True or False.
+ mdToken tk) // [IN] Given token.
+ {
+ if (TypeFromToken(tk) == mdtAssemblyRef)
+ return m_pWinMDAdapter->IsValidAssemblyRefToken(tk);
+
+ return m_pRawInternalImport->IsValidToken(tk);
+ }
+
+
+ __checkReturn
+ STDMETHODIMP 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);
+ }
+
+
+ STDMETHODIMP_(IMetaModelCommon*) GetMetaModelCommon() // Return MetaModelCommon interface.
+ {
+ return static_cast<IMetaModelCommon*>(this);
+ }
+
+ STDMETHODIMP_(IUnknown *) GetCachedPublicInterface(BOOL fWithLock) // return the cached public interface
+ {
+ return m_pRawInternalImport->GetCachedPublicInterface(fWithLock);
+ }
+ __checkReturn
+
+ STDMETHODIMP SetCachedPublicInterface(IUnknown *pUnk) // no return value
+ {
+ return m_pRawInternalImport->SetCachedPublicInterface(pUnk);
+ }
+
+ STDMETHODIMP_(UTSemReadWrite*) GetReaderWriterLock() // return the reader writer lock
+ {
+ return m_pRawInternalImport->GetReaderWriterLock();
+ }
+
+ __checkReturn
+ STDMETHODIMP SetReaderWriterLock(UTSemReadWrite * pSem)
+ {
+ return m_pRawInternalImport->SetReaderWriterLock(pSem);
+ }
+
+ STDMETHODIMP_(mdModule) GetModuleFromScope() // [OUT] Put mdModule token here.
+ {
+ return m_pRawInternalImport->GetModuleFromScope();
+ }
+
+
+ //-----------------------------------------------------------------
+ // Additional custom methods
+
+ // finding a particular method
+ __checkReturn
+ STDMETHODIMP FindMethodDefUsingCompare(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR 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 *pmd) // [OUT] matching memberdef
+ {
+ if (pvSigBlob == NULL || cbSigBlob == 0 || pSignatureCompare == NULL)
+ {
+ // if signature matching is not needed, we can delegate to the underlying implementation
+ return m_pRawInternalImport->FindMethodDefUsingCompare(classdef, szName, pvSigBlob, cbSigBlob, pSignatureCompare, pSignatureArgs, pmd);
+ }
+
+ // The following code emulates MDInternalRO::FindMethodDefUsingCompare. We cannot call the underlying
+ // implementation because we need to compare pvSigBlob to reinterpreted signatures.
+ _ASSERTE(szName && pmd);
+
+ HRESULT hr = S_OK;
+ HENUMInternal hEnum;
+
+ CQuickBytes qbSig; // holds non-varargs signature
+
+ *pmd = mdMethodDefNil;
+
+ // check to see if this is a vararg signature
+ PCCOR_SIGNATURE pvSigTemp = pvSigBlob;
+ if (isCallConv(CorSigUncompressCallingConv(pvSigTemp), IMAGE_CEE_CS_CALLCONV_VARARG))
+ {
+ // Get the fixed part of VARARG signature
+ IfFailGo(_GetFixedSigOfVarArg(pvSigBlob, cbSigBlob, &qbSig, &cbSigBlob));
+ pvSigBlob = (PCCOR_SIGNATURE) qbSig.Ptr();
+ }
+
+ // now iterate all methods in td and compare name and signature
+ IfFailGo(EnumInit(mdtMethodDef, classdef, &hEnum));
+
+ mdMethodDef md;
+ while(EnumNext(&hEnum, &md))
+ {
+ PCCOR_SIGNATURE pvMethodSigBlob;
+ ULONG cbMethodSigBlob;
+ LPCSTR szMethodName;
+
+ IfFailGo(GetNameAndSigOfMethodDef(md, &pvMethodSigBlob, &cbMethodSigBlob, &szMethodName));
+
+ if (strcmp(szName, szMethodName) == 0)
+ {
+ // we have a name match, check signature
+ if (pSignatureCompare(pvMethodSigBlob, cbMethodSigBlob, pvSigBlob, cbSigBlob, pSignatureArgs) == FALSE)
+ continue;
+
+ // ignore PrivateScope methods
+ DWORD dwMethodAttr;
+ IfFailGo(GetMethodDefProps(md, &dwMethodAttr));
+
+ if (IsMdPrivateScope(dwMethodAttr))
+ continue;
+
+ // we found the method
+ *pmd = md;
+ goto ErrExit;
+ }
+ }
+ hr = CLDB_E_RECORD_NOTFOUND;
+
+ ErrExit:
+ EnumClose(&hEnum);
+ return hr;
+ }
+
+ // Additional v2 methods.
+
+ //*****************************************
+ // return a field offset for a given field
+ //*****************************************
+ __checkReturn
+ STDMETHODIMP GetFieldOffset(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulOffset) // [OUT] FieldOffset
+ {
+ return m_pRawInternalImport->GetFieldOffset(fd, pulOffset);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetMethodSpecProps(
+ mdMethodSpec ms, // [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 = S_OK;
+ PCCOR_SIGNATURE pOrigSig;
+ ULONG cbOrigSig;
+
+ IfFailRet(m_pRawInternalImport->GetMethodSpecProps(ms, tkParent, &pOrigSig, &cbOrigSig));
+
+ return m_pWinMDAdapter->GetSignatureForToken<IMDInternalImport, mdtMethodSpec>(
+ ms,
+ &pOrigSig, // ppOrigSig
+ &cbOrigSig, // pcbOrigSig
+ ppvSigBlob,
+ pcbSigBlob,
+ m_pRawInternalImport);
+ }
+
+ __checkReturn
+ STDMETHODIMP GetTableInfoWithIndex(
+ ULONG index, // [IN] pass in the table index
+ void **pTable, // [OUT] pointer to table at index
+ void **pTableSize) // [OUT] size of table at index
+ {
+ // This abstraction breaker is apparently used only by SOS... at this time of writing.
+ return m_pRawInternalImport->GetTableInfoWithIndex(index, pTable, pTableSize);
+ }
+
+ __checkReturn
+ STDMETHODIMP ApplyEditAndContinue(
+ void *pDeltaMD, // [IN] the delta metadata
+ ULONG cbDeltaMD, // [IN] length of pData
+ IMDInternalImport **ppv) // [OUT] the resulting metadata interface
+ {
+ WINMD_COMPAT_ASSERT("IMDInternalImport::ApplyEditAndContinue() not supported on .winmd files.");
+ return E_NOTIMPL;
+ }
+
+ //**********************************
+ // Generics APIs
+ //**********************************
+ __checkReturn
+ STDMETHODIMP 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
+ {
+ return m_pRawInternalImport->GetGenericParamProps(rd, pulSequence, pdwAttr, ptOwner, reserved, szName);
+ }
+
+ __checkReturn
+ STDMETHODIMP 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
+ {
+ return m_pRawInternalImport->GetGenericParamConstraintProps(rd, ptGenericParam, ptkConstraintType);
+ }
+
+ //*****************************************************************************
+ // 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
+ STDMETHODIMP GetVersionString( // S_OK or error.
+ LPCSTR *pVer) // [OUT] Put version string here.
+ {
+ return m_pWinMDAdapter->GetVersionString(pVer);
+ }
+
+ __checkReturn
+ STDMETHODIMP 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
+ {
+ WinMDAdapter::ConvertWellKnownTypeNameFromClrToWinRT(&szName);
+ return m_pRawInternalImport->SafeAndSlowEnumCustomAttributeByNameInit(tkParent, szName, phEnum);
+ }
+ __checkReturn
+ STDMETHODIMP 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
+ {
+ WinMDAdapter::ConvertWellKnownTypeNameFromClrToWinRT(&szName);
+ return m_pRawInternalImport->SafeAndSlowEnumCustomAttributeByNameNext(tkParent, szName, phEnum, mdAttribute);
+ }
+
+
+ __checkReturn
+ STDMETHODIMP 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_pRawInternalImport->GetTypeDefRefTokenInTypeSpec(tkTypeSpec, tkEnclosedToken);
+ }
+
+ STDMETHODIMP_(DWORD) GetMetadataStreamVersion() //returns DWORD with major version of
+ // MD stream in senior word and minor version--in junior word
+ {
+ return m_pRawInternalImport->GetMetadataStreamVersion();
+ }
+
+ __checkReturn
+ STDMETHODIMP 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;
+ IfFailRet(m_pRawInternalImport->GetNameOfCustomAttribute(mdAttribute, pszNamespace, pszName));
+ WinMDAdapter::ConvertWellKnownTypeNameFromWinRTToClr(pszNamespace, pszName);
+ return hr;
+ }
+
+ STDMETHODIMP SetOptimizeAccessForSpeed(// S_OK or error
+ BOOL fOptSpeed)
+ {
+ return m_pRawInternalImport->SetOptimizeAccessForSpeed(fOptSpeed);
+ }
+
+ STDMETHODIMP SetVerifiedByTrustedSource(// S_OK or error
+ BOOL fVerified)
+ {
+ return m_pRawInternalImport->SetVerifiedByTrustedSource(fVerified);
+ }
+
+ STDMETHODIMP GetRvaOffsetData(// S_OK or error
+ DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table.
+ DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table.
+ DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table.
+ DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table.
+ DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table.
+ DWORD *pFieldRvaCount) // [OUT] Number of records in FieldRVA table.
+ {
+ return m_pRawInternalImport->GetRvaOffsetData(
+ pFirstMethodRvaOffset,
+ pMethodDefRecordSize,
+ pMethodDefCount,
+ pFirstFieldRvaOffset,
+ pFieldRvaRecordSize,
+ pFieldRvaCount);
+ }
+
+
+ // ********************************************************************************
+ // **************** Implementation of IMetaModelCommon methods ********************
+ // ********************************************************************************
+
+ __checkReturn
+ HRESULT CommonGetScopeProps(
+ LPCUTF8 *pszName,
+ GUID *pMvid)
+ {
+ return m_pRawMetaModelCommon->CommonGetScopeProps(pszName, pMvid);
+ }
+
+ __checkReturn
+ HRESULT CommonGetTypeRefProps(
+ mdTypeRef tr,
+ LPCUTF8 *pszNamespace,
+ LPCUTF8 *pszName,
+ mdToken *ptkResolution)
+ {
+ return m_pWinMDAdapter->GetTypeRefProps(tr, pszNamespace, pszName, ptkResolution);
+ }
+
+ __checkReturn
+ HRESULT CommonGetTypeDefProps(
+ mdTypeDef td,
+ LPCUTF8 *pszNameSpace,
+ LPCUTF8 *pszName,
+ DWORD *pdwFlags,
+ mdToken *pdwExtends,
+ ULONG *pMethodList)
+ {
+ // We currently don't support retrieving the method list.
+ if (pMethodList)
+ return E_NOTIMPL;
+
+ return m_pWinMDAdapter->GetTypeDefProps(td, pszNameSpace, pszName, pdwFlags, pdwExtends);
+ }
+
+ __checkReturn
+ HRESULT CommonGetTypeSpecProps(
+ mdTypeSpec ts,
+ PCCOR_SIGNATURE *ppvSig,
+ ULONG *pcbSig)
+ {
+ return m_pWinMDAdapter->GetSignatureForToken<IMDInternalImport, mdtTypeSpec>(
+ ts,
+ NULL, // ppOrigSig
+ NULL, // pcbOrigSig
+ ppvSig,
+ pcbSig,
+ m_pRawInternalImport);
+ }
+
+ __checkReturn
+ HRESULT CommonGetEnclosingClassOfTypeDef(
+ mdTypeDef td,
+ mdTypeDef *ptkEnclosingTypeDef)
+ {
+ return m_pRawMetaModelCommon->CommonGetEnclosingClassOfTypeDef(td, ptkEnclosingTypeDef);
+ }
+
+ __checkReturn
+ HRESULT CommonGetAssemblyProps(
+ USHORT *pusMajorVersion,
+ USHORT *pusMinorVersion,
+ USHORT *pusBuildNumber,
+ USHORT *pusRevisionNumber,
+ DWORD *pdwFlags,
+ const void **ppbPublicKey,
+ ULONG *pcbPublicKey,
+ LPCUTF8 *pszName,
+ LPCUTF8 *pszLocale)
+ {
+ HRESULT hr;
+ AssemblyMetaDataInternal MetaData;
+ mdToken tkAssembly = TokenFromRid(1, mdtAssembly);
+
+ IfFailRet(GetAssemblyProps(
+ tkAssembly,
+ ppbPublicKey,
+ pcbPublicKey,
+ NULL,
+ pszName,
+ &MetaData,
+ pdwFlags));
+
+ if (pusMajorVersion)
+ *pusMajorVersion = MetaData.usMajorVersion;
+ if (pusMinorVersion)
+ *pusMinorVersion = MetaData.usMinorVersion;
+ if (pusBuildNumber)
+ *pusBuildNumber = MetaData.usBuildNumber;
+ if (pusRevisionNumber)
+ *pusRevisionNumber = MetaData.usRevisionNumber;
+ if (pszLocale)
+ *pszLocale = MetaData.szLocale;
+
+ return S_OK;
+ }
+
+ __checkReturn
+ HRESULT CommonGetAssemblyRefProps(
+ mdAssemblyRef tkAssemRef,
+ USHORT *pusMajorVersion,
+ USHORT *pusMinorVersion,
+ USHORT *pusBuildNumber,
+ USHORT *pusRevisionNumber,
+ DWORD *pdwFlags,
+ const void **ppbPublicKeyOrToken,
+ ULONG *pcbPublicKeyOrToken,
+ LPCUTF8 *pszName,
+ LPCUTF8 *pszLocale,
+ const void **ppbHashValue,
+ ULONG *pcbHashValue)
+ {
+ HRESULT hr;
+
+ AssemblyMetaDataInternal MetaData;
+ IfFailRet(GetAssemblyRefProps(
+ tkAssemRef,
+ ppbPublicKeyOrToken,
+ pcbPublicKeyOrToken,
+ pszName,
+ &MetaData,
+ ppbHashValue,
+ pcbHashValue,
+ pdwFlags));
+
+ if (pusMajorVersion)
+ *pusMajorVersion = MetaData.usMajorVersion;
+ if (pusMinorVersion)
+ *pusMinorVersion = MetaData.usMinorVersion;
+ if (pusBuildNumber)
+ *pusBuildNumber = MetaData.usBuildNumber;
+ if (pusRevisionNumber)
+ *pusRevisionNumber = MetaData.usRevisionNumber;
+ if (pszLocale)
+ *pszLocale = MetaData.szLocale;
+
+ return S_OK;
+ }
+
+ __checkReturn
+ HRESULT CommonGetModuleRefProps(
+ mdModuleRef tkModuleRef,
+ LPCUTF8 *pszName)
+ {
+ return m_pRawMetaModelCommon->CommonGetModuleRefProps(tkModuleRef, pszName);
+ }
+
+ __checkReturn
+ HRESULT CommonFindExportedType(
+ LPCUTF8 szNamespace,
+ LPCUTF8 szName,
+ mdToken tkEnclosingType,
+ mdExportedType *ptkExportedType)
+ {
+ return m_pWinMDAdapter->FindExportedType(szNamespace, szName, tkEnclosingType, ptkExportedType);
+ }
+
+ __checkReturn
+ HRESULT CommonGetExportedTypeProps(
+ mdToken tkExportedType,
+ LPCUTF8 *pszNamespace,
+ LPCUTF8 *pszName,
+ mdToken *ptkImpl)
+ {
+ return GetExportedTypeProps(
+ tkExportedType,
+ pszNamespace,
+ pszName,
+ ptkImpl,
+ NULL,
+ NULL);
+ }
+
+ __checkReturn
+ HRESULT 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.
+ {
+ return m_pWinMDAdapter->GetCustomAttributeByName(tkObj, szName, ptkCA, ppData, pcbData);
+ }
+
+ __checkReturn
+ HRESULT FindParentOfMethodHelper(mdMethodDef md, mdTypeDef *ptd)
+ {
+ return m_pRawMetaModelCommon->FindParentOfMethodHelper(md, ptd);
+ }
+
+ int CommonIsRo()
+ {
+ return m_pRawMetaModelCommon->CommonIsRo();
+ }
+
+ private:
+ //=========================================================
+ // Private instance data
+ //=========================================================
+ IMDCommon *m_pRawMDCommon;
+ IMetaModelCommon *m_pRawMetaModelCommon;
+ IMDInternalImport *m_pRawInternalImport;
+ WinMDAdapter *m_pWinMDAdapter;
+ LONG m_cRef;
+}; // class WinMDInternalImportRO
+
+
+
+
+//========================================================================================
+// Entrypoint called by GetMDInternalInterface()
+//========================================================================================
+HRESULT CreateWinMDInternalImportRO(IMDCommon * pRawMDCommon, REFIID riid, /*[out]*/ void **ppWinMDInternalImport)
+{
+ HRESULT hr;
+ *ppWinMDInternalImport = NULL;
+ WinMDInternalImportRO *pNewImport = NULL;
+ IfFailGo(WinMDInternalImportRO::Create(pRawMDCommon, &pNewImport));
+ IfFailGo(pNewImport->QueryInterface(riid, ppWinMDInternalImport));
+ hr = S_OK;
+
+ ErrExit:
+ if (pNewImport)
+ pNewImport->Release();
+ return hr;
+
+}
+
+#endif // FEATURE_METADATA_INTERNAL_APIS
+
+
+
diff --git a/src/md/winmd/wks/.gitmirror b/src/md/winmd/wks/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/md/winmd/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/winmd/wks/CMakeLists.txt b/src/md/winmd/wks/CMakeLists.txt
new file mode 100644
index 0000000000..139b68b28c
--- /dev/null
+++ b/src/md/winmd/wks/CMakeLists.txt
@@ -0,0 +1,4 @@
+include(../../md_wks.cmake)
+
+add_precompiled_header(stdafx.h ../stdafx.cpp MDWINMD_SOURCES)
+add_library_clr(mdwinmd_wks ${MDWINMD_SOURCES}) \ No newline at end of file
diff --git a/src/md/winmd/wks/MDWinMD_wks.nativeproj b/src/md/winmd/wks/MDWinMD_wks.nativeproj
new file mode 100644
index 0000000000..cc0a6f2bc1
--- /dev/null
+++ b/src/md/winmd/wks/MDWinMD_wks.nativeproj
@@ -0,0 +1,19 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <!-- All features are set in file:..\..\MD.props -->
+ <MetadataFlavor>wks</MetadataFlavor>
+ </PropertyGroup>
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\WinMD\WinMD.settings.targets" />
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>MDWinMD_wks</OutputName>
+ </PropertyGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>