-- v10: rometadata: Implement IMetaDataTables::{GetColumn, GetColumnInfo}. rometadata/tests: Add tests for IMetaDataTables::{GetColumnInfo, GetColumn}. rometadata: Implement IMetaDataTables::Get{String,Blob,Guid}. rometadata: Implement IMetaDataTables::GetRow. rometadata: Implement IMetaDataTables::GetTableInfo. rometadata: Implement IMetaDataTables::{Get{String,Blob,Guid,UserString}HeapSize, GetNumTables}.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/Makefile.in | 1 + dlls/rometadata/assembly.c | 255 +++++++++++++++++++++++++++++ dlls/rometadata/main.c | 2 +- dlls/rometadata/mdtables.c | 48 ++++-- dlls/rometadata/rometadatapriv.h | 17 +- dlls/rometadata/tests/rometadata.c | 10 -- 6 files changed, 309 insertions(+), 24 deletions(-) create mode 100644 dlls/rometadata/assembly.c
diff --git a/dlls/rometadata/Makefile.in b/dlls/rometadata/Makefile.in index 52c39ba8f87..29edc10fb16 100644 --- a/dlls/rometadata/Makefile.in +++ b/dlls/rometadata/Makefile.in @@ -3,5 +3,6 @@ IMPORTLIB = rometadata IMPORTS = combase
SOURCES = \ + assembly.c \ main.c \ mdtables.c diff --git a/dlls/rometadata/assembly.c b/dlls/rometadata/assembly.c new file mode 100644 index 00000000000..d15776970f1 --- /dev/null +++ b/dlls/rometadata/assembly.c @@ -0,0 +1,255 @@ +/* + * CIL assembly parser + * + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "rometadatapriv.h" + +#include <assert.h> +#include <wine/debug.h> + +WINE_DEFAULT_DEBUG_CHANNEL(rometadata); + +#pragma pack(push, 1) + +#define METADATA_MAGIC ('B' | ('S' << 8) | ('J' << 16) | ('B' << 24)) + +/* ECMA-335 Partition II.24.2.1, "Metadata root" */ +struct metadata_hdr +{ + UINT32 signature; + UINT16 major_version; + UINT16 minor_version; + UINT32 reserved; + UINT32 length; + char version[0]; +}; + +/* ECMA-335 Partition II.24.2.2, "Stream header" */ +struct stream_hdr +{ + UINT32 offset; + UINT32 size; + char name[0]; +}; + +#pragma pack(pop) + +struct metadata_stream +{ + UINT32 size; + const BYTE *start; +}; + +struct assembly +{ + HANDLE file; + HANDLE map; + const BYTE *data; + UINT64 size; + + struct metadata_stream stream_tables; + struct metadata_stream stream_strings; + struct metadata_stream stream_blobs; + struct metadata_stream stream_guids; + struct metadata_stream stream_user_strings; +}; + +static BOOL pe_rva_to_offset(const IMAGE_SECTION_HEADER *sections, UINT32 num_sections, UINT32 rva, UINT32 *offset) +{ + UINT32 i; + + for (i = 0; i < num_sections; i++) + { + if (rva >= sections[i].VirtualAddress && rva < (sections[i].VirtualAddress + sections[i].Misc.VirtualSize)) + { + *offset = rva - sections[i].VirtualAddress + sections[i].PointerToRawData; + return TRUE; + } + } + return FALSE; +} + +static HRESULT assembly_parse_headers(assembly_t *assembly) +{ + const IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)assembly->data; + const IMAGE_SECTION_HEADER *sections; + const IMAGE_NT_HEADERS32 *nt_hdrs; + const IMAGE_COR20_HEADER *cor_hdr; + const struct metadata_hdr *md_hdr; + const BYTE *streams_cur, *md_start, *ptr; + UINT32 rva, num_sections, offset; + UINT8 num_streams, i; + + if (assembly->size < sizeof(IMAGE_DOS_HEADER) || dos_hdr->e_magic != IMAGE_DOS_SIGNATURE || + assembly->size < (dos_hdr->e_lfanew + sizeof(IMAGE_NT_HEADERS32))) + return E_INVALIDARG; + + nt_hdrs = (IMAGE_NT_HEADERS32 *)(assembly->data + dos_hdr->e_lfanew); + if (!(num_sections = nt_hdrs->FileHeader.NumberOfSections)) return E_INVALIDARG; + switch (nt_hdrs->OptionalHeader.Magic) + { + case IMAGE_NT_OPTIONAL_HDR32_MAGIC: + rva = nt_hdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress; + sections = (IMAGE_SECTION_HEADER *)(assembly->data + dos_hdr->e_lfanew + sizeof(IMAGE_NT_HEADERS32)); + break; + case IMAGE_NT_OPTIONAL_HDR64_MAGIC: + { + const IMAGE_NT_HEADERS64 *hdr64 = (IMAGE_NT_HEADERS64 *)(assembly->data + dos_hdr->e_lfanew); + + if (dos_hdr->e_lfanew + sizeof(IMAGE_NT_HEADERS64) > assembly->size) return E_INVALIDARG; + rva = hdr64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress; + sections = (IMAGE_SECTION_HEADER *)(assembly->data + dos_hdr->e_lfanew + sizeof(IMAGE_NT_HEADERS64)); + break; + } + default: + return E_INVALIDARG; + } + + if (!pe_rva_to_offset(sections, num_sections, rva, &offset) || offset + sizeof(IMAGE_COR20_HEADER) > assembly->size) + return E_INVALIDARG; + + cor_hdr = (IMAGE_COR20_HEADER *)(assembly->data + offset); + if (cor_hdr->cb != sizeof(IMAGE_COR20_HEADER)) return E_INVALIDARG; + if (!(pe_rva_to_offset(sections, num_sections, cor_hdr->MetaData.VirtualAddress, &offset)) || + offset + sizeof(struct metadata_hdr) > assembly->size) + return E_INVALIDARG; + + md_start = assembly->data + offset; + md_hdr = (struct metadata_hdr *)md_start; + if (md_hdr->signature != METADATA_MAGIC || + offset + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16) > assembly->size) + return E_INVALIDARG; + + num_streams = *(UINT8 *)(md_start + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16)); /* Flags */ + streams_cur = md_start + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16) * 2; /* Flags + Streams */ + + for (i = 0; i < num_streams; i++) + { + const struct stream_hdr *md_stream_hdr = (struct stream_hdr *)streams_cur; + const struct + { + const char *name; + DWORD name_len; + struct metadata_stream *stream; + } streams[] = + { + { "#~", 2, &assembly->stream_tables }, + { "#Strings", 8, &assembly->stream_strings }, + { "#Blob", 5, &assembly->stream_blobs }, + { "#GUID", 5, &assembly->stream_guids }, + { "#US", 3, &assembly->stream_user_strings } + }; + HRESULT hr = E_INVALIDARG; + int j; + + if ((UINT_PTR)(streams_cur - assembly->data) > assembly->size) return E_INVALIDARG; + for (j = 0; j < ARRAY_SIZE(streams); j++) + { + if (!strncmp(streams[j].name, md_stream_hdr->name, streams[j].name_len)) + { + if (md_stream_hdr->offset + md_stream_hdr->size <= assembly->size) + { + streams[j].stream->size = md_stream_hdr->size; + streams[j].stream->start = md_start + md_stream_hdr->offset; + hr = S_OK; + } + break; + } + } + if (FAILED(hr)) return hr; + /* The stream name is padded to the next 4-byte boundary (ECMA-335 Partition II.24.2.2) */ + streams_cur += offsetof(struct stream_hdr, name[(streams[j].name_len + 4) & ~3]); + } + + /* IMetaDataTables::GetStringHeapSize returns the string heap size without the nul byte padding. + * Partition II.24.2.3 says that if there is a string heap, the first entry is always the empty string, so + * we'll only subtract padding bytes as long as stream_strings.size > 1. */ + ptr = assembly->stream_strings.start + assembly->stream_strings.size - 1; + while (assembly->stream_strings.size > 1 && !ptr[0] && !ptr[-1]) + { + assembly->stream_strings.size--; + ptr--; + } + + return S_OK; +} + +HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **ret) +{ + assembly_t *assembly; + HRESULT hr = S_OK; + + TRACE("(%s, %p)\n", debugstr_w(path), ret); + + if (!(assembly = calloc(1, sizeof(*assembly)))) return E_OUTOFMEMORY; + assembly->file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (assembly->file == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + if (!(assembly->map = CreateFileMappingW(assembly->file, NULL, PAGE_READONLY, 0, 0, NULL))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + if (!(assembly->data = MapViewOfFile(assembly->map, FILE_MAP_READ, 0, 0, 0))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + if (!GetFileSizeEx(assembly->file, (LARGE_INTEGER *)&assembly->size)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + + hr = assembly_parse_headers(assembly); + +done: + if (FAILED(hr)) + assembly_free(assembly); + else + *ret = assembly; + return hr; +} + +void assembly_free(assembly_t *assembly) +{ + if (assembly->map) UnmapViewOfFile(assembly->map); + CloseHandle(assembly->map); + CloseHandle(assembly->file); + free(assembly); +} + +ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap) +{ + switch (heap) + { + case HEAP_BLOB: + return assembly->stream_blobs.size; + case HEAP_STRING: + return assembly->stream_strings.size; + case HEAP_GUID: + return assembly->stream_guids.size; + case HEAP_USER_STRING: + return assembly->stream_user_strings.size; + DEFAULT_UNREACHABLE; + } +} \ No newline at end of file diff --git a/dlls/rometadata/main.c b/dlls/rometadata/main.c index 6cded07c11f..346753009a7 100644 --- a/dlls/rometadata/main.c +++ b/dlls/rometadata/main.c @@ -96,7 +96,7 @@ static HRESULT WINAPI MetaDataDispenser_OpenScope(IMetaDataDispenserEx *iface, c FIXME("%p %s %lx %s %p semi-stub!\n", iface, debugstr_w(scope), open_flags, debugstr_guid(riid), obj);
*obj = NULL; - hr = IMetaDataTables_create(&tables); + hr = IMetaDataTables_create(scope, &tables); if (FAILED(hr)) return hr;
diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c index 180dbb09b68..9affb80f5bf 100644 --- a/dlls/rometadata/mdtables.c +++ b/dlls/rometadata/mdtables.c @@ -32,6 +32,8 @@ struct metadata_tables { IMetaDataTables IMetaDataTables_iface; LONG ref; + + assembly_t *assembly; };
static inline struct metadata_tables *impl_from_IMetaDataTables(IMetaDataTables *iface) @@ -70,38 +72,53 @@ static ULONG WINAPI tables_Release(IMetaDataTables *iface)
TRACE("(%p)\n", iface);
- if (!ref) free(impl); + if (!ref) + { + assembly_free(impl->assembly); + free(impl); + } return ref; }
+static HRESULT get_heap_size(IMetaDataTables *iface, enum heap_type type, ULONG *size) +{ + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + *size = assembly_get_heap_size(impl->assembly, type); + return S_OK; +} + static HRESULT WINAPI tables_GetStringHeapSize(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + return get_heap_size(iface, HEAP_STRING, size); }
static HRESULT WINAPI tables_GetBlobHeapSize(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + return get_heap_size(iface, HEAP_BLOB, size); }
static HRESULT WINAPI tables_GetGuidHeapSize(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + return get_heap_size(iface, HEAP_GUID, size); }
static HRESULT WINAPI tables_GetUserStringHeapSize(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + return get_heap_size(iface, HEAP_USER_STRING, size); }
static HRESULT WINAPI tables_GetNumTables(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + + /* This returns the number of tables known to the metadata parser, not the number of tables that exist in this + * assembly. */ + *size = 45; + return S_OK; }
static HRESULT WINAPI tables_GetTableIndex(IMetaDataTables *iface, ULONG token, ULONG *idx) @@ -217,11 +234,18 @@ static const struct IMetaDataTablesVtbl tables_vtbl = tables_GetNextUserString, };
-HRESULT IMetaDataTables_create(IMetaDataTables **iface) +HRESULT IMetaDataTables_create(const WCHAR *path, IMetaDataTables **iface) { struct metadata_tables *impl; + HRESULT hr;
if (!(impl = calloc(1, sizeof(*impl)))) return E_OUTOFMEMORY; + if (FAILED(hr = assembly_open_from_file(path, &impl->assembly))) + { + free( impl ); + return hr; + } + impl->IMetaDataTables_iface.lpVtbl = &tables_vtbl; impl->ref = 1; *iface = &impl->IMetaDataTables_iface; diff --git a/dlls/rometadata/rometadatapriv.h b/dlls/rometadata/rometadatapriv.h index e6ff2ada18a..6ca83cefbf6 100644 --- a/dlls/rometadata/rometadatapriv.h +++ b/dlls/rometadata/rometadatapriv.h @@ -19,6 +19,21 @@ #ifndef __WINE_ROMETADATA_PRIVATE__ #define __WINE_ROMETADATA_PRIVATE__
-extern HRESULT IMetaDataTables_create(IMetaDataTables **iface); +#include <rometadataapi.h> + +extern HRESULT IMetaDataTables_create(const WCHAR *path, IMetaDataTables **iface); + +typedef struct assembly assembly_t; +enum heap_type +{ + HEAP_STRING = 0, + HEAP_GUID = 1, + HEAP_BLOB = 2, + HEAP_USER_STRING = 3 +}; + +extern HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **out); +extern void assembly_free(assembly_t *assembly); +extern ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap);
#endif /* __WINE_ROMETADATA_PRIVATE__ */ diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index c80714671d6..dc4452a3d34 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -287,37 +287,27 @@ static void test_MetaDataDispenser_OpenScope(void) IMetaDataDispenser_Release(dispenser);
hr = IMetaDataTables_GetStringHeapSize(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == 329, "got val %lu\n", val);
val = 0; hr = IMetaDataTables_GetBlobHeapSize(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == 156, "got val %lu\n", val);
val = 0; hr = IMetaDataTables_GetGuidHeapSize(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == 16, "got val %lu\n", val);
val = 0; hr = IMetaDataTables_GetUserStringHeapSize(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == 8, "got val %lu\n", val);
val = 0; hr = IMetaDataTables_GetNumTables(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == TABLE_MAX, "got val %lu\n", val);
for (i = 0; i < TABLE_MAX; i++)
From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/assembly.c | 594 ++++++++++++++++++++++++++++- dlls/rometadata/mdtables.c | 16 +- dlls/rometadata/rometadatapriv.h | 15 + dlls/rometadata/tests/rometadata.c | 11 - 4 files changed, 621 insertions(+), 15 deletions(-)
diff --git a/dlls/rometadata/assembly.c b/dlls/rometadata/assembly.c index d15776970f1..00824d55d36 100644 --- a/dlls/rometadata/assembly.c +++ b/dlls/rometadata/assembly.c @@ -48,6 +48,19 @@ struct stream_hdr char name[0]; };
+/* ECMA-335 Partition II.24.2.6, "#~ stream" */ +struct stream_tables_hdr +{ + UINT32 reserved; + UINT8 major_version; + UINT8 minor_version; + UINT8 heap_size_bits; /* enum heap_type */ + UINT8 reserved2; + UINT64 valid_tables_bits; + UINT64 sorted_tables_bits; + UINT32 table_rows[0]; +}; + #pragma pack(pop)
struct metadata_stream @@ -56,6 +69,416 @@ struct metadata_stream const BYTE *start; };
+enum table +{ + TABLE_MODULE = 0x00, + TABLE_TYPEREF = 0x01, + TABLE_TYPEDEF = 0x02, + TABLE_FIELDPTR = 0x03, + TABLE_FIELD = 0x04, + TABLE_METHODPTR = 0x05, + TABLE_METHODDEF = 0x06, + TABLE_PARAMPTR = 0x07, + TABLE_PARAM = 0x08, + TABLE_INTERFACEIMPL = 0x09, + TABLE_MEMBERREF = 0x0a, + TABLE_CONSTANT = 0x0b, + TABLE_CUSTOMATTRIBUTE = 0x0c, + TABLE_FIELDMARSHAL = 0x0d, + TABLE_DECLSECURITY = 0x0e, + TABLE_CLASSLAYOUT = 0x0f, + TABLE_FIELDLAYOUT = 0x10, + TABLE_STANDALONESIG = 0x11, + TABLE_EVENTMAP = 0x12, + TABLE_EVENTPTR = 0x13, + TABLE_EVENT = 0x14, + TABLE_PROPERTYMAP = 0x15, + TABLE_PROPERTYPTR = 0x16, + TABLE_PROPERTY = 0x17, + TABLE_METHODSEMANTICS = 0x18, + TABLE_METHODIMPL = 0x19, + TABLE_MODULEREF = 0x1a, + TABLE_TYPESPEC = 0x1b, + TABLE_IMPLMAP = 0x1c, + TABLE_FIELDRVA = 0x1d, + TABLE_ENCLOG = 0x1e, + TABLE_ENCMAP = 0x1f, + TABLE_ASSEMBLY = 0x20, + TABLE_ASSEMBLYPROCESSOR = 0x21, + TABLE_ASSEMBLYOS = 0x22, + TABLE_ASSEMBLYREF = 0x23, + TABLE_ASSEMBLYREFPROCESSOR = 0x24, + TABLE_ASSEMBLYREFOS = 0x25, + TABLE_FILE = 0x26, + TABLE_EXPORTEDTYPE = 0x27, + TABLE_MANIFESTRESOURCE = 0x28, + TABLE_NESTEDCLASS = 0x29, + TABLE_GENERICPARAM = 0x2a, + TABLE_METHODSPEC = 0x2b, + TABLE_GENERICPARAMCONSTRAINT = 0x2c, + TABLE_MAX = 0x2d +}; + +struct table_coded_idx +{ + UINT8 len; + const enum table *tables; +}; + +enum table_column_type +{ + /* A basic data type */ + COLUMN_BASIC, + /* An index into one of the heaps */ + COLUMN_HEAP_IDX, + /* An index into a metadata table */ + COLUMN_TABLE_IDX, + /* A coded index */ + COLUMN_CODED_IDX +}; + +union table_column_size +{ + UINT8 basic; + enum heap_type heap; + enum table table; + struct table_coded_idx coded; +}; + +struct table_column +{ + enum table_column_type type; + union table_column_size size; + const char *name; + BOOL primary_key; +}; + +/* We use TABLE_MAX for unused tags (See CustomAttribute). */ +#define DEFINE_CODED_IDX(name, ...) static const enum table name##_tables[] = {__VA_ARGS__} + +DEFINE_CODED_IDX(TypeDefOrRef, TABLE_TYPEDEF, TABLE_TYPEREF, TABLE_TYPESPEC); +DEFINE_CODED_IDX(HasConstant, TABLE_FIELD, TABLE_PARAM, TABLE_PROPERTY); +DEFINE_CODED_IDX(HasCustomAttribute, + TABLE_METHODDEF, + TABLE_FIELD, + TABLE_TYPEREF, + TABLE_TYPEDEF, + TABLE_PARAM, + TABLE_INTERFACEIMPL, + TABLE_MEMBERREF, + TABLE_MODULE, + TABLE_PROPERTY, + TABLE_EVENT, + TABLE_STANDALONESIG, + TABLE_MODULEREF, + TABLE_TYPESPEC, + TABLE_ASSEMBLY, + TABLE_ASSEMBLYREF, + TABLE_FILE, + TABLE_EXPORTEDTYPE, + TABLE_MANIFESTRESOURCE, + TABLE_GENERICPARAM, + TABLE_GENERICPARAMCONSTRAINT, + TABLE_METHODSPEC); +DEFINE_CODED_IDX(HasFieldMarshal, TABLE_FIELD, TABLE_PARAM); +DEFINE_CODED_IDX(HasDeclSecurity, TABLE_TYPEDEF, TABLE_METHODDEF, TABLE_ASSEMBLY); +DEFINE_CODED_IDX(MemberRefParent, TABLE_TYPEDEF, TABLE_TYPEREF, TABLE_MODULEREF, TABLE_METHODDEF, TABLE_TYPESPEC); +DEFINE_CODED_IDX(HasSemantics, TABLE_EVENT, TABLE_PROPERTY); +DEFINE_CODED_IDX(MethodDefOrRef, TABLE_METHODDEF, TABLE_MEMBERREF); +DEFINE_CODED_IDX(MemberForwarded, TABLE_FIELD, TABLE_METHODDEF); +DEFINE_CODED_IDX(Implementation, TABLE_FILE, TABLE_ASSEMBLYREF, TABLE_EXPORTEDTYPE); +DEFINE_CODED_IDX(CustomAttributeType, TABLE_MAX, TABLE_MAX, TABLE_METHODDEF, TABLE_MEMBERREF, TABLE_MAX); +DEFINE_CODED_IDX(ResolutionScope, TABLE_MODULE, TABLE_MODULEREF, TABLE_ASSEMBLYREF, TABLE_TYPEREF); +DEFINE_CODED_IDX(TypeOrMethodDef, TABLE_TYPEDEF, TABLE_METHODDEF); + +#undef DEFINE_CODED_IDX + +struct table_schema +{ + const ULONG columns_len; + const struct table_column *const columns; + const char *const name; +}; + +/* Partition II.22, "Metadata logical format: tables" */ +#define DEFINE_TABLE_SCHEMA(name, ...) \ + static const struct table_column name##_columns[] = {__VA_ARGS__}; \ + static const struct table_schema name##_schema = { ARRAY_SIZE(name##_columns), name##_columns, #name } + +#define BASIC(n, t) { COLUMN_BASIC, { .basic = sizeof(t) }, n } +#define HEAP(n, h) { COLUMN_HEAP_IDX, { .heap = (HEAP_##h) }, n } +#define TABLE(n, t) { COLUMN_TABLE_IDX, { .table = TABLE_##t }, n } +#define TABLE_PRIMARY(n, t) { COLUMN_TABLE_IDX, { .table= TABLE_##t }, n, TRUE } +#define CODED(n, c) { COLUMN_CODED_IDX, { .coded = { ARRAY_SIZE(c##_tables), c##_tables } }, n } +#define CODED_PRIMARY(n, c) { COLUMN_CODED_IDX, { .coded = { ARRAY_SIZE(c##_tables), c##_tables } }, n, TRUE } + +/* Partition II.22.2, "Assembly" */ +DEFINE_TABLE_SCHEMA(Assembly, + BASIC("HashAlgId", ULONG), + BASIC("MajorVersion", USHORT), + BASIC("MinorVersion", USHORT), + BASIC("BuildNumber", USHORT), + BASIC("RevisionNumber", USHORT), + BASIC("Flags", ULONG), + HEAP("PublicKey", BLOB), + HEAP("Name", STRING), + HEAP("Locale", STRING)); + +/* Partition II.22.3, "AssemblyOS" */ +DEFINE_TABLE_SCHEMA(AssemblyOS, + BASIC("OSPlatformId", ULONG), + BASIC("OSMajorVersion", ULONG), + BASIC("OSMinorVersion", ULONG)); + +/* Partition II.2.4, "AssemblyProcessor" */ +DEFINE_TABLE_SCHEMA(AssemblyProcessor, BASIC("Processor", ULONG)); + +/* Partition II.2.5, "AssemblyRef" */ +DEFINE_TABLE_SCHEMA(AssemblyRef, + BASIC("MajorVersion", USHORT), + BASIC("MinorVersion", USHORT), + BASIC("BuildNumber", USHORT), + BASIC("RevisionNumber", USHORT), + BASIC("Flags", ULONG), + HEAP("PublicKeyOrToken", BLOB), + HEAP("Name", STRING), + HEAP("Locale", STRING), + HEAP("HashValue", BLOB)); + +/* Partition II.2.6, "AssemblyRefOS" */ +DEFINE_TABLE_SCHEMA(AssemblyRefOS, + BASIC("OSPlatformId", ULONG), + BASIC("OSMajorVersion", ULONG), + BASIC("OSMinorVersion", ULONG), + TABLE("AssemblyRef", ASSEMBLYREF)); + +/* Partition II.2.7, "AssemblyRefProcessor" */ +DEFINE_TABLE_SCHEMA(AssemblyRefProcessor, BASIC("Processor", ULONG), TABLE("AssemblyRef", ASSEMBLYREF)); + +/* Partition II.2.8, "ClassLayout" */ +DEFINE_TABLE_SCHEMA(ClassLayout, + BASIC("PackingSize", USHORT), + BASIC("ClassSize", ULONG), + TABLE_PRIMARY("Parent", TYPEDEF)); + +/* Partition II.22.9, "Constant" */ +DEFINE_TABLE_SCHEMA(Constant, BASIC("Type", USHORT), CODED_PRIMARY("Parent", HasConstant), HEAP("Value", BLOB)); + +/* Partition II.2.10, "CustomAttribute" */ +DEFINE_TABLE_SCHEMA(CustomAttribute, + CODED_PRIMARY("Parent", HasCustomAttribute), + CODED("Type", CustomAttributeType), + HEAP("Value", BLOB)); + +/* Partition II.22.11, "DeclSecurity" */ +DEFINE_TABLE_SCHEMA(DeclSecurity, + BASIC("Action", SHORT), + CODED_PRIMARY("Parent", HasDeclSecurity), + HEAP("PermissionSet", BLOB)); + +DEFINE_TABLE_SCHEMA(ENCLog, BASIC("Token", ULONG), BASIC("FuncCode", ULONG)); + +DEFINE_TABLE_SCHEMA(ENCMap, BASIC("Token", ULONG)); + +/* Partition II.22.12, "EventMap" */ +DEFINE_TABLE_SCHEMA(EventMap, TABLE("Parent", TYPEDEF), TABLE("EventList", EVENT)); + +/* Partition II.22.13, "Event" */ +DEFINE_TABLE_SCHEMA(Event, BASIC("EventFlags", USHORT), HEAP("Name", STRING), CODED("EventType", TypeDefOrRef)); + +DEFINE_TABLE_SCHEMA(EventPtr, TABLE("Event", EVENT)); + +/* Partition II.22.14, "ExportedType" */ +DEFINE_TABLE_SCHEMA(ExportedType, + BASIC("Flags", ULONG), + BASIC("TypeDefId", ULONG), + HEAP("TypeName", STRING), + HEAP("TypeNamespace", STRING), + CODED("Implementation", Implementation)); + +/* Partition II.22.15, "Field" */ +DEFINE_TABLE_SCHEMA(Field, BASIC("Flags", USHORT), HEAP("Name", STRING), HEAP("Signature", BLOB)); + +/* Partition II.22.16, "FieldLayout" */ +DEFINE_TABLE_SCHEMA(FieldLayout, BASIC("OffSet", ULONG), TABLE_PRIMARY("Field", FIELD)); + +/* Partition II.22.17, "FieldMarshal" */ +DEFINE_TABLE_SCHEMA(FieldMarshal, CODED_PRIMARY("Parent", HasFieldMarshal), HEAP("NativeType", BLOB)); + +DEFINE_TABLE_SCHEMA(FieldPtr, TABLE("Field", FIELD)); + +/* Partition II.22.18, "FieldRVA" */ +DEFINE_TABLE_SCHEMA(FieldRVA, BASIC("RVA", ULONG), TABLE_PRIMARY("Field", FIELD)); + +/* Partition II.22.19, "File" */ +DEFINE_TABLE_SCHEMA(File, BASIC("Flags", ULONG), HEAP("Name", STRING), HEAP("HashValue", BLOB)); + +/* Partition II.22.20, "GenericParam" */ +DEFINE_TABLE_SCHEMA(GenericParam, + BASIC("Number", USHORT), + BASIC("Flags", USHORT), + CODED_PRIMARY("Owner", TypeOrMethodDef), + HEAP("Name", STRING)); + +/* Partition II.22.21, "GenericParamConstraint" */ +DEFINE_TABLE_SCHEMA(GenericParamConstraint, TABLE_PRIMARY("Owner", GENERICPARAM), CODED("Constraint", TypeDefOrRef)); + +/* Partition II.22.22, "ImplMap" */ +DEFINE_TABLE_SCHEMA(ImplMap, + BASIC("MappingFlags", USHORT), + CODED_PRIMARY("MemberForwarded", MemberForwarded), + HEAP("ImportName", STRING), + TABLE("ImportScope", MODULEREF)); + +/* Partition II.22.23, "InterfaceImplMap" */ +DEFINE_TABLE_SCHEMA(InterfaceImpl, TABLE_PRIMARY("Class", TYPEDEF), CODED("Interface", TypeDefOrRef)); + +/* Partition II.22.24, "ManifestResource" */ +DEFINE_TABLE_SCHEMA(ManifestResource, + BASIC("Offset", ULONG), + BASIC("Flags", ULONG), + HEAP("Name", STRING), + CODED("Implementation", Implementation)); + +/* Partition II.22.25, "MemberRef" */ +DEFINE_TABLE_SCHEMA(MemberRef, CODED("Class", MemberRefParent), HEAP("Name", STRING), HEAP("Signature", BLOB)); + +/* Partition II.22.26, "MethodDef" */ +DEFINE_TABLE_SCHEMA(Method, + BASIC("RVA", ULONG), + BASIC("ImplFlags", USHORT), + BASIC("Flags", USHORT), + HEAP("Name", STRING), + HEAP("Signature", BLOB), + TABLE("ParamList", PARAM)); + +/* Partition II.22.27, "MethodImpl" */ +DEFINE_TABLE_SCHEMA(MethodImpl, + TABLE_PRIMARY("Class", TYPEDEF), + CODED("MethodBody", MethodDefOrRef), + CODED("MethodDeclaration", MethodDefOrRef)); + +DEFINE_TABLE_SCHEMA(MethodPtr, TABLE("Method", METHODDEF)); + +/* Partition II.22.28, "MethodSemantics" */ +DEFINE_TABLE_SCHEMA(MethodSemantics, + BASIC("Semantic", USHORT), + TABLE("Method", METHODDEF), + CODED_PRIMARY("Association", HasSemantics)); + +/* Partition II.22.29, "MethodSpec" */ +DEFINE_TABLE_SCHEMA(MethodSpec, CODED("Method", MethodDefOrRef), HEAP("Instantiation", BLOB)); + +/* Partition II.22.30, "Module" */ +DEFINE_TABLE_SCHEMA(Module, + BASIC("Generation", USHORT), + HEAP("Name", STRING), + HEAP("Mvid", GUID), + HEAP("EncId", GUID), + HEAP("EncBaseId", GUID)); + +/* Partition II.22.31, "ModuleRef"*/ +DEFINE_TABLE_SCHEMA(ModuleRef, HEAP("Name", STRING)); + +/* Partition II.22.32, "NestedClass" */ +DEFINE_TABLE_SCHEMA(NestedClass, TABLE_PRIMARY("NestedClass", TYPEDEF), TABLE("EnclosingClass", TYPEDEF)); + +/* Partition II.22.33, "Param" */ +DEFINE_TABLE_SCHEMA(Param, BASIC("Flags", USHORT), BASIC("Sequence", USHORT), HEAP("Name", STRING)); + +DEFINE_TABLE_SCHEMA(ParamPtr, TABLE("Param", PARAM)); + +/* Partition II.22.34, "Property" */ +DEFINE_TABLE_SCHEMA(Property, BASIC("PropFlags", USHORT), HEAP("Name", STRING), HEAP("Type", BLOB)); + +/* Partition II.22.35, "PropertyMap" */ +DEFINE_TABLE_SCHEMA(PropertyMap, TABLE("Parent", TYPEDEF), TABLE("PropertyList", PROPERTY)); + +DEFINE_TABLE_SCHEMA(PropertyPtr, TABLE("Property", PROPERTY)); + +/* Partition II.22.36, "StandAloneSig" */ +DEFINE_TABLE_SCHEMA(StandAloneSig, HEAP("Signature", BLOB)); + +/* Partition II.22.37, "TypeDef" */ +DEFINE_TABLE_SCHEMA(TypeDef, + BASIC("Flags", ULONG), + HEAP("Name", STRING), + HEAP("Namespace", STRING), + CODED("Extends", TypeDefOrRef), + TABLE("FieldList", FIELD), + TABLE("MethodList", METHODDEF)); + +/* Partition II.22.38, "TypeRef" */ +DEFINE_TABLE_SCHEMA(TypeRef, CODED("ResolutionScope", ResolutionScope), HEAP("Name", STRING), HEAP("Namespace", STRING)); + +/* Partition II.22.39, "TypeSpec" */ +DEFINE_TABLE_SCHEMA(TypeSpec, HEAP("Signature", BLOB)); + +#undef DEFINE_TABLE_SCHEMA +#undef BASIC +#undef HEAP +#undef TABLE +#undef TABLE_PRIMARY +#undef CODED +#undef CODED_PRIMARY + +static const struct table_schema *table_schemas[] = { + [TABLE_MODULE] = &Module_schema, + [TABLE_TYPEREF] = &TypeRef_schema, + [TABLE_TYPEDEF] = &TypeDef_schema, + [TABLE_FIELDPTR] = &FieldPtr_schema, + [TABLE_FIELD] = &Field_schema, + [TABLE_METHODPTR] = &MethodPtr_schema, + [TABLE_METHODDEF] = &Method_schema, + [TABLE_PARAMPTR] = &ParamPtr_schema, + [TABLE_PARAM] = &Param_schema, + [TABLE_INTERFACEIMPL] = &InterfaceImpl_schema, + [TABLE_MEMBERREF] = &MemberRef_schema, + [TABLE_CONSTANT] = &Constant_schema, + [TABLE_CUSTOMATTRIBUTE] = &CustomAttribute_schema, + [TABLE_FIELDMARSHAL] = &FieldMarshal_schema, + [TABLE_DECLSECURITY] = &DeclSecurity_schema, + [TABLE_CLASSLAYOUT] = &ClassLayout_schema, + [TABLE_FIELDLAYOUT] = &FieldLayout_schema, + [TABLE_STANDALONESIG] = &StandAloneSig_schema, + [TABLE_EVENTMAP] = &EventMap_schema, + [TABLE_EVENTPTR] = &EventPtr_schema, + [TABLE_EVENT] = &Event_schema, + [TABLE_PROPERTYMAP] = &PropertyMap_schema, + [TABLE_PROPERTYPTR] = &PropertyPtr_schema, + [TABLE_PROPERTY] = &Property_schema, + [TABLE_METHODSEMANTICS] = &MethodSemantics_schema, + [TABLE_METHODIMPL] = &MethodImpl_schema, + [TABLE_MODULEREF] = &ModuleRef_schema, + [TABLE_TYPESPEC] = &TypeSpec_schema, + [TABLE_IMPLMAP] = &ImplMap_schema, + [TABLE_FIELDRVA] = &FieldRVA_schema, + [TABLE_ENCLOG] = &ENCLog_schema, + [TABLE_ENCMAP] = &ENCMap_schema, + [TABLE_ASSEMBLY] = &Assembly_schema, + [TABLE_ASSEMBLYPROCESSOR] = &AssemblyProcessor_schema, + [TABLE_ASSEMBLYOS] = &AssemblyOS_schema, + [TABLE_ASSEMBLYREF] = &AssemblyRef_schema, + [TABLE_ASSEMBLYREFPROCESSOR] = &AssemblyRefProcessor_schema, + [TABLE_ASSEMBLYREFOS] = &AssemblyRefOS_schema, + [TABLE_FILE] = &File_schema, + [TABLE_EXPORTEDTYPE] = &ExportedType_schema, + [TABLE_MANIFESTRESOURCE] = &ManifestResource_schema, + [TABLE_NESTEDCLASS] = &NestedClass_schema, + [TABLE_GENERICPARAM] = &GenericParam_schema, + [TABLE_METHODSPEC] = &MethodSpec_schema, + [TABLE_GENERICPARAMCONSTRAINT] = &GenericParamConstraint_schema +}; +C_ASSERT(ARRAY_SIZE(table_schemas) == TABLE_MAX); + +struct table_info +{ + const BYTE *start; + ULONG num_rows; + ULONG row_size; + ULONG *columns_size; +}; + struct assembly { HANDLE file; @@ -68,6 +491,9 @@ struct assembly struct metadata_stream stream_blobs; struct metadata_stream stream_guids; struct metadata_stream stream_user_strings; + + const struct stream_tables_hdr *tables_hdr; + struct table_info tables[TABLE_MAX]; };
static BOOL pe_rva_to_offset(const IMAGE_SECTION_HEADER *sections, UINT32 num_sections, UINT32 rva, UINT32 *offset) @@ -85,6 +511,136 @@ static BOOL pe_rva_to_offset(const IMAGE_SECTION_HEADER *sections, UINT32 num_se return FALSE; }
+static UINT8 assembly_heap_idx_size(const assembly_t *assembly, enum heap_type type) +{ + assert(type >= HEAP_STRING && type <= HEAP_BLOB); + /* ECMA-335 Partition II.24.2.6, "Heap size flag" */ + return (assembly->tables_hdr->heap_size_bits & (1 << type)) ? 4 : 2; +} + +static BOOL assembly_table_exists(const assembly_t *assembly, UINT8 idx) +{ + assert(idx < 64); + return !!(assembly->tables_hdr->valid_tables_bits & (1ULL << idx)); +} + +static ULONG index_size(ULONG n) +{ + return n < (1 << 16) ? 2 : 4; +} + +static ULONG assembly_get_table_index_size(const assembly_t *assembly, enum table table) +{ + assert(table < TABLE_MAX && table_schemas[table]); + return index_size(assembly->tables[table].num_rows); +} + +static ULONG bit_width(ULONG n) +{ + ULONG bits = 1; + + for (n = n - 1; n; n >>= 1) + bits++; + return bits; +} + +/* From Partition II.24.2.6, "#~stream": + * + * If e is a coded index that points into table t_i out of n possible tables t_0,...,t_{n-1}, then it + * is stored as e << (log n) | tag{ t_0, ..., t_{n-1} }[ t_i ] using 2 bytes if the maximum number + * of rows of tables t_0,...,t_{n-1}, is less than 2 (16 – (log n)), and using 4 bytes otherwise. + */ +static ULONG assembly_get_coded_index_size(const assembly_t *assembly, const struct table_coded_idx *coded) +{ + const ULONG tag_bits = bit_width(coded->len); + ULONG max_row_idx = 0, i; + + for (i = 0; i < coded->len; i++) + { + if (coded->tables[i] != TABLE_MAX) + max_row_idx = max(max_row_idx, assembly->tables[coded->tables[i]].num_rows); + } + return max_row_idx < (1 << (16 - tag_bits)) ? 2 : 4; +} + +static HRESULT assembly_calculate_table_sizes(assembly_t *assembly, enum table table) +{ + const struct table_schema *schema; + struct table_info *table_info; + int i; + + assert(table < TABLE_MAX); + schema = table_schemas[table]; + table_info = &assembly->tables[table]; + if (!(table_info->columns_size = calloc(sizeof(*table_info->columns_size), schema->columns_len))) + return E_OUTOFMEMORY; + + for (i = 0; i < schema->columns_len; i++) + { + const struct table_column *column = &schema->columns[i]; + ULONG column_size; + + switch (column->type) + { + case COLUMN_BASIC: + column_size = column->size.basic; + break; + case COLUMN_HEAP_IDX: + column_size = assembly_heap_idx_size(assembly, column->size.heap); + break; + case COLUMN_TABLE_IDX: + column_size = assembly_get_table_index_size(assembly, table); + break; + case COLUMN_CODED_IDX: + column_size = assembly_get_coded_index_size(assembly, &column->size.coded); + break; + DEFAULT_UNREACHABLE; + } + table_info->columns_size[i] = column_size; + table_info->row_size += column_size; + } + return S_OK; +} + +static HRESULT assembly_parse_metadata_tables(assembly_t *assembly) +{ + const BYTE *cur_table, *tables_stream_start = assembly->stream_tables.start; + ULONG num_tables = 0; + int i; + + for (i = 0; i < 64; i++) + { + if (!assembly_table_exists(assembly, i)) continue; + if (i >= TABLE_MAX) return E_INVALIDARG; + assert(table_schemas[i]); + num_tables++; + } + + cur_table = tables_stream_start + offsetof(struct stream_tables_hdr, table_rows[num_tables]); + if ((UINT_PTR)(cur_table - assembly->data) > assembly->size) return E_INVALIDARG; + + num_tables = 0; + for (i = 0; i < TABLE_MAX; i++) + { + if (!assembly_table_exists(assembly, i)) continue; + assembly->tables[i].num_rows = assembly->tables_hdr->table_rows[num_tables++]; + } + + for (i = 0; i < TABLE_MAX; i++) + { + struct table_info *table = &assembly->tables[i]; + + assembly_calculate_table_sizes(assembly, i); + if (assembly_table_exists(assembly, i)) + { + table->start = cur_table; + cur_table += (size_t)(assembly->tables[i].num_rows * table->row_size); + if ((UINT_PTR)(cur_table - assembly->data) > assembly->size) return E_INVALIDARG; + } + } + return S_OK; +} + static HRESULT assembly_parse_headers(assembly_t *assembly) { const IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)assembly->data; @@ -133,7 +689,7 @@ static HRESULT assembly_parse_headers(assembly_t *assembly) md_start = assembly->data + offset; md_hdr = (struct metadata_hdr *)md_start; if (md_hdr->signature != METADATA_MAGIC || - offset + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16) > assembly->size) + offset + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16) * 2 > assembly->size) return E_INVALIDARG;
num_streams = *(UINT8 *)(md_start + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16)); /* Flags */ @@ -187,7 +743,10 @@ static HRESULT assembly_parse_headers(assembly_t *assembly) ptr--; }
- return S_OK; + if (assembly->stream_tables.size < sizeof(struct stream_tables_hdr)) return E_INVALIDARG; + assembly->tables_hdr = (struct stream_tables_hdr *)assembly->stream_tables.start; + + return assembly_parse_metadata_tables(assembly); }
HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **ret) @@ -232,12 +791,43 @@ done:
void assembly_free(assembly_t *assembly) { + ULONG i; + + for (i = 0; i < TABLE_MAX; i++) free(assembly->tables[i].columns_size); if (assembly->map) UnmapViewOfFile(assembly->map); CloseHandle(assembly->map); CloseHandle(assembly->file); free(assembly); }
+HRESULT assembly_get_table(const assembly_t *assembly, ULONG table_idx, struct metadata_table_info *info) +{ + const struct table_schema *schema; + const struct table_info *table; + ULONG i; + + if (table_idx >= TABLE_MAX) return E_INVALIDARG; + + schema = table_schemas[table_idx]; + table = &assembly->tables[table_idx]; + info->key_idx = -1; + for (i = 0; i < schema->columns_len; i++) + { + if (schema->columns[i].primary_key) + { + info->key_idx = i; + break; + } + } + info->num_rows = table->num_rows; + info->num_columns = schema->columns_len; + info->row_size = table->row_size; + info->column_sizes = table->columns_size; + info->name = schema->name; + info->start = table->start; + return S_OK; +} + ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap) { switch (heap) diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c index 9affb80f5bf..b9bce1a431c 100644 --- a/dlls/rometadata/mdtables.c +++ b/dlls/rometadata/mdtables.c @@ -130,8 +130,20 @@ static HRESULT WINAPI tables_GetTableIndex(IMetaDataTables *iface, ULONG token, static HRESULT WINAPI tables_GetTableInfo(IMetaDataTables *iface, ULONG idx_tbl, ULONG *row_size, ULONG *num_rows, ULONG *num_cols, ULONG *idx_key, const char **name) { - FIXME("(%p, %lu, %p, %p, %p, %p, %p): stub!\n", iface, idx_tbl, row_size, num_rows, num_cols, idx_key, name); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + struct metadata_table_info table; + HRESULT hr; + + TRACE("(%p, %lu, %p, %p, %p, %p, %p)\n", iface, idx_tbl, row_size, num_rows, num_cols, idx_key, name); + + if (FAILED(hr = assembly_get_table(impl->assembly, idx_tbl, &table))) return hr; + + *row_size = table.row_size; + *num_rows = table.num_rows; + *num_cols = table.num_columns; + *idx_key = table.key_idx; + *name = table.name; + return S_OK; }
static HRESULT WINAPI tables_GetColumnInfo(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_col, ULONG *offset, diff --git a/dlls/rometadata/rometadatapriv.h b/dlls/rometadata/rometadatapriv.h index 6ca83cefbf6..fe67903608d 100644 --- a/dlls/rometadata/rometadatapriv.h +++ b/dlls/rometadata/rometadatapriv.h @@ -24,6 +24,20 @@ extern HRESULT IMetaDataTables_create(const WCHAR *path, IMetaDataTables **iface);
typedef struct assembly assembly_t; +struct metadata_table_info +{ + ULONG num_rows; + ULONG num_columns; + + ULONG key_idx; + + ULONG row_size; + const ULONG *column_sizes; + + const char *name; + const BYTE *start; +}; + enum heap_type { HEAP_STRING = 0, @@ -34,6 +48,7 @@ enum heap_type
extern HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **out); extern void assembly_free(assembly_t *assembly); +extern HRESULT assembly_get_table(const assembly_t *assembly, ULONG table_idx, struct metadata_table_info *info); extern ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap);
#endif /* __WINE_ROMETADATA_PRIVATE__ */ diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index dc4452a3d34..c754368f212 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -319,23 +319,12 @@ static void test_MetaDataDispenser_OpenScope(void) winetest_push_context("tables[%lu]", i);
hr = IMetaDataTables_GetTableInfo(md_tables, i, &row_size, &rows, &cols, &key_idx, &name); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - if (FAILED(hr)) - { - winetest_pop_context(); - continue; - }
- todo_wine ok(row_size == table->exp_row_size, "got row_size %lu != %lu\n", row_size, table->exp_row_size); - todo_wine ok(rows == table->exp_rows, "got rows %lu != %lu\n", rows, table->exp_rows); - todo_wine ok(cols == table->exp_cols, "got cols %lu != %lu\n", cols, table->exp_cols); - todo_wine ok(key_idx == table->exp_key_idx, "got key_idx %lu != %lu\n", key_idx, table->exp_key_idx); - todo_wine ok(!strcmp(name, table->exp_name), "got name %s != %s\n", debugstr_a(name), debugstr_a(table->exp_name));
winetest_pop_context();
From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/mdtables.c | 17 +++++++++-- dlls/rometadata/tests/rometadata.c | 48 +++++++++++------------------- 2 files changed, 32 insertions(+), 33 deletions(-)
diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c index b9bce1a431c..862582788d8 100644 --- a/dlls/rometadata/mdtables.c +++ b/dlls/rometadata/mdtables.c @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <assert.h> + #define COBJMACROS #include "objbase.h" #include "cor.h" @@ -162,8 +164,19 @@ static HRESULT WINAPI tables_GetCodedTokenInfo(IMetaDataTables *iface, ULONG typ
static HRESULT WINAPI tables_GetRow(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_row, const BYTE *row) { - FIXME("(%p, %lu, %lu, %p): stub!\n", iface, idx_tbl, idx_row, row); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + struct metadata_table_info table; + HRESULT hr; + + TRACE("(%p, %lu, %lu, %p)\n", iface, idx_tbl, idx_row, row); + + if (FAILED(hr = assembly_get_table(impl->assembly, idx_tbl, &table))) return hr; + + assert(table.start); + idx_row--; /* Row indices are 1-based. */ + if (idx_row >= table.num_rows) return E_INVALIDARG; + *(const BYTE **)row = table.start + (size_t)(table.row_size * idx_row); + return S_OK; }
static HRESULT WINAPI tables_GetColumn(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_col, ULONG idx_row, ULONG *val) diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index c754368f212..b1dd9739eb6 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -332,22 +332,21 @@ static void test_MetaDataDispenser_OpenScope(void)
/* Read module information */ hr = IMetaDataTables_GetRow(md_tables, TABLE_MODULE, 1, (BYTE *)&module); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(!!module, "got module=%p\n", module);
- if (SUCCEEDED(hr)) - { - str = NULL; - hr = IMetaDataTables_GetString(md_tables, module->Name, &str); - ok(hr == S_OK, "got hr %#lx\n", hr); - ok(str &&!strcmp(str, "dlls/rometadata/tests/test-simple.winmd"), "got str %s\n", debugstr_a(str)); + str = NULL; + hr = IMetaDataTables_GetString(md_tables, module->Name, &str); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(str &&!strcmp(str, "dlls/rometadata/tests/test-simple.winmd"), "got str %s\n", debugstr_a(str));
- hr = IMetaDataTables_GetGuid(md_tables, module->Mvid, &guid); - ok(hr == S_OK, "got hr %#lx\n", hr); - ok(!!guid, "got guid %p\n", guid); - } + hr = IMetaDataTables_GetGuid(md_tables, module->Mvid, &guid); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(!!guid, "got guid %p\n", guid);
/* Read defined types. */ for (i = 0; i < ARRAY_SIZE(type_defs); i++) @@ -358,26 +357,24 @@ static void test_MetaDataDispenser_OpenScope(void)
type_def = NULL; hr = IMetaDataTables_GetRow(md_tables, TABLE_TYPEDEF, i + 1, (BYTE *)&type_def); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(!!type_def, "got type_def=%p\n", type_def); - if (FAILED(hr)) - { - winetest_pop_context(); - continue; - }
ok(type_def->Flags == type_info->exp_flags, "got Flags %#lx != %#lx\n", type_def->Flags, type_info->exp_flags); str = NULL; hr = IMetaDataTables_GetString(md_tables, type_def->Name, &str); + todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine ok(str && !strcmp(str, type_info->exp_name), "got str %s != %s\n", debugstr_a(str), debugstr_a(type_info->exp_name)); if (type_info->exp_namespace) { str = NULL; hr = IMetaDataTables_GetString(md_tables, type_def->Namespace, &str); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine ok(str && !strcmp(str, type_info->exp_namespace), "got str %s != %s\n", debugstr_a(str), debugstr_a(type_info->exp_namespace)); if (!strcmp(type_info->exp_name, "ITest1") && !strcmp(type_info->exp_namespace, "Wine.Test")) @@ -395,15 +392,10 @@ static void test_MetaDataDispenser_OpenScope(void) winetest_push_context("i=%lu", i);
hr = IMetaDataTables_GetRow(md_tables, TABLE_MEMBERREF, i + 1, (BYTE *)&ref); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - if (FAILED(hr)) - { - winetest_pop_context(); - continue; - }
hr = IMetaDataTables_GetString(md_tables, ref->Name, &str); + todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); if (str && !strcmp(str, ".ctor")) { @@ -442,13 +434,7 @@ static void test_MetaDataDispenser_OpenScope(void)
attr = NULL; hr = IMetaDataTables_GetRow(md_tables, TABLE_CUSTOMATTRIBUTE, i + 1, (BYTE *)&attr); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - if (FAILED(hr)) - { - winetest_pop_context(); - continue; - }
if (attr->Type == guid_ctor_idx && attr->Parent == itest1_def_idx) {
From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/assembly.c | 49 +++++++++++++++++++++++++++++- dlls/rometadata/mdtables.c | 15 +++++---- dlls/rometadata/rometadatapriv.h | 3 ++ dlls/rometadata/tests/rometadata.c | 10 ------ 4 files changed, 60 insertions(+), 17 deletions(-)
diff --git a/dlls/rometadata/assembly.c b/dlls/rometadata/assembly.c index 00824d55d36..b4298ec3bcd 100644 --- a/dlls/rometadata/assembly.c +++ b/dlls/rometadata/assembly.c @@ -842,4 +842,51 @@ ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap) return assembly->stream_user_strings.size; DEFAULT_UNREACHABLE; } -} \ No newline at end of file +} + +const char *assembly_get_string(const assembly_t *assembly, ULONG idx) +{ + return idx < assembly->stream_strings.size ? (const char *)&assembly->stream_strings.start[idx] : NULL; +} + +static HRESULT decode_int(const BYTE *encoded, ULONG *val, ULONG *len) +{ + if (!(encoded[0] & 0x80)) + { + *len = 1; + *val = encoded[0]; + } + else if (!(encoded[0] & 0x40)) + { + *len = 2; + *val = ((encoded[0] & ~0xc0) << 8) + encoded[1]; + } + else if (!(encoded[0] & 0x20)) + { + *len = 4; + *val = ((encoded[0] & ~0xe0) << 24) + (encoded[1] << 16) + (encoded[2] << 8) + encoded[3]; + } + else + return E_INVALIDARG; + return S_OK; +} + +HRESULT assembly_get_blob(const assembly_t *assembly, ULONG idx, const BYTE **blob, ULONG *size) +{ + const BYTE *ptr; + ULONG size_len; + HRESULT hr; + + if (idx >= assembly->stream_blobs.size) return E_INVALIDARG; + ptr = assembly->stream_blobs.start + idx; + if (FAILED(hr = decode_int(ptr, size, &size_len))) return hr; + *blob = ptr + size_len; + return S_OK; +} + +const GUID *assembly_get_guid(const assembly_t *assembly, ULONG idx) +{ + ULONG offset = (idx - 1) * sizeof(GUID); /* Indices into the GUID heap are 1-based */ + + return offset < assembly->stream_guids.size ? (const GUID *)(assembly->stream_guids.start + offset) : NULL; +} diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c index 862582788d8..b66fac6487f 100644 --- a/dlls/rometadata/mdtables.c +++ b/dlls/rometadata/mdtables.c @@ -187,20 +187,23 @@ static HRESULT WINAPI tables_GetColumn(IMetaDataTables *iface, ULONG idx_tbl, UL
static HRESULT WINAPI tables_GetString(IMetaDataTables *iface, ULONG idx, const char **str) { - FIXME("(%p, %lu, %p): stub!\n", iface, idx, str); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + TRACE("(%p, %lu, %p)\n", iface, idx, str); + return (*str = assembly_get_string(impl->assembly, idx)) ? S_OK : E_INVALIDARG; }
static HRESULT WINAPI tables_GetBlob(IMetaDataTables *iface, ULONG idx, ULONG *size, const BYTE **blob) { - FIXME("(%p, %lu, %p, %p): stub!\n", iface, idx, size, blob); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + TRACE("(%p, %lu, %p, %p)\n", iface, idx, size, blob); + return assembly_get_blob(impl->assembly, idx, blob, size); }
static HRESULT WINAPI tables_GetGuid(IMetaDataTables *iface, ULONG idx, const GUID **guid) { - FIXME("(%p, %lu, %p): stub!\n", iface, idx, guid); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + TRACE("(%p, %lu, %p)!\n", iface, idx, guid); + return (*guid = assembly_get_guid(impl->assembly, idx)) ? S_OK : E_INVALIDARG; }
static HRESULT WINAPI tables_GetUserString(IMetaDataTables *iface, ULONG idx, ULONG *size, const BYTE **string) diff --git a/dlls/rometadata/rometadatapriv.h b/dlls/rometadata/rometadatapriv.h index fe67903608d..3d1d2f21873 100644 --- a/dlls/rometadata/rometadatapriv.h +++ b/dlls/rometadata/rometadatapriv.h @@ -50,5 +50,8 @@ extern HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **out); extern void assembly_free(assembly_t *assembly); extern HRESULT assembly_get_table(const assembly_t *assembly, ULONG table_idx, struct metadata_table_info *info); extern ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap); +extern const char *assembly_get_string(const assembly_t *assembly, ULONG idx); +extern HRESULT assembly_get_blob(const assembly_t *assembly, ULONG idx, const BYTE **blob, ULONG *size); +extern const GUID *assembly_get_guid(const assembly_t *assembly, ULONG idx);
#endif /* __WINE_ROMETADATA_PRIVATE__ */ diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index b1dd9739eb6..03e39d61e77 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -337,15 +337,11 @@ static void test_MetaDataDispenser_OpenScope(void)
str = NULL; hr = IMetaDataTables_GetString(md_tables, module->Name, &str); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(str &&!strcmp(str, "dlls/rometadata/tests/test-simple.winmd"), "got str %s\n", debugstr_a(str));
hr = IMetaDataTables_GetGuid(md_tables, module->Mvid, &guid); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(!!guid, "got guid %p\n", guid);
/* Read defined types. */ @@ -363,18 +359,14 @@ static void test_MetaDataDispenser_OpenScope(void) ok(type_def->Flags == type_info->exp_flags, "got Flags %#lx != %#lx\n", type_def->Flags, type_info->exp_flags); str = NULL; hr = IMetaDataTables_GetString(md_tables, type_def->Name, &str); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(str && !strcmp(str, type_info->exp_name), "got str %s != %s\n", debugstr_a(str), debugstr_a(type_info->exp_name)); if (type_info->exp_namespace) { str = NULL; hr = IMetaDataTables_GetString(md_tables, type_def->Namespace, &str); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(str && !strcmp(str, type_info->exp_namespace), "got str %s != %s\n", debugstr_a(str), debugstr_a(type_info->exp_namespace)); if (!strcmp(type_info->exp_name, "ITest1") && !strcmp(type_info->exp_namespace, "Wine.Test")) @@ -395,7 +387,6 @@ static void test_MetaDataDispenser_OpenScope(void) ok(hr == S_OK, "got hr %#lx\n", hr);
hr = IMetaDataTables_GetString(md_tables, ref->Name, &str); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); if (str && !strcmp(str, ".ctor")) { @@ -422,7 +413,6 @@ static void test_MetaDataDispenser_OpenScope(void) winetest_pop_context(); }
- todo_wine ok(!!guid_ctor_idx, "got guid_ctor_coded_idx %lu\n", guid_ctor_idx);
/* Verify ITest1 has the correct GuidAttribute value. */
From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/tests/rometadata.c | 358 ++++++++++++++++++++++++++++- include/cor.idl | 26 +++ include/corhdr.h | 2 + 3 files changed, 385 insertions(+), 1 deletion(-)
diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index 03e39d61e77..d752770302b 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -17,6 +17,7 @@ */
#define COBJMACROS +#define _DEFINE_META_DATA_META_CONSTANTS #include <windows.h> #include "initguid.h" #include "cor.h" @@ -214,6 +215,31 @@ struct type_info const char *exp_namespace; };
+struct column_info +{ + ULONG exp_offset; + ULONG exp_col_size; + ULONG exp_type; + const char *exp_name; +}; + +enum coded_idx_type +{ + CT_TypeDefOrRef = 64, + CT_HasConstant = 65, + CT_HasCustomAttribute = 66, + CT_HasFieldMarshal = 67, + CT_HasDeclSecurity = 68, + CT_MemberRefParent = 69, + CT_HasSemantics = 70, + CT_MethodDefOrRef = 71, + CT_MemberForwarded = 72, + CT_Implementation = 73, + CT_CustomAttributeType = 74, + CT_ResolutionScope = 75, + CT_TypeOrMethodDef = 76 +}; + static void test_MetaDataDispenser_OpenScope(void) { static const struct table_info tables[TABLE_MAX] = { @@ -263,6 +289,246 @@ static void test_MetaDataDispenser_OpenScope(void) {4, 0, 2, -1, "MethodSpec"}, {4, 0, 2, 0, "GenericParamConstraint"}, }; + static const struct { + ULONG len; + const struct column_info columns[9]; + } table_columns[TABLE_MAX] = { + {5, + { + {0, 2, iUSHORT, "Generation"}, + {2, 2, iSTRING, "Name"}, + {4, 2, iGUID, "Mvid"}, + {6, 2, iGUID, "EncId"}, + {8, 2, iGUID, "EncBaseId"} + }}, + {3, + { + {0, 2, CT_ResolutionScope, "ResolutionScope"}, + {2, 2, iSTRING, "Name"}, + {4, 2, iSTRING, "Namespace"} + }}, + {6, + { + {0, 4, iULONG, "Flags"}, + {4, 2, iSTRING, "Name"}, + {6, 2, iSTRING, "Namespace"}, + {8, 2, CT_TypeDefOrRef, "Extends"}, + {10, 2, TABLE_FIELD, "FieldList"}, + {12, 2, TABLE_METHODDEF, "MethodList"} + }}, + {1, {{0, 2, TABLE_FIELD, "Field"}}}, + {3, + { + {0, 2, iUSHORT, "Flags"}, + {2, 2, iSTRING, "Name"}, + {4, 2, iBLOB, "Signature"}, + }}, + {1, {{0, 2, TABLE_METHODDEF, "Method"}}}, + {6, + { + {0, 4, iULONG, "RVA"}, + {4, 2, iUSHORT, "ImplFlags"}, + {6, 2, iUSHORT, "Flags"}, + {8, 2, iSTRING, "Name"}, + {10, 2, iBLOB, "Signature"}, + {12, 2, TABLE_PARAM, "ParamList"} + }}, + {1, {{0, 2, TABLE_PARAM, "Param"}}}, + {3, + { + {0, 2, iUSHORT, "Flags"}, + {2, 2, iUSHORT, "Sequence"}, + {4, 2, iSTRING, "Name"} + }}, + {2, + { + {0, 2, TABLE_TYPEDEF, "Class"}, + {2, 2, CT_TypeDefOrRef, "Interface"}, + }}, + {3, + { + {0, 2, CT_MemberRefParent, "Class"}, + {2, 2, iSTRING, "Name"}, + {4, 2, iBLOB, "Signature"}, + }}, + {3, + { + {0, 1, iBYTE, "Type"}, + {2, 2, CT_HasConstant, "Parent"}, + {4, 2, iBLOB, "Value"} + }}, + {3, + { + {0, 2, CT_HasCustomAttribute, "Parent"}, + {2, 2, CT_CustomAttributeType, "Type"}, + {4, 2, iBLOB, "Value"} + }}, + {2, + { + {0, 2, CT_HasFieldMarshal, "Parent"}, + {2, 2, iBLOB, "NativeType"} + }}, + {3, + { + {0, 2, iSHORT, "Action"}, + {2, 2, CT_HasDeclSecurity, "Parent"}, + {4, 2, iBLOB, "PermissionSet"} + }}, + {3, + { + {0, 2, iUSHORT, "PackingSize"}, + {2, 4, iULONG, "ClassSize"}, + {6, 2, TABLE_TYPEDEF, "Parent"} + }}, + {2, + { + {0, 4, iULONG, "OffSet"}, + {4, 2, TABLE_FIELD, "Field"} + }}, + {1, {{0, 2, iBLOB, "Signature"}}}, + {2, + { + {0, 2, TABLE_TYPEDEF, "Parent"}, + {2, 2, TABLE_EVENT, "EventList"}, + }}, + {1, {{0, 2, TABLE_EVENT, "Event"}}}, + {3, + { + {0, 2, iUSHORT, "EventFlags"}, + {2, 2, iSTRING, "Name"}, + {4, 2, CT_TypeDefOrRef, "EventType"} + }}, + {2, + { + {0, 2, TABLE_TYPEDEF, "Parent"}, + {2, 2, TABLE_PROPERTY, "PropertyList"} + }}, + {1, {{0, 2, TABLE_PROPERTY, "Property"}}}, + {3, + { + {0, 2, iUSHORT, "PropFlags"}, + {2, 2, iSTRING, "Name"}, + {4, 2, iBLOB, "Type"} + }}, + {3, + { + {0, 2, iUSHORT, "Semantic"}, + {2, 2, TABLE_METHODDEF, "Method"}, + {4, 2, CT_HasSemantics, "Association"} + }}, + {3, + { + {0, 2, TABLE_TYPEDEF, "Class"}, + {2, 2, CT_MethodDefOrRef, "MethodBody"}, + {4, 2, CT_MethodDefOrRef, "MethodDeclaration"} + }}, + {1, {{0, 2, iSTRING, "Name"}}}, + {1, {{0, 2, iBLOB, "Signature"}}}, + {4, + { + {0, 2, iUSHORT, "MappingFlags"}, + {2, 2, CT_MemberForwarded, "MemberForwarded"}, + {4, 2, iSTRING, "ImportName"}, + {6, 2, TABLE_MODULEREF, "ImportScope"} + }}, + {2, + { + {0, 4, iULONG, "RVA"}, + {4, 2, TABLE_FIELD, "Field"} + }}, + {2, + { + {0, 4, iULONG, "Token"}, + {4, 4, iULONG, "FuncCode"} + }}, + {1, {{0, 4, iULONG, "Token"}}}, + {9, + { + {0, 4, iULONG, "HashAlgId"}, + {4, 2, iUSHORT, "MajorVersion"}, + {6, 2, iUSHORT, "MinorVersion"}, + {8, 2, iUSHORT, "BuildNumber"}, + {10, 2, iUSHORT, "RevisionNumber"}, + {12, 4, iULONG, "Flags"}, + {16, 2, iBLOB, "PublicKey"}, + {18, 2, iSTRING, "Name"}, + {20, 2, iSTRING, "Locale"} + }}, + {1, {{0, 4, iULONG, "Processor"}}}, + {3, + { + {0, 4, iULONG, "OSPlatformId"}, + {4, 4, iULONG, "OSMajorVersion"}, + {8, 4, iULONG, "OSMinorVersion"} + }}, + {9, + { + {0, 2, iUSHORT, "MajorVersion"}, + {2, 2, iUSHORT, "MinorVersion"}, + {4, 2, iUSHORT, "BuildNumber"}, + {6, 2, iUSHORT, "RevisionNumber"}, + {8, 4, iULONG, "Flags"}, + {12, 2, iBLOB, "PublicKeyOrToken"}, + {14, 2, iSTRING, "Name"}, + {16, 2, iSTRING, "Locale"}, + {18, 2, iBLOB, "HashValue"} + }}, + {2, + { + {0, 4, iULONG, "Processor"}, + {4, 2, TABLE_ASSEMBLYREF, "AssemblyRef"} + }}, + {4, + { + {0, 4, iULONG, "OSPlatformId"}, + {4, 4, iULONG, "OSMajorVersion"}, + {8, 4, iULONG, "OSMinorVersion"}, + {12, 2, TABLE_ASSEMBLYREF, "AssemblyRef"} + }}, + {3, + { + {0, 4, iULONG, "Flags"}, + {4, 2, iSTRING, "Name"}, + {6, 2, iBLOB, "HashValue"} + }}, + {5, + { + {0, 4, iULONG, "Flags"}, + {4, 4, iULONG, "TypeDefId"}, + {8, 2, iSTRING, "TypeName"}, + {10, 2, iSTRING, "TypeNamespace"}, + {12, 2, CT_Implementation, "Implementation"} + }}, + {4, + { + {0, 4, iULONG, "Offset"}, + {4, 4, iULONG, "Flags"}, + {8, 2, iSTRING, "Name"}, + {10, 2, CT_Implementation, "Implementation"} + }}, + {2, + { + {0, 2, TABLE_TYPEDEF, "NestedClass"}, + {2, 2, TABLE_TYPEDEF, "EnclosingClass"} + }}, + {4, + { + {0, 2, iUSHORT, "Number"}, + {2, 2, iUSHORT, "Flags"}, + {4, 2, CT_TypeOrMethodDef, "Owner"}, + {6, 2, iSTRING, "Name"} + }}, + {2, + { + {0, 2, CT_MethodDefOrRef, "Method"}, + {2, 2, iBLOB, "Instantiation"} + }}, + {2, + { + {0, 2, TABLE_GENERICPARAM, "Owner"}, + {2, 2, CT_TypeDefOrRef, "Constraint"} + }}, + }; static const struct type_info type_defs[3] = { { 0, "<Module>", NULL }, @@ -315,6 +581,7 @@ static void test_MetaDataDispenser_OpenScope(void) const struct table_info *table = &tables[i]; ULONG row_size, rows, cols, key_idx; const char *name; + ULONG j;
winetest_push_context("tables[%lu]", i);
@@ -327,6 +594,30 @@ static void test_MetaDataDispenser_OpenScope(void) ok(key_idx == table->exp_key_idx, "got key_idx %lu != %lu\n", key_idx, table->exp_key_idx); ok(!strcmp(name, table->exp_name), "got name %s != %s\n", debugstr_a(name), debugstr_a(table->exp_name));
+ for (j = 0; j < table_columns[i].len; j++) + { + const struct column_info *column = &table_columns[i].columns[j]; + ULONG offset, col_size, type; + + winetest_push_context("column=%lu", j); + + hr = IMetaDataTables_GetColumnInfo(md_tables, i, j, &offset, &col_size, &type, &name); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + if (FAILED(hr)) + { + winetest_pop_context(); + continue; + } + + ok(offset == column->exp_offset, "got offset %lu != %lu\n", offset, column->exp_offset); + ok(col_size == column->exp_col_size, "got col_size %lu != %lu\n", col_size, column->exp_col_size); + ok(type == column->exp_type, "got type %lu != %lu\n", type, column->exp_type); + ok(!strcmp(name, column->exp_name), "got name %s != %s\n", debugstr_a(name), debugstr_a(column->exp_name)); + + winetest_pop_context(); + } + winetest_pop_context(); }
@@ -344,6 +635,18 @@ static void test_MetaDataDispenser_OpenScope(void) ok(hr == S_OK, "got hr %#lx\n", hr); ok(!!guid, "got guid %p\n", guid);
+ hr = IMetaDataTables_GetColumn(md_tables, TABLE_MODULE, 1, 1, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == module->Name, "got val %#lx != %#x\n", val, module->Name); + + hr = IMetaDataTables_GetColumn(md_tables, TABLE_MODULE, 2, 1, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == module->Mvid, "got val %#lx != %#x\n", val, module->Mvid); + /* Read defined types. */ for (i = 0; i < ARRAY_SIZE(type_defs); i++) { @@ -373,6 +676,24 @@ static void test_MetaDataDispenser_OpenScope(void) itest1_def_idx = ((i + 1) << 5) | 3; }
+ hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEDEF, 0, i + 1, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == type_def->Flags, "got val %#lx != %#lx\n", val, type_def->Flags); + + hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEDEF, 1, i + 1, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == type_def->Name, "got val %#lx != %#x\n", val, type_def->Name); + + hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEDEF, 2, i + 1, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == type_def->Namespace, "got val %#lx != %#x\n", val, type_def->Namespace); + winetest_pop_context(); }
@@ -390,10 +711,11 @@ static void test_MetaDataDispenser_OpenScope(void) ok(hr == S_OK, "got hr %#lx\n", hr); if (str && !strcmp(str, ".ctor")) { + ULONG rid, type, typeref_row = (ref->Class & ~7) >> 3; const struct row_typeref *typeref = NULL;
/* All MemberRefParent coded indices in test-simple.winmd point to TypeRef entries. */ - hr = IMetaDataTables_GetRow(md_tables, TABLE_TYPEREF, (ref->Class & ~7) >> 3, (BYTE *)&typeref); + hr = IMetaDataTables_GetRow(md_tables, TABLE_TYPEREF, typeref_row, (BYTE *)&typeref); ok(hr == S_OK, "got hr %#lx\n", hr); hr = IMetaDataTables_GetString(md_tables, typeref->Name, &str); ok(hr == S_OK, "got hr %#lx\n", hr); @@ -408,8 +730,36 @@ static void test_MetaDataDispenser_OpenScope(void) break; } } + + hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEREF, 1, typeref_row, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == typeref->Name, "got val %#lx != %#x\n", val, typeref->Name); + + hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEREF, 2, typeref_row, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == typeref->Namespace, "got val %#lx != %#x\n", val, typeref->Namespace); + + hr = IMetaDataTables_GetColumn(md_tables, TABLE_MEMBERREF, 0, i + 1, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + rid = RidFromToken(val); + type = TypeFromToken(val); + todo_wine + ok(rid == typeref_row, "got rid %#lx != %#lx\n", rid, typeref_row); + todo_wine + ok(type == mdtTypeRef, "got type %#lx != %#x\n", type, mdtTypeRef); }
+ hr = IMetaDataTables_GetColumn(md_tables, TABLE_MEMBERREF, 1, i + 1, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == ref->Name, "got val %#lx != %#x\n", val, ref->Name); + winetest_pop_context(); }
@@ -439,6 +789,12 @@ static void test_MetaDataDispenser_OpenScope(void) ok(IsEqualGUID(guid, &IID_ITest1), "got guid %s\n", debugstr_guid(guid)); }
+ hr = IMetaDataTables_GetColumn(md_tables, TABLE_CUSTOMATTRIBUTE, 2, i + 1, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == attr->Value, "got val %#lx != %#x\n", val, attr->Value); + winetest_pop_context(); }
diff --git a/include/cor.idl b/include/cor.idl index 7c731d60c98..792c73e6373 100644 --- a/include/cor.idl +++ b/include/cor.idl @@ -106,3 +106,29 @@ cpp_quote("VOID WINAPI _CorImageUnloading(PVOID);") cpp_quote("HRESULT WINAPI _CorValidateImage(PVOID*,LPCWSTR);") cpp_quote("HRESULT WINAPI CoInitializeCor(DWORD);") cpp_quote("void WINAPI CoUninitializeCor(void);") + +cpp_quote("#if defined(_DEFINE_META_DATA_META_CONSTANTS) && !defined(_META_DATA_META_CONSTANTS_DEFINED)") +cpp_quote("#define _META_DATA_META_CONSTANTS_DEFINED") + +cpp_quote("const UINT iRidMax = 63;") + +cpp_quote("const UINT iCodedToken = 64;") +cpp_quote("const UINT iCodedTokenMax = 95;") + +cpp_quote("const UINT iSHORT = 96;") +cpp_quote("const UINT iUSHORT = 97;") +cpp_quote("const UINT iLONG = 98;") +cpp_quote("const UINT iULONG = 99;") +cpp_quote("const UINT iBYTE = 100;") + +cpp_quote("const UINT iSTRING = 101;") +cpp_quote("const UINT iGUID = 102;") +cpp_quote("const UINT iBLOB = 103;") + +cpp_quote("inline int IsRidType(ULONG t) { return t <= iRidMax; }") +cpp_quote("inline int IsCodedTokenType(ULONG t) { return t >= iCodedToken && t <= iCodedTokenMax; }") +cpp_quote("inline int IsRidOrToken(ULONG t) { return t <= iCodedTokenMax; }") +cpp_quote("inline int IsHeapType(ULONG t) { return t >= iSTRING; }") +cpp_quote("inline int IsFixedType(ULONG t) { return t >= iSHORT && t <= iBYTE; }") + +cpp_quote("#endif /* defined(_DEFINE_META_DATA_META_CONSTANTS) && !defined(_META_DATA_META_CONSTANTS_DEFINED) */ ") \ No newline at end of file diff --git a/include/corhdr.h b/include/corhdr.h index 5ca84fe9363..91f67d25c0f 100644 --- a/include/corhdr.h +++ b/include/corhdr.h @@ -98,6 +98,8 @@ typedef enum CorElementType #define TypeFromToken(tk) ((ULONG32)((tk) & 0xff000000)) #define IsNilToken(tk) ((RidFromToken(tk)) == 0)
+typedef ULONG RID; + typedef LPVOID mdScope; typedef ULONG32 mdToken;
From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/assembly.c | 98 ++++++++++++++++++++++++++++-- dlls/rometadata/mdtables.c | 32 ++++++++-- dlls/rometadata/rometadatapriv.h | 10 +++ dlls/rometadata/tests/rometadata.c | 27 -------- 4 files changed, 130 insertions(+), 37 deletions(-)
diff --git a/dlls/rometadata/assembly.c b/dlls/rometadata/assembly.c index b4298ec3bcd..2703d62fbc7 100644 --- a/dlls/rometadata/assembly.c +++ b/dlls/rometadata/assembly.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define _DEFINE_META_DATA_META_CONSTANTS #include "rometadatapriv.h"
#include <assert.h> @@ -119,8 +120,26 @@ enum table TABLE_MAX = 0x2d };
+enum coded_idx_type +{ + CT_TypeDefOrRef = 64, + CT_HasConstant = 65, + CT_HasCustomAttribute = 66, + CT_HasFieldMarshal = 67, + CT_HasDeclSecurity = 68, + CT_MemberRefParent = 69, + CT_HasSemantics = 70, + CT_MethodDefOrRef = 71, + CT_MemberForwarded = 72, + CT_Implementation = 73, + CT_CustomAttributeType = 74, + CT_ResolutionScope = 75, + CT_TypeOrMethodDef = 76 +}; + struct table_coded_idx { + enum coded_idx_type type; UINT8 len; const enum table *tables; }; @@ -139,7 +158,11 @@ enum table_column_type
union table_column_size { - UINT8 basic; + struct { + UINT8 type; + UINT8 size; + UINT8 size_padded; + } basic; enum heap_type heap; enum table table; struct table_coded_idx coded; @@ -205,12 +228,13 @@ struct table_schema static const struct table_column name##_columns[] = {__VA_ARGS__}; \ static const struct table_schema name##_schema = { ARRAY_SIZE(name##_columns), name##_columns, #name }
-#define BASIC(n, t) { COLUMN_BASIC, { .basic = sizeof(t) }, n } +#define BASIC_PADDED(n, t, p) { COLUMN_BASIC, { .basic = {i##t, sizeof(t), sizeof(p) } }, n } +#define BASIC(n, t) BASIC_PADDED(n, t, t) #define HEAP(n, h) { COLUMN_HEAP_IDX, { .heap = (HEAP_##h) }, n } #define TABLE(n, t) { COLUMN_TABLE_IDX, { .table = TABLE_##t }, n } #define TABLE_PRIMARY(n, t) { COLUMN_TABLE_IDX, { .table= TABLE_##t }, n, TRUE } -#define CODED(n, c) { COLUMN_CODED_IDX, { .coded = { ARRAY_SIZE(c##_tables), c##_tables } }, n } -#define CODED_PRIMARY(n, c) { COLUMN_CODED_IDX, { .coded = { ARRAY_SIZE(c##_tables), c##_tables } }, n, TRUE } +#define CODED(n, c) { COLUMN_CODED_IDX, { .coded = { CT_##c, ARRAY_SIZE(c##_tables), c##_tables } }, n } +#define CODED_PRIMARY(n, c) { COLUMN_CODED_IDX, { .coded = { CT_##c, ARRAY_SIZE(c##_tables), c##_tables } }, n, TRUE }
/* Partition II.22.2, "Assembly" */ DEFINE_TABLE_SCHEMA(Assembly, @@ -262,7 +286,7 @@ DEFINE_TABLE_SCHEMA(ClassLayout, TABLE_PRIMARY("Parent", TYPEDEF));
/* Partition II.22.9, "Constant" */ -DEFINE_TABLE_SCHEMA(Constant, BASIC("Type", USHORT), CODED_PRIMARY("Parent", HasConstant), HEAP("Value", BLOB)); +DEFINE_TABLE_SCHEMA(Constant, BASIC_PADDED("Type", BYTE, USHORT), CODED_PRIMARY("Parent", HasConstant), HEAP("Value", BLOB));
/* Partition II.2.10, "CustomAttribute" */ DEFINE_TABLE_SCHEMA(CustomAttribute, @@ -563,6 +587,23 @@ static ULONG assembly_get_coded_index_size(const assembly_t *assembly, const str return max_row_idx < (1 << (16 - tag_bits)) ? 2 : 4; }
+#define TokenFromTable(idx) ((idx) << 24) + +/* Encode a coded index value as a token. The table and column *must* point to a coded index type. */ +ULONG metadata_coded_value_as_token(ULONG table_idx, ULONG column_idx, ULONG value) +{ + const struct table_column *column; + ULONG table_mask, tag_bits; + + assert(table_idx < TABLE_MAX && column_idx < table_schemas[table_idx]->columns_len); + column = &table_schemas[table_idx]->columns[column_idx]; + assert(column->type == COLUMN_CODED_IDX); + + tag_bits = bit_width(column->size.coded.len - 1); + table_mask = ((1UL << tag_bits) - 1); + return TokenFromRid((value & ~table_mask) >> tag_bits, TokenFromTable(column->size.coded.tables[value & table_mask])); +} + static HRESULT assembly_calculate_table_sizes(assembly_t *assembly, enum table table) { const struct table_schema *schema; @@ -583,7 +624,7 @@ static HRESULT assembly_calculate_table_sizes(assembly_t *assembly, enum table t switch (column->type) { case COLUMN_BASIC: - column_size = column->size.basic; + column_size = column->size.basic.size_padded; break; case COLUMN_HEAP_IDX: column_size = assembly_heap_idx_size(assembly, column->size.heap); @@ -828,6 +869,51 @@ HRESULT assembly_get_table(const assembly_t *assembly, ULONG table_idx, struct m return S_OK; }
+HRESULT assembly_get_column(const assembly_t *assembly, ULONG table_idx, ULONG column_idx, struct metadata_column_info *info) +{ + const struct table_schema *schema; + const struct table_column *column; + struct metadata_table_info table; + ULONG i, offset = 0; + HRESULT hr; + + if (FAILED(hr = assembly_get_table(assembly, table_idx, &table))) return hr; + if (column_idx >= table.num_columns) return E_INVALIDARG; + + schema = table_schemas[table_idx]; + column = &schema->columns[column_idx]; + if (column->type == COLUMN_BASIC) + info->size = column->size.basic.size; + else + info->size = table.column_sizes[column_idx]; + switch (column->type) + { + case COLUMN_BASIC: + info->type = column->size.basic.type; + break; + case COLUMN_HEAP_IDX: + info->type = iSTRING + column->size.heap; + break; + case COLUMN_TABLE_IDX: + info->type = column->size.table; + break; + case COLUMN_CODED_IDX: + info->type = column->size.coded.type; + break; + DEFAULT_UNREACHABLE; + } + for (i = 0; i < column_idx; i++) + { + if (schema->columns[i].type == COLUMN_BASIC) + offset += schema->columns[i].size.basic.size_padded; + else + offset += table.column_sizes[i]; + } + info->offset = offset; + info->name = column->name; + return S_OK; +} + ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap) { switch (heap) diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c index b66fac6487f..b5804bd987b 100644 --- a/dlls/rometadata/mdtables.c +++ b/dlls/rometadata/mdtables.c @@ -151,8 +151,19 @@ static HRESULT WINAPI tables_GetTableInfo(IMetaDataTables *iface, ULONG idx_tbl, static HRESULT WINAPI tables_GetColumnInfo(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_col, ULONG *offset, ULONG *col_size, ULONG *type, const char **name) { - FIXME("(%p, %lu, %lu, %p, %p, %p, %p) stub!\n", iface, idx_tbl, idx_col, offset, col_size, type, name); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + struct metadata_column_info column; + HRESULT hr; + + TRACE("(%p, %lu, %lu, %p, %p, %p, %p)\n", iface, idx_tbl, idx_col, offset, col_size, type, name); + + if (FAILED(hr = assembly_get_column(impl->assembly, idx_tbl, idx_col, &column))) return hr; + + *offset = column.offset; + *col_size = column.size; + *type = column.type; + *name = column.name; + return S_OK; }
static HRESULT WINAPI tables_GetCodedTokenInfo(IMetaDataTables *iface, ULONG type, ULONG *tokens_len, @@ -181,8 +192,21 @@ static HRESULT WINAPI tables_GetRow(IMetaDataTables *iface, ULONG idx_tbl, ULONG
static HRESULT WINAPI tables_GetColumn(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_col, ULONG idx_row, ULONG *val) { - FIXME("(%p, %lu, %lu, %lu, %p): stub!\n", iface, idx_tbl, idx_col, idx_row, val); - return E_NOTIMPL; + ULONG raw_val = 0, offset, size, type; + const BYTE *row = NULL; + const char *name; + HRESULT hr; + + TRACE("(%p, %lu, %lu, %lu, %p)\n", iface, idx_tbl, idx_col, idx_row, val); + + if (FAILED(hr = IMetaDataTables_GetRow(iface, idx_tbl, idx_row, (BYTE *)&row))) return hr; + if (FAILED(hr = IMetaDataTables_GetColumnInfo(iface, idx_tbl, idx_col, &offset, &size, &type, &name))) return hr; + + memcpy(&raw_val, row + offset, size); + if (type >= 64 && type <= 95) /* IsCodedTokenType */ + raw_val = metadata_coded_value_as_token(idx_tbl, idx_col, raw_val); + *val = raw_val; + return S_OK; }
static HRESULT WINAPI tables_GetString(IMetaDataTables *iface, ULONG idx, const char **str) diff --git a/dlls/rometadata/rometadatapriv.h b/dlls/rometadata/rometadatapriv.h index 3d1d2f21873..3e5ddaf8649 100644 --- a/dlls/rometadata/rometadatapriv.h +++ b/dlls/rometadata/rometadatapriv.h @@ -38,6 +38,14 @@ struct metadata_table_info const BYTE *start; };
+struct metadata_column_info +{ + ULONG offset; + ULONG size; + ULONG type; + const char *name; +}; + enum heap_type { HEAP_STRING = 0, @@ -49,9 +57,11 @@ enum heap_type extern HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **out); extern void assembly_free(assembly_t *assembly); extern HRESULT assembly_get_table(const assembly_t *assembly, ULONG table_idx, struct metadata_table_info *info); +extern HRESULT assembly_get_column(const assembly_t *assembly, ULONG table_idx, ULONG column_idx, struct metadata_column_info *info); extern ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap); extern const char *assembly_get_string(const assembly_t *assembly, ULONG idx); extern HRESULT assembly_get_blob(const assembly_t *assembly, ULONG idx, const BYTE **blob, ULONG *size); extern const GUID *assembly_get_guid(const assembly_t *assembly, ULONG idx); +extern ULONG metadata_coded_value_as_token(ULONG table_idx, ULONG column_idx, ULONG value);
#endif /* __WINE_ROMETADATA_PRIVATE__ */ diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index d752770302b..ad62d6759af 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -602,13 +602,7 @@ static void test_MetaDataDispenser_OpenScope(void) winetest_push_context("column=%lu", j);
hr = IMetaDataTables_GetColumnInfo(md_tables, i, j, &offset, &col_size, &type, &name); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - if (FAILED(hr)) - { - winetest_pop_context(); - continue; - }
ok(offset == column->exp_offset, "got offset %lu != %lu\n", offset, column->exp_offset); ok(col_size == column->exp_col_size, "got col_size %lu != %lu\n", col_size, column->exp_col_size); @@ -636,15 +630,11 @@ static void test_MetaDataDispenser_OpenScope(void) ok(!!guid, "got guid %p\n", guid);
hr = IMetaDataTables_GetColumn(md_tables, TABLE_MODULE, 1, 1, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == module->Name, "got val %#lx != %#x\n", val, module->Name);
hr = IMetaDataTables_GetColumn(md_tables, TABLE_MODULE, 2, 1, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == module->Mvid, "got val %#lx != %#x\n", val, module->Mvid);
/* Read defined types. */ @@ -677,21 +667,15 @@ static void test_MetaDataDispenser_OpenScope(void) }
hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEDEF, 0, i + 1, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == type_def->Flags, "got val %#lx != %#lx\n", val, type_def->Flags);
hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEDEF, 1, i + 1, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == type_def->Name, "got val %#lx != %#x\n", val, type_def->Name);
hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEDEF, 2, i + 1, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == type_def->Namespace, "got val %#lx != %#x\n", val, type_def->Namespace);
winetest_pop_context(); @@ -732,32 +716,23 @@ static void test_MetaDataDispenser_OpenScope(void) }
hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEREF, 1, typeref_row, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == typeref->Name, "got val %#lx != %#x\n", val, typeref->Name);
hr = IMetaDataTables_GetColumn(md_tables, TABLE_TYPEREF, 2, typeref_row, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == typeref->Namespace, "got val %#lx != %#x\n", val, typeref->Namespace);
hr = IMetaDataTables_GetColumn(md_tables, TABLE_MEMBERREF, 0, i + 1, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); rid = RidFromToken(val); type = TypeFromToken(val); - todo_wine ok(rid == typeref_row, "got rid %#lx != %#lx\n", rid, typeref_row); - todo_wine ok(type == mdtTypeRef, "got type %#lx != %#x\n", type, mdtTypeRef); }
hr = IMetaDataTables_GetColumn(md_tables, TABLE_MEMBERREF, 1, i + 1, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == ref->Name, "got val %#lx != %#x\n", val, ref->Name);
winetest_pop_context(); @@ -790,9 +765,7 @@ static void test_MetaDataDispenser_OpenScope(void) }
hr = IMetaDataTables_GetColumn(md_tables, TABLE_CUSTOMATTRIBUTE, 2, i + 1, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == attr->Value, "got val %#lx != %#x\n", val, attr->Value);
winetest_pop_context();
On Mon Oct 13 12:44:54 2025 +0000, Vibhav Pant wrote:
But then wouldn't we need to always iterate through the array with `strcmp` to get a particular stream (for instance, in `assembly_get_heap_size`, `assembly_get_{string, blob, guid}`)? The spec doesn't say that streams need to appear in a certain order, so we wouldn't be able to use fixed indices to this array.
stream_tables would become something like streams[STREAM_TABLE], but it's fine like this too.
This merge request was approved by Hans Leidekker.