diff --git a/.gitignore b/.gitignore index bf4ee2cd..64c07366 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ artifacts/ .vs/ .idea/ -*~ \ No newline at end of file +*~ +TestResults/ diff --git a/configure.cmake b/configure.cmake index dbdb931f..6813b7f1 100644 --- a/configure.cmake +++ b/configure.cmake @@ -55,4 +55,10 @@ endif() # # Agnostic compiler/platform settings # -add_compile_definitions(__STDC_WANT_LIB_EXT1__=1) # https://en.cppreference.com/w/c/error#Bounds_checking \ No newline at end of file +add_compile_definitions(__STDC_WANT_LIB_EXT1__=1) # https://en.cppreference.com/w/c/error#Bounds_checking + +option(DNMD_ENABLE_PROFILING OFF) + +if (DNMD_ENABLE_PROFILING AND MSVC) + add_link_options(/PROFILE) +endif() \ No newline at end of file diff --git a/external/dncp b/external/dncp index 2b2e74b5..fd609bbb 160000 --- a/external/dncp +++ b/external/dncp @@ -1 +1 @@ -Subproject commit 2b2e74b5c79b5b61b6778c37d3e68be85b71c1c5 +Subproject commit fd609bbbaa4a2416992460d5558b9c680cc98204 diff --git a/src/dnmd/CMakeLists.txt b/src/dnmd/CMakeLists.txt index 5d7a5ecc..436451be 100644 --- a/src/dnmd/CMakeLists.txt +++ b/src/dnmd/CMakeLists.txt @@ -37,10 +37,12 @@ target_include_directories(dnmd PUBLIC $) target_include_directories(dnmd_pdb PUBLIC $) set_target_properties(dnmd PROPERTIES - PUBLIC_HEADER "../inc/dnmd.h;../inc/dnmd.hpp") + PUBLIC_HEADER "../inc/dnmd.h;../inc/dnmd.hpp" + POSITION_INDEPENDENT_CODE ON) set_target_properties(dnmd_pdb PROPERTIES - PUBLIC_HEADER "../inc/dnmd.h;../inc/dnmd.hpp;../inc/dnmd_pdb.h") + PUBLIC_HEADER "../inc/dnmd.h;../inc/dnmd.hpp;../inc/dnmd_pdb.h" + POSITION_INDEPENDENT_CODE ON) install(TARGETS dnmd dnmd_pdb EXPORT dnmd PUBLIC_HEADER DESTINATION include diff --git a/src/dnmd/editor.c b/src/dnmd/editor.c index fc4d7c81..753dbd5d 100644 --- a/src/dnmd/editor.c +++ b/src/dnmd/editor.c @@ -568,7 +568,7 @@ static bool reserve_heap_space(mdeditor_t* editor, uint32_t space_size, mdtcol_t { // Set the default heap size based on likely reasonable sizes for the heaps. // In most images, there won't be more than three guids, so we can start with a small heap in that case. - const size_t initial_heap_size = heap_id == mdtc_hguid ? sizeof(mdguid_t) * 3 : 0x100; + size_t const initial_heap_size = heap_id == mdtc_hguid ? sizeof(mdguid_t) * 3 : 0x100; void* mem = alloc_mdmem(editor->cxt, initial_heap_size); if (mem == NULL) return false; @@ -628,6 +628,14 @@ static bool reserve_heap_space(mdeditor_t* editor, uint32_t space_size, mdtcol_t uint32_t add_to_string_heap(mdcxt_t* cxt, char const* str) { + // II.24.2.3 - When the #String heap is present, the first entry is always the empty string (i.e., \0). + // II.24.2.2 - Streams need not be there if they are empty. + // We can avoid allocating the heap if the only entry is the empty string. + // Columns that point to the string heap can be 0 if there is no #String heap. + // In that case, they represent the empty string. + if (str[0] == '\0') + return 0; + mdeditor_t* editor = get_editor(cxt); if (editor == NULL) return 0; @@ -646,12 +654,21 @@ uint32_t add_to_string_heap(mdcxt_t* cxt, char const* str) uint32_t add_to_blob_heap(mdcxt_t* cxt, uint8_t const* data, uint32_t length) { + // II.24.2.4 - When the #Blob heap is present, the first entry is always the empty blob. + // II.24.2.2 - Streams need not be there if they are empty. + // We can avoid allocating the heap if the only entry is the empty blob. + // Columns that point to the blob heap can be 0 if there is no #Blob heap. + // In that case, they represent the empty blob. + if (length == 0) + return 0; + mdeditor_t* editor = get_editor(cxt); if (editor == NULL) return 0; + // TODO: Deduplicate heap uint8_t compressed_length[4]; - size_t compressed_length_size = 0; + size_t compressed_length_size = ARRAY_SIZE(compressed_length); if (!compress_u32(length, compressed_length, &compressed_length_size)) return 0; @@ -670,16 +687,12 @@ uint32_t add_to_blob_heap(mdcxt_t* cxt, uint8_t const* data, uint32_t length) uint32_t add_to_user_string_heap(mdcxt_t* cxt, char16_t const* str) { - mdeditor_t* editor = get_editor(cxt); - if (editor == NULL) - return 0; - // TODO: Deduplicate heap uint32_t str_len; uint8_t has_special_char = 0; for (str_len = 0; str[str_len] != (char16_t)0; str_len++) { char16_t c = str[str_len]; - // II.23.2.4 + // II.24.2.4 // There is an additional terminal byte which holds a 1 or 0. // The 1 signifies Unicode characters that require handling beyond // that normally provided for 8-bit encoding sets. @@ -713,31 +726,61 @@ uint32_t add_to_user_string_heap(mdcxt_t* cxt, char16_t const* str) } } - uint8_t compressed_length[sizeof(str_len)]; - size_t compressed_length_size = 0; - if (!compress_u32(str_len, compressed_length, &compressed_length_size)) + // II.24.2.4 - When the #US heap is present, the first entry is always the empty blob. + // II.24.2.2 - Streams need not be there if they are empty. + // We can avoid allocating the heap if the only entry is the empty blob. + // Indices into the #US heap can be 0 if there is no #US heap. + // In that case, they represent the empty userstring blob. + if (str_len == 0) return 0; - uint32_t heap_slot_size = str_len + (uint32_t)compressed_length_size + 1; + mdeditor_t* editor = get_editor(cxt); + if (editor == NULL) + return 0; + + // TODO: Deduplicate heap + + // II.24.2.4 + // Strings in the #US (user string) heap are encoded using 16-bit Unicode encodings. + // The count on each string is the number of bytes (not characters) in the string. + // Furthermore, there is an additional terminal byte (so all byte counts are odd, not even). + size_t us_blob_bytes = str_len * sizeof(char16_t) + 1; + + // The string is too long to represent in the heap. + if (us_blob_bytes > INT32_MAX) + return 0; + uint8_t compressed_length[sizeof(uint32_t)]; + size_t compressed_length_size = ARRAY_SIZE(compressed_length); + if (!compress_u32((uint32_t)us_blob_bytes, compressed_length, &compressed_length_size)) + return 0; + + uint32_t heap_slot_size = (uint32_t)us_blob_bytes + (uint32_t)compressed_length_size; uint32_t heap_offset; if (!reserve_heap_space(editor, heap_slot_size, mdtc_hus, false, &heap_offset)) { return 0; } + // Copy the compressed blob length into the heap. memcpy(editor->user_string_heap.heap.ptr + heap_offset, compressed_length, compressed_length_size); - memcpy(editor->user_string_heap.heap.ptr + heap_offset + compressed_length_size, str, str_len); + // Copy the UTF-16-encoded user string into the heap. + memcpy(editor->user_string_heap.heap.ptr + heap_offset + compressed_length_size, str, us_blob_bytes - 1); - editor->user_string_heap.heap.ptr[heap_offset + compressed_length_size + str_len] = has_special_char; + // Set the trailing byte. + editor->user_string_heap.heap.ptr[heap_offset + compressed_length_size + us_blob_bytes - 1] = has_special_char; return heap_offset; } +mdguid_t const empty_guid = { 0 }; + uint32_t add_to_guid_heap(mdcxt_t* cxt, mdguid_t guid) { mdeditor_t* editor = get_editor(cxt); if (editor == NULL) return 0; // TODO: Deduplicate heap + if (memcmp(&guid, &empty_guid, sizeof(mdguid_t)) == 0) + return 0; uint32_t heap_offset; if (!reserve_heap_space(editor, sizeof(mdguid_t), mdtc_hguid, false, &heap_offset)) @@ -746,7 +789,10 @@ uint32_t add_to_guid_heap(mdcxt_t* cxt, mdguid_t guid) } memcpy(editor->guid_heap.heap.ptr + heap_offset, &guid, sizeof(mdguid_t)); - return heap_offset / sizeof(mdguid_t); + // II.22 - The Guid heap is an array of GUIDs, each 16 bytes wide. Its + // first element is numbered 1, its second 2, and so on. + // So, we need to make the offset 1-based and at the scale of the GUID size. + return (heap_offset / sizeof(mdguid_t)) + 1; } bool append_heap(mdcxt_t* cxt, mdcxt_t* delta, mdtcol_t heap_id) diff --git a/src/dnmd/entry.c b/src/dnmd/entry.c index 7a68a84b..ffe71352 100644 --- a/src/dnmd/entry.c +++ b/src/dnmd/entry.c @@ -297,7 +297,7 @@ static bool initialize_minimal_table_rows(mdcxt_t* cxt) if (1 != md_set_column_value_as_utf8(global_type_cursor, mdtTypeDef_TypeNamespace, 1, &namespace)) return false; - mdToken nil_typedef = CreateTokenType(mdtTypeDef); + mdToken nil_typedef = CreateTokenType(mdtid_TypeDef); if (1 != md_set_column_value_as_token(global_type_cursor, mdtTypeDef_Extends, 1, &nil_typedef)) return false; @@ -361,26 +361,21 @@ mdhandle_t md_create_new_pdb_handle() } #endif // DNMD_PORTABLE_PDB -bool md_apply_delta(mdhandle_t handle, void const* data, size_t data_len) +bool md_apply_delta(mdhandle_t handle, mdhandle_t delta_handle) { mdcxt_t* base = extract_mdcxt(handle); if (base == NULL) return false; - mdhandle_t h; - if (!md_create_handle(data, data_len, &h)) + mdcxt_t* delta = extract_mdcxt(delta_handle); + if (delta == NULL) return false; - mdcxt_t* delta = extract_mdcxt(h); - assert(delta != NULL); - // Verify the supplied delta is actually a delta file bool result = false; if (delta->context_flags & mdc_minimal_delta) result = merge_in_delta(base, delta); - // Free all data for the delta - md_destroy_handle(h); return result; } diff --git a/src/dnmd/internal.h b/src/dnmd/internal.h index 46d408fa..58b6c980 100644 --- a/src/dnmd/internal.h +++ b/src/dnmd/internal.h @@ -430,4 +430,6 @@ bool sort_list_by_column(mdcursor_t parent, col_index_t list_col, col_index_t co // Add the heap with the specified id from the delta image to the cxt image. bool append_heap(mdcxt_t* cxt, mdcxt_t* delta, mdtcol_t heap_id); +extern mdguid_t const empty_guid; + #endif // _SRC_DNMD_INTERNAL_H_ diff --git a/src/dnmd/streams.c b/src/dnmd/streams.c index 2288b5ab..16f303ca 100644 --- a/src/dnmd/streams.c +++ b/src/dnmd/streams.c @@ -5,6 +5,17 @@ bool try_get_string(mdcxt_t* cxt, size_t offset, char const** str) assert(cxt != NULL && str != NULL); mdstream_t* h = &cxt->strings_heap; + + // II.24.2.3 - When the #String heap is present, the first entry is always the empty string (i.e., \0). + // II.24.2.2 - Streams need not be there if they are empty. + // If the offset into the heap is 0, we can treat that as a "null" index into the heap and return + // the empty string. + if (h->size == 0 && offset == 0) + { + *str = "\0"; // II.24.2.3 'The first character must be the '\0' character. + return true; + } + if (h->size <= offset) return false; @@ -81,6 +92,15 @@ bool try_get_blob(mdcxt_t* cxt, size_t offset, uint8_t const** blob, uint32_t* b assert(cxt != NULL && blob != NULL && blob_len != NULL); mdstream_t* h = &cxt->blob_heap; + + if (h->size == 0 && offset == 0) + { + // The first element must be the 0 - II.24.2.4 + *blob = h->ptr; + *blob_len = 0; + return true; + } + if (h->size <= offset) return false; diff --git a/src/dnmd/tables.c b/src/dnmd/tables.c index b5b05696..ef890e15 100644 --- a/src/dnmd/tables.c +++ b/src/dnmd/tables.c @@ -772,11 +772,18 @@ bool initialize_new_table_details( // Set the new table's row count temporarily to 1 to ensure that we initialize the table. table_row_counts[id] = 1; + // We'll treat any new table that has keys as sorted. + // We only want to do this for tables with keys as tables without keys + // never use the is_sorted bit. + md_key_info_t const* keys; + uint8_t key_count = get_table_keys(id, &keys); + bool has_keys = key_count != 0; + if (!initialize_table_details( table_row_counts, cxt->context_flags, id, - false, + has_keys, table)) return false; diff --git a/src/dnmd/write.c b/src/dnmd/write.c index 23fb5b16..2821d3da 100644 --- a/src/dnmd/write.c +++ b/src/dnmd/write.c @@ -343,16 +343,10 @@ int32_t md_set_column_value_as_utf8(mdcursor_t c, col_index_t col_idx, uint32_t do { uint32_t heap_offset; - if (str[written][0] == '\0') - { - heap_offset = 0; - } - else - { - heap_offset = add_to_string_heap(CursorTable(&c)->cxt, str[written]); - if (heap_offset == 0) - return -1; - } + heap_offset = add_to_string_heap(CursorTable(&c)->cxt, str[written]); + + if (heap_offset == 0 && str[written][0] != '\0') + return -1; if (!write_column_data(&acxt, heap_offset)) return -1; @@ -384,7 +378,7 @@ int32_t md_set_column_value_as_blob(mdcursor_t c, col_index_t col_idx, uint32_t { uint32_t heap_offset = add_to_blob_heap(CursorTable(&c)->cxt, blob[written], blob_len[written]); - if (heap_offset == 0) + if (heap_offset == 0 && blob_len[written] != 0) return -1; if (!write_column_data(&acxt, heap_offset)) @@ -417,7 +411,7 @@ int32_t md_set_column_value_as_guid(mdcursor_t c, col_index_t col_idx, uint32_t { uint32_t index = add_to_guid_heap(CursorTable(&c)->cxt, guid[written]); - if (index == 0) + if (index == 0 && memcmp(&guid[written], &empty_guid, sizeof(mdguid_t)) != 0) return -1; if (!write_column_data(&acxt, index)) @@ -450,7 +444,7 @@ int32_t md_set_column_value_as_userstring(mdcursor_t c, col_index_t col_idx, uin { uint32_t index = add_to_user_string_heap(CursorTable(&c)->cxt, userstring[written]); - if (index == 0) + if (index == 0 && userstring[written][0] != 0) return -1; if (!write_column_data(&acxt, index)) @@ -701,9 +695,10 @@ static bool add_new_row_to_list(mdcursor_t list_owner, col_index_t list_col, mdc if (!md_get_column_value_as_range(list_owner, list_col, &range, &count)) return false; - // Assert that the insertion location is in our range. + // Assert that the insertion location is in our range or points to the first row of the next range. + // For a zero-length range, row_to_insert_before will be the first row of the next range, so we need to account for that. assert(CursorTable(&range) == CursorTable(&row_to_insert_before)); - assert(CursorRow(&range) <= CursorRow(&row_to_insert_before) && CursorRow(&row_to_insert_before) <= CursorRow(&range) + count); + assert(CursorRow(&range) <= CursorRow(&row_to_insert_before) && CursorRow(&row_to_insert_before) <= CursorRow(&range) + (count == 0 ? 1 : count)); mdcursor_t target_row; // If the range is in an indirection table, we'll normalize our insert to the actual target table. @@ -724,10 +719,11 @@ static bool add_new_row_to_list(mdcursor_t list_owner, col_index_t list_col, mdc if (!md_set_column_value_as_cursor(new_indirection_row, index_to_col(0, CursorTable(&row_to_insert_before)->table_id), 1, new_row)) return false; - if (count == 0) + if (count == 0 || CursorRow(&range) == CursorRow(&row_to_insert_before)) { // If our original count was zero, then this is the first element in the list for this parent. - // We need to update the parent's row column to point to the newly inserted row. + // If the start of our range is the same as the row we're inserting before, then we're inserting at the start of the list. + // In both of these cases, we need to update the parent's row column to point to the newly inserted row. // Otherwise, this element would be associated with the entry before the parent row. if (!md_set_column_value_as_cursor(list_owner, list_col, 1, &new_indirection_row)) return false; @@ -869,10 +865,9 @@ bool md_add_new_row_to_sorted_list(mdcursor_t list_owner, col_index_t list_col, // so start searching there to make this a little faster. mdcursor_t row_to_check = row_to_insert_before; - // Move our cursor to the last row in the list. + // Move our cursor to the last row in the list and move back one more row each iteration. // This can't return false as we got to row_to_insert_before by moving forward at least one row. - (void)md_cursor_move(&row_to_check, -1); - for (; CursorRow(&row_to_check) >= CursorRow(&existing_range); (void)md_cursor_move(&row_to_check, -1)) + for (; md_cursor_move(&row_to_check, -1) && CursorRow(&row_to_check) >= CursorRow(&existing_range);) { // If the range is in an indirection table, we need to normalize to the target table to // get the sort column value. @@ -894,6 +889,15 @@ bool md_add_new_row_to_sorted_list(mdcursor_t list_owner, col_index_t list_col, break; } } + + // If we didn't find a row with a sort order less than or equal to the new row, we want to insert the new row at the beginning of the list. + // If our cursor is pointing at the first row, that means that our existing range starts at the first row. + if (CursorRow(&row_to_check) == 1 || CursorRow(&row_to_check) < CursorRow(&existing_range)) + { + // We didn't find a row with a sort order less than or equal to the new row. + // So we want to insert the new row at the beginning of the list. + row_to_insert_before = existing_range; + } } if (!add_new_row_to_list(list_owner, list_col, row_to_insert_before, new_row)) @@ -954,6 +958,13 @@ static bool validate_row_sorted_within_table(mdcursor_t row) void md_commit_row_add(mdcursor_t row) { mdtable_t* table = CursorTable(&row); + + // If this method is called with a zero-initialized cursor, + // no-op. This helps make the C++ helper md_added_row_t function more easily. + // This also allows users to call this method in all cases, even if the row-add fails. + if (table == NULL) + return; + assert(table->is_adding_new_row); // If the table was previously sorted, diff --git a/src/inc/dnmd.h b/src/inc/dnmd.h index 98361142..19e5a9ea 100644 --- a/src/inc/dnmd.h +++ b/src/inc/dnmd.h @@ -54,7 +54,7 @@ mdhandle_t md_create_new_pdb_handle(); #endif // DNMD_PORTABLE_PDB // Apply delta data to the current metadata. -bool md_apply_delta(mdhandle_t handle, void const* data, size_t data_len); +bool md_apply_delta(mdhandle_t handle, mdhandle_t delta_handle); // Destroy the metadata handle and free all associated memory. void md_destroy_handle(mdhandle_t handle); diff --git a/src/interfaces/CMakeLists.txt b/src/interfaces/CMakeLists.txt index cfbaadca..5b449917 100644 --- a/src/interfaces/CMakeLists.txt +++ b/src/interfaces/CMakeLists.txt @@ -2,6 +2,7 @@ set(SOURCES ./dispenser.cpp ./symbinder.cpp ./metadataimport.cpp + ./metadataemit.cpp ./hcorenum.cpp ./pal.cpp ./signatures.cpp @@ -12,6 +13,7 @@ set(HEADERS ../inc/dnmd_interfaces.hpp ../inc/internal/span.hpp ./metadataimportro.hpp + ./metadataemit.hpp ./hcorenum.hpp ./controllingiunknown.hpp ./tearoffbase.hpp @@ -26,6 +28,12 @@ if(NOT MSVC) list(APPEND SOURCES ./iids.cpp ./options.cpp) endif() +if (WIN32) + # Disable "secure CRT" warnings when targeting Windows + # as the "secure CRT" is not cross-platform. + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) +endif() + add_library(dnmd_interfaces_static STATIC ${SOURCES} diff --git a/src/interfaces/dispenser.cpp b/src/interfaces/dispenser.cpp index 8f9802b0..4141de6b 100644 --- a/src/interfaces/dispenser.cpp +++ b/src/interfaces/dispenser.cpp @@ -10,6 +10,7 @@ #include "dnmd_interfaces.hpp" #include "controllingiunknown.hpp" #include "metadataimportro.hpp" +#include "metadataemit.hpp" #include @@ -60,6 +61,7 @@ namespace try { mdhandle_view handle_view{ obj->CreateAndAddTearOff(std::move(md_ptr)) }; + (void)obj->CreateAndAddTearOff(handle_view); (void)obj->CreateAndAddTearOff(std::move(handle_view)); } catch(std::bad_alloc const&) @@ -93,10 +95,6 @@ namespace if (ppIUnk == nullptr) return E_INVALIDARG; - // Only support the read-only state - if (!(dwOpenFlags & ofReadOnly)) - return E_INVALIDARG; - dncp::cotaskmem_ptr nowOwned; if (dwOpenFlags & ofTakeOwnership) nowOwned.reset((void*)pData); @@ -126,6 +124,10 @@ namespace try { mdhandle_view handle_view{ obj->CreateAndAddTearOff(std::move(md_ptr), std::move(copiedMem), std::move(nowOwned)) }; + + if (!(dwOpenFlags & ofReadOnly)) + (void)obj->CreateAndAddTearOff(handle_view); + (void)obj->CreateAndAddTearOff(std::move(handle_view)); } catch(std::bad_alloc const&) diff --git a/src/interfaces/iids.cpp b/src/interfaces/iids.cpp index c4dd0b3a..4c8dfc77 100644 --- a/src/interfaces/iids.cpp +++ b/src/interfaces/iids.cpp @@ -14,6 +14,7 @@ MIDL_DEFINE_GUID(IID_IMetaDataImport2,0xfce5efa0,0x8bba,0x4f8e,0xa0,0x36,0x8f,0x MIDL_DEFINE_GUID(IID_IMetaDataAssemblyImport,0xee62470b,0xe94b,0x424e,0x9b,0x7c,0x2f,0x00,0xc9,0x24,0x9f,0x93); MIDL_DEFINE_GUID(IID_IMetaDataEmit, 0xba3fee4c, 0xecb9, 0x4e41, 0x83, 0xb7, 0x18, 0x3f, 0xa4, 0x1c, 0xd8, 0x59); MIDL_DEFINE_GUID(IID_IMetaDataEmit2, 0xf5dd9950, 0xf693, 0x42e6, 0x83, 0xe, 0x7b, 0x83, 0x3e, 0x81, 0x46, 0xa9); +MIDL_DEFINE_GUID(IID_IMetaDataAssemblyEmit, 0x211ef15b, 0x5317, 0x4438, 0xb1, 0x96, 0xde, 0xc8, 0x7b, 0x88, 0x76, 0x93); // Define the ISymUnmanaged* IIDs here - corsym.h provides the declaration. MIDL_DEFINE_GUID(IID_ISymUnmanagedBinder, 0xaa544d42, 0x28cb, 0x11d3, 0xbd, 0x22, 0x00, 0x00, 0xf8, 0x08, 0x49, 0xbd); diff --git a/src/interfaces/importhelpers.cpp b/src/interfaces/importhelpers.cpp index 85dfbbcc..3dbb33e6 100644 --- a/src/interfaces/importhelpers.cpp +++ b/src/interfaces/importhelpers.cpp @@ -71,9 +71,9 @@ namespace // The byte values of the ECMA pseudo public key and its token. // Arcade SDK StrongNameKeyId: ECMA - // See II.6.2.1.3 for th definition of this key. + // See II.6.2.1.3 for the definition of this key. uint8_t const EcmaPublicKey[] = { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0 }; - const StrongNameToken EcmaToken = { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }; + StrongNameToken const EcmaToken = { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }; // Arcade SDK StrongNameKeyId: Microsoft uint8_t const Microsoft[] = @@ -167,7 +167,7 @@ namespace StrongNameToken const& Token; }; - static const WellKnownKey WellKnownKeys[] = + static WellKnownKey const WellKnownKeys[] = { { EcmaPublicKey, sizeof(EcmaPublicKey), EcmaToken }, { Microsoft, sizeof(Microsoft), MicrosoftToken }, @@ -1713,3 +1713,107 @@ HRESULT ImportReferenceToTypeDefOrRefOrSpec( return E_INVALIDARG; } } + +HRESULT DefineImportMember( + IMetaDataEmit* emit, // [In] Module into which the Member is imported. + IMetaDataAssemblyImport *pAssemImport, // [IN] Assembly containing the Member. + void const *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. +{ + HRESULT hr; + assert(pImport && pmr); + assert(TypeFromToken(tkImport) == mdtTypeRef || TypeFromToken(tkImport) == mdtModuleRef || + IsNilToken(tkImport) || TypeFromToken(tkImport) == mdtTypeSpec); + assert((TypeFromToken(mbMember) == mdtMethodDef && mbMember != mdMethodDefNil) || + (TypeFromToken(mbMember) == mdtFieldDef && mbMember != mdFieldDefNil)); + + size_t memberNameSize = 128; + std::unique_ptr memberName { new WCHAR[memberNameSize] }; // Name of the imported member. + GUID mvidImport; // MVID of the import module. + GUID mvidEmit; // MVID of the emit module. + PCCOR_SIGNATURE pvSig; // Member's signature. + ULONG cbSig; // Length of member's signature. + ULONG translatedSigLength; // Length of translated signature. + + if (TypeFromToken(mbMember) == mdtMethodDef) + { + ULONG acutalNameLength; + for (;;) + { + hr = pImport->GetMethodProps(mbMember, nullptr, memberName.get(), (DWORD)memberNameSize, &acutalNameLength, + nullptr, &pvSig, &cbSig, nullptr, nullptr); + if (hr == CLDB_S_TRUNCATION) + { + memberName.reset(new WCHAR[acutalNameLength]); + memberNameSize = (size_t)acutalNameLength; + continue; + } + break; + } + } + else // TypeFromToken(mbMember) == mdtFieldDef + { + ULONG acutalNameLength; + for (;;) + { + hr = pImport->GetMethodProps(mbMember, nullptr, memberName.get(),(DWORD)memberNameSize, &acutalNameLength, + nullptr, &pvSig,&cbSig, nullptr, nullptr); + if (hr == CLDB_S_TRUNCATION) + { + memberName.reset(new WCHAR[acutalNameLength]); + memberNameSize = (size_t)acutalNameLength; + continue; + } + break; + } + } + RETURN_IF_FAILED(hr); + + ULONG sigSizeMax = cbSig * 3; // Set translated signature buffer size conservatively. + std::unique_ptr translatedSig { new uint8_t[sigSizeMax] }; + + RETURN_IF_FAILED(emit->TranslateSigWithScope( + pAssemImport, + pbHashValue, + cbHashValue, + pImport, + pvSig, + cbSig, + pAssemEmit, + emit, + translatedSig.get(), + sigSizeMax, + &translatedSigLength)); + + // Define ModuleRef for imported Member functions + + // Check if the Member being imported is a global function. + dncp::com_ptr pEmitImport; + RETURN_IF_FAILED(emit->QueryInterface(IID_IMetaDataImport, (void**)&pEmitImport)); + RETURN_IF_FAILED(pEmitImport->GetScopeProps(nullptr, 0, nullptr, &mvidEmit)); + + DWORD scopeNameSize; + RETURN_IF_FAILED(pImport->GetScopeProps(nullptr, 0, &scopeNameSize, &mvidImport)); + if (mvidEmit != mvidImport && IsNilToken(tkImport)) + { + std::unique_ptr scopeName { new WCHAR[scopeNameSize] }; // Name of the imported member's scope. + RETURN_IF_FAILED(pImport->GetScopeProps(scopeName.get(), scopeNameSize, + nullptr, nullptr)); + RETURN_IF_FAILED(emit->DefineModuleRef(scopeName.get(), &tkImport)); + } + + // Define MemberRef base on the name, sig, and parent + RETURN_IF_FAILED(emit->DefineMemberRef( + tkImport, + memberName.get(), + translatedSig.get(), + translatedSigLength, + pmr)); + + return S_OK; +} diff --git a/src/interfaces/importhelpers.hpp b/src/interfaces/importhelpers.hpp index c739fb60..df675119 100644 --- a/src/interfaces/importhelpers.hpp +++ b/src/interfaces/importhelpers.hpp @@ -27,4 +27,17 @@ HRESULT ImportReferenceToTypeDefOrRefOrSpec( std::function onRowAdded, mdToken* importedToken); +// Import a reference to a MemberRef row from one module and assembly pair to another. +// This method works at the IMetadataEmit/Import level as it is implementation-agnostic. +HRESULT DefineImportMember( + IMetaDataEmit* emit, + IMetaDataAssemblyImport *pAssemImport, + void const *pbHashValue, + ULONG cbHashValue, + IMetaDataImport *pImport, + mdToken mbMember, + IMetaDataAssemblyEmit *pAssemEmit, + mdToken tkImport, + mdMemberRef *pmr); + #endif // _SRC_INTERFACES_IMPORTHELPERS_HPP diff --git a/src/interfaces/metadataemit.cpp b/src/interfaces/metadataemit.cpp new file mode 100644 index 00000000..446ce5cf --- /dev/null +++ b/src/interfaces/metadataemit.cpp @@ -0,0 +1,3172 @@ +#include "metadataemit.hpp" +#include "importhelpers.hpp" +#include "signatures.hpp" +#include "pal.hpp" +#include +#include +#include +#include +#include +#include + +#define RETURN_IF_FAILED(exp) \ +{ \ + hr = (exp); \ + if (FAILED(hr)) \ + { \ + return hr; \ + } \ +} + +#define MD_MODULE_TOKEN TokenFromRid(1, mdtModule) +#define MD_GLOBAL_PARENT_TOKEN TokenFromRid(1, mdtTypeDef) + +namespace +{ + void SplitTypeName( + char* typeName, + char const** nspace, + char const** name) + { + // Search for the last delimiter. + char* pos = std::strrchr(typeName, '.'); + if (pos == nullptr) + { + // No namespace is indicated by an empty string. + *nspace = ""; + *name = typeName; + } + else + { + *pos = '\0'; + *nspace = typeName; + *name = pos + 1; + } + } +} + +HRESULT MetadataEmit::SetModuleProps( + LPCWSTR szName) +{ + // If the name is null, we have nothing to do. + // COMPAT-BREAK: CoreCLR would still record the token in the EncLog in this case. + if (szName == nullptr) + return S_OK; + + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + mdcursor_t c; + uint32_t count; + if (!md_create_cursor(MetaData(), mdtid_Module, &c, &count)) + { + if (md_append_row(MetaData(), mdtid_Module, &c)) + { + md_commit_row_add(c); + } + else + { + return E_FAIL; + } + } + + // Search for a file name in the provided path + // and use that as the module name. + char* modulePath = cvt; + std::size_t len = std::strlen(modulePath); + char const* start = modulePath; + for (char const* p = modulePath + len - 1; p >= modulePath; p--) + { + if (*p == '\\' || *p == '/') + { + start = p + 1; + break; + } + } + + if (1 != md_set_column_value_as_utf8(c, mdtModule_Name, 1, &start)) + return E_FAIL; + + // TODO: Record ENC Log. + + return S_OK; +} + +HRESULT MetadataEmit::Save( + LPCWSTR szFile, + DWORD dwSaveFlags) +{ + if (dwSaveFlags != 0) + return E_INVALIDARG; + + pal::StringConvert cvt(szFile); + if (!cvt.Success()) + return E_INVALIDARG; + + size_t saveSize; + md_write_to_buffer(MetaData(), nullptr, &saveSize); + std::unique_ptr buffer { new uint8_t[saveSize] }; + if (!md_write_to_buffer(MetaData(), buffer.get(), &saveSize)) + return E_FAIL; + + std::FILE* file = std::fopen(cvt, "wb"); + if (file == nullptr) + { + return E_FAIL; + } + + size_t totalSaved = 0; + while (totalSaved < saveSize) + { + totalSaved += std::fwrite(buffer.get(), sizeof(uint8_t), saveSize - totalSaved, file); + if (ferror(file) != 0) + { + std::fclose(file); + return E_FAIL; + } + } + + if (std::fclose(file) == EOF) + { + return E_FAIL; + } + + return S_OK; +} + +HRESULT MetadataEmit::SaveToStream( + IStream *pIStream, + DWORD dwSaveFlags) +{ + HRESULT hr; + if (dwSaveFlags != 0) + return E_INVALIDARG; + + size_t saveSize; + md_write_to_buffer(MetaData(), nullptr, &saveSize); + std::unique_ptr buffer { new uint8_t[saveSize] }; + md_write_to_buffer(MetaData(), buffer.get(), &saveSize); + + size_t totalSaved = 0; + while (totalSaved < saveSize) + { + ULONG numBytesToWrite = (ULONG)std::min(saveSize, (size_t)std::numeric_limits::max()); + RETURN_IF_FAILED(pIStream->Write((char const*)buffer.get() + totalSaved, numBytesToWrite, nullptr)); + totalSaved += numBytesToWrite; + } + + return pIStream->Write(buffer.get(), (ULONG)saveSize, nullptr); +} + +HRESULT MetadataEmit::GetSaveSize( + CorSaveSize fSave, + DWORD *pdwSaveSize) +{ + // TODO: Do we want to support different save modes (as specified through dispenser options)? + // If so, we'll need to handle that here in addition to the ::Save* methods. + UNREFERENCED_PARAMETER(fSave); + size_t saveSize; + md_write_to_buffer(MetaData(), nullptr, &saveSize); + if (saveSize > std::numeric_limits::max()) + return CLDB_E_TOO_BIG; + *pdwSaveSize = (DWORD)saveSize; + return S_OK; +} + +HRESULT MetadataEmit::DefineTypeDef( + LPCWSTR szTypeDef, + DWORD dwTypeDefFlags, + mdToken tkExtends, + mdToken rtkImplements[], + mdTypeDef *ptd) +{ + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_TypeDef, &c)) + return E_FAIL; + + pal::StringConvert cvt(szTypeDef); + if (!cvt.Success()) + return E_INVALIDARG; + + // TODO: Check for duplicate type definitions + + char const* ns; + char const* name; + SplitTypeName(cvt, &ns, &name); + if (1 != md_set_column_value_as_utf8(c, mdtTypeDef_TypeNamespace, 1, &ns)) + return E_FAIL; + if (1 != md_set_column_value_as_utf8(c, mdtTypeDef_TypeName, 1, &name)) + return E_FAIL; + + // TODO: Handle reserved flags + uint32_t flags = (uint32_t)dwTypeDefFlags; + if (1 != md_set_column_value_as_constant(c, mdtTypeDef_Flags, 1, &flags)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtTypeDef_Extends, 1, &tkExtends)) + return E_FAIL; + + mdcursor_t fieldCursor; + uint32_t numFields; + if (!md_create_cursor(MetaData(), mdtid_Field, &fieldCursor, &numFields)) + { + mdToken nilField = mdFieldDefNil; + if (1 != md_set_column_value_as_token(c, mdtTypeDef_FieldList, 1, &nilField)) + return E_FAIL; + } + else + { + md_cursor_move(&fieldCursor, numFields); + if (1 != md_set_column_value_as_cursor(c, mdtTypeDef_FieldList, 1, &fieldCursor)) + return E_FAIL; + } + + mdcursor_t methodCursor; + uint32_t numMethods; + if (!md_create_cursor(MetaData(), mdtid_MethodDef, &methodCursor, &numMethods)) + { + mdToken nilMethod = mdMethodDefNil; + if (1 != md_set_column_value_as_token(c, mdtTypeDef_MethodList, 1, &nilMethod)) + return E_FAIL; + } + else + { + md_cursor_move(&methodCursor, numMethods); + if (1 != md_set_column_value_as_cursor(c, mdtTypeDef_MethodList, 1, &methodCursor)) + return E_FAIL; + } + + size_t i = 0; + + if (rtkImplements != nullptr) + { + for (mdToken currentImplementation = rtkImplements[i]; currentImplementation != mdTokenNil; currentImplementation = rtkImplements[++i]) + { + md_added_row_t interfaceImpl; + if (!md_append_row(MetaData(), mdtid_InterfaceImpl, &interfaceImpl)) + return E_FAIL; + + if (1 != md_set_column_value_as_cursor(interfaceImpl, mdtInterfaceImpl_Class, 1, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(interfaceImpl, mdtInterfaceImpl_Interface, 1, ¤tImplementation)) + return E_FAIL; + } + } + + // TODO: Update Enc Log + + if (!md_cursor_to_token(c, ptd)) + return E_FAIL; + + return S_OK; +} + +HRESULT MetadataEmit::DefineNestedType( + LPCWSTR szTypeDef, + DWORD dwTypeDefFlags, + mdToken tkExtends, + mdToken rtkImplements[], + mdTypeDef tdEncloser, + mdTypeDef *ptd) +{ + HRESULT hr; + + if (TypeFromToken(tdEncloser) != mdtTypeDef || IsNilToken(tdEncloser)) + return E_INVALIDARG; + + if (IsTdNested(dwTypeDefFlags)) + return E_INVALIDARG; + + RETURN_IF_FAILED(DefineTypeDef(szTypeDef, dwTypeDefFlags, tkExtends, rtkImplements, ptd)); + + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_NestedClass, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtNestedClass_NestedClass, 1, ptd)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtNestedClass_EnclosingClass, 1, &tdEncloser)) + return E_FAIL; + + // TODO: Update ENC log + return S_OK; +} + +HRESULT MetadataEmit::SetHandler( + IUnknown *pUnk) +{ + // The this implementation of MetadataEmit doesn't ever remap tokens, + // so this method (which is for registering a callback for when tokens are remapped) + // is a no-op. + UNREFERENCED_PARAMETER(pUnk); + return S_OK; +} + +HRESULT MetadataEmit::DefineMethod( + mdTypeDef td, + LPCWSTR szName, + DWORD dwMethodFlags, + PCCOR_SIGNATURE pvSigBlob, + ULONG cbSigBlob, + ULONG ulCodeRVA, + DWORD dwImplFlags, + mdMethodDef *pmd) +{ + if (TypeFromToken(td) != mdtTypeDef) + return E_INVALIDARG; + + mdcursor_t type; + if (!md_token_to_cursor(MetaData(), td, &type)) + return CLDB_E_FILE_CORRUPT; + + md_added_row_t newMethod; + if (!md_add_new_row_to_list(type, mdtTypeDef_MethodList, &newMethod)) + return E_FAIL; + + pal::StringConvert cvt(szName); + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(newMethod, mdtMethodDef_Name, 1, &name)) + return E_FAIL; + + uint32_t flags = dwMethodFlags; + if (1 != md_set_column_value_as_constant(newMethod, mdtMethodDef_Flags, 1, &flags)) + return E_FAIL; + + uint32_t sigLength = cbSigBlob; + if (1 != md_set_column_value_as_blob(newMethod, mdtMethodDef_Signature, 1, &pvSigBlob, &sigLength)) + return E_FAIL; + + uint32_t implFlags = dwImplFlags; + if (1 != md_set_column_value_as_constant(newMethod, mdtMethodDef_ImplFlags, 1, &implFlags)) + return E_FAIL; + + uint32_t rva = ulCodeRVA; + if (1 != md_set_column_value_as_constant(newMethod, mdtMethodDef_Rva, 1, &rva)) + return E_FAIL; + + if (!md_cursor_to_token(newMethod, pmd)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update ENC log + return S_OK; +} + +HRESULT MetadataEmit::DefineMethodImpl( + mdTypeDef td, + mdToken tkBody, + mdToken tkDecl) +{ + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_MethodImpl, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtMethodImpl_Class, 1, &td)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtMethodImpl_MethodBody, 1, &tkBody)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtMethodImpl_MethodDeclaration, 1, &tkDecl)) + return E_FAIL; + + // TODO: Update ENC log + return S_OK; +} + +HRESULT MetadataEmit::DefineTypeRefByName( + mdToken tkResolutionScope, + LPCWSTR szName, + mdTypeRef *ptr) +{ + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_TypeRef, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtTypeRef_ResolutionScope, 1, &tkResolutionScope)) + return E_FAIL; + + pal::StringConvert cv(szName); + + if (!cv.Success()) + return E_FAIL; + + char const* ns; + char const* name; + SplitTypeName(cv, &ns, &name); + + if (1 != md_set_column_value_as_utf8(c, mdtTypeRef_TypeNamespace, 1, &ns)) + return E_FAIL; + if (1 != md_set_column_value_as_utf8(c, mdtTypeRef_TypeName, 1, &name)) + return E_FAIL; + + if (!md_cursor_to_token(c, ptr)) + return E_FAIL; + + // TODO: Update ENC log + return S_OK; +} + +HRESULT MetadataEmit::DefineImportType( + IMetaDataAssemblyImport *pAssemImport, + void const *pbHashValue, + ULONG cbHashValue, + IMetaDataImport *pImport, + mdTypeDef tdImport, + IMetaDataAssemblyEmit *pAssemEmit, + mdTypeRef *ptr) +{ + HRESULT hr; + dncp::com_ptr assemImport{}; + + if (pAssemImport != nullptr) + RETURN_IF_FAILED(pAssemImport->QueryInterface(IID_IDNMDOwner, (void**)&assemImport)); + + dncp::com_ptr assemEmit{}; + if (pAssemEmit != nullptr) + RETURN_IF_FAILED(pAssemEmit->QueryInterface(IID_IDNMDOwner, (void**)&assemEmit)); + + if (pImport == nullptr) + return E_INVALIDARG; + + dncp::com_ptr import{}; + RETURN_IF_FAILED(pImport->QueryInterface(IID_IDNMDOwner, (void**)&import)); + + mdcursor_t originalTypeDef; + if (!md_token_to_cursor(import->MetaData(), tdImport, &originalTypeDef)) + return CLDB_E_FILE_CORRUPT; + + mdcursor_t importedTypeDef; + + RETURN_IF_FAILED(ImportReferenceToTypeDef( + originalTypeDef, + assemImport->MetaData(), + { reinterpret_cast(pbHashValue), cbHashValue }, + assemEmit->MetaData(), + MetaData(), + false, + [](mdcursor_t){}, + &importedTypeDef + )); + + if (!md_cursor_to_token(importedTypeDef, ptr)) + return E_FAIL; + + return S_OK; +} + +HRESULT MetadataEmit::DefineMemberRef( + mdToken tkImport, + LPCWSTR szName, + PCCOR_SIGNATURE pvSigBlob, + ULONG cbSigBlob, + mdMemberRef *pmr) +{ + if (IsNilToken(tkImport)) + tkImport = MD_GLOBAL_PARENT_TOKEN; + + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + char const* name = cvt; + + // TODO: Check for duplicates + + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_MemberRef, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtMemberRef_Class, 1, &tkImport)) + return E_FAIL; + + if (1 != md_set_column_value_as_utf8(c, mdtMemberRef_Name, 1, &name)) + return E_FAIL; + + uint8_t const* sig = (uint8_t const*)pvSigBlob; + uint32_t sigLength = cbSigBlob; + if (1 != md_set_column_value_as_blob(c, mdtMemberRef_Signature, 1, &sig, &sigLength)) + return E_FAIL; + + if (!md_cursor_to_token(c, pmr)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::DefineImportMember( + IMetaDataAssemblyImport *pAssemImport, + void const *pbHashValue, + ULONG cbHashValue, + IMetaDataImport *pImport, + mdToken mbMember, + IMetaDataAssemblyEmit *pAssemEmit, + mdToken tkParent, + mdMemberRef *pmr) +{ + return ::DefineImportMember( + this, + pAssemImport, + pbHashValue, + cbHashValue, + pImport, + mbMember, + pAssemEmit, + tkParent, + pmr); +} + +namespace +{ + HRESULT AddMethodSemantic(mdhandle_t md, mdcursor_t parent, CorMethodSemanticsAttr semantic, mdMethodDef method) + { + md_added_row_t addMethodSemantic; + if (!md_append_row(md, mdtid_MethodSemantics, &addMethodSemantic)) + return E_FAIL; + + if (1 != md_set_column_value_as_cursor(addMethodSemantic, mdtMethodSemantics_Association, 1, &parent)) + return E_FAIL; + + uint32_t semantics = semantic; + if (1 != md_set_column_value_as_constant(addMethodSemantic, mdtMethodSemantics_Semantics, 1, &semantics)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(addMethodSemantic, mdtMethodSemantics_Method, 1, &method)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; + } + + HRESULT DeleteParentedToken(mdhandle_t md, mdToken parent, mdtable_id_t childTable, col_index_t parentColumn) + { + mdcursor_t c; + uint32_t count; + if (!md_create_cursor(md, childTable, &c, &count)) + return CLDB_E_RECORD_NOTFOUND; + + if (!md_find_row_from_cursor(c, parentColumn, parent, &c)) + return CLDB_E_RECORD_NOTFOUND; + + mdToken nilParent = mdFieldDefNil; + if (1 != md_set_column_value_as_token(c, mdtFieldMarshal_Parent, 1, &nilParent)) + return E_FAIL; + + mdcursor_t parentCursor; + if (!md_token_to_cursor(md, parent, &parentCursor)) + return CLDB_E_FILE_CORRUPT; + // TODO: Update EncLog + return S_OK; + } + + HRESULT RemoveFlag(mdhandle_t md, mdToken tk, col_index_t flagsColumn, uint32_t flagToRemove) + { + // TODO: Update EncLog + mdcursor_t c; + if (!md_token_to_cursor(md, tk, &c)) + return CLDB_E_FILE_CORRUPT; + + uint32_t flags; + if (1 != md_get_column_value_as_constant(c, flagsColumn, 1, &flags)) + return E_FAIL; + + flags &= ~flagToRemove; + if (1 != md_set_column_value_as_constant(c, flagsColumn, 1, &flags)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; + } + + HRESULT AddFlag(mdhandle_t md, mdToken tk, col_index_t flagsColumn, uint32_t flagToAdd) + { + // TODO: Update EncLog + mdcursor_t c; + if (!md_token_to_cursor(md, tk, &c)) + return CLDB_E_FILE_CORRUPT; + + uint32_t flags; + if (1 != md_get_column_value_as_constant(c, flagsColumn, 1, &flags)) + return E_FAIL; + + flags |= flagToAdd; + if (1 != md_set_column_value_as_constant(c, flagsColumn, 1, &flags)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; + } + + template + HRESULT FindOrCreateParentedRow(mdhandle_t md, mdToken parent, mdtable_id_t childTable, col_index_t parentCol, T const& setTableData) + { + HRESULT hr; + mdcursor_t c; + md_added_row_t addedRow; + uint32_t count; + if (!md_create_cursor(md, childTable, &c, &count) + || !md_find_row_from_cursor(c, parentCol, parent, &c)) + { + // TODO: Update EncLog + if (!md_append_row(md, childTable, &addedRow)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(addedRow, parentCol, 1, &parent)) + return E_FAIL; + c = addedRow; + } + RETURN_IF_FAILED(setTableData(c)); + + return S_OK; + } +} + +HRESULT MetadataEmit::DefineEvent( + mdTypeDef td, + LPCWSTR szEvent, + DWORD dwEventFlags, + mdToken tkEventType, + mdMethodDef mdAddOn, + mdMethodDef mdRemoveOn, + mdMethodDef mdFire, + mdMethodDef rmdOtherMethods[], + mdEvent *pmdEvent) +{ + assert(TypeFromToken(td) == mdtTypeDef && td != mdTypeDefNil); + assert(IsNilToken(tkEventType) || TypeFromToken(tkEventType) == mdtTypeDef || + TypeFromToken(tkEventType) == mdtTypeRef || TypeFromToken(tkEventType) == mdtTypeSpec); + assert(TypeFromToken(mdAddOn) == mdtMethodDef && mdAddOn != mdMethodDefNil); + assert(TypeFromToken(mdRemoveOn) == mdtMethodDef && mdRemoveOn != mdMethodDefNil); + assert(IsNilToken(mdFire) || TypeFromToken(mdFire) == mdtMethodDef); + assert(szEvent && pmdEvent); + + pal::StringConvert cvt(szEvent); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + + return FindOrCreateParentedRow(MetaData(), td, mdtid_EventMap, mdtEventMap_Parent, [=](mdcursor_t c) + { + HRESULT hr; + // TODO: Check for duplicates + md_added_row_t addedEvent; + if (!md_add_new_row_to_list(c, mdtEventMap_EventList, &addedEvent)) + return E_FAIL; + + if (1 != md_set_column_value_as_utf8(addedEvent, mdtEvent_Name, 1, &name)) + return E_FAIL; + + uint32_t flags = dwEventFlags; + if (1 != md_set_column_value_as_constant(addedEvent, mdtEvent_EventFlags, 1, &flags)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(addedEvent, mdtEvent_EventType, 1, &tkEventType)) + return E_FAIL; + + if (mdAddOn != mdMethodDefNil) + { + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), addedEvent, msAddOn, mdAddOn)); + } + + if (mdRemoveOn != mdMethodDefNil) + { + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), addedEvent, msRemoveOn, mdRemoveOn)); + } + + if (mdFire != mdMethodDefNil) + { + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), addedEvent, msFire, mdFire)); + } + + if (rmdOtherMethods != nullptr) + { + for (size_t i = 0; !IsNilToken(rmdOtherMethods[i]); i++) + { + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), addedEvent, msOther, rmdOtherMethods[i])); + } + } + + if (!md_cursor_to_token(addedEvent, pmdEvent)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; + }); +} + +HRESULT MetadataEmit::SetClassLayout( + mdTypeDef td, + DWORD dwPackSize, + COR_FIELD_OFFSET rFieldOffsets[], + ULONG ulClassSize) +{ + HRESULT hr; + assert(TypeFromToken(td) == mdtTypeDef); + + if (rFieldOffsets != nullptr) + { + for (size_t i = 0; rFieldOffsets[i].ridOfField != mdFieldDefNil; ++i) + { + if (rFieldOffsets[i].ulOffset != UINT32_MAX) + { + mdToken field = TokenFromRid(rFieldOffsets[i].ridOfField, mdtFieldDef); + uint32_t offset = rFieldOffsets[i].ulOffset; + RETURN_IF_FAILED(FindOrCreateParentedRow(MetaData(), field, mdtid_FieldLayout, mdtFieldLayout_Field, [=](mdcursor_t c) + { + if (1 != md_set_column_value_as_constant(c, mdtFieldLayout_Offset, 1, &offset)) + return E_FAIL; + + return S_OK; + })); + } + } + } + + RETURN_IF_FAILED(FindOrCreateParentedRow(MetaData(), td, mdtid_ClassLayout, mdtClassLayout_Parent, [=](mdcursor_t c) + { + uint32_t packSize = (uint32_t)dwPackSize; + if (1 != md_set_column_value_as_constant(c, mdtClassLayout_PackingSize, 1, &packSize)) + return E_FAIL; + + uint32_t classSize = (uint32_t)ulClassSize; + if (1 != md_set_column_value_as_constant(c, mdtClassLayout_ClassSize, 1, &classSize)) + return E_FAIL; + + return S_OK; + })); + + return S_OK; +} + +HRESULT MetadataEmit::DeleteClassLayout( + mdTypeDef td) +{ + assert(TypeFromToken(td) == mdtTypeDef); + HRESULT hr; + mdcursor_t c; + uint32_t count; + if (!md_create_cursor(MetaData(), mdtid_ClassLayout, &c, &count)) + return CLDB_E_RECORD_NOTFOUND; + + if (!md_find_row_from_cursor(c, mdtClassLayout_Parent, td, &c)) + return CLDB_E_RECORD_NOTFOUND; + + RETURN_IF_FAILED(DeleteParentedToken(MetaData(), td, mdtid_ClassLayout, mdtClassLayout_Parent)); + + // Now that we've deleted the class layout entry, + // we need to delete the field layout entries for the fields of the type. + mdcursor_t type; + if (!md_token_to_cursor(MetaData(), td, &type)) + return CLDB_E_FILE_CORRUPT; + + mdcursor_t field; + uint32_t fieldCount; + if (!md_get_column_value_as_range(type, mdtTypeDef_FieldList, &field, &fieldCount)) + return S_OK; + + for (uint32_t i = 0; i < fieldCount; ++i, md_cursor_next(&field)) + { + mdcursor_t resolvedField; + if (!md_resolve_indirect_cursor(field, &resolvedField)) + return E_FAIL; + + mdToken fieldToken; + if (!md_cursor_to_token(resolvedField, &fieldToken)) + return E_FAIL; + + hr = DeleteParentedToken(MetaData(), fieldToken, mdtid_FieldLayout, mdtFieldLayout_Field); + + // If we couldn't find the field layout entry, that's fine. + // If we hit another error, return that error. + if (hr == CLDB_E_RECORD_NOTFOUND) + continue; + RETURN_IF_FAILED(hr); + } + + return S_OK; +} + +HRESULT MetadataEmit::SetFieldMarshal( + mdToken tk, + PCCOR_SIGNATURE pvNativeType, + ULONG cbNativeType) +{ + mdcursor_t parent; + if (!md_token_to_cursor(MetaData(), tk, &parent)) + return CLDB_E_FILE_CORRUPT; + + col_index_t col = TypeFromToken(tk) == mdtFieldDef ? mdtField_Flags : mdtParam_Flags; + uint32_t flagToAdd = TypeFromToken(tk) == mdtFieldDef ? (uint32_t)fdHasFieldMarshal : (uint32_t)pdHasFieldMarshal; + uint32_t flags; + if (1 != md_get_column_value_as_constant(parent, col, 1, &flags)) + return E_FAIL; + + flags |= flagToAdd; + if (1 != md_set_column_value_as_constant(parent, col, 1, &flags)) + return E_FAIL; + + FindOrCreateParentedRow(MetaData(), tk, mdtid_FieldMarshal, mdtFieldMarshal_Parent, [=](mdcursor_t c) + { + uint8_t const* sig = (uint8_t const*)pvNativeType; + uint32_t sigLength = cbNativeType; + if (1 != md_set_column_value_as_blob(c, mdtFieldMarshal_NativeType, 1, &sig, &sigLength)) + return E_FAIL; + + return S_OK; + }); + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::DeleteFieldMarshal( + mdToken tk) +{ + HRESULT hr; + assert(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtParamDef); + assert(!IsNilToken(tk)); + + RETURN_IF_FAILED(DeleteParentedToken( + MetaData(), + tk, + mdtid_FieldMarshal, + mdtFieldMarshal_Parent)); + + RETURN_IF_FAILED(RemoveFlag( + MetaData(), + tk, + TypeFromToken(tk) == mdtFieldDef ? mdtField_Flags : mdtParam_Flags, + TypeFromToken(tk) == mdtFieldDef ? (uint32_t)fdHasFieldMarshal : (uint32_t)pdHasFieldMarshal)); + return S_OK; +} + +HRESULT MetadataEmit::DefinePermissionSet( + mdToken tk, + DWORD dwAction, + void const *pvPermission, + ULONG cbPermission, + mdPermission *ppm) +{ + // TODO: Check for duplicates + assert(TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtMethodDef || + TypeFromToken(tk) == mdtAssembly); + + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_DeclSecurity, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtDeclSecurity_Parent, 1, &tk)) + return E_FAIL; + + if (TypeFromToken(tk) == mdtTypeDef + || TypeFromToken(tk) == mdtMethodDef) + { + uint32_t flagToAdd = TypeFromToken(tk) == mdtTypeDef ? (uint32_t)tdHasSecurity : (uint32_t)mdHasSecurity; + col_index_t flagsCol = TypeFromToken(tk) == mdtTypeDef ? mdtTypeDef_Flags : mdtMethodDef_Flags; + + mdcursor_t parent; + if (1 != md_get_column_value_as_cursor(c, mdtDeclSecurity_Parent, 1, &parent)) + return E_FAIL; + + uint32_t flags; + if (1 != md_get_column_value_as_constant(parent, flagsCol, 1, &flags)) + return E_FAIL; + + flags |= flagToAdd; + + if (1 != md_set_column_value_as_constant(parent, flagsCol, 1, &flags)) + return E_FAIL; + // TODO: Update EncLog + } + + uint32_t action = dwAction; + if (1 != md_set_column_value_as_constant(c, mdtDeclSecurity_Action, 1, &action)) + return E_FAIL; + + uint8_t const* permission = (uint8_t const*)pvPermission; + uint32_t permissionLength = cbPermission; + if (1 != md_set_column_value_as_blob(c, mdtDeclSecurity_PermissionSet, 1, &permission, &permissionLength)) + return E_FAIL; + + if (!md_cursor_to_token(c, ppm)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::SetRVA( + mdMethodDef md, + ULONG ulRVA) +{ + mdcursor_t method; + if (!md_token_to_cursor(MetaData(), md, &method)) + return CLDB_E_FILE_CORRUPT; + + uint32_t rva = ulRVA; + if (1 != md_set_column_value_as_constant(method, mdtMethodDef_Rva, 1, &rva)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::GetTokenFromSig( + PCCOR_SIGNATURE pvSig, + ULONG cbSig, + mdSignature *pmsig) +{ + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_StandAloneSig, &c)) + return E_FAIL; + + uint32_t sigLength = cbSig; + if (1 != md_set_column_value_as_blob(c, mdtStandAloneSig_Signature, 1, &pvSig, &sigLength)) + return E_FAIL; + + if (!md_cursor_to_token(c, pmsig)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::DefineModuleRef( + LPCWSTR szName, + mdModuleRef *pmur) +{ + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_ModuleRef, &c)) + return E_FAIL; + + pal::StringConvert cvt(szName); + char const* name = cvt; + + if (1 != md_set_column_value_as_utf8(c, mdtModuleRef_Name, 1, &name)) + return E_FAIL; + + if (!md_cursor_to_token(c, pmur)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update EncLog + return S_OK; +} + + +HRESULT MetadataEmit::SetParent( + mdMemberRef mr, + mdToken tk) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), mr, &c)) + return CLDB_E_FILE_CORRUPT; + + if (1 != md_set_column_value_as_token(c, mdtMemberRef_Class, 1, &tk)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::GetTokenFromTypeSpec( + PCCOR_SIGNATURE pvSig, + ULONG cbSig, + mdTypeSpec *ptypespec) +{ + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_TypeSpec, &c)) + return E_FAIL; + + uint32_t sigLength = cbSig; + if (1 != md_set_column_value_as_blob(c, mdtTypeSpec_Signature, 1, &pvSig, &sigLength)) + return E_FAIL; + + if (!md_cursor_to_token(c, ptypespec)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::SaveToMemory( + void *pbData, + ULONG cbData) +{ + size_t saveSize = cbData; + return md_write_to_buffer(MetaData(), (uint8_t*)pbData, &saveSize) ? S_OK : E_OUTOFMEMORY; +} + +HRESULT MetadataEmit::DefineUserString( + LPCWSTR szString, + ULONG cchString, + mdString *pstk) +{ + std::unique_ptr pString{ new char16_t[cchString + 1] }; + std::memcpy(pString.get(), szString, cchString * sizeof(char16_t)); + pString[cchString] = u'\0'; + + mduserstringcursor_t c = md_add_userstring_to_heap(MetaData(), pString.get()); + + if (c == 0) + return E_FAIL; + + if ((c & 0xff000000) != 0) + return META_E_STRINGSPACE_FULL; + + *pstk = TokenFromRid((mdString)c, mdtString); + return S_OK; +} + +HRESULT MetadataEmit::DeleteToken( + mdToken tkObj) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), tkObj, &c)) + return E_INVALIDARG; + + char const* deletedName = COR_DELETED_NAME_A; + switch (TypeFromToken(tkObj)) + { + case mdtTypeDef: + { + if (1 != md_set_column_value_as_utf8(c, mdtTypeDef_TypeName, 1, &deletedName)) + return E_FAIL; + return AddFlag(MetaData(), tkObj, mdtTypeDef_Flags, tdSpecialName | tdRTSpecialName); + } + case mdtMethodDef: + { + if (1 != md_set_column_value_as_utf8(c, mdtMethodDef_Name, 1, &deletedName)) + return E_FAIL; + return AddFlag(MetaData(), tkObj, mdtMethodDef_Flags, mdSpecialName | mdRTSpecialName); + } + case mdtFieldDef: + { + if (1 != md_set_column_value_as_utf8(c, mdtField_Name, 1, &deletedName)) + return E_FAIL; + return AddFlag(MetaData(), tkObj, mdtField_Flags, fdSpecialName | fdRTSpecialName); + } + case mdtEvent: + { + if (1 != md_set_column_value_as_utf8(c, mdtEvent_Name, 1, &deletedName)) + return E_FAIL; + return AddFlag(MetaData(), tkObj, mdtEvent_EventFlags, evSpecialName | evRTSpecialName); + } + case mdtProperty: + { + if (1 != md_set_column_value_as_utf8(c, mdtProperty_Name, 1, &deletedName)) + return E_FAIL; + return AddFlag(MetaData(), tkObj, mdtProperty_Flags, prSpecialName | prRTSpecialName); + } + case mdtExportedType: + { + if (1 != md_set_column_value_as_utf8(c, mdtExportedType_TypeName, 1, &deletedName)) + return E_FAIL; + return S_OK; + } + case mdtCustomAttribute: + { + mdToken parent; + if (1 != md_get_column_value_as_token(c, mdtCustomAttribute_Parent, 1, &parent)) + return E_FAIL; + + // Change the parent to the nil token. + parent = TokenFromRid(mdTokenNil, TypeFromToken(parent)); + + if (1 != md_set_column_value_as_token(c, mdtCustomAttribute_Parent, 1, &parent)) + return E_FAIL; + + return S_OK; + } + case mdtGenericParam: + { + mdToken parent; + if (1 != md_get_column_value_as_token(c, mdtGenericParam_Owner, 1, &parent)) + return E_FAIL; + + // Change the parent to the nil token. + parent = TokenFromRid(mdTokenNil, TypeFromToken(parent)); + + if (1 != md_set_column_value_as_token(c, mdtGenericParam_Owner, 1, &parent)) + return E_FAIL; + + return S_OK; + } + case mdtGenericParamConstraint: + { + mdToken parent = mdGenericParamNil; + if (1 != md_set_column_value_as_token(c, mdtGenericParamConstraint_Owner, 1, &parent)) + return E_FAIL; + + return S_OK; + } + case mdtPermission: + { + mdToken parent; + if (1 != md_get_column_value_as_token(c, mdtDeclSecurity_Parent, 1, &parent)) + return E_FAIL; + + // Change the parent to the nil token. + mdToken originalParent = parent; + parent = TokenFromRid(mdTokenNil, TypeFromToken(parent)); + + if (1 != md_set_column_value_as_token(c, mdtDeclSecurity_Parent, 1, &parent)) + return E_FAIL; + + if (TypeFromToken(originalParent) == mdtAssembly) + { + // There is no HasSecurity flag for an assembly, so we're done. + return S_OK; + } + + mdcursor_t permissions; + uint32_t numPermissions; + if (!md_create_cursor(MetaData(), mdtid_DeclSecurity, &permissions, &numPermissions)) + return E_FAIL; + + // If we have no more permissions for this parent, remove the HasSecurity bit. + // Since we just need to know if there's any matching row and we don't need a range of rows, + // we can use find_row instead of find_range. + if (!md_find_row_from_cursor(permissions, mdtDeclSecurity_Parent, originalParent, &permissions)) + { + return RemoveFlag( + MetaData(), + originalParent, + TypeFromToken(originalParent) == mdtTypeDef ? mdtTypeDef_Flags : mdtMethodDef_Flags, + TypeFromToken(originalParent) == mdtTypeDef ? (uint32_t)tdHasSecurity : (uint32_t)mdHasSecurity); + } + + return S_OK; + } + default: + break; + } + return E_INVALIDARG; +} + +HRESULT MetadataEmit::SetMethodProps( + mdMethodDef md, + DWORD dwMethodFlags, + ULONG ulCodeRVA, + DWORD dwImplFlags) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), md, &c)) + return CLDB_E_FILE_CORRUPT; + + if (dwMethodFlags != std::numeric_limits::max()) + { + // TODO: Strip the reserved flags from user input and preserve the existing reserved flags. + uint32_t flags = dwMethodFlags; + if (1 != md_set_column_value_as_constant(c, mdtMethodDef_Flags, 1, &flags)) + return E_FAIL; + } + + if (ulCodeRVA != std::numeric_limits::max()) + { + uint32_t rva = ulCodeRVA; + if (1 != md_set_column_value_as_constant(c, mdtMethodDef_Rva, 1, &rva)) + return E_FAIL; + } + + if (dwImplFlags != std::numeric_limits::max()) + { + uint32_t implFlags = dwImplFlags; + if (1 != md_set_column_value_as_constant(c, mdtMethodDef_ImplFlags, 1, &implFlags)) + return E_FAIL; + } + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::SetTypeDefProps( + mdTypeDef td, + DWORD dwTypeDefFlags, + mdToken tkExtends, + mdToken rtkImplements[]) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), td, &c)) + return CLDB_E_FILE_CORRUPT; + + if (dwTypeDefFlags != std::numeric_limits::max()) + { + // TODO: Strip the reserved flags from user input and preserve the existing reserved flags. + uint32_t flags = dwTypeDefFlags; + if (1 != md_set_column_value_as_constant(c, mdtTypeDef_Flags, 1, &flags)) + return E_FAIL; + } + + if (tkExtends != std::numeric_limits::max()) + { + if (IsNilToken(tkExtends)) + tkExtends = mdTypeDefNil; + + if (1 != md_set_column_value_as_token(c, mdtTypeDef_Extends, 1, &tkExtends)) + return E_FAIL; + } + + if (rtkImplements) + { + // First null-out the Class columns of the current implementations. + // We can't delete here as we hand out tokens into this table to the caller. + // This would be much more efficient if we could delete rows, as nulling out the parent will almost assuredly make the column + // unsorted. + mdcursor_t interfaceImplCursor; + uint32_t numInterfaceImpls; + if (md_create_cursor(MetaData(), mdtid_InterfaceImpl, &interfaceImplCursor, &numInterfaceImpls) + && md_find_range_from_cursor(interfaceImplCursor, mdtInterfaceImpl_Class, RidFromToken(td), &interfaceImplCursor, &numInterfaceImpls) != MD_RANGE_NOT_FOUND) + { + for (uint32_t i = 0; i < numInterfaceImpls; ++i) + { + mdToken parent; + if (1 != md_get_column_value_as_token(interfaceImplCursor, mdtInterfaceImpl_Class, 1, &parent)) + return E_FAIL; + + // If getting a range was unsupported, then we're doing a whole table scan here. + // In that case, we can't assume that we've already validated the parent. + // Update it here. + if (parent == td) + { + mdToken newParent = mdTypeDefNil; + if (1 != md_set_column_value_as_token(interfaceImplCursor, mdtInterfaceImpl_Class, 1, &newParent)) + return E_FAIL; + } + } + } + + size_t implIndex = 0; + mdToken currentImplementation = rtkImplements[implIndex]; + do + { + md_added_row_t interfaceImpl; + if (!md_append_row(MetaData(), mdtid_InterfaceImpl, &interfaceImpl)) + return E_FAIL; + + if (1 != md_set_column_value_as_cursor(interfaceImpl, mdtInterfaceImpl_Class, 1, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(interfaceImpl, mdtInterfaceImpl_Interface, 1, ¤tImplementation)) + return E_FAIL; + } while ((currentImplementation = rtkImplements[++implIndex]) != mdTokenNil); + } + + // TODO: Update EncLog + return S_OK; +} + +namespace +{ + // Set all rows in the MethodSemantic table with a matching Association column of parent to the nil token of parent's table. + HRESULT RemoveSemantics(mdhandle_t md, mdToken parent, CorMethodSemanticsAttr semantic) + { + mdcursor_t c; + uint32_t count; + if (!md_create_cursor(md, mdtid_MethodSemantics, &c, &count)) + return CLDB_E_RECORD_NOTFOUND; + + md_range_result_t result = md_find_range_from_cursor(c, mdtMethodSemantics_Association, parent, &c, &count); + if (result == MD_RANGE_NOT_FOUND) + return S_OK; + + for (uint32_t i = 0; i < count; ++i, md_cursor_next(&c)) + { + mdToken association; + if (1 != md_get_column_value_as_token(c, mdtMethodSemantics_Association, 1, &association)) + return E_FAIL; + + uint32_t recordSemantic; + if (1 != md_get_column_value_as_constant(c, mdtMethodSemantics_Semantics, 1, &recordSemantic)) + return E_FAIL; + + if (association == parent && recordSemantic == (uint32_t)semantic) + { + association = TokenFromRid(mdTokenNil, TypeFromToken(association)); + if (1 != md_set_column_value_as_token(c, mdtMethodSemantics_Association, 1, &association)) + return E_FAIL; + } + } + + return S_OK; + } +} + +HRESULT MetadataEmit::SetEventProps( + mdEvent ev, + DWORD dwEventFlags, + mdToken tkEventType, + mdMethodDef mdAddOn, + mdMethodDef mdRemoveOn, + mdMethodDef mdFire, + mdMethodDef rmdOtherMethods[]) +{ + HRESULT hr; + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), ev, &c)) + return CLDB_E_FILE_CORRUPT; + + if (dwEventFlags != std::numeric_limits::max()) + { + uint32_t eventFlags = dwEventFlags; + if (1 != md_set_column_value_as_constant(c, mdtEvent_EventFlags, 1, &eventFlags)) + return E_FAIL; + } + + if (!IsNilToken(tkEventType)) + { + if (1 != md_set_column_value_as_token(c, mdtEvent_EventType, 1, &tkEventType)) + return E_FAIL; + } + + if (!IsNilToken(mdAddOn)) + { + RemoveSemantics(MetaData(), ev, msAddOn); + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msAddOn, mdAddOn)); + } + + if (!IsNilToken(mdRemoveOn)) + { + RemoveSemantics(MetaData(), ev, msRemoveOn); + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msRemoveOn, mdRemoveOn)); + } + + if (!IsNilToken(mdFire)) + { + RemoveSemantics(MetaData(), ev, msFire); + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msFire, mdFire)); + } + + if (rmdOtherMethods) + { + RemoveSemantics(MetaData(), ev, msOther); + for (size_t i = 0; rmdOtherMethods[i] != mdMethodDefNil; ++i) + { + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msOther, rmdOtherMethods[i])); + } + } + + // TODO: Update EncLog + + return S_OK; +} + +HRESULT MetadataEmit::SetPermissionSetProps( + mdToken tk, + DWORD dwAction, + void const *pvPermission, + ULONG cbPermission, + mdPermission *ppm) +{ + assert(TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtMethodDef || + TypeFromToken(tk) == mdtAssembly); + + if (dwAction == UINT32_MAX || dwAction == 0 || dwAction > dclMaximumValue) + return E_INVALIDARG; + + mdcursor_t c; + uint32_t count; + if (!md_create_cursor(MetaData(), mdtid_DeclSecurity, &c, &count)) + return CLDB_E_RECORD_NOTFOUND; + + if (!md_find_row_from_cursor(c, mdtDeclSecurity_Parent, tk, &c)) + return CLDB_E_RECORD_NOTFOUND; + + uint32_t action = dwAction; + if (1 != md_set_column_value_as_constant(c, mdtDeclSecurity_Action, 1, &action)) + return E_FAIL; + + uint8_t const* permission = (uint8_t const*)pvPermission; + uint32_t permissionLength = cbPermission; + if (1 != md_set_column_value_as_blob(c, mdtDeclSecurity_PermissionSet, 1, &permission, &permissionLength)) + return E_FAIL; + + if (!md_cursor_to_token(c, ppm)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::DefinePinvokeMap( + mdToken tk, + DWORD dwMappingFlags, + LPCWSTR szImportName, + mdModuleRef mrImportDLL) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), tk, &c)) + return CLDB_E_FILE_CORRUPT; + + if (TypeFromToken(tk) == mdtMethodDef) + { + AddFlag(MetaData(), tk, mdtMethodDef_Flags, mdPinvokeImpl); + } + else if (TypeFromToken(tk) == mdtFieldDef) + { + AddFlag(MetaData(), tk, mdtField_Flags, fdPinvokeImpl); + } + // TODO: check for duplicates + + // If we found a duplicate and ENC is on, update. + // If we found a duplicate and ENC is off, fail. + // Otherwise, we need to make a new row + mdcursor_t row_to_edit; + md_added_row_t added_row_wrapper; + + // TODO: We don't expose tokens for the ImplMap table, so as long as we aren't generating ENC deltas + // we can insert in-place. + if (!md_append_row(MetaData(), mdtid_ImplMap, &row_to_edit)) + return E_FAIL; + added_row_wrapper = md_added_row_t(row_to_edit); + + if (1 != md_set_column_value_as_token(row_to_edit, mdtImplMap_MemberForwarded, 1, &tk)) + return E_FAIL; + + if (dwMappingFlags == std::numeric_limits::max()) + { + // Unspecified by the user, set to the default. + dwMappingFlags = 0; + } + + uint32_t mappingFlags = dwMappingFlags; + if (1 != md_set_column_value_as_constant(row_to_edit, mdtImplMap_MappingFlags, 1, &mappingFlags)) + return E_FAIL; + + pal::StringConvert cvt(szImportName); + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(row_to_edit, mdtImplMap_ImportName, 1, &name)) + return E_FAIL; + + if (IsNilToken(mrImportDLL)) + { + // TODO: If the token is nil, create a module ref to "" (if it doesn't exist) and use that. + } + + if (1 != md_set_column_value_as_token(row_to_edit, mdtImplMap_ImportScope, 1, &mrImportDLL)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::SetPinvokeMap( + mdToken tk, + DWORD dwMappingFlags, + LPCWSTR szImportName, + mdModuleRef mrImportDLL) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), tk, &c)) + return CLDB_E_FILE_CORRUPT; + + mdcursor_t implMapCursor; + uint32_t numImplMaps; + if (!md_create_cursor(MetaData(), mdtid_ImplMap, &implMapCursor, &numImplMaps)) + return E_FAIL; + + mdcursor_t row_to_edit; + if (!md_find_row_from_cursor(implMapCursor, mdtImplMap_MemberForwarded, tk, &row_to_edit)) + return CLDB_E_RECORD_NOTFOUND; + + if (dwMappingFlags != std::numeric_limits::max()) + { + uint32_t mappingFlags = dwMappingFlags; + if (1 != md_set_column_value_as_constant(row_to_edit, mdtImplMap_MappingFlags, 1, &mappingFlags)) + return E_FAIL; + } + + if (szImportName != nullptr) + { + pal::StringConvert cvt(szImportName); + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(row_to_edit, mdtImplMap_ImportName, 1, &name)) + return E_FAIL; + } + + if (1 != md_set_column_value_as_token(row_to_edit, mdtImplMap_ImportScope, 1, &mrImportDLL)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::DeletePinvokeMap( + mdToken tk) +{ + HRESULT hr; + assert(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtMethodDef); + assert(!IsNilToken(tk)); + + RETURN_IF_FAILED(DeleteParentedToken( + MetaData(), + tk, + mdtid_ImplMap, + mdtImplMap_MemberForwarded)); + + RETURN_IF_FAILED(RemoveFlag( + MetaData(), + tk, + TypeFromToken(tk) == mdtFieldDef ? mdtField_Flags : mdtMethodDef_Flags, + TypeFromToken(tk) == mdtFieldDef ? (uint32_t)fdPinvokeImpl : (uint32_t)mdPinvokeImpl)); + + return S_OK; +} + + +HRESULT MetadataEmit::DefineCustomAttribute( + mdToken tkOwner, + mdToken tkCtor, + void const *pCustomAttribute, + ULONG cbCustomAttribute, + mdCustomAttribute *pcv) +{ + if (TypeFromToken(tkOwner) == mdtCustomAttribute) + return E_INVALIDARG; + + if (IsNilToken(tkOwner) + || IsNilToken(tkCtor) + || (TypeFromToken(tkCtor) != mdtMethodDef + && TypeFromToken(tkCtor) != mdtMemberRef) ) + { + return E_INVALIDARG; + } + + // TODO: Recognize pseudoattributes and handle them appropriately. + + // We hand out tokens here, so we can't move rows to keep the parent column sorted. + md_added_row_t new_row; + if (!md_append_row(MetaData(), mdtid_CustomAttribute, &new_row)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(new_row, mdtCustomAttribute_Parent, 1, &tkOwner)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(new_row, mdtCustomAttribute_Type, 1, &tkCtor)) + return E_FAIL; + + uint8_t const* pCustomAttributeBlob = (uint8_t const*)pCustomAttribute; + uint32_t customAttributeBlobLen = cbCustomAttribute; + if (1 != md_set_column_value_as_blob(new_row, mdtCustomAttribute_Value, 1, &pCustomAttributeBlob, &customAttributeBlobLen)) + return E_FAIL; + + if (!md_cursor_to_token(new_row, pcv)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::SetCustomAttributeValue( + mdCustomAttribute pcv, + void const *pCustomAttribute, + ULONG cbCustomAttribute) +{ + if (TypeFromToken(pcv) != mdtCustomAttribute) + return E_INVALIDARG; + + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), pcv, &c)) + return CLDB_E_FILE_CORRUPT; + + uint8_t const* pCustomAttributeBlob = (uint8_t const*)pCustomAttribute; + uint32_t customAttributeBlobLen = cbCustomAttribute; + if (1 != md_set_column_value_as_blob(c, mdtCustomAttribute_Value, 1, &pCustomAttributeBlob, &customAttributeBlobLen)) + return E_FAIL; + + // TODO: Update EncLog + return S_OK; +} + +namespace +{ + // 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. + uint32_t GetSizeOfConstantBlob( + int32_t type, + void const* pValue, + uint32_t strLen) + { + uint32_t size = 0; + + switch (type) + { + case ELEMENT_TYPE_BOOLEAN: + size = sizeof(bool); + break; + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + size = sizeof(uint8_t); + break; + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + size = sizeof(uint16_t); + break; + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_R4: + size = sizeof(uint32_t); + + break; + + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R8: + size = sizeof(uint64_t); + break; + + case ELEMENT_TYPE_STRING: + if (pValue == 0) + size = 0; + else + if (strLen != (uint32_t) -1) + size = strLen * sizeof(WCHAR); + else + size = (uint32_t)(sizeof(WCHAR) * PAL_wcslen((LPWSTR)pValue)); + break; + + case ELEMENT_TYPE_CLASS: + // 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. + size = sizeof(uint32_t); + break; + default: + assert(!"Not a valid type to specify default value!"); + break; + } + return size; + } +} + +HRESULT MetadataEmit::DefineField( + mdTypeDef td, + LPCWSTR szName, + DWORD dwFieldFlags, + PCCOR_SIGNATURE pvSigBlob, + ULONG cbSigBlob, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue, + mdFieldDef *pmd) +{ + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + + md_added_row_t c; + mdcursor_t typeDef; + if (!md_token_to_cursor(MetaData(), td, &typeDef)) + return CLDB_E_FILE_CORRUPT; + + if (!md_add_new_row_to_list(typeDef, mdtTypeDef_FieldList, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_utf8(c, mdtField_Name, 1, &name)) + return E_FAIL; + + bool hasConstant = false; + // See if there is a Constant. + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + hasConstant = true; + } + + if (dwFieldFlags != std::numeric_limits::max()) + { + // TODO: Handle reserved flags + uint32_t fieldFlags = dwFieldFlags; + + // If the field name has the special name for enum fields, + // set the special name and RTSpecialName flags. + // COMPAT: CoreCLR does not check if the field is actually in an enum type. + if (strcmp(name, COR_ENUM_FIELD_NAME) == 0) + { + fieldFlags |= fdRTSpecialName | fdSpecialName; + } + if (1 != md_set_column_value_as_constant(c, mdtField_Flags, 1, &fieldFlags)) + return E_FAIL; + } + else + { + uint32_t fieldFlags = 0; + + // If the field name has the special name for enum fields, + // set the special name and RTSpecialName flags. + // COMPAT: CoreCLR does not check if the field is actually in an enum type. + if (strcmp(name, COR_ENUM_FIELD_NAME) == 0) + { + fieldFlags |= fdRTSpecialName | fdSpecialName; + } + if (1 != md_set_column_value_as_constant(c, mdtField_Flags, 1, &fieldFlags)) + return E_FAIL; + } + + uint8_t const* sig = (uint8_t const*)pvSigBlob; + uint32_t sigLength = cbSigBlob; + if (sigLength != 0) + { + if (1 != md_set_column_value_as_blob(c, mdtField_Signature, 1, &sig, &sigLength)) + return E_FAIL; + } + + if (hasConstant) + { + md_added_row_t constant; + if (!md_append_row(MetaData(), mdtid_Constant, &constant)) + return E_FAIL; + + if (1 != md_set_column_value_as_cursor(constant, mdtConstant_Parent, 1, &c)) + return E_FAIL; + + uint32_t type = dwCPlusTypeFlag; + if (1 != md_set_column_value_as_constant(constant, mdtConstant_Type, 1, &type)) + return E_FAIL; + + uint64_t defaultConstantValue = 0; + uint8_t const* pConstantValue = (uint8_t const*)pValue; + if (pConstantValue == nullptr) + pConstantValue = (uint8_t const*)&defaultConstantValue; + + uint32_t constantSize = GetSizeOfConstantBlob(dwCPlusTypeFlag, pConstantValue, cchValue); + if (1 != md_set_column_value_as_blob(constant, mdtConstant_Value, 1, &pConstantValue, &constantSize)) + return E_FAIL; + + } + + if (!md_cursor_to_token(c, pmd)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::DefineProperty( + mdTypeDef td, + LPCWSTR szProperty, + DWORD dwPropFlags, + PCCOR_SIGNATURE pvSig, + ULONG cbSig, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue, + mdMethodDef mdSetter, + mdMethodDef mdGetter, + mdMethodDef rmdOtherMethods[], + mdProperty *pmdProp) +{ + return FindOrCreateParentedRow( + MetaData(), + td, + mdtid_PropertyMap, + mdtPropertyMap_Parent, + [=] (mdcursor_t map) + { + HRESULT hr; + md_added_row_t c; + if (!md_add_new_row_to_list(map, mdtPropertyMap_Parent, &c)) + return E_FAIL; + + pal::StringConvert cvt(szProperty); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(c, mdtProperty_Name, 1, &name)) + return E_FAIL; + + + if (pvSig != nullptr) + { + uint8_t const* sig = (uint8_t const*)pvSig; + uint32_t sigLength = cbSig; + if (1 != md_set_column_value_as_blob(c, mdtProperty_Type, 1, &sig, &sigLength)) + return E_FAIL; + } + + uint32_t propFlags = (uint32_t)dwPropFlags; + if (propFlags != std::numeric_limits::max()) + { + propFlags &= ~prReservedMask; + } + else + { + propFlags = 0; + } + + bool hasConstant = false; + // See if there is a Constant. + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + if (propFlags == std::numeric_limits::max()) + propFlags = 0; + propFlags |= prHasDefault; + hasConstant = true; + } + + if (1 != md_set_column_value_as_constant(c, mdtProperty_Flags, 1, &propFlags)) + return E_FAIL; + + if (mdGetter != mdMethodDefNil) + { + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msGetter, mdGetter)); + } + + if (mdSetter != mdMethodDefNil) + { + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msSetter, mdSetter)); + } + + if (rmdOtherMethods) + { + for (size_t i = 0; RidFromToken(rmdOtherMethods[i]) != mdTokenNil; ++i) + { + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msOther, rmdOtherMethods[i])); + } + } + + if (hasConstant) + { + md_added_row_t constant; + if (!md_append_row(MetaData(), mdtid_Constant, &constant)) + return E_FAIL; + + if (1 != md_set_column_value_as_cursor(constant, mdtConstant_Parent, 1, &c)) + return E_FAIL; + + uint32_t type = dwCPlusTypeFlag; + if (1 != md_set_column_value_as_constant(constant, mdtConstant_Type, 1, &type)) + return E_FAIL; + + uint64_t defaultConstantValue = 0; + uint8_t const* pConstantValue = (uint8_t const*)pValue; + if (pConstantValue == nullptr) + pConstantValue = (uint8_t const*)&defaultConstantValue; + + uint32_t constantSize = GetSizeOfConstantBlob(dwCPlusTypeFlag, pConstantValue, cchValue); + if (1 != md_set_column_value_as_blob(constant, mdtConstant_Value, 1, &pConstantValue, &constantSize)) + return E_FAIL; + } + + if (!md_cursor_to_token(c, pmdProp)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update EncLog + + return S_OK; + } + ); +} + +HRESULT MetadataEmit::DefineParam( + mdMethodDef md, + ULONG ulParamSeq, + LPCWSTR szName, + DWORD dwParamFlags, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue, + mdParamDef *ppd) +{ + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + + md_added_row_t c; + mdcursor_t method; + if (!md_token_to_cursor(MetaData(), md, &method)) + return CLDB_E_FILE_CORRUPT; + + if (!md_add_new_row_to_sorted_list(method, mdtMethodDef_ParamList, mdtParam_Sequence, (uint32_t)ulParamSeq, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_utf8(c, mdtParam_Name, 1, &name)) + return E_FAIL; + + bool hasConstant = false; + // See if there is a Constant. + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + hasConstant = true; + } + + if (dwParamFlags != std::numeric_limits::max()) + { + // TODO: Handle reserved flags + uint32_t flags = dwParamFlags; + + if (1 != md_set_column_value_as_constant(c, mdtParam_Flags, 1, &flags)) + return E_FAIL; + } + else + { + uint32_t flags = 0; + if (1 != md_set_column_value_as_constant(c, mdtParam_Flags, 1, &flags)) + return E_FAIL; + } + + if (hasConstant) + { + md_added_row_t constant; + if (!md_append_row(MetaData(), mdtid_Constant, &constant)) + return E_FAIL; + + if (1 != md_set_column_value_as_cursor(constant, mdtConstant_Parent, 1, &c)) + return E_FAIL; + + uint32_t type = dwCPlusTypeFlag; + if (1 != md_set_column_value_as_constant(constant, mdtConstant_Type, 1, &type)) + return E_FAIL; + + uint64_t defaultConstantValue = 0; + uint8_t const* pConstantValue = (uint8_t const*)pValue; + if (pConstantValue == nullptr) + pConstantValue = (uint8_t const*)&defaultConstantValue; + + uint32_t constantSize = GetSizeOfConstantBlob(dwCPlusTypeFlag, pConstantValue, cchValue); + if (1 != md_set_column_value_as_blob(constant, mdtConstant_Value, 1, &pConstantValue, &constantSize)) + return E_FAIL; + + } + + if (!md_cursor_to_token(c, ppd)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update EncLog + return S_OK; +} + +HRESULT MetadataEmit::SetFieldProps( + mdFieldDef fd, + DWORD dwFieldFlags, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), fd, &c)) + return CLDB_E_FILE_CORRUPT; + + bool hasConstant = false; + // See if there is a Constant. + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + hasConstant = true; + } + + if (dwFieldFlags != std::numeric_limits::max()) + { + // TODO: Handle reserved flags + uint32_t fieldFlags = dwFieldFlags; + if (1 != md_set_column_value_as_constant(c, mdtField_Flags, 1, &fieldFlags)) + return E_FAIL; + } + + if (hasConstant) + { + // Create or update the Constant record that points to this field. + return FindOrCreateParentedRow(MetaData(), fd, mdtid_Constant, mdtConstant_Parent, [=](mdcursor_t constant) + { + uint32_t type = dwCPlusTypeFlag; + if (1 != md_set_column_value_as_constant(constant, mdtConstant_Type, 1, &type)) + return E_FAIL; + + uint64_t defaultConstantValue = 0; + uint8_t const* pConstantValue = (uint8_t const*)pValue; + if (pConstantValue == nullptr) + pConstantValue = (uint8_t const*)&defaultConstantValue; + + uint32_t constantSize = GetSizeOfConstantBlob(dwCPlusTypeFlag, pConstantValue, cchValue); + if (1 != md_set_column_value_as_blob(constant, mdtConstant_Value, 1, &pConstantValue, &constantSize)) + return E_FAIL; + + return S_OK; + }); + } + return S_OK; +} + +HRESULT MetadataEmit::SetPropertyProps( + mdProperty pr, + DWORD dwPropFlags, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue, + mdMethodDef mdSetter, + mdMethodDef mdGetter, + mdMethodDef rmdOtherMethods[]) +{ + HRESULT hr; + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), pr, &c)) + return CLDB_E_FILE_CORRUPT; + + if (dwPropFlags != std::numeric_limits::max()) + { + dwPropFlags &= ~prReservedMask; + } + + bool hasConstant = false; + // See if there is a Constant. + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + if (dwPropFlags == std::numeric_limits::max()) + dwPropFlags = 0; + dwPropFlags |= prHasDefault; + hasConstant = true; + } + + if (dwPropFlags != std::numeric_limits::max()) + { + // TODO: Preserve reserved flags + uint32_t flags = dwPropFlags; + if (1 != md_set_column_value_as_constant(c, mdtProperty_Flags, 1, &flags)) + return E_FAIL; + } + + if (mdGetter != mdMethodDefNil) + { + RETURN_IF_FAILED(RemoveSemantics(MetaData(), pr, msGetter)); + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msGetter, mdGetter)); + } + + if (mdSetter != mdMethodDefNil) + { + RETURN_IF_FAILED(RemoveSemantics(MetaData(), pr, msSetter)); + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msSetter, mdSetter)); + } + + if (rmdOtherMethods) + { + RETURN_IF_FAILED(RemoveSemantics(MetaData(), pr, msOther)); + for (size_t i = 0; RidFromToken(rmdOtherMethods[i]) != mdTokenNil; ++i) + { + RETURN_IF_FAILED(AddMethodSemantic(MetaData(), c, msOther, rmdOtherMethods[i])); + } + } + + if (hasConstant) + { + // Create or update the Constant record that points to this property. + return FindOrCreateParentedRow(MetaData(), pr, mdtid_Constant, mdtConstant_Parent, [=](mdcursor_t constant) + { + uint32_t type = dwCPlusTypeFlag; + if (1 != md_set_column_value_as_constant(constant, mdtConstant_Type, 1, &type)) + return E_FAIL; + + uint64_t defaultConstantValue = 0; + uint8_t const* pConstantValue = (uint8_t const*)pValue; + if (pConstantValue == nullptr) + pConstantValue = (uint8_t const*)&defaultConstantValue; + + uint32_t constantSize = GetSizeOfConstantBlob(dwCPlusTypeFlag, pConstantValue, cchValue); + if (1 != md_set_column_value_as_blob(constant, mdtConstant_Value, 1, &pConstantValue, &constantSize)) + return E_FAIL; + + return S_OK; + }); + } + + // TODO: Update EncLog + + return S_OK; +} + +HRESULT MetadataEmit::SetParamProps( + mdParamDef pd, + LPCWSTR szName, + DWORD dwParamFlags, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), pd, &c)) + return CLDB_E_FILE_CORRUPT; + + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(c, mdtParam_Name, 1, &name)) + return E_FAIL; + + bool hasConstant = false; + // See if there is a Constant. + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + hasConstant = true; + } + + if (dwParamFlags != std::numeric_limits::max()) + { + // TODO: Handle reserved flags + uint32_t flags = dwParamFlags; + if (1 != md_set_column_value_as_constant(c, mdtParam_Flags, 1, &flags)) + return E_FAIL; + } + + if (hasConstant) + { + // Create or update the Constant record that points to this field. + return FindOrCreateParentedRow(MetaData(), pd, mdtid_Constant, mdtConstant_Parent, [=](mdcursor_t constant) + { + uint32_t type = dwCPlusTypeFlag; + if (1 != md_set_column_value_as_constant(constant, mdtConstant_Type, 1, &type)) + return E_FAIL; + + uint64_t defaultConstantValue = 0; + uint8_t const* pConstantValue = (uint8_t const*)pValue; + if (pConstantValue == nullptr) + pConstantValue = (uint8_t const*)&defaultConstantValue; + + uint32_t constantSize = GetSizeOfConstantBlob(dwCPlusTypeFlag, pConstantValue, cchValue); + if (1 != md_set_column_value_as_blob(constant, mdtConstant_Value, 1, &pConstantValue, &constantSize)) + return E_FAIL; + + return S_OK; + }); + } + + return S_OK; +} + + +HRESULT MetadataEmit::DefineSecurityAttributeSet( + mdToken tkObj, + COR_SECATTR rSecAttrs[], + ULONG cSecAttrs, + ULONG *pulErrorAttr) +{ + // Not implemented in CoreCLR + UNREFERENCED_PARAMETER(tkObj); + UNREFERENCED_PARAMETER(rSecAttrs); + UNREFERENCED_PARAMETER(cSecAttrs); + UNREFERENCED_PARAMETER(pulErrorAttr); + return E_NOTIMPL; +} + +HRESULT MetadataEmit::ApplyEditAndContinue( + IUnknown *pImport) +{ + HRESULT hr; + dncp::com_ptr delta; + RETURN_IF_FAILED(pImport->QueryInterface(IID_IDNMDOwner, (void**)&delta)); + + if (!md_apply_delta(MetaData(), delta->MetaData())) + return E_INVALIDARG; + + // TODO: Reset and copy EncLog from delta metadata to this metadata. + return S_OK; +} + +HRESULT MetadataEmit::TranslateSigWithScope( + IMetaDataAssemblyImport *pAssemImport, + void const *pbHashValue, + ULONG cbHashValue, + IMetaDataImport *import, + PCCOR_SIGNATURE pbSigBlob, + ULONG cbSigBlob, + IMetaDataAssemblyEmit *pAssemEmit, + IMetaDataEmit *emit, + PCOR_SIGNATURE pvTranslatedSig, + ULONG cbTranslatedSigMax, + ULONG *pcbTranslatedSig) +{ + HRESULT hr; + dncp::com_ptr assemImport{}; + + if (pAssemImport != nullptr) + RETURN_IF_FAILED(pAssemImport->QueryInterface(IID_IDNMDOwner, (void**)&assemImport)); + + dncp::com_ptr assemEmit{}; + if (pAssemEmit != nullptr) + RETURN_IF_FAILED(pAssemEmit->QueryInterface(IID_IDNMDOwner, (void**)&assemEmit)); + + if (import == nullptr) + return E_INVALIDARG; + + dncp::com_ptr moduleImport{}; + RETURN_IF_FAILED(import->QueryInterface(IID_IDNMDOwner, (void**)&moduleImport)); + + dncp::com_ptr moduleEmit{}; + RETURN_IF_FAILED(emit->QueryInterface(IID_IDNMDOwner, (void**)&moduleEmit)); + + malloc_span translatedSig; + RETURN_IF_FAILED(ImportSignatureIntoModule( + assemImport->MetaData(), + moduleImport->MetaData(), + { reinterpret_cast(pbHashValue), cbHashValue }, + assemEmit->MetaData(), + moduleEmit->MetaData(), + { pbSigBlob, cbSigBlob }, + [](mdcursor_t){}, + translatedSig)); + + std::copy_n(translatedSig.begin(), std::min(translatedSig.size(), (size_t)cbTranslatedSigMax), (uint8_t*)pvTranslatedSig); + + *pcbTranslatedSig = (ULONG)translatedSig.size(); + return translatedSig.size() > cbTranslatedSigMax ? CLDB_S_TRUNCATION : S_OK; +} + +HRESULT MetadataEmit::SetMethodImplFlags( + mdMethodDef md, + DWORD dwImplFlags) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), md, &c)) + return E_INVALIDARG; + + uint32_t flags = (uint32_t)dwImplFlags; + if (1 != md_set_column_value_as_constant(c, mdtMethodDef_ImplFlags, 1, &flags)) + return E_FAIL; + + // TODO: Update ENC log + return S_OK; +} + +HRESULT MetadataEmit::SetFieldRVA( + mdFieldDef fd, + ULONG ulRVA) +{ + uint32_t rva = (uint32_t)ulRVA; + + HRESULT hr = FindOrCreateParentedRow(MetaData(), fd, mdtid_FieldRva, mdtFieldRva_Field, [=](mdcursor_t c) + { + if (1 != md_set_column_value_as_constant(c, mdtFieldRva_Rva, 1, &rva)) + return E_FAIL; + + return S_OK; + }); + + RETURN_IF_FAILED(hr); + + mdcursor_t field; + if (!md_token_to_cursor(MetaData(), fd, &field)) + return E_INVALIDARG; + + uint32_t flags; + if (1 != md_get_column_value_as_constant(field, mdtField_Flags, 1, &flags)) + return CLDB_E_FILE_CORRUPT; + + flags |= fdHasFieldRVA; + if (1 != md_set_column_value_as_constant(field, mdtField_Flags, 1, &flags)) + return E_FAIL; + + // TODO: Update ENC log + + return S_OK; +} + +HRESULT MetadataEmit::Merge( + IMetaDataImport *pImport, + IMapToken *pHostMapToken, + IUnknown *pHandler) +{ + // Not Implemented in CoreCLR + UNREFERENCED_PARAMETER(pImport); + UNREFERENCED_PARAMETER(pHostMapToken); + UNREFERENCED_PARAMETER(pHandler); + return E_NOTIMPL; +} + +HRESULT MetadataEmit::MergeEnd() +{ + // Not Implemented in CoreCLR + return E_NOTIMPL; +} + +HRESULT MetadataEmit::DefineMethodSpec( + mdToken tkParent, + PCCOR_SIGNATURE pvSigBlob, + ULONG cbSigBlob, + mdMethodSpec *pmi) +{ + if (TypeFromToken(tkParent) != mdtMethodDef && TypeFromToken(tkParent) != mdtMemberRef) + return META_E_BAD_INPUT_PARAMETER; + + if (cbSigBlob == 0 || pvSigBlob == nullptr || pmi == nullptr) + return META_E_BAD_INPUT_PARAMETER; + + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_MethodSpec, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtMethodSpec_Method, 1, &tkParent)) + return E_FAIL; + + uint32_t sigLength = cbSigBlob; + if (1 != md_set_column_value_as_blob(c, mdtMethodSpec_Instantiation, 1, &pvSigBlob, &sigLength)) + return E_FAIL; + + if (!md_cursor_to_token(c, pmi)) + return CLDB_E_FILE_CORRUPT; + + // TODO: Update EncLog + return S_OK; +} + +// TODO: Add EnC mode support to the emit implementation. +// Maybe we can do a layering model where we have a base emit implementation that doesn't support EnC, +// and then a wrapper that does? +HRESULT MetadataEmit::GetDeltaSaveSize( + CorSaveSize fSave, + DWORD *pdwSaveSize) +{ + UNREFERENCED_PARAMETER(fSave); + UNREFERENCED_PARAMETER(pdwSaveSize); + return META_E_NOT_IN_ENC_MODE; +} + +HRESULT MetadataEmit::SaveDelta( + LPCWSTR szFile, + DWORD dwSaveFlags) +{ + UNREFERENCED_PARAMETER(szFile); + UNREFERENCED_PARAMETER(dwSaveFlags); + return META_E_NOT_IN_ENC_MODE; +} + +HRESULT MetadataEmit::SaveDeltaToStream( + IStream *pIStream, + DWORD dwSaveFlags) +{ + UNREFERENCED_PARAMETER(pIStream); + UNREFERENCED_PARAMETER(dwSaveFlags); + return META_E_NOT_IN_ENC_MODE; +} + +HRESULT MetadataEmit::SaveDeltaToMemory( + void *pbData, + ULONG cbData) +{ + UNREFERENCED_PARAMETER(pbData); + UNREFERENCED_PARAMETER(cbData); + return META_E_NOT_IN_ENC_MODE; +} + +HRESULT MetadataEmit::DefineGenericParam( + mdToken tk, + ULONG ulParamSeq, + DWORD dwParamFlags, + LPCWSTR szname, + DWORD reserved, + mdToken rtkConstraints[], + mdGenericParam *pgp) +{ + if (reserved != 0) + return META_E_BAD_INPUT_PARAMETER; + + if (TypeFromToken(tk) != mdtMethodDef && TypeFromToken(tk) != mdtTypeDef) + return META_E_BAD_INPUT_PARAMETER; + + // TODO: Check for duplicates + + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_GenericParam, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(c, mdtGenericParam_Owner, 1, &tk)) + return E_FAIL; + + uint32_t paramSeq = ulParamSeq; + if (1 != md_set_column_value_as_constant(c, mdtGenericParam_Number, 1, ¶mSeq)) + return E_FAIL; + + uint32_t flags = dwParamFlags; + if (1 != md_set_column_value_as_constant(c, mdtGenericParam_Flags, 1, &flags)) + return E_FAIL; + + if (szname != nullptr) + { + pal::StringConvert cvt(szname); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(c, mdtGenericParam_Name, 1, &name)) + return E_FAIL; + } + else + { + char const* name = nullptr; + if (1 != md_set_column_value_as_utf8(c, mdtGenericParam_Name, 1, &name)) + return E_FAIL; + } + + if (rtkConstraints != nullptr) + { + for (size_t i = 0; RidFromToken(rtkConstraints[i]) != mdTokenNil; i++) + { + md_added_row_t added_row; + if (!md_append_row(MetaData(), mdtid_GenericParamConstraint, &added_row)) + return E_FAIL; + + if (1 != md_set_column_value_as_cursor(added_row, mdtGenericParamConstraint_Owner, 1, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(added_row, mdtGenericParamConstraint_Constraint, 1, &rtkConstraints[i])) + return E_FAIL; + + // TODO: Update EncLog + } + } + + // TODO: Update EncLog + if (!md_cursor_to_token(c, pgp)) + return CLDB_E_FILE_CORRUPT; + + return S_OK; +} + +HRESULT MetadataEmit::SetGenericParamProps( + mdGenericParam gp, + DWORD dwParamFlags, + LPCWSTR szName, + DWORD reserved, + mdToken rtkConstraints[]) +{ + if (reserved != 0) + return META_E_BAD_INPUT_PARAMETER; + + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), gp, &c)) + return E_INVALIDARG; + + uint32_t flags = dwParamFlags; + if (1 != md_set_column_value_as_constant(c, mdtGenericParam_Flags, 1, &flags)) + return E_FAIL; + + if (szName != nullptr) + { + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(c, mdtGenericParam_Name, 1, &name)) + return E_FAIL; + } + + if (rtkConstraints != nullptr) + { + // Delete all existing constraints + mdcursor_t constraint; + uint32_t count; + if (!md_create_cursor(MetaData(), mdtid_GenericParamConstraint, &constraint, &count)) + return E_FAIL; + + md_range_result_t result = md_find_range_from_cursor(constraint, mdtGenericParamConstraint_Owner, gp, &constraint, &count); + if (result != MD_RANGE_NOT_FOUND) + { + for (uint32_t i = 0; i < count; ++i, md_cursor_next(&constraint)) + { + mdToken parent; + if (1 != md_get_column_value_as_token(constraint, mdtGenericParamConstraint_Owner, 1, &parent)) + return E_FAIL; + + if (parent == gp) + { + parent = mdGenericParamNil; + if (1 != md_set_column_value_as_token(constraint, mdtGenericParamConstraint_Owner, 1, &parent)) + return E_FAIL; + } + } + } + + for (size_t i = 0; RidFromToken(rtkConstraints[i]) != mdTokenNil; i++) + { + md_added_row_t added_row; + if (!md_append_row(MetaData(), mdtid_GenericParamConstraint, &added_row)) + return E_FAIL; + + if (1 != md_set_column_value_as_cursor(added_row, mdtGenericParamConstraint_Owner, 1, &c)) + return E_FAIL; + + if (1 != md_set_column_value_as_token(added_row, mdtGenericParamConstraint_Constraint, 1, &rtkConstraints[i])) + return E_FAIL; + + // TODO: Update EncLog + } + } + + // TODO: Update EncLog + + return S_OK; +} + +HRESULT MetadataEmit::ResetENCLog() +{ + return META_E_NOT_IN_ENC_MODE; +} + +HRESULT MetadataEmit::DefineAssembly( + void const *pbPublicKey, + ULONG cbPublicKey, + ULONG ulHashAlgId, + LPCWSTR szName, + ASSEMBLYMETADATA const *pMetaData, + DWORD dwAssemblyFlags, + mdAssembly *pma) +{ + if (szName == nullptr || pMetaData == nullptr || pma == nullptr) + return E_INVALIDARG; + + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + mdcursor_t c; + uint32_t count; + if (!md_create_cursor(MetaData(), mdtid_Assembly, &c, &count)) + { + if (md_append_row(MetaData(), mdtid_Assembly, &c)) + { + md_commit_row_add(c); + } + else + { + return E_FAIL; + } + } + + uint32_t assemblyFlags = dwAssemblyFlags; + if (cbPublicKey != 0) + { + assemblyFlags |= afPublicKey; + } + + const uint8_t* publicKey = (const uint8_t*)pbPublicKey; + if (publicKey != nullptr) + { + uint32_t publicKeyLength = cbPublicKey; + if (1 != md_set_column_value_as_blob(c, mdtAssembly_PublicKey, 1, &publicKey, &publicKeyLength)) + return E_FAIL; + } + else + { + uint32_t publicKeyLength = 0; + if (1 != md_set_column_value_as_blob(c, mdtAssembly_PublicKey, 1, &publicKey, &publicKeyLength)) + return E_FAIL; + } + + if (1 != md_set_column_value_as_constant(c, mdtAssembly_Flags, 1, &assemblyFlags)) + return E_FAIL; + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(c, mdtAssembly_Name, 1, &name)) + return E_FAIL; + + uint32_t hashAlgId = ulHashAlgId; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_HashAlgId, 1, &hashAlgId)) + return E_FAIL; + + uint32_t majorVersion = pMetaData->usMajorVersion != std::numeric_limits::max() ? pMetaData->usMajorVersion : 0; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_MajorVersion, 1, &majorVersion)) + return E_FAIL; + + uint32_t minorVersion = pMetaData->usMinorVersion != std::numeric_limits::max() ? pMetaData->usMinorVersion : 0; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_MinorVersion, 1, &minorVersion)) + return E_FAIL; + + uint32_t buildNumber = pMetaData->usBuildNumber != std::numeric_limits::max() ? pMetaData->usBuildNumber : 0; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_BuildNumber, 1, &buildNumber)) + return E_FAIL; + + uint32_t revisionNumber = pMetaData->usRevisionNumber != std::numeric_limits::max() ? pMetaData->usRevisionNumber : 0; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_RevisionNumber, 1, &revisionNumber)) + return E_FAIL; + + if (pMetaData->szLocale != nullptr) + { + pal::StringConvert cvtLocale(pMetaData->szLocale); + if (!cvtLocale.Success()) + return E_INVALIDARG; + + char const* locale = cvtLocale; + if (1 != md_set_column_value_as_utf8(c, mdtAssembly_Culture, 1, &locale)) + return E_FAIL; + } + else + { + char const* locale = nullptr; + if (1 != md_set_column_value_as_utf8(c, mdtAssembly_Culture, 1, &locale)) + return E_FAIL; + } + + if (!md_cursor_to_token(c, pma)) + return E_FAIL; + + // TODO: Update ENC Log + + return S_OK; +} + +HRESULT MetadataEmit::DefineAssemblyRef( + void const *pbPublicKeyOrToken, + ULONG cbPublicKeyOrToken, + LPCWSTR szName, + ASSEMBLYMETADATA const *pMetaData, + void const *pbHashValue, + ULONG cbHashValue, + DWORD dwAssemblyRefFlags, + mdAssemblyRef *pmdar) +{ + if (szName == nullptr || pMetaData == nullptr || pmdar == nullptr) + return E_INVALIDARG; + + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_AssemblyRef, &c)) + return E_FAIL; + + const uint8_t* publicKey = (const uint8_t*)pbPublicKeyOrToken; + if (publicKey != nullptr) + { + uint32_t publicKeyLength = cbPublicKeyOrToken; + if (1 != md_set_column_value_as_blob(c, mdtAssemblyRef_PublicKeyOrToken, 1, &publicKey, &publicKeyLength)) + return E_FAIL; + } + else + { + uint32_t publicKeyLength = 0; + if (1 != md_set_column_value_as_blob(c, mdtAssemblyRef_PublicKeyOrToken, 1, &publicKey, &publicKeyLength)) + return E_FAIL; + } + + if (pbHashValue != nullptr) + { + uint8_t const* hashValue = (uint8_t const*)pbHashValue; + uint32_t hashValueLength = cbHashValue; + if (1 != md_set_column_value_as_blob(c, mdtAssemblyRef_HashValue, 1, &hashValue, &hashValueLength)) + return E_FAIL; + } + else + { + uint8_t const* hashValue = nullptr; + uint32_t hashValueLength = 0; + if (1 != md_set_column_value_as_blob(c, mdtAssemblyRef_HashValue, 1, &hashValue, &hashValueLength)) + return E_FAIL; + } + + uint32_t assemblyFlags = PrepareForSaving(dwAssemblyRefFlags); + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_Flags, 1, &assemblyFlags)) + return E_FAIL; + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(c, mdtAssemblyRef_Name, 1, &name)) + return E_FAIL; + + uint32_t majorVersion = pMetaData->usMajorVersion != std::numeric_limits::max() ? pMetaData->usMajorVersion : 0; + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_MajorVersion, 1, &majorVersion)) + return E_FAIL; + + uint32_t minorVersion = pMetaData->usMinorVersion != std::numeric_limits::max() ? pMetaData->usMinorVersion : 0; + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_MinorVersion, 1, &minorVersion)) + return E_FAIL; + + uint32_t buildNumber = pMetaData->usBuildNumber != std::numeric_limits::max() ? pMetaData->usBuildNumber : 0; + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_BuildNumber, 1, &buildNumber)) + return E_FAIL; + + uint32_t revisionNumber = pMetaData->usRevisionNumber != std::numeric_limits::max() ? pMetaData->usRevisionNumber : 0; + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_RevisionNumber, 1, &revisionNumber)) + return E_FAIL; + + if (pMetaData->szLocale != nullptr) + { + pal::StringConvert cvtLocale(pMetaData->szLocale); + if (!cvtLocale.Success()) + return E_INVALIDARG; + + char const* locale = cvtLocale; + if (1 != md_set_column_value_as_utf8(c, mdtAssemblyRef_Culture, 1, &locale)) + return E_FAIL; + } + else + { + char const* locale = nullptr; + if (1 != md_set_column_value_as_utf8(c, mdtAssemblyRef_Culture, 1, &locale)) + return E_FAIL; + } + + if (!md_cursor_to_token(c, pmdar)) + return E_FAIL; + + // TODO: Update ENC Log + + return S_OK; +} + +HRESULT MetadataEmit::DefineFile( + LPCWSTR szName, + void const *pbHashValue, + ULONG cbHashValue, + DWORD dwFileFlags, + mdFile *pmdf) +{ + + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + md_added_row_t c; + + if (!md_append_row(MetaData(), mdtid_File, &c)) + return E_FAIL; + + char const* name = cvt; + + if (1 != md_set_column_value_as_utf8(c, mdtFile_Name, 1, &name)) + return E_FAIL; + + if (pbHashValue != nullptr) + { + uint8_t const* hashValue = (uint8_t const*)pbHashValue; + uint32_t hashValueLength = cbHashValue; + if (1 != md_set_column_value_as_blob(c, mdtFile_HashValue, 1, &hashValue, &hashValueLength)) + return E_FAIL; + } + else + { + uint8_t const* hashValue = nullptr; + uint32_t hashValueLength = 0; + if (1 != md_set_column_value_as_blob(c, mdtFile_HashValue, 1, &hashValue, &hashValueLength)) + return E_FAIL; + } + + uint32_t fileFlags = dwFileFlags != std::numeric_limits::max() ? dwFileFlags : 0; + if (1 != md_set_column_value_as_constant(c, mdtFile_Flags, 1, &fileFlags)) + return E_FAIL; + + if (!md_cursor_to_token(c, pmdf)) + return E_FAIL; + + // TODO: Update ENC Log + return S_OK; +} + +HRESULT MetadataEmit::DefineExportedType( + LPCWSTR szName, + mdToken tkImplementation, + mdTypeDef tkTypeDef, + DWORD dwExportedTypeFlags, + mdExportedType *pmdct) +{ + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_ExportedType, &c)) + return E_FAIL; + + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + // TODO: check for duplicates + char const* ns; + char const* name; + SplitTypeName(cvt, &ns, &name); + if (1 != md_set_column_value_as_utf8(c, mdtExportedType_TypeNamespace, 1, &ns)) + return E_FAIL; + if (1 != md_set_column_value_as_utf8(c, mdtExportedType_TypeName, 1, &name)) + return E_FAIL; + + if (!IsNilToken(tkImplementation)) + { + if (1 != md_set_column_value_as_token(c, mdtExportedType_Implementation, 1, &tkImplementation)) + return E_FAIL; + } + else + { + // COMPAT: When the implementation column isn't defined, it is defaulted to the 0 value. + // For the Implementation coded index, the nil File token is the 0 value; + mdToken nilToken = mdFileNil; + if (1 != md_set_column_value_as_token(c, mdtExportedType_Implementation, 1, &nilToken)) + return E_FAIL; + } + + if (!IsNilToken(tkTypeDef)) + { + if (1 != md_set_column_value_as_constant(c, mdtExportedType_TypeDefId, 1, &tkTypeDef)) + return E_FAIL; + } + else + { + mdToken nilToken = 0; + if (1 != md_set_column_value_as_constant(c, mdtExportedType_TypeDefId, 1, &nilToken)) + return E_FAIL; + } + + uint32_t exportedTypeFlags = dwExportedTypeFlags != std::numeric_limits::max() ? dwExportedTypeFlags : 0; + if (1 != md_set_column_value_as_constant(c, mdtExportedType_Flags, 1, &exportedTypeFlags)) + return E_FAIL; + + if (!md_cursor_to_token(c, pmdct)) + return E_FAIL; + + // TODO: Update ENC Log + return S_OK; +} + +HRESULT MetadataEmit::DefineManifestResource( + LPCWSTR szName, + mdToken tkImplementation, + DWORD dwOffset, + DWORD dwResourceFlags, + mdManifestResource *pmdmr) +{ + // TODO: check for duplicates + md_added_row_t c; + if (!md_append_row(MetaData(), mdtid_ManifestResource, &c)) + return E_FAIL; + + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(c, mdtManifestResource_Name, 1, &name)) + return E_FAIL; + + if (!IsNilToken(tkImplementation)) + { + if (1 != md_set_column_value_as_token(c, mdtManifestResource_Implementation, 1, &tkImplementation)) + return E_FAIL; + } + else + { + // COMPAT: When the implementation column isn't defined, it is defaulted to the 0 value. + // For the Implementation coded index, the nil File token is the 0 value; + mdToken nilToken = mdFileNil; + if (1 != md_set_column_value_as_token(c, mdtManifestResource_Implementation, 1, &nilToken)) + return E_FAIL; + } + + uint32_t offset = dwOffset != std::numeric_limits::max() ? dwOffset : 0; + if (1 != md_set_column_value_as_constant(c, mdtManifestResource_Offset, 1, &offset)) + return E_FAIL; + + uint32_t resourceFlags = dwResourceFlags != std::numeric_limits::max() ? dwResourceFlags : 0; + if (1 != md_set_column_value_as_constant(c, mdtManifestResource_Flags, 1, &resourceFlags)) + return E_FAIL; + + if (!md_cursor_to_token(c, pmdmr)) + return E_FAIL; + + // TODO: Update ENC Log + return S_OK; +} + +HRESULT MetadataEmit::SetAssemblyProps( + mdAssembly pma, + void const *pbPublicKey, + ULONG cbPublicKey, + ULONG ulHashAlgId, + LPCWSTR szName, + ASSEMBLYMETADATA const *pMetaData, + DWORD dwAssemblyFlags) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), pma, &c)) + return E_INVALIDARG; + + uint32_t assemblyFlags = dwAssemblyFlags; + if (cbPublicKey != 0) + { + assemblyFlags |= afPublicKey; + } + + const uint8_t* publicKey = (const uint8_t*)pbPublicKey; + if (publicKey != nullptr) + { + uint32_t publicKeyLength = cbPublicKey; + if (1 != md_set_column_value_as_blob(c, mdtAssembly_PublicKey, 1, &publicKey, &publicKeyLength)) + return E_FAIL; + } + + if (1 != md_set_column_value_as_constant(c, mdtAssembly_Flags, 1, &assemblyFlags)) + return E_FAIL; + + if (szName != nullptr) + { + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(c, mdtAssembly_Name, 1, &name)) + return E_FAIL; + } + + if (ulHashAlgId != std::numeric_limits::max()) + { + uint32_t hashAlgId = ulHashAlgId; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_HashAlgId, 1, &hashAlgId)) + return E_FAIL; + } + + if (pMetaData->usMajorVersion != std::numeric_limits::max()) + { + uint32_t majorVersion = pMetaData->usMajorVersion; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_MajorVersion, 1, &majorVersion)) + return E_FAIL; + } + + if (pMetaData->usMinorVersion != std::numeric_limits::max()) + { + uint32_t minorVersion = pMetaData->usMinorVersion; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_MinorVersion, 1, &minorVersion)) + return E_FAIL; + } + + if (pMetaData->usBuildNumber != std::numeric_limits::max()) + { + uint32_t buildNumber = pMetaData->usBuildNumber; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_BuildNumber, 1, &buildNumber)) + return E_FAIL; + } + + if (pMetaData->usRevisionNumber != std::numeric_limits::max()) + { + uint32_t revisionNumber = pMetaData->usRevisionNumber; + if (1 != md_set_column_value_as_constant(c, mdtAssembly_RevisionNumber, 1, &revisionNumber)) + return E_FAIL; + } + + if (pMetaData->szLocale != nullptr) + { + pal::StringConvert cvtLocale(pMetaData->szLocale); + if (!cvtLocale.Success()) + return E_INVALIDARG; + + char const* locale = cvtLocale; + if (1 != md_set_column_value_as_utf8(c, mdtAssembly_Culture, 1, &locale)) + return E_FAIL; + } + + // TODO: Update ENC Log + + return S_OK; +} + +HRESULT MetadataEmit::SetAssemblyRefProps( + mdAssemblyRef ar, + void const *pbPublicKeyOrToken, + ULONG cbPublicKeyOrToken, + LPCWSTR szName, + ASSEMBLYMETADATA const *pMetaData, + void const *pbHashValue, + ULONG cbHashValue, + DWORD dwAssemblyRefFlags) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), ar, &c)) + return E_INVALIDARG; + + uint32_t assemblyFlags = dwAssemblyRefFlags; + if (cbPublicKeyOrToken != 0) + { + assemblyFlags |= afPublicKey; + } + + const uint8_t* publicKey = (const uint8_t*)pbPublicKeyOrToken; + if (publicKey != nullptr) + { + uint32_t publicKeyLength = cbPublicKeyOrToken; + if (1 != md_set_column_value_as_blob(c, mdtAssemblyRef_PublicKeyOrToken, 1, &publicKey, &publicKeyLength)) + return E_FAIL; + } + + if (pbHashValue != nullptr) + { + uint8_t const* hashValue = (uint8_t const*)pbHashValue; + uint32_t hashValueLength = cbHashValue; + if (1 != md_set_column_value_as_blob(c, mdtAssemblyRef_HashValue, 1, &hashValue, &hashValueLength)) + return E_FAIL; + } + + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_Flags, 1, &assemblyFlags)) + return E_FAIL; + + if (szName != nullptr) + { + pal::StringConvert cvt(szName); + if (!cvt.Success()) + return E_INVALIDARG; + + char const* name = cvt; + if (1 != md_set_column_value_as_utf8(c, mdtAssemblyRef_Name, 1, &name)) + return E_FAIL; + } + + if (pMetaData->usMajorVersion != std::numeric_limits::max()) + { + uint32_t majorVersion = pMetaData->usMajorVersion; + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_MajorVersion, 1, &majorVersion)) + return E_FAIL; + } + + if (pMetaData->usMinorVersion != std::numeric_limits::max()) + { + uint32_t minorVersion = pMetaData->usMinorVersion; + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_MinorVersion, 1, &minorVersion)) + return E_FAIL; + } + + if (pMetaData->usBuildNumber != std::numeric_limits::max()) + { + uint32_t buildNumber = pMetaData->usBuildNumber; + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_BuildNumber, 1, &buildNumber)) + return E_FAIL; + } + + if (pMetaData->usRevisionNumber != std::numeric_limits::max()) + { + uint32_t revisionNumber = pMetaData->usRevisionNumber; + if (1 != md_set_column_value_as_constant(c, mdtAssemblyRef_RevisionNumber, 1, &revisionNumber)) + return E_FAIL; + } + + if (pMetaData->szLocale != nullptr) + { + pal::StringConvert cvtLocale(pMetaData->szLocale); + if (!cvtLocale.Success()) + return E_INVALIDARG; + + char const* locale = cvtLocale; + if (1 != md_set_column_value_as_utf8(c, mdtAssemblyRef_Culture, 1, &locale)) + return E_FAIL; + } + + // TODO: Update ENC Log + + return S_OK; +} + +HRESULT MetadataEmit::SetFileProps( + mdFile file, + void const *pbHashValue, + ULONG cbHashValue, + DWORD dwFileFlags) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), file, &c)) + return E_INVALIDARG; + + if (pbHashValue != nullptr) + { + uint8_t const* hashValue = (uint8_t const*)pbHashValue; + uint32_t hashValueLength = cbHashValue; + if (1 != md_set_column_value_as_blob(c, mdtFile_HashValue, 1, &hashValue, &hashValueLength)) + return E_FAIL; + } + + if (dwFileFlags != std::numeric_limits::max()) + { + uint32_t fileFlags = dwFileFlags; + if (1 != md_set_column_value_as_constant(c, mdtFile_Flags, 1, &fileFlags)) + return E_FAIL; + } + + // TODO: Update ENC Log + + return S_OK; +} + +HRESULT MetadataEmit::SetExportedTypeProps( + mdExportedType ct, + mdToken tkImplementation, + mdTypeDef tkTypeDef, + DWORD dwExportedTypeFlags) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), ct, &c)) + return E_INVALIDARG; + + if (!IsNilToken(tkImplementation)) + { + if (1 != md_set_column_value_as_token(c, mdtExportedType_Implementation, 1, &tkImplementation)) + return E_FAIL; + } + + if (!IsNilToken(tkTypeDef)) + { + if (1 != md_set_column_value_as_token(c, mdtExportedType_TypeDefId, 1, &tkTypeDef)) + return E_FAIL; + } + + if (dwExportedTypeFlags != std::numeric_limits::max()) + { + uint32_t exportedTypeFlags = dwExportedTypeFlags; + if (1 != md_set_column_value_as_constant(c, mdtExportedType_Flags, 1, &exportedTypeFlags)) + return E_FAIL; + } + + // TODO: Update ENC Log + + return S_OK; +} + +HRESULT MetadataEmit::SetManifestResourceProps( + mdManifestResource mr, + mdToken tkImplementation, + DWORD dwOffset, + DWORD dwResourceFlags) +{ + mdcursor_t c; + if (!md_token_to_cursor(MetaData(), mr, &c)) + return E_INVALIDARG; + + if (!IsNilToken(tkImplementation)) + { + if (1 != md_set_column_value_as_token(c, mdtManifestResource_Implementation, 1, &tkImplementation)) + return E_FAIL; + } + + if (dwOffset != std::numeric_limits::max()) + { + uint32_t offset = dwOffset; + if (1 != md_set_column_value_as_constant(c, mdtManifestResource_Offset, 1, &offset)) + return E_FAIL; + } + + if (dwResourceFlags != std::numeric_limits::max()) + { + uint32_t resourceFlags = dwResourceFlags; + if (1 != md_set_column_value_as_constant(c, mdtManifestResource_Flags, 1, &resourceFlags)) + return E_FAIL; + } + + // TODO: Update ENC Log + + return S_OK; +} \ No newline at end of file diff --git a/src/interfaces/metadataemit.hpp b/src/interfaces/metadataemit.hpp new file mode 100644 index 00000000..65ebd17e --- /dev/null +++ b/src/interfaces/metadataemit.hpp @@ -0,0 +1,472 @@ +#ifndef _SRC_INTERFACES_METADATAEMIT_HPP_ +#define _SRC_INTERFACES_METADATAEMIT_HPP_ + +#include "internal/dnmd_platform.hpp" +#include "tearoffbase.hpp" +#include "controllingiunknown.hpp" +#include "dnmdowner.hpp" + +#include +#include + +#include +#include + +class MetadataEmit final : public TearOffBase +{ + mdhandle_view _md_ptr; + +protected: + bool TryGetInterfaceOnThis(REFIID riid, void** ppvObject) override + { + if (riid == IID_IMetaDataEmit || riid == IID_IMetaDataEmit) + { + *ppvObject = static_cast(this); + return true; + } + else if (riid == IID_IMetaDataAssemblyEmit) + { + *ppvObject = static_cast(this); + return true; + } + return false; + } + +public: + MetadataEmit(IUnknown* controllingUnknown, mdhandle_view md_ptr) + : TearOffBase(controllingUnknown) + , _md_ptr{ std::move(md_ptr) } + { } + + virtual ~MetadataEmit() = default; + + mdhandle_t MetaData() + { + return _md_ptr.get(); + } + +public: // IMetaDataEmit + STDMETHOD(SetModuleProps)( + LPCWSTR szName) override; + + STDMETHOD(Save)( + LPCWSTR szFile, + DWORD dwSaveFlags) override; + + STDMETHOD(SaveToStream)( + IStream *pIStream, + DWORD dwSaveFlags) override; + + STDMETHOD(GetSaveSize)( + CorSaveSize fSave, + DWORD *pdwSaveSize) override; + + STDMETHOD(DefineTypeDef)( + LPCWSTR szTypeDef, + DWORD dwTypeDefFlags, + mdToken tkExtends, + mdToken rtkImplements[], + mdTypeDef *ptd) override; + + STDMETHOD(DefineNestedType)( + LPCWSTR szTypeDef, + DWORD dwTypeDefFlags, + mdToken tkExtends, + mdToken rtkImplements[], + mdTypeDef tdEncloser, + mdTypeDef *ptd) override; + + STDMETHOD(SetHandler)( + IUnknown *pUnk) override; + + STDMETHOD(DefineMethod)( + mdTypeDef td, + LPCWSTR szName, + DWORD dwMethodFlags, + PCCOR_SIGNATURE pvSigBlob, + ULONG cbSigBlob, + ULONG ulCodeRVA, + DWORD dwImplFlags, + mdMethodDef *pmd) override; + + STDMETHOD(DefineMethodImpl)( + mdTypeDef td, + mdToken tkBody, + mdToken tkDecl) override; + + STDMETHOD(DefineTypeRefByName)( + mdToken tkResolutionScope, + LPCWSTR szName, + mdTypeRef *ptr) override; + + STDMETHOD(DefineImportType)( + IMetaDataAssemblyImport *pAssemImport, + void const *pbHashValue, + ULONG cbHashValue, + IMetaDataImport *pImport, + mdTypeDef tdImport, + IMetaDataAssemblyEmit *pAssemEmit, + mdTypeRef *ptr) override; + + STDMETHOD(DefineMemberRef)( + mdToken tkImport, + LPCWSTR szName, + PCCOR_SIGNATURE pvSigBlob, + ULONG cbSigBlob, + mdMemberRef *pmr) override; + + STDMETHOD(DefineImportMember)( + IMetaDataAssemblyImport *pAssemImport, + void const *pbHashValue, + ULONG cbHashValue, + IMetaDataImport *pImport, + mdToken mbMember, + IMetaDataAssemblyEmit *pAssemEmit, + mdToken tkParent, + mdMemberRef *pmr) override; + + STDMETHOD(DefineEvent) ( + mdTypeDef td, + LPCWSTR szEvent, + DWORD dwEventFlags, + mdToken tkEventType, + mdMethodDef mdAddOn, + mdMethodDef mdRemoveOn, + mdMethodDef mdFire, + mdMethodDef rmdOtherMethods[], + mdEvent *pmdEvent) override; + + STDMETHOD(SetClassLayout) ( + mdTypeDef td, + DWORD dwPackSize, + COR_FIELD_OFFSET rFieldOffsets[], + ULONG ulClassSize) override; + + STDMETHOD(DeleteClassLayout) ( + mdTypeDef td) override; + + STDMETHOD(SetFieldMarshal) ( + mdToken tk, + PCCOR_SIGNATURE pvNativeType, + ULONG cbNativeType) override; + + STDMETHOD(DeleteFieldMarshal) ( + mdToken tk) override; + + STDMETHOD(DefinePermissionSet) ( + mdToken tk, + DWORD dwAction, + void const *pvPermission, + ULONG cbPermission, + mdPermission *ppm) override; + + STDMETHOD(SetRVA)( + mdMethodDef md, + ULONG ulRVA) override; + + STDMETHOD(GetTokenFromSig)( + PCCOR_SIGNATURE pvSig, + ULONG cbSig, + mdSignature *pmsig) override; + + STDMETHOD(DefineModuleRef)( + LPCWSTR szName, + mdModuleRef *pmur) override; + + + STDMETHOD(SetParent)( + mdMemberRef mr, + mdToken tk) override; + + STDMETHOD(GetTokenFromTypeSpec)( + PCCOR_SIGNATURE pvSig, + ULONG cbSig, + mdTypeSpec *ptypespec) override; + + STDMETHOD(SaveToMemory)( + void *pbData, + ULONG cbData) override; + + STDMETHOD(DefineUserString)( + LPCWSTR szString, + ULONG cchString, + mdString *pstk) override; + + STDMETHOD(DeleteToken)( + mdToken tkObj) override; + + STDMETHOD(SetMethodProps)( + mdMethodDef md, + DWORD dwMethodFlags, + ULONG ulCodeRVA, + DWORD dwImplFlags) override; + + STDMETHOD(SetTypeDefProps)( + mdTypeDef td, + DWORD dwTypeDefFlags, + mdToken tkExtends, + mdToken rtkImplements[]) override; + + STDMETHOD(SetEventProps)( + mdEvent ev, + DWORD dwEventFlags, + mdToken tkEventType, + mdMethodDef mdAddOn, + mdMethodDef mdRemoveOn, + mdMethodDef mdFire, + mdMethodDef rmdOtherMethods[]) override; + + STDMETHOD(SetPermissionSetProps)( + mdToken tk, + DWORD dwAction, + void const *pvPermission, + ULONG cbPermission, + mdPermission *ppm) override; + + STDMETHOD(DefinePinvokeMap)( + mdToken tk, + DWORD dwMappingFlags, + LPCWSTR szImportName, + mdModuleRef mrImportDLL) override; + + STDMETHOD(SetPinvokeMap)( + mdToken tk, + DWORD dwMappingFlags, + LPCWSTR szImportName, + mdModuleRef mrImportDLL) override; + + STDMETHOD(DeletePinvokeMap)( + mdToken tk) override; + + + STDMETHOD(DefineCustomAttribute)( + mdToken tkOwner, + mdToken tkCtor, + void const *pCustomAttribute, + ULONG cbCustomAttribute, + mdCustomAttribute *pcv) override; + + STDMETHOD(SetCustomAttributeValue)( + mdCustomAttribute pcv, + void const *pCustomAttribute, + ULONG cbCustomAttribute) override; + + STDMETHOD(DefineField)( + mdTypeDef td, + LPCWSTR szName, + DWORD dwFieldFlags, + PCCOR_SIGNATURE pvSigBlob, + ULONG cbSigBlob, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue, + mdFieldDef *pmd) override; + + STDMETHOD(DefineProperty)( + mdTypeDef td, + LPCWSTR szProperty, + DWORD dwPropFlags, + PCCOR_SIGNATURE pvSig, + ULONG cbSig, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue, + mdMethodDef mdSetter, + mdMethodDef mdGetter, + mdMethodDef rmdOtherMethods[], + mdProperty *pmdProp) override; + + STDMETHOD(DefineParam)( + mdMethodDef md, + ULONG ulParamSeq, + LPCWSTR szName, + DWORD dwParamFlags, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue, + mdParamDef *ppd) override; + + STDMETHOD(SetFieldProps)( + mdFieldDef fd, + DWORD dwFieldFlags, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue) override; + + STDMETHOD(SetPropertyProps)( + mdProperty pr, + DWORD dwPropFlags, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue, + mdMethodDef mdSetter, + mdMethodDef mdGetter, + mdMethodDef rmdOtherMethods[]) override; + + STDMETHOD(SetParamProps)( + mdParamDef pd, + LPCWSTR szName, + DWORD dwParamFlags, + DWORD dwCPlusTypeFlag, + void const *pValue, + ULONG cchValue) override; + + + STDMETHOD(DefineSecurityAttributeSet)( + mdToken tkObj, + COR_SECATTR rSecAttrs[], + ULONG cSecAttrs, + ULONG *pulErrorAttr) override; + + STDMETHOD(ApplyEditAndContinue)( + IUnknown *pImport) override; + + STDMETHOD(TranslateSigWithScope)( + IMetaDataAssemblyImport *pAssemImport, + void const *pbHashValue, + ULONG cbHashValue, + IMetaDataImport *import, + PCCOR_SIGNATURE pbSigBlob, + ULONG cbSigBlob, + IMetaDataAssemblyEmit *pAssemEmit, + IMetaDataEmit *emit, + PCOR_SIGNATURE pvTranslatedSig, + ULONG cbTranslatedSigMax, + ULONG *pcbTranslatedSig) override; + + STDMETHOD(SetMethodImplFlags)( + mdMethodDef md, + DWORD dwImplFlags) override; + + STDMETHOD(SetFieldRVA)( + mdFieldDef fd, + ULONG ulRVA) override; + + STDMETHOD(Merge)( + IMetaDataImport *pImport, + IMapToken *pHostMapToken, + IUnknown *pHandler) override; + + STDMETHOD(MergeEnd)() override; + +public: // IMetaDataEmit2 + STDMETHOD(DefineMethodSpec)( + mdToken tkParent, + PCCOR_SIGNATURE pvSigBlob, + ULONG cbSigBlob, + mdMethodSpec *pmi) override; + + STDMETHOD(GetDeltaSaveSize)( + CorSaveSize fSave, + DWORD *pdwSaveSize) override; + + STDMETHOD(SaveDelta)( + LPCWSTR szFile, + DWORD dwSaveFlags) override; + + STDMETHOD(SaveDeltaToStream)( + IStream *pIStream, + DWORD dwSaveFlags) override; + + STDMETHOD(SaveDeltaToMemory)( + void *pbData, + ULONG cbData) override; + + STDMETHOD(DefineGenericParam)( + mdToken tk, + ULONG ulParamSeq, + DWORD dwParamFlags, + LPCWSTR szname, + DWORD reserved, + mdToken rtkConstraints[], + mdGenericParam *pgp) override; + + STDMETHOD(SetGenericParamProps)( + mdGenericParam gp, + DWORD dwParamFlags, + LPCWSTR szName, + DWORD reserved, + mdToken rtkConstraints[]) override; + + STDMETHOD(ResetENCLog)() override; + +public: // IMetaDataAssemblyEmit + STDMETHOD(DefineAssembly)( + void const *pbPublicKey, + ULONG cbPublicKey, + ULONG ulHashAlgId, + LPCWSTR szName, + ASSEMBLYMETADATA const *pMetaData, + DWORD dwAssemblyFlags, + mdAssembly *pma) override; + + STDMETHOD(DefineAssemblyRef)( + void const *pbPublicKeyOrToken, + ULONG cbPublicKeyOrToken, + LPCWSTR szName, + ASSEMBLYMETADATA const *pMetaData, + void const *pbHashValue, + ULONG cbHashValue, + DWORD dwAssemblyRefFlags, + mdAssemblyRef *pmdar) override; + + STDMETHOD(DefineFile)( + LPCWSTR szName, + void const *pbHashValue, + ULONG cbHashValue, + DWORD dwFileFlags, + mdFile *pmdf) override; + + STDMETHOD(DefineExportedType)( + LPCWSTR szName, + mdToken tkImplementation, + mdTypeDef tkTypeDef, + DWORD dwExportedTypeFlags, + mdExportedType *pmdct) override; + + STDMETHOD(DefineManifestResource)( + LPCWSTR szName, + mdToken tkImplementation, + DWORD dwOffset, + DWORD dwResourceFlags, + mdManifestResource *pmdmr) override; + + STDMETHOD(SetAssemblyProps)( + mdAssembly pma, + void const *pbPublicKey, + ULONG cbPublicKey, + ULONG ulHashAlgId, + LPCWSTR szName, + ASSEMBLYMETADATA const *pMetaData, + DWORD dwAssemblyFlags) override; + + STDMETHOD(SetAssemblyRefProps)( + mdAssemblyRef ar, + void const *pbPublicKeyOrToken, + ULONG cbPublicKeyOrToken, + LPCWSTR szName, + ASSEMBLYMETADATA const *pMetaData, + void const *pbHashValue, + ULONG cbHashValue, + DWORD dwAssemblyRefFlags) override; + + STDMETHOD(SetFileProps)( + mdFile file, + void const *pbHashValue, + ULONG cbHashValue, + DWORD dwFileFlags) override; + + STDMETHOD(SetExportedTypeProps)( + mdExportedType ct, + mdToken tkImplementation, + mdTypeDef tkTypeDef, + DWORD dwExportedTypeFlags) override; + + STDMETHOD(SetManifestResourceProps)( + mdManifestResource mr, + mdToken tkImplementation, + DWORD dwOffset, + DWORD dwResourceFlags) override; +}; + +#endif \ No newline at end of file diff --git a/src/interfaces/metadataimport.cpp b/src/interfaces/metadataimport.cpp index de232412..dcb008ea 100644 --- a/src/interfaces/metadataimport.cpp +++ b/src/interfaces/metadataimport.cpp @@ -200,7 +200,7 @@ namespace i += read; } - enumImpl = cleanup.release(); + *pEnumImpl = cleanup.release(); return S_OK; } } @@ -397,8 +397,7 @@ HRESULT STDMETHODCALLTYPE MetadataImportRO::EnumInterfaceImpls( if (!md_create_cursor(_md_ptr.get(), mdtid_InterfaceImpl, &cursor, &rows)) return CLDB_E_RECORD_NOTFOUND; - uint32_t id = RidFromToken(td); - RETURN_IF_FAILED(CreateEnumTokenRangeForSortedTableKey(_md_ptr.get(), mdtid_InterfaceImpl, mdtInterfaceImpl_Class, id, &enumImpl)); + RETURN_IF_FAILED(CreateEnumTokenRangeForSortedTableKey(_md_ptr.get(), mdtid_InterfaceImpl, mdtInterfaceImpl_Class, td, &enumImpl)); *phEnum = enumImpl; } return enumImpl->ReadTokens(rImpls, cMax, pcImpls); @@ -2737,7 +2736,7 @@ HRESULT STDMETHODCALLTYPE MetadataImportRO::GetCustomAttributeByName( struct { HRESULT hr; - const void** ppData; + void const** ppData; ULONG* pcbData; pal::StringConvert const& cvt; diff --git a/src/mddump/main.cpp b/src/mddump/main.cpp index f4d948f3..c9bf35b9 100644 --- a/src/mddump/main.cpp +++ b/src/mddump/main.cpp @@ -16,11 +16,20 @@ bool apply_deltas(mdhandle_t handle, std::vector& deltas, std::vect std::fprintf(stderr, "Failed to read '%s'.\n", p); return false; } - if (!md_apply_delta(handle, d, d.size())) + + mdhandle_ptr delta; + if (!create_mdhandle(d, delta)) + { + std::fprintf(stderr, "Failed to create handle for '%s'.\n", p); + return false; + } + + if (!md_apply_delta(handle, delta.get())) { std::fprintf(stderr, "Failed to apply delta, '%s'.\n", p); return false; } + // Store the loaded delta data data.push_back(std::move(d)); } diff --git a/src/mdmerge/main.cpp b/src/mdmerge/main.cpp index 39e1c8dc..f317ef38 100644 --- a/src/mdmerge/main.cpp +++ b/src/mdmerge/main.cpp @@ -17,7 +17,15 @@ bool apply_deltas(mdhandle_t handle, std::vector& deltas, std::vect std::fprintf(stderr, "Failed to read '%s'.\n", p); return false; } - if (!md_apply_delta(handle, d, d.size())) + + mdhandle_ptr delta; + if (!create_mdhandle(d, delta)) + { + std::fprintf(stderr, "Failed to create handle for '%s'.\n", p); + return false; + } + + if (!md_apply_delta(handle, delta.get())) { std::fprintf(stderr, "Failed to apply delta, '%s'.\n", p); return false; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fb59f89f..e7b2029d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -38,3 +38,4 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_subdirectory(regpal) add_subdirectory(regperf) add_subdirectory(regtest) +add_subdirectory(emit) diff --git a/test/emit/CMakeLists.txt b/test/emit/CMakeLists.txt new file mode 100644 index 00000000..99abb888 --- /dev/null +++ b/test/emit/CMakeLists.txt @@ -0,0 +1,21 @@ +set(SOURCES + typeref.cpp + module.cpp + typedef.cpp + moduleref.cpp + methoddef.cpp + standalonesig.cpp + memberref.cpp + typespec.cpp + assembly.cpp + assemblyref.cpp + param.cpp + fieldmarshal.cpp + fieldrva.cpp) + +set(HEADERS emit.hpp) + +add_executable(emit ${SOURCES} ${HEADERS}) +target_link_libraries(emit PRIVATE dnmd::interfaces_static gtest_main gmock) + +gtest_discover_tests(emit) \ No newline at end of file diff --git a/test/emit/assembly.cpp b/test/emit/assembly.cpp new file mode 100644 index 00000000..d7c124c4 --- /dev/null +++ b/test/emit/assembly.cpp @@ -0,0 +1,47 @@ +#include "emit.hpp" + +TEST(Assembly, DefineNoPublicKey) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdAssembly assembly; + + WSTR_string name = W("AssemblyName"); + ASSEMBLYMETADATA assemblyMetadata; + assemblyMetadata.usMajorVersion = 1; + assemblyMetadata.usMinorVersion = 2; + assemblyMetadata.usBuildNumber = 3; + assemblyMetadata.usRevisionNumber = 4; + assemblyMetadata.szLocale = const_cast(W("en-us")); + assemblyMetadata.cbLocale = 5; + uint32_t hashAlgId = 4; + ASSERT_EQ(S_OK, emit->DefineAssembly(nullptr, 0, hashAlgId, name.c_str(), &assemblyMetadata, 0, &assembly)); + ASSERT_EQ(1, RidFromToken(assembly)); + ASSERT_EQ(mdtAssembly, TypeFromToken(assembly)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&import)); + + ASSEMBLYMETADATA metadata; + std::unique_ptr localeName = std::make_unique(20); + metadata.szLocale = localeName.get(); + metadata.cbLocale = 20; + ULONG importHashAlgId; + DWORD assemblyFlags; + WSTR_string assemblyName; + assemblyName.resize(name.capacity() + 1); + ULONG assemblyNameLen; + void const* publicKey; + ULONG publicKeyLength; + ASSERT_EQ(S_OK, import->GetAssemblyProps(assembly, &publicKey, &publicKeyLength, &importHashAlgId, assemblyName.data(), (ULONG)assemblyName.capacity(), &assemblyNameLen, &metadata, & assemblyFlags)); + EXPECT_EQ(0, assemblyFlags); + EXPECT_EQ(W("AssemblyName"), assemblyName.substr(0, assemblyNameLen - 1)); + EXPECT_EQ(1, metadata.usMajorVersion); + EXPECT_EQ(2, metadata.usMinorVersion); + EXPECT_EQ(3, metadata.usBuildNumber); + EXPECT_EQ(4, metadata.usRevisionNumber); + + WSTR_string locale{ metadata.szLocale }; + EXPECT_EQ(W("en-us"), locale); + EXPECT_EQ(locale.length() + 1, metadata.cbLocale); +} diff --git a/test/emit/assemblyref.cpp b/test/emit/assemblyref.cpp new file mode 100644 index 00000000..9a13eb84 --- /dev/null +++ b/test/emit/assemblyref.cpp @@ -0,0 +1,49 @@ +#include "emit.hpp" + +TEST(AssemblyRef, DefineNoPublicKey) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdAssembly assembly; + + WSTR_string name = W("AssemblyName"); + ASSEMBLYMETADATA assemblyMetadata; + assemblyMetadata.usMajorVersion = 1; + assemblyMetadata.usMinorVersion = 2; + assemblyMetadata.usBuildNumber = 3; + assemblyMetadata.usRevisionNumber = 4; + assemblyMetadata.szLocale = const_cast(W("en-us")); + assemblyMetadata.cbLocale = 5; + ASSERT_EQ(S_OK, emit->DefineAssemblyRef(nullptr, 0, name.c_str(), &assemblyMetadata, nullptr, 0, 0, &assembly)); + ASSERT_EQ(1, RidFromToken(assembly)); + ASSERT_EQ(mdtAssemblyRef, TypeFromToken(assembly)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&import)); + + ASSEMBLYMETADATA metadata; + std::unique_ptr localeName = std::make_unique(20); + metadata.szLocale = localeName.get(); + metadata.cbLocale = 20; + DWORD assemblyFlags; + WSTR_string assemblyName; + assemblyName.resize(name.capacity() + 1); + ULONG assemblyNameLen; + void const* publicKey; + ULONG publicKeyLength; + void const* hash; + ULONG hashLength; + ASSERT_EQ(S_OK, import->GetAssemblyRefProps(assembly, &publicKey, &publicKeyLength, assemblyName.data(), (ULONG)assemblyName.capacity(), &assemblyNameLen, &metadata, &hash, &hashLength, &assemblyFlags)); + EXPECT_EQ(0, assemblyFlags); + EXPECT_EQ(nullptr, hash); + EXPECT_EQ(0, hashLength); + EXPECT_EQ(W("AssemblyName"), assemblyName.substr(0, assemblyNameLen - 1)); + EXPECT_EQ(1, metadata.usMajorVersion); + EXPECT_EQ(2, metadata.usMinorVersion); + EXPECT_EQ(3, metadata.usBuildNumber); + EXPECT_EQ(4, metadata.usRevisionNumber); + + WSTR_string locale{ metadata.szLocale }; + EXPECT_EQ(W("en-us"), locale); + EXPECT_EQ(locale.length() + 1, metadata.cbLocale); +} diff --git a/test/emit/emit.hpp b/test/emit/emit.hpp new file mode 100644 index 00000000..fa214a63 --- /dev/null +++ b/test/emit/emit.hpp @@ -0,0 +1,39 @@ +#ifndef DNMD_TEST_EMIT_EMIT_HPP +#define DNMD_TEST_EMIT_EMIT_HPP + +#ifdef BUILD_WINDOWS +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include + +using WSTR_string = std::basic_string; + +inline void CreateEmit(dncp::com_ptr& emit) +{ + dncp::com_ptr dispenser; + ASSERT_EQ(S_OK, GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); + ASSERT_EQ(S_OK, dispenser->DefineScope(CLSID_CorMetaDataRuntime, 0, IID_IMetaDataEmit, (IUnknown**)&emit)); +} + +inline void CreateEmit(dncp::com_ptr& emit) +{ + dncp::com_ptr dispenser; + ASSERT_EQ(S_OK, GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); + ASSERT_EQ(S_OK, dispenser->DefineScope(CLSID_CorMetaDataRuntime, 0, IID_IMetaDataEmit2, (IUnknown**)&emit)); +} + +inline void CreateEmit(dncp::com_ptr& emit) +{ + dncp::com_ptr dispenser; + ASSERT_EQ(S_OK, GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); + ASSERT_EQ(S_OK, dispenser->DefineScope(CLSID_CorMetaDataRuntime, 0, IID_IMetaDataAssemblyEmit, (IUnknown**)&emit)); +} +#endif // DNMD_TEST_EMIT_EMIT_HPP diff --git a/test/emit/fieldmarshal.cpp b/test/emit/fieldmarshal.cpp new file mode 100644 index 00000000..3b2d02ec --- /dev/null +++ b/test/emit/fieldmarshal.cpp @@ -0,0 +1,36 @@ +#include "emit.hpp" + +#include + +TEST(FieldMarshal, DefineAndDelete) +{ + // Define a field + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdFieldDef field; + mdTypeDef type; + ASSERT_EQ(S_OK, emit->DefineTypeDef(W("Type"), tdPublic, TokenFromRid(1, mdtTypeDef), nullptr, &type)); + std::array signature = { IMAGE_CEE_CS_CALLCONV_FIELD, ELEMENT_TYPE_VOID, ELEMENT_TYPE_I4 }; + ASSERT_EQ(S_OK, emit->DefineField(type, W("Field"), fdPublic, signature.data(), (ULONG)signature.size(), 0, nullptr, 0, &field)); + + // Define the field marshal signature + std::array marshalSignature = { NATIVE_TYPE_I4 }; + ASSERT_EQ(S_OK, emit->SetFieldMarshal(field, marshalSignature.data(), (ULONG)marshalSignature.size())); + + // Read the field marshal signature + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + ULONG readMarshalLength; + PCCOR_SIGNATURE readMarshal; + ASSERT_EQ(S_OK, import->GetFieldMarshal(field, & readMarshal, & readMarshalLength)); + + EXPECT_EQ(marshalSignature.size(), readMarshalLength); + EXPECT_THAT(std::vector(readMarshal, readMarshal + readMarshalLength), testing::ContainerEq(std::vector(marshalSignature.begin(), marshalSignature.end()))); + + // Delete the field marshal entry + ASSERT_EQ(S_OK, emit->DeleteFieldMarshal(field)); + + // Verify we can't find the field marshal entry after deletion + ASSERT_EQ(CLDB_E_RECORD_NOTFOUND, import->GetFieldMarshal(field, & readMarshal, & readMarshalLength)); +} \ No newline at end of file diff --git a/test/emit/fieldrva.cpp b/test/emit/fieldrva.cpp new file mode 100644 index 00000000..b46b9754 --- /dev/null +++ b/test/emit/fieldrva.cpp @@ -0,0 +1,28 @@ +#include "emit.hpp" + +TEST(FieldRva, Define) +{ + // Define a field + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdFieldDef field; + mdTypeDef type; + ASSERT_EQ(S_OK, emit->DefineTypeDef(W("Type"), tdPublic, TokenFromRid(1, mdtTypeDef), nullptr, &type)); + std::array signature = { IMAGE_CEE_CS_CALLCONV_FIELD, ELEMENT_TYPE_VOID, ELEMENT_TYPE_I4 }; + ASSERT_EQ(S_OK, emit->DefineField(type, W("Field"), fdPublic, signature.data(), (ULONG)signature.size(), 0, nullptr, 0, &field)); + + // Define the field marshal signature + uint32_t rva = 0x424242; + ASSERT_EQ(S_OK, emit->SetFieldRVA(field, rva)); + + // Read the field marshal signature + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + DWORD readRva = 0; + DWORD implFlags = 0; + ASSERT_EQ(S_OK, import->GetRVA(field, & readRva, & implFlags)); + + EXPECT_EQ(rva, readRva); + EXPECT_EQ(0, implFlags); +} \ No newline at end of file diff --git a/test/emit/memberref.cpp b/test/emit/memberref.cpp new file mode 100644 index 00000000..0aafdd0b --- /dev/null +++ b/test/emit/memberref.cpp @@ -0,0 +1,62 @@ +#include "emit.hpp" +#include +#include +#include + +TEST(MemberRef, Define) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdMemberRef memberRef; + std::array signature = {0x01, 0x02, 0x03}; + ASSERT_EQ(S_OK, emit->DefineMemberRef(TokenFromRid(1, mdtTypeDef), W("Foo"), signature.data(), (ULONG)signature.size(), &memberRef)); + ASSERT_EQ(1, RidFromToken(memberRef)); + ASSERT_EQ(mdtMemberRef, TypeFromToken(memberRef)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + mdTypeDef type; + WSTR_string readName; + readName.resize(3); + ULONG readNameLength; + PCCOR_SIGNATURE sigBlob; + ULONG sigBlobLength; + ASSERT_EQ(S_OK, import->GetMemberRefProps(memberRef, &type, readName.data(), (ULONG)readName.capacity(), &readNameLength, &sigBlob, &sigBlobLength)); + EXPECT_EQ(W("Foo"), readName.substr(0, readNameLength - 1)); + EXPECT_EQ(TokenFromRid(1, mdtTypeDef), type); + EXPECT_THAT(std::vector(sigBlob, sigBlob + sigBlobLength), testing::ContainerEq(std::vector(signature.begin(), signature.end()))); +} + +TEST(MemberRef, SetParent) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdMemberRef memberRef; + std::array signature = {0x01, 0x02, 0x03}; + ASSERT_EQ(S_OK, emit->DefineMemberRef(TokenFromRid(1, mdtTypeDef), W("Foo"), signature.data(), (ULONG)signature.size(), &memberRef)); + ASSERT_EQ(1, RidFromToken(memberRef)); + ASSERT_EQ(mdtMemberRef, TypeFromToken(memberRef)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + mdToken parent; + WSTR_string readName; + readName.resize(3); + ULONG readNameLength; + PCCOR_SIGNATURE sigBlob; + ULONG sigBlobLength; + ASSERT_EQ(S_OK, import->GetMemberRefProps(memberRef, &parent, readName.data(), (ULONG)readName.capacity(), &readNameLength, &sigBlob, &sigBlobLength)); + EXPECT_EQ(W("Foo"), readName.substr(0, readNameLength - 1)); + EXPECT_EQ(TokenFromRid(1, mdtTypeDef), parent); + EXPECT_THAT(std::vector(sigBlob, sigBlob + sigBlobLength), testing::ContainerEq(std::vector(signature.begin(), signature.end()))); + + ASSERT_EQ(S_OK, emit->SetParent(memberRef, TokenFromRid(2, mdtTypeRef))); + + readName.resize(3); + ASSERT_EQ(S_OK, import->GetMemberRefProps(memberRef, &parent, readName.data(), (ULONG)readName.capacity(), &readNameLength, &sigBlob, &sigBlobLength)); + EXPECT_EQ(W("Foo"), readName.substr(0, readNameLength - 1)); + EXPECT_EQ(TokenFromRid(2, mdtTypeRef), parent); + EXPECT_THAT(std::vector(sigBlob, sigBlob + sigBlobLength), testing::ContainerEq(std::vector(signature.begin(), signature.end()))); +} \ No newline at end of file diff --git a/test/emit/methoddef.cpp b/test/emit/methoddef.cpp new file mode 100644 index 00000000..d6ab9483 --- /dev/null +++ b/test/emit/methoddef.cpp @@ -0,0 +1,117 @@ +#include "emit.hpp" +#include +#include + +TEST(MethodDef, Define) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdMethodDef methodDef; + + // TypeDef,1 is the type. + std::array sig = { (uint8_t)IMAGE_CEE_CS_CALLCONV_DEFAULT, (uint8_t)0, (uint8_t)ELEMENT_TYPE_VOID }; + ULONG rva = 0x424242; + ASSERT_EQ(S_OK, emit->DefineMethod(TokenFromRid(1, mdtTypeDef), W("Foo"), mdStatic, sig.data(), (ULONG)sig.size(), rva, 0, &methodDef)); + ASSERT_EQ(1, RidFromToken(methodDef)); + ASSERT_EQ(mdtMethodDef, TypeFromToken(methodDef)); + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + mdTypeDef type; + WSTR_string readName; + readName.resize(3); + ULONG readNameLength; + + DWORD attr; + PCCOR_SIGNATURE sigBlob; + ULONG sigBlobLength; + ULONG codeRVA; + DWORD implFlags; + ASSERT_EQ(S_OK, import->GetMethodProps(methodDef, &type, readName.data(), (ULONG)readName.capacity(), &readNameLength, &attr, &sigBlob, &sigBlobLength, &codeRVA, &implFlags)); + EXPECT_EQ(W("Foo"), readName.substr(0, readNameLength - 1)); + EXPECT_EQ(mdStatic, attr); + EXPECT_EQ(rva, codeRVA); + EXPECT_EQ(0, implFlags); + EXPECT_THAT(std::vector(sigBlob, sigBlob + sigBlobLength), testing::ContainerEq(std::vector(sig.begin(), sig.end()))); +} + +TEST(MethodDef, DefineWithInvalidType) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdMethodDef methodDef; + + std::array sig = { (uint8_t)IMAGE_CEE_CS_CALLCONV_DEFAULT, (uint8_t)0, (uint8_t)ELEMENT_TYPE_VOID }; + ULONG rva = 0x424242; + ASSERT_EQ(CLDB_E_FILE_CORRUPT, emit->DefineMethod(TokenFromRid(2, mdtTypeDef), W("Foo"), mdStatic, sig.data(), (ULONG)sig.size(), rva, 0, &methodDef)); +} + +TEST(MethodDef, SetRva) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdMethodDef methodDef; + + std::array sig = { (uint8_t)IMAGE_CEE_CS_CALLCONV_DEFAULT, (uint8_t)0, (uint8_t)ELEMENT_TYPE_VOID }; + ULONG rva = 0x424242; + ASSERT_EQ(S_OK, emit->DefineMethod(TokenFromRid(1, mdtTypeDef), W("Foo"), mdStatic, sig.data(), (ULONG)sig.size(), rva, 0, &methodDef)); + ASSERT_EQ(1, RidFromToken(methodDef)); + ASSERT_EQ(mdtMethodDef, TypeFromToken(methodDef)); + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + ULONG newRva = 0x123456; + ASSERT_EQ(S_OK, emit->SetRVA(methodDef, newRva)); + + mdTypeDef type; + WSTR_string readName; + readName.resize(3); + ULONG readNameLength; + + DWORD attr; + PCCOR_SIGNATURE sigBlob; + ULONG sigBlobLength; + ULONG codeRVA; + DWORD implFlags; + ASSERT_EQ(S_OK, import->GetMethodProps(methodDef, &type, readName.data(), (ULONG)readName.capacity(), &readNameLength, &attr, &sigBlob, &sigBlobLength, &codeRVA, &implFlags)); + EXPECT_EQ(W("Foo"), readName.substr(0, readNameLength - 1)); + EXPECT_EQ(mdStatic, attr); + EXPECT_EQ(newRva, codeRVA); + EXPECT_EQ(0, implFlags); + EXPECT_THAT(std::vector(sigBlob, sigBlob + sigBlobLength), testing::ContainerEq(std::vector(sig.begin(), sig.end()))); +} + +TEST(MethodDef, SetProps) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdMethodDef methodDef; + + std::array sig = { (uint8_t)IMAGE_CEE_CS_CALLCONV_DEFAULT, (uint8_t)0, (uint8_t)ELEMENT_TYPE_VOID }; + ULONG rva = 0x424242; + ASSERT_EQ(S_OK, emit->DefineMethod(TokenFromRid(1, mdtTypeDef), W("Foo"), mdStatic, sig.data(), (ULONG)sig.size(), rva, 0, &methodDef)); + ASSERT_EQ(1, RidFromToken(methodDef)); + ASSERT_EQ(mdtMethodDef, TypeFromToken(methodDef)); + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + ULONG newRva = 0x123456; + ASSERT_EQ(S_OK, emit->SetMethodProps(methodDef, mdPublic, newRva, miForwardRef)); + + mdTypeDef type; + WSTR_string readName; + readName.resize(3); + ULONG readNameLength; + + DWORD attr; + PCCOR_SIGNATURE sigBlob; + ULONG sigBlobLength; + ULONG codeRVA; + DWORD implFlags; + ASSERT_EQ(S_OK, import->GetMethodProps(methodDef, &type, readName.data(), (ULONG)readName.capacity(), &readNameLength, &attr, &sigBlob, &sigBlobLength, &codeRVA, &implFlags)); + EXPECT_EQ(W("Foo"), readName.substr(0, readNameLength - 1)); + EXPECT_EQ(mdPublic, attr); + EXPECT_EQ(newRva, codeRVA); + EXPECT_EQ(miForwardRef, implFlags); + EXPECT_THAT(std::vector(sigBlob, sigBlob + sigBlobLength), testing::ContainerEq(std::vector(sig.begin(), sig.end()))); +} diff --git a/test/emit/module.cpp b/test/emit/module.cpp new file mode 100644 index 00000000..0e5c6914 --- /dev/null +++ b/test/emit/module.cpp @@ -0,0 +1,88 @@ +#include "emit.hpp" + +TEST(Module, ModuleNameExcludesDirectoryWin32Paths) +{ + WSTR_string moduleName = W("C:\\foo\\bar\\baz.dll"); + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + ASSERT_EQ(S_OK, emit->SetModuleProps(moduleName.c_str())); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readModuleName; + readModuleName.resize(moduleName.capacity() + 1); + ULONG readModuleNameLength; + + GUID mvid; + ASSERT_EQ(S_OK, import->GetScopeProps(readModuleName.data(), (ULONG)readModuleName.capacity(), &readModuleNameLength, & mvid)); + + EXPECT_EQ(W("baz.dll"), readModuleName.substr(0, readModuleNameLength - 1)); +} + +TEST(Module, ModuleNameExcludesDirectoryUnixPaths) +{ + WSTR_string moduleName = W("/home/foo/bar/baz.dll"); + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + ASSERT_EQ(S_OK, emit->SetModuleProps(moduleName.c_str())); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readModuleName; + readModuleName.resize(moduleName.capacity() + 1); + ULONG readModuleNameLength; + + GUID mvid; + ASSERT_EQ(S_OK, import->GetScopeProps(readModuleName.data(), (ULONG)readModuleName.capacity(), &readModuleNameLength, & mvid)); + + EXPECT_EQ(W("baz.dll"), readModuleName.substr(0, readModuleNameLength - 1)); +} + +TEST(Module, ModuleNameWithoutDirectory) +{ + WSTR_string moduleName = W("baz.dll"); + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + ASSERT_EQ(S_OK, emit->SetModuleProps(moduleName.c_str())); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readModuleName; + readModuleName.resize(moduleName.capacity() + 1); + ULONG readModuleNameLength; + + GUID mvid; + ASSERT_EQ(S_OK, import->GetScopeProps(readModuleName.data(), (ULONG)readModuleName.capacity(), &readModuleNameLength, & mvid)); + + EXPECT_EQ(moduleName.length(), readModuleNameLength - 1); + EXPECT_EQ(moduleName, readModuleName.substr(0, readModuleNameLength - 1)); +} + +TEST(Module, EmptyName) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + ASSERT_EQ(S_OK, emit->SetModuleProps(W(""))); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + std::array readModuleName; + ULONG readModuleNameLength; + + GUID mvid; + ASSERT_EQ(S_OK, import->GetScopeProps(readModuleName.data(), (ULONG)readModuleName.size(), & readModuleNameLength, & mvid)); + + EXPECT_EQ(0, readModuleNameLength); + EXPECT_EQ('\0', readModuleName[0]); +} + +TEST(Module, NullName) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + ASSERT_EQ(S_OK, emit->SetModuleProps(nullptr)); +} \ No newline at end of file diff --git a/test/emit/moduleref.cpp b/test/emit/moduleref.cpp new file mode 100644 index 00000000..696186ec --- /dev/null +++ b/test/emit/moduleref.cpp @@ -0,0 +1,23 @@ +#include "emit.hpp" + +TEST(ModuleRef, Define) +{ + WSTR_string name = W("Foo"); + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdModuleRef moduleRef; + + ASSERT_EQ(S_OK, emit->DefineModuleRef(name.c_str(), &moduleRef)); + + ASSERT_EQ(1, RidFromToken(moduleRef)); + ASSERT_EQ(mdtModuleRef, TypeFromToken(moduleRef)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readName; + readName.resize(name.capacity() + 1); + ULONG readNameLength; + ASSERT_EQ(S_OK, import->GetModuleRefProps(moduleRef, readName.data(), (ULONG)readName.capacity(), &readNameLength)); + EXPECT_EQ(name, readName.substr(0, readNameLength - 1)); +} \ No newline at end of file diff --git a/test/emit/param.cpp b/test/emit/param.cpp new file mode 100644 index 00000000..4420d9f3 --- /dev/null +++ b/test/emit/param.cpp @@ -0,0 +1,150 @@ +#include "emit.hpp" + +TEST(Param, Define) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdParamDef param; + mdMethodDef method; + std::array signature = { IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 1, ELEMENT_TYPE_VOID, ELEMENT_TYPE_I4 }; + ASSERT_EQ(S_OK, emit->DefineMethod(TokenFromRid(1, mdtTypeDef), W("Method"), 0, signature.data(), (ULONG)signature.size(), 0, 0, &method)); + WSTR_string paramName{ W("Param") }; + ASSERT_EQ(S_OK, emit->DefineParam(method, 1, paramName.c_str(), pdIn, ELEMENT_TYPE_VOID, nullptr, 0, ¶m)); + ASSERT_EQ(1, RidFromToken(param)); + ASSERT_EQ(mdtParamDef, TypeFromToken(param)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readName; + readName.resize(paramName.size() + 1); + mdMethodDef readMethod; + ULONG readNameLength; + ULONG flags; + ULONG sequence; + DWORD paramConstType; + UVCP_CONSTANT constValue; + ULONG constValueLength; + ASSERT_EQ(S_OK, import->GetParamProps(param, & readMethod, & sequence, readName.data(), (ULONG)readName.capacity(), & readNameLength, & flags, & paramConstType, & constValue, & constValueLength)); + + EXPECT_EQ(paramName, readName.substr(0, readNameLength - 1)); + EXPECT_EQ(method, readMethod); + EXPECT_EQ(1, sequence); + EXPECT_EQ(pdIn, flags); + EXPECT_EQ(ELEMENT_TYPE_VOID, paramConstType); + EXPECT_EQ(nullptr, constValue); + EXPECT_EQ(0, constValueLength); +} + +TEST(Param, DefineWithConstant) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdParamDef param; + mdMethodDef method; + std::array signature = { IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 1, ELEMENT_TYPE_VOID, ELEMENT_TYPE_I4 }; + ASSERT_EQ(S_OK, emit->DefineMethod(TokenFromRid(1, mdtTypeDef), W("Method"), 0, signature.data(), (ULONG)signature.size(), 0, 0, &method)); + WSTR_string paramName{ W("Param") }; + + int value = 42; + ASSERT_EQ(S_OK, emit->DefineParam(method, 1, paramName.c_str(), pdIn, ELEMENT_TYPE_I4, &value, sizeof(int), ¶m)); + ASSERT_EQ(1, RidFromToken(param)); + ASSERT_EQ(mdtParamDef, TypeFromToken(param)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readName; + readName.resize(paramName.size() + 1); + mdMethodDef readMethod; + ULONG readNameLength; + ULONG flags; + ULONG sequence; + DWORD paramConstType; + UVCP_CONSTANT constValue; + ULONG constValueLength; + ASSERT_EQ(S_OK, import->GetParamProps(param, & readMethod, & sequence, readName.data(), (ULONG)readName.capacity(), & readNameLength, & flags, & paramConstType, & constValue, & constValueLength)); + + EXPECT_EQ(paramName, readName.substr(0, readNameLength - 1)); + EXPECT_EQ(method, readMethod); + EXPECT_EQ(1, sequence); + EXPECT_EQ(pdIn, flags); + EXPECT_EQ(ELEMENT_TYPE_I4, paramConstType); + + // The constant value should be stored in the metadata in the #Blob heap, not just as a pointer to the passed-in value. + EXPECT_NE(&value, constValue); + EXPECT_EQ(value, *(int*)constValue); + + // Constant length only returned for string constants. + EXPECT_EQ(0, constValueLength); +} + +TEST(Param, DefineWithConstantString) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdParamDef param; + mdMethodDef method; + std::array signature = { IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 1, ELEMENT_TYPE_VOID, ELEMENT_TYPE_STRING }; + ASSERT_EQ(S_OK, emit->DefineMethod(TokenFromRid(1, mdtTypeDef), W("Method"), 0, signature.data(), (ULONG)signature.size(), 0, 0, &method)); + WSTR_string paramName{ W("Param") }; + + WSTR_string value = { W("ConstantValue") }; + ASSERT_EQ(S_OK, emit->DefineParam(method, 1, paramName.c_str(), pdIn, ELEMENT_TYPE_STRING, value.c_str(), (ULONG)value.length(), ¶m)); + ASSERT_EQ(1, RidFromToken(param)); + ASSERT_EQ(mdtParamDef, TypeFromToken(param)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readName; + readName.resize(paramName.size() + 1); + mdMethodDef readMethod; + ULONG readNameLength; + ULONG flags; + ULONG sequence; + DWORD paramConstType; + UVCP_CONSTANT constValue; + ULONG constValueLength; + ASSERT_EQ(S_OK, import->GetParamProps(param, &readMethod, &sequence, readName.data(), (ULONG)readName.capacity(), &readNameLength, &flags, ¶mConstType, &constValue, &constValueLength)); + + EXPECT_EQ(paramName, readName.substr(0, readNameLength - 1)); + EXPECT_EQ(method, readMethod); + EXPECT_EQ(1, sequence); + EXPECT_EQ(pdIn, flags); + EXPECT_EQ(ELEMENT_TYPE_STRING, paramConstType); + + // The constant value should be stored in the metadata in the #Blob heap, not just as a pointer to the passed-in value. + EXPECT_NE(&value, constValue); + WSTR_string retrievedConstant{ (WCHAR*)constValue, constValueLength }; + EXPECT_EQ(value, retrievedConstant); +} + +TEST(Param, DefineOutOfOrder) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdParamDef param1; + mdParamDef param; + mdMethodDef method; + std::array signature = { IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 1, ELEMENT_TYPE_VOID, ELEMENT_TYPE_I4 }; + ASSERT_EQ(S_OK, emit->DefineMethod(TokenFromRid(1, mdtTypeDef), W("Method"), 0, signature.data(), (ULONG)signature.size(), 0, 0, &method)); + WSTR_string param1Name{ W("Param") }; + WSTR_string paramName{ W("Param0") }; + ASSERT_EQ(S_OK, emit->DefineParam(method, 1, param1Name.c_str(), pdIn, ELEMENT_TYPE_VOID, nullptr, 0, ¶m1)); + ASSERT_EQ(S_OK, emit->DefineParam(method, 0, paramName.c_str(), pdIn, ELEMENT_TYPE_VOID, nullptr, 0, ¶m)); + ASSERT_EQ(2, RidFromToken(param)); + ASSERT_EQ(mdtParamDef, TypeFromToken(param)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + HCORENUM paramEnum = nullptr; + mdParamDef readParams[2]; + ULONG readParamsCount; + ASSERT_EQ(S_OK, import->EnumParams(¶mEnum, method, readParams, 2, &readParamsCount)); + import->CloseEnum(paramEnum); + EXPECT_EQ(2, readParamsCount); + EXPECT_EQ(param, readParams[0]); + EXPECT_EQ(param1, readParams[1]); +} \ No newline at end of file diff --git a/test/emit/standalonesig.cpp b/test/emit/standalonesig.cpp new file mode 100644 index 00000000..bd9138c0 --- /dev/null +++ b/test/emit/standalonesig.cpp @@ -0,0 +1,23 @@ +#include "emit.hpp" +#include +#include +#include + +TEST(StandaloneSig, Define) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdSignature sig; + std::array signature = {0x01, 0x02, 0x03}; + ASSERT_EQ(S_OK, emit->GetTokenFromSig(signature.data(), (ULONG)signature.size(), &sig)); + ASSERT_EQ(1, RidFromToken(sig)); + ASSERT_EQ(mdtSignature, TypeFromToken(sig)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + PCCOR_SIGNATURE sigBlob; + ULONG sigBlobLength; + ASSERT_EQ(S_OK, import->GetSigFromToken(sig, &sigBlob, &sigBlobLength)); + EXPECT_THAT(std::vector(sigBlob, sigBlob + sigBlobLength), testing::ContainerEq(std::vector(signature.begin(), signature.end()))); +} \ No newline at end of file diff --git a/test/emit/typedef.cpp b/test/emit/typedef.cpp new file mode 100644 index 00000000..eace5b84 --- /dev/null +++ b/test/emit/typedef.cpp @@ -0,0 +1,203 @@ +#include "emit.hpp" + +TEST(TypeDef, Define) +{ + WSTR_string name = W("Foo"); + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdTypeDef typeDef; + mdToken implements = mdTokenNil; + + ASSERT_EQ(S_OK, emit->DefineTypeDef(name.c_str(), 0, mdTypeDefNil, &implements, &typeDef)); + + // The first type is the type, + // so the second type is the one we just defined. + ASSERT_EQ(2, RidFromToken(typeDef)); + ASSERT_EQ(mdtTypeDef, TypeFromToken(typeDef)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readName; + readName.resize(name.capacity() + 1); + ULONG readNameLength; + DWORD typeDefFlags; + mdToken extends; + ASSERT_EQ(S_OK, import->GetTypeDefProps(typeDef, readName.data(), (ULONG)readName.capacity(), &readNameLength, &typeDefFlags, &extends)); + EXPECT_EQ(name, readName.substr(0, readNameLength - 1)); + EXPECT_EQ(0, typeDefFlags); + EXPECT_EQ(mdTypeRefNil, extends); + + HCORENUM hEnum = nullptr; + mdFieldDef field; + ULONG count; + EXPECT_EQ(S_FALSE, import->EnumFields(&hEnum, typeDef, &field, 1, &count)); + import->CloseEnum(hEnum); + + hEnum = nullptr; + mdMethodDef method; + EXPECT_EQ(S_FALSE, import->EnumMethods(&hEnum, typeDef, &method, 1, &count)); + import->CloseEnum(hEnum); +} + +TEST(TypeDef, DefineWithInterfaces) +{ + WSTR_string name = W("Foo"); + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdTypeDef typeDef; + mdToken implements[] = { TokenFromRid(1, mdtTypeRef), mdTokenNil }; + + ASSERT_EQ(S_OK, emit->DefineTypeDef(name.c_str(), 0, mdTypeDefNil, implements, &typeDef)); + + // The first type is the type, + // so the second type is the one we just defined. + ASSERT_EQ(2, RidFromToken(typeDef)); + ASSERT_EQ(mdtTypeDef, TypeFromToken(typeDef)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + HCORENUM hEnum = nullptr; + mdInterfaceImpl interfaceImpls[2] = {}; + ULONG count; + ASSERT_EQ(S_OK, import->EnumInterfaceImpls(&hEnum, typeDef, interfaceImpls, 2, &count)); + ASSERT_EQ(1, count); + EXPECT_EQ(TokenFromRid(1, mdtInterfaceImpl), interfaceImpls[0]); + EXPECT_EQ(0, interfaceImpls[1]); // The second element should not be touched. + import->CloseEnum(hEnum); + + mdTypeDef classType; + mdToken interfaceType; + ASSERT_EQ(S_OK, import->GetInterfaceImplProps(interfaceImpls[0], &classType, &interfaceType)); + EXPECT_EQ(typeDef, classType); + EXPECT_EQ(TokenFromRid(1, mdtTypeRef), interfaceType); + +} + +TEST(TypeDef, DefineWithBase) +{ + WSTR_string name = W("Foo"); + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdTypeDef typeDef; + mdToken base = TokenFromRid(1, mdtTypeRef); + mdToken implements = mdTokenNil; + + ASSERT_EQ(S_OK, emit->DefineTypeDef(name.c_str(), 0, base, &implements, &typeDef)); + + // The first type is the type, + // so the second type is the one we just defined. + ASSERT_EQ(2, RidFromToken(typeDef)); + ASSERT_EQ(mdtTypeDef, TypeFromToken(typeDef)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readName; + readName.resize(name.capacity() + 1); + ULONG readNameLength; + DWORD typeDefFlags; + mdToken extends; + ASSERT_EQ(S_OK, import->GetTypeDefProps(typeDef, readName.data(), (ULONG)readName.capacity(), &readNameLength, &typeDefFlags, &extends)); + EXPECT_EQ(name, readName.substr(0, readNameLength - 1)); + EXPECT_EQ(0, typeDefFlags); + EXPECT_EQ(base, extends); +} + +TEST(TypeDef, NestedDefine) +{ + WSTR_string name = W("Foo"); + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdTypeDef outerTypeDef, typeDef; + mdToken base = TokenFromRid(1, mdtTypeRef); + mdToken outerImplements = mdTokenNil; + mdToken implements[] = { TokenFromRid(1, mdtTypeRef), mdTokenNil }; + + ASSERT_EQ(S_OK, emit->DefineTypeDef(name.c_str(), 0, mdTypeDefNil, &outerImplements, &outerTypeDef)); + ASSERT_EQ(S_OK, emit->DefineNestedType(name.c_str(), 0, base, implements, outerTypeDef, &typeDef)); + + // The first type is the type, + // the second is the outer type, + // so the third is the nested type. + ASSERT_EQ(3, RidFromToken(typeDef)); + ASSERT_EQ(mdtTypeDef, TypeFromToken(typeDef)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readName; + readName.resize(name.capacity() + 1); + ULONG readNameLength; + DWORD typeDefFlags; + mdToken extends; + ASSERT_EQ(S_OK, import->GetTypeDefProps(typeDef, readName.data(), (ULONG)readName.capacity(), &readNameLength, &typeDefFlags, &extends)); + EXPECT_EQ(name, readName.substr(0, readNameLength - 1)); + EXPECT_EQ(0, typeDefFlags); + EXPECT_EQ(base, extends); + + HCORENUM hEnum = nullptr; + mdInterfaceImpl interfaceImpls[2] = {}; + ULONG count; + ASSERT_EQ(S_OK, import->EnumInterfaceImpls(&hEnum, typeDef, interfaceImpls, 2, &count)); + ASSERT_EQ(1, count); + EXPECT_EQ(TokenFromRid(1, mdtInterfaceImpl), interfaceImpls[0]); + EXPECT_EQ(0, interfaceImpls[1]); // The second element should not be touched. + import->CloseEnum(hEnum); + + mdTypeDef classType; + mdToken interfaceType; + ASSERT_EQ(S_OK, import->GetInterfaceImplProps(interfaceImpls[0], &classType, &interfaceType)); + EXPECT_EQ(typeDef, classType); + EXPECT_EQ(TokenFromRid(1, mdtTypeRef), interfaceType); + + mdTypeDef enclosing; + ASSERT_EQ(S_OK, import->GetNestedClassProps(typeDef, &enclosing)); + EXPECT_EQ(outerTypeDef, enclosing); +} + +TEST(TypeDef, SetProps) +{ + WSTR_string name = W("Foo"); + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdTypeDef typeDef; + mdToken initialImplements[] = { TokenFromRid(2, mdtTypeSpec), mdTokenNil }; + mdToken extends = TokenFromRid(1, mdtTypeRef); + + ASSERT_EQ(S_OK, emit->DefineTypeDef(name.c_str(), 0, mdTypeDefNil, initialImplements, &typeDef)); + + mdToken implements[] = { TokenFromRid(4, mdtTypeRef), mdTokenNil }; + ASSERT_EQ(S_OK, emit->SetTypeDefProps(typeDef, tdAbstract, extends, implements)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + WSTR_string readName; + readName.resize(name.capacity() + 1); + ULONG readNameLength; + DWORD typeDefFlags; + mdToken readExtends; + ASSERT_EQ(S_OK, import->GetTypeDefProps(typeDef, readName.data(), (ULONG)readName.capacity(), &readNameLength, &typeDefFlags, &readExtends)); + EXPECT_EQ(name, readName.substr(0, readNameLength - 1)); + EXPECT_EQ(tdAbstract, typeDefFlags); + EXPECT_EQ(extends, readExtends); + + HCORENUM hEnum = nullptr; + mdInterfaceImpl interfaceImpls[2] = {}; + ULONG count; + ASSERT_EQ(S_OK, import->EnumInterfaceImpls(&hEnum, typeDef, interfaceImpls, 2, &count)); + ASSERT_EQ(1, count); + // We should have created a new entry for the new interface implementation, + // not modified the existing one. + EXPECT_EQ(TokenFromRid(2, mdtInterfaceImpl), interfaceImpls[0]); + EXPECT_EQ(0, interfaceImpls[1]); // The second element should not be touched. + import->CloseEnum(hEnum); + + mdTypeDef classType; + mdToken interfaceType; + ASSERT_EQ(S_OK, import->GetInterfaceImplProps(interfaceImpls[0], &classType, &interfaceType)); + EXPECT_EQ(typeDef, classType); + EXPECT_EQ(implements[0], interfaceType); +} diff --git a/test/emit/typeref.cpp b/test/emit/typeref.cpp new file mode 100644 index 00000000..7e4fa298 --- /dev/null +++ b/test/emit/typeref.cpp @@ -0,0 +1,53 @@ +#include "emit.hpp" + +TEST(TypeRef, ValidScopeAndDottedName) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdTypeRef typeRef; + WSTR_string name = W("System.Object"); + ASSERT_EQ(S_OK, emit->DefineTypeRefByName(TokenFromRid(1, mdtModule), name.c_str(), &typeRef)); + ASSERT_EQ(1, RidFromToken(typeRef)); + ASSERT_EQ(mdtTypeRef, TypeFromToken(typeRef)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + mdToken resolutionScope; + WSTR_string readName; + readName.resize(name.capacity() + 1); + ULONG readNameLength; + ASSERT_EQ(S_OK, import->GetTypeRefProps(typeRef, &resolutionScope, readName.data(), (ULONG) readName.size(), &readNameLength)); + EXPECT_EQ(TokenFromRid(1, mdtModule), resolutionScope); + EXPECT_EQ(readNameLength, name.size() + 1); + EXPECT_EQ(name, readName.substr(0, readNameLength - 1)); +} + +TEST(TypeRef, InvalidScope) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdTypeRef typeRef; + ASSERT_EQ(E_FAIL, emit->DefineTypeRefByName(TokenFromRid(1, mdtTypeDef), W("System.Object"), &typeRef)); +} + +TEST(TypeRef, ValidScopeAndNonDottedName) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdTypeRef typeRef; + WSTR_string name = W("Bar"); + ASSERT_EQ(S_OK, emit->DefineTypeRefByName(TokenFromRid(1, mdtModule), name.c_str(), &typeRef)); + ASSERT_EQ(1, RidFromToken(typeRef)); + ASSERT_EQ(mdtTypeRef, TypeFromToken(typeRef)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + mdToken resolutionScope; + WSTR_string readName; + readName.resize(name.capacity() + 1); + ULONG readNameLength; + ASSERT_EQ(S_OK, import->GetTypeRefProps(typeRef, &resolutionScope, readName.data(), (ULONG) readName.size(), &readNameLength)); + EXPECT_EQ(TokenFromRid(1, mdtModule), resolutionScope); + EXPECT_EQ(readNameLength, name.size() + 1); + EXPECT_EQ(name, readName.substr(0, readNameLength - 1)); +} \ No newline at end of file diff --git a/test/emit/typespec.cpp b/test/emit/typespec.cpp new file mode 100644 index 00000000..23076c49 --- /dev/null +++ b/test/emit/typespec.cpp @@ -0,0 +1,23 @@ +#include "emit.hpp" +#include +#include +#include + +TEST(TypeSpec, Define) +{ + dncp::com_ptr emit; + ASSERT_NO_FATAL_FAILURE(CreateEmit(emit)); + mdTypeSpec spec; + std::array signature = {0x01, 0x02, 0x03}; + ASSERT_EQ(S_OK, emit->GetTokenFromTypeSpec(signature.data(), (ULONG)signature.size(), &spec)); + ASSERT_EQ(1, RidFromToken(spec)); + ASSERT_EQ(mdtTypeSpec, TypeFromToken(spec)); + + dncp::com_ptr import; + ASSERT_EQ(S_OK, emit->QueryInterface(IID_IMetaDataImport, (void**)&import)); + + PCCOR_SIGNATURE sigBlob; + ULONG sigBlobLength; + ASSERT_EQ(S_OK, import->GetTypeSpecFromToken(spec, &sigBlob, &sigBlobLength)); + EXPECT_THAT(std::vector(sigBlob, sigBlob + sigBlobLength), testing::ContainerEq(std::vector(signature.begin(), signature.end()))); +} \ No newline at end of file diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 5eed48dc..78cf3dae 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -52,4 +52,4 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy $ $) endif() -gtest_discover_tests(regtest) \ No newline at end of file +gtest_discover_tests(regtest DISCOVERY_TIMEOUT 1200) \ No newline at end of file