├── .github ├── FUNDING.yml ├── CODEOWNERS ├── dependabot.yml ├── workflows │ ├── prepare_release.yml │ ├── publish.yml │ ├── semantic_pr.yml │ ├── pull_request_labeler.yml │ ├── sync_labels.yml │ ├── examples.yml │ ├── release.yml │ └── winmd.yml ├── labeler.yml ├── ISSUE_TEMPLATE │ ├── build.yaml │ ├── feature.yaml │ ├── ci.yaml │ ├── style.yaml │ ├── chore.yaml │ ├── test.yaml │ ├── refactor.yaml │ ├── revert.yaml │ ├── performance.yaml │ ├── documentation.yaml │ └── bug.yaml └── PULL_REQUEST_TEMPLATE.md ├── analysis_options.yaml ├── .vscode └── settings.json ├── AUTHORS ├── lib ├── src │ ├── exception.dart │ ├── logger.dart │ ├── reader │ │ ├── heap │ │ │ ├── metadata_heap.dart │ │ │ ├── guid.dart │ │ │ └── string.dart │ │ ├── type_category.dart │ │ ├── table │ │ │ ├── type_spec.dart │ │ │ ├── module_ref.dart │ │ │ ├── assembly_processor.dart │ │ │ ├── stand_alone_sig.dart │ │ │ ├── field_rva.dart │ │ │ ├── file.dart │ │ │ ├── nested_class.dart │ │ │ ├── event_map.dart │ │ │ ├── method_spec.dart │ │ │ ├── property_map.dart │ │ │ ├── field_marshal.dart │ │ │ ├── field_layout.dart │ │ │ ├── assembly_os.dart │ │ │ ├── assembly_ref_processor.dart │ │ │ ├── module.dart │ │ │ ├── generic_param_constraint.dart │ │ │ ├── decl_security.dart │ │ │ ├── member_ref.dart │ │ │ ├── type_ref.dart │ │ │ ├── method_semantics.dart │ │ │ ├── class_layout.dart │ │ │ ├── assembly_ref_os.dart │ │ │ ├── method_impl.dart │ │ │ ├── interface_impl.dart │ │ │ └── exported_type.dart │ │ ├── extensions.dart │ │ └── has_custom_attributes.dart │ ├── writer │ │ ├── table │ │ │ ├── module_ref.dart │ │ │ ├── type_spec.dart │ │ │ ├── assembly_processor.dart │ │ │ ├── stand_alone_sig.dart │ │ │ ├── field_rva.dart │ │ │ ├── field_layout.dart │ │ │ ├── event_map.dart │ │ │ ├── property_map.dart │ │ │ ├── nested_class.dart │ │ │ ├── interface_impl.dart │ │ │ ├── method_spec.dart │ │ │ ├── field_marshal.dart │ │ │ ├── assembly_ref_processor.dart │ │ │ ├── assembly_os.dart │ │ │ ├── class_layout.dart │ │ │ ├── generic_param_constraint.dart │ │ │ ├── field.dart │ │ │ ├── param.dart │ │ │ ├── file.dart │ │ │ ├── property.dart │ │ │ ├── constant.dart │ │ │ ├── type_ref.dart │ │ │ ├── event.dart │ │ │ ├── custom_attribute.dart │ │ │ ├── method_impl.dart │ │ │ ├── decl_security.dart │ │ │ ├── method_semantics.dart │ │ │ ├── assembly_ref_os.dart │ │ │ ├── module.dart │ │ │ ├── manifest_resource.dart │ │ │ ├── impl_map.dart │ │ │ ├── member_ref.dart │ │ │ ├── exported_type.dart │ │ │ ├── method_def.dart │ │ │ ├── type_def.dart │ │ │ ├── generic_param.dart │ │ │ ├── assembly.dart │ │ │ └── assembly_ref.dart │ │ ├── table.dart │ │ ├── heap │ │ │ ├── guid.dart │ │ │ ├── string.dart │ │ │ ├── blob.dart │ │ │ └── metadata_heap.dart │ │ └── stream.dart │ ├── type_name.dart │ ├── property_sig.dart │ ├── method_signature.dart │ ├── windows_metadata │ │ └── package.dart │ └── culture.dart ├── mdmerge.dart ├── windows_metadata.dart └── writer.dart ├── .gitignore ├── test ├── exception_test.dart ├── reader │ ├── table │ │ ├── assembly_processor_test.dart │ │ ├── assembly_os_test.dart │ │ ├── assembly_ref_processor_test.dart │ │ ├── module_test.dart │ │ ├── assembly_ref_os_test.dart │ │ ├── field_rva_test.dart │ │ ├── module_ref_test.dart │ │ ├── class_layout_test.dart │ │ ├── field_layout_test.dart │ │ ├── interface_impl_test.dart │ │ ├── assembly_test.dart │ │ ├── type_spec_test.dart │ │ ├── file_test.dart │ │ ├── impl_map_test.dart │ │ ├── assembly_ref_test.dart │ │ ├── event_map_test.dart │ │ ├── method_impl_test.dart │ │ ├── exported_type_test.dart │ │ ├── property_map_test.dart │ │ ├── member_ref_test.dart │ │ ├── property_test.dart │ │ ├── stand_alone_sig_test.dart │ │ ├── manifest_resource_test.dart │ │ ├── generic_param_test.dart │ │ ├── generic_param_constraint_test.dart │ │ ├── method_semantics_test.dart │ │ ├── decl_security_test.dart │ │ ├── method_spec_test.dart │ │ └── field_marshal_test.dart │ ├── table_stream_test.dart │ └── heap │ │ └── string_test.dart ├── culture_test.dart ├── windows_metadata │ └── package_test.dart ├── versions.dart ├── method_signature_test.dart ├── type_name_test.dart └── property_sig_test.dart ├── lefthook.yml ├── pubspec.yaml ├── .gitattributes ├── example ├── main.dart ├── writer.dart └── windows_metadata.dart ├── LICENSE └── PUBLISHING.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: halildurmus 2 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:halildurmus_lints/dart.yaml 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Every request must be reviewed and accepted by: 2 | 3 | * @halildurmus 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "markdownlint.config": { 3 | "MD033": false, 4 | "MD041": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: pub 8 | directory: / 9 | schedule: 10 | interval: daily 11 | -------------------------------------------------------------------------------- /.github/workflows/prepare_release.yml: -------------------------------------------------------------------------------- 1 | name: Prepare Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | prepare-release: 8 | uses: halildurmus/workflows/.github/workflows/prepare_release.yml@main 9 | secrets: 10 | token: ${{ secrets.PAT }} 11 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Below is a list of people and organizations that have contributed to the 2 | # project. Names should be added to the list like so, ordered alphabetically by 3 | # GitHub handle: 4 | # 5 | # Name/Organization (@github_handle) 6 | 7 | Halil Durmus (@halildurmus) 8 | Tim Sneath (@timsneath) 9 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: ['v[0-9]+.[0-9]+.[0-9]+*'] 6 | 7 | jobs: 8 | publish: 9 | name: Publish to pub.dev 10 | permissions: 11 | id-token: write 12 | uses: dart-lang/setup-dart/.github/workflows/publish.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/semantic_pr.yml: -------------------------------------------------------------------------------- 1 | name: Semantic PR 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | - reopened 10 | 11 | jobs: 12 | build: 13 | uses: halildurmus/workflows/.github/workflows/semantic_pr.yml@main 14 | -------------------------------------------------------------------------------- /lib/src/exception.dart: -------------------------------------------------------------------------------- 1 | /// An exception generated by the `winmd` package. 2 | class WinmdException implements Exception { 3 | const WinmdException(this.message); 4 | 5 | /// A message describing the exception. 6 | final String message; 7 | 8 | @override 9 | String toString() => 'WinmdException: $message'; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | 5 | .idea/ 6 | *.iml 7 | 8 | # Directory created by dartdoc 9 | doc/api/ 10 | 11 | # Avoid committing pubspec.lock for library packages; see 12 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 13 | pubspec.lock 14 | -------------------------------------------------------------------------------- /lib/src/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:logging/logging.dart'; 2 | 3 | /// The [Logger] used for all log messages from this package. 4 | /// 5 | /// This logger is initially unconfigured; no log messages will be printed 6 | /// unless the user sets up a [Logger.root] handler or attaches a listener to 7 | /// [winmdLogger]. 8 | final winmdLogger = Logger('winmd'); 9 | -------------------------------------------------------------------------------- /test/exception_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart'; 4 | 5 | void main() { 6 | test('WinmdException', () { 7 | const exception = WinmdException('Test exception'); 8 | check(exception.toString()).equals('WinmdException: Test exception'); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /lib/mdmerge.dart: -------------------------------------------------------------------------------- 1 | /// Provides the [mdmerge] function, which reads and consolidates type 2 | /// definitions, methods, fields, attributes, and related metadata from multiple 3 | /// Windows Metadata (`.winmd`) files or directories containing `.winmd` files, 4 | /// producing a single, unified metadata file. 5 | /// 6 | /// @docImport 'src/mdmerge.dart'; 7 | library; 8 | 9 | export 'src/mdmerge.dart'; 10 | -------------------------------------------------------------------------------- /.github/workflows/pull_request_labeler.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Labeler 2 | 3 | on: 4 | pull_request_target: 5 | 6 | jobs: 7 | label: 8 | permissions: 9 | contents: read 10 | pull-requests: write 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/labeler@v6 16 | with: 17 | repo-token: ${{ secrets.GITHUB_TOKEN }} 18 | sync-labels: true 19 | -------------------------------------------------------------------------------- /lib/src/reader/heap/metadata_heap.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | /// Represents a contiguous region of memory used to store structured binary 4 | /// data, such as strings, blobs, or GUIDs in a metadata file. 5 | abstract class MetadataHeap { 6 | /// Creates a new metadata heap from the given binary [data]. 7 | const MetadataHeap(this.data); 8 | 9 | /// The raw contents of the heap. 10 | final Uint8List data; 11 | } 12 | -------------------------------------------------------------------------------- /lib/windows_metadata.dart: -------------------------------------------------------------------------------- 1 | /// Provides high-level APIs for discovering, downloading, caching, and loading 2 | /// Windows Metadata (`.winmd`) files for use in Dart-based tooling. 3 | /// 4 | /// For usage, refer to the [WindowsMetadataLoader] class. 5 | /// 6 | /// @docImport 'src/windows_metadata/loader.dart'; 7 | library; 8 | 9 | export 'src/windows_metadata/loader.dart'; 10 | export 'src/windows_metadata/local_storage_manager.dart'; 11 | export 'src/windows_metadata/package.dart'; 12 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: true 3 | commands: 4 | analyze: 5 | glob: '*.{dart}' 6 | run: dart run hooks:analyze example lib test 7 | format: 8 | glob: '*.{dart}' 9 | run: dart run hooks:format {staged_files} 10 | test: 11 | glob: '{lib,test}/**/*.dart' 12 | run: dart run hooks:test -- -j 1 --test-randomize-ordering-seed=random 13 | 14 | commit-msg: 15 | commands: 16 | check_commit: 17 | run: dart run hooks:check_commit {1} 18 | -------------------------------------------------------------------------------- /.github/workflows/sync_labels.yml: -------------------------------------------------------------------------------- 1 | name: Sync Labels 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | paths: 7 | - .github/labels.yml 8 | workflow_dispatch: 9 | 10 | permissions: 11 | issues: write 12 | 13 | jobs: 14 | labels: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: 📚 Git Checkout 19 | uses: actions/checkout@v6 20 | 21 | - name: 🚀 Run Label Sync 22 | uses: srealmoreno/label-sync-action@v2 23 | with: 24 | clean-labels: true 25 | config-file: https://raw.githubusercontent.com/halildurmus/workflows/main/.github/labels.yml 26 | -------------------------------------------------------------------------------- /test/reader/table/assembly_processor_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWin32Metadata( 10 | version: win32MetadataVersion, 11 | ); 12 | 13 | test('AssemblyProcessor', () { 14 | final assemblyProcessor = AssemblyProcessor(index, 0, 0); 15 | check(assemblyProcessor.token).equals(0x21000000); 16 | check(assemblyProcessor.processor).equals(0); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /lib/writer.dart: -------------------------------------------------------------------------------- 1 | /// A basic [WinMD] writer based on the [ECMA-335] standard. 2 | /// 3 | /// For usage, refer to the [MetadataWriter] class. 4 | /// 5 | /// [WinMD]: https://learn.microsoft.com/en-us/uwp/winrt-cref/winmd-files 6 | /// 7 | /// @docImport 'src/writer/metadata_writer.dart'; 8 | library; 9 | 10 | export 'src/attribute_arg.dart'; 11 | export 'src/attributes.dart'; 12 | export 'src/guid.dart'; 13 | export 'src/metadata_type.dart'; 14 | export 'src/metadata_value.dart'; 15 | export 'src/method_signature.dart'; 16 | export 'src/writer/codes.dart'; 17 | export 'src/writer/metadata_writer.dart'; 18 | export 'src/writer/table/index.dart'; 19 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | ci: 2 | - changed-files: 3 | - any-glob-to-all-files: 4 | - .github/** 5 | - cliff.toml 6 | - lefthook.yml 7 | 8 | documentation: 9 | - changed-files: 10 | - any-glob-to-all-files: 11 | - CONTRIBUTING.md 12 | - PUBLISHING.md 13 | - README.md 14 | 15 | example: 16 | - changed-files: 17 | - any-glob-to-all-files: example/** 18 | 19 | 'external dependency': 20 | - head-branch: [^dependabot] 21 | 22 | release: 23 | - head-branch: [^release, release] 24 | 25 | test: 26 | - changed-files: 27 | - any-glob-to-all-files: 28 | - test/** 29 | -------------------------------------------------------------------------------- /.github/workflows/examples.yml: -------------------------------------------------------------------------------- 1 | name: Examples 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - .github/workflows/examples.yml 7 | - example/** 8 | - lib/** 9 | - analysis_options.yaml 10 | - pubspec.yaml 11 | push: 12 | branches: [main] 13 | paths: 14 | - .github/workflows/examples.yml 15 | - example/** 16 | - lib/** 17 | - analysis_options.yaml 18 | - pubspec.yaml 19 | 20 | jobs: 21 | build: 22 | uses: halildurmus/workflows/.github/workflows/dart.yml@main 23 | with: 24 | analyze_directories: example 25 | format_directories: example 26 | run_tests: false 27 | -------------------------------------------------------------------------------- /test/reader/table/assembly_os_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWin32Metadata( 10 | version: win32MetadataVersion, 11 | ); 12 | 13 | test('AssemblyOS', () { 14 | final assemblyOS = AssemblyOS(index, 0, 0); 15 | check(assemblyOS.token).equals(0x22000000); 16 | check(assemblyOS.osPlatformId).equals(0); 17 | check(assemblyOS.osMajorVersion).equals(0); 18 | check(assemblyOS.osMinorVersion).equals(0); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /test/reader/table_stream_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart'; 4 | 5 | void main() { 6 | group('TableStream', () { 7 | test('has correct number of tables', () { 8 | final stream = TableStream(); 9 | check(stream.tables.length).equals(38); 10 | }); 11 | 12 | test('table has correct initial data', () { 13 | final stream = TableStream(); 14 | final table = stream[MetadataTable.typeSpec]; 15 | check(table.offset).equals(0); 16 | check(table.rows).equals(0); 17 | check(table.width).equals(0); 18 | check(table.columns.length).equals(6); 19 | }); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /test/reader/table/assembly_ref_processor_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWin32Metadata( 10 | version: win32MetadataVersion, 11 | ); 12 | 13 | test('AssemblyRefProcessor', () { 14 | final assemblyRefProcessor = AssemblyRefProcessor(index, 0, 0); 15 | check(assemblyRefProcessor.token).equals(0x24000000); 16 | check(assemblyRefProcessor.processor).equals(0); 17 | check(assemblyRefProcessor.assemblyRef).isNull(); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /test/reader/table/module_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | 5 | import '../../versions.dart'; 6 | 7 | void main() async { 8 | final index = await WindowsMetadataLoader().loadWin32Metadata( 9 | version: win32MetadataVersion, 10 | ); 11 | 12 | group('Module', () { 13 | test('Windows.Win32.winmd', () { 14 | check(index.module.length).equals(1); 15 | final module = index.module.first; 16 | check(module.token).equals(0x00000000); 17 | check(module.name).equals('Windows.Win32.winmd'); 18 | check( 19 | module.mvid.toString(), 20 | ).equals('4a520299-e458-4675-befb-318972b60e6a'); 21 | }); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /test/reader/table/assembly_ref_os_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWin32Metadata( 10 | version: win32MetadataVersion, 11 | ); 12 | 13 | test('AssemblyRefOS', () { 14 | final assemblyRefOS = AssemblyRefOS(index, 0, 0); 15 | check(assemblyRefOS.token).equals(0x25000000); 16 | check(assemblyRefOS.osPlatformId).equals(0); 17 | check(assemblyRefOS.osMajorVersion).equals(0); 18 | check(assemblyRefOS.osMinorVersion).equals(0); 19 | check(assemblyRefOS.assemblyRef).isNull(); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /test/reader/table/field_rva_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart' show MetadataIndex, MetadataReader; 4 | import 'package:winmd/writer.dart'; 5 | 6 | void main() { 7 | test('FieldRVA', () { 8 | final writer = MetadataWriter(name: 'MyMetadata'); 9 | final field = writer.writeField(name: 'foo', signature: const StringType()); 10 | writer.writeFieldRVA(field: field, rva: 0x1000); 11 | final reader = MetadataReader.read(writer.toBytes()); 12 | final index = MetadataIndex.fromReader(reader); 13 | final fieldRVA = index.fieldRVA.first; 14 | check(fieldRVA.token).equals(0x1D000000); 15 | check(fieldRVA.field.name).equals('foo'); 16 | check(fieldRVA.rva).equals(0x1000); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: winmd 2 | description: Inspect and generate Windows Metadata (.winmd) files based on the 3 | ECMA-335 standard. 4 | version: 7.0.0 5 | repository: https://github.com/halildurmus/winmd 6 | issue_tracker: https://github.com/halildurmus/winmd/issues 7 | 8 | topics: 9 | - winmd 10 | - windows 11 | - metadata 12 | 13 | environment: 14 | sdk: ^3.8.0 15 | 16 | platforms: 17 | linux: 18 | macos: 19 | windows: 20 | 21 | dependencies: 22 | archive: ^4.0.7 23 | ffi: ^2.1.4 24 | logging: ^1.3.0 25 | meta: ^1.17.0 26 | nuget: ^0.2.0 27 | path: ^1.9.1 28 | 29 | dev_dependencies: 30 | checks: ^0.3.0 31 | halildurmus_lints: ^1.0.0 32 | hooks: 33 | git: 34 | url: https://github.com/halildurmus/workflows.git 35 | path: packages/hooks 36 | test: ^1.26.2 37 | -------------------------------------------------------------------------------- /test/culture_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart'; 4 | 5 | void main() { 6 | group('Culture', () { 7 | test('creates valid Culture instances', () { 8 | check(() => Culture('en-US')).returnsNormally(); 9 | check(() => Culture('en-us')).returnsNormally(); 10 | }); 11 | 12 | test('is case-insensitive', () { 13 | check(Culture.isValidCulture('en-us')).isTrue(); 14 | check(Culture.isValidCulture('eN-uS')).isTrue(); 15 | check(Culture.isValidCulture('EN-US')).isTrue(); 16 | }); 17 | 18 | test('throws on invalid culture code', () { 19 | check(() => Culture('xx-yy')) 20 | .throws() 21 | .has((e) => e.message, 'message') 22 | .equals('Invalid culture'); 23 | }); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /test/reader/table/module_ref_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final metadata = MetadataLookup( 10 | await WindowsMetadataLoader().loadWin32Metadata( 11 | version: win32MetadataVersion, 12 | ), 13 | ); 14 | 15 | group('ModuleRef', () { 16 | test('kernel32.dll', () { 17 | final function = metadata.findFunction( 18 | 'Windows.Win32.Foundation', 19 | 'SetLastError', 20 | ); 21 | final moduleRef = function.implMap?.importScope; 22 | check(moduleRef).isNotNull(); 23 | check(moduleRef!.token).equals(0x1A000005); 24 | check(moduleRef.name).equals('KERNEL32.dll'); 25 | }); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /test/reader/table/class_layout_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | 5 | import '../../versions.dart'; 6 | 7 | void main() async { 8 | final index = await WindowsMetadataLoader().loadWin32Metadata( 9 | version: win32MetadataVersion, 10 | ); 11 | 12 | group('ClassLayout', () { 13 | test('BITMAPFILEHEADER', () { 14 | final typeDef = index.findSingleType( 15 | 'Windows.Win32.Graphics.Gdi', 16 | 'BITMAPFILEHEADER', 17 | ); 18 | final classLayout = typeDef.classLayout; 19 | check(classLayout).isNotNull(); 20 | check(classLayout!.token).equals(0x0F0000B6); 21 | check(classLayout.packingSize).equals(2); 22 | check(classLayout.classSize).equals(0); 23 | check(classLayout.parent.name).equals('BITMAPFILEHEADER'); 24 | }); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /test/windows_metadata/package_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | 5 | void main() { 6 | group('WindowsMetadataPackage', () { 7 | test('wdk', () { 8 | const wdk = WindowsMetadataPackage.wdk; 9 | check(wdk.packageId).equals('microsoft.windows.wdk.win32metadata'); 10 | check(wdk.assetName).equals('Windows.Wdk.winmd'); 11 | }); 12 | 13 | test('win32', () { 14 | const win32 = WindowsMetadataPackage.win32; 15 | check(win32.packageId).equals('microsoft.windows.sdk.win32metadata'); 16 | check(win32.assetName).equals('Windows.Win32.winmd'); 17 | }); 18 | 19 | test('winrt', () { 20 | const winrt = WindowsMetadataPackage.winrt; 21 | check(winrt.packageId).equals('microsoft.windows.sdk.contracts'); 22 | check(winrt.assetName).equals('Windows.winmd'); 23 | }); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /test/reader/table/field_layout_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWin32Metadata( 10 | version: win32MetadataVersion, 11 | ); 12 | 13 | group('FieldLayout', () { 14 | test('WHV_PARTITION_PROPERTY.ProcessorCount', () { 15 | final typeDef = index.findSingleType( 16 | 'Windows.Win32.System.Hypervisor', 17 | 'WHV_PARTITION_PROPERTY', 18 | ); 19 | final field = typeDef.findField('ProcessorCount'); 20 | final layout = field.layout; 21 | check(layout).isNotNull(); 22 | check(layout!.token).equals(0x100004E9); 23 | check(layout.offset).equals(0); 24 | check(layout.field.name).equals('ProcessorCount'); 25 | }); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization. 2 | * text=auto 3 | 4 | # Always perform LF normalization on these files. 5 | *.dart text 6 | *.expect text 7 | *.gradle text 8 | *.golden text eol=lf 9 | *.html text 10 | *.java text 11 | *.json text 12 | *.md text 13 | *.mdx text 14 | *.py text 15 | *.sh text 16 | *.txt text 17 | *.xml text 18 | *.yaml text 19 | 20 | # Make sure that these Windows files always have CRLF line endings in checkout. 21 | *.bat text eol=crlf 22 | *.ps1 text eol=crlf 23 | *.rc text eol=crlf 24 | *.sln text eol=crlf 25 | *.props text eol=crlf 26 | *.vcxproj text eol=crlf 27 | *.vcxproj.filters text eol=crlf 28 | # Including templatized versions. 29 | *.sln.tmpl text eol=crlf 30 | *.props.tmpl text eol=crlf 31 | *.vcxproj.tmpl text eol=crlf 32 | 33 | # Never perform LF normalization on these files. 34 | *.ico binary 35 | *.jar binary 36 | *.png binary 37 | *.zip binary 38 | -------------------------------------------------------------------------------- /test/reader/table/interface_impl_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWin32Metadata( 10 | version: win32MetadataVersion, 11 | ); 12 | 13 | group('InterfaceImpl', () { 14 | test('IFileDialog', () { 15 | final typeDef = index.findSingleType( 16 | 'Windows.Win32.UI.Shell', 17 | 'IFileDialog', 18 | ); 19 | check(typeDef.interfaceImpls).isNotEmpty(); 20 | final interfaceImpl = typeDef.interfaceImpls.first; 21 | check(interfaceImpl.token).equals(0x09001D29); 22 | check(interfaceImpl.class$.name).equals('IFileDialog'); 23 | check(interfaceImpl.interface).equals( 24 | const NamedClassType( 25 | TypeName('Windows.Win32.UI.Shell', 'IModalWindow'), 26 | ), 27 | ); 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/writer/table/module_ref.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../heap/metadata_heap.dart'; 7 | import '../helpers.dart'; 8 | import '../row.dart'; 9 | import '../table_stream.dart'; 10 | 11 | /// Represents a row in the `ModuleRef` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.31`. 15 | /// 16 | /// The `ModuleRef` table has the following columns: 17 | /// - **Name** (String Heap Index) 18 | final class ModuleRef implements Row { 19 | const ModuleRef({required this.name}); 20 | 21 | final StringIndex name; 22 | 23 | @override 24 | void serialize(BytesBuilder buffer, TableStream stream) { 25 | buffer.writeHeapIndex(name, stream); 26 | } 27 | } 28 | 29 | @internal 30 | final class ModuleRefCompanion extends RowCompanion { 31 | const ModuleRefCompanion(); 32 | 33 | @override 34 | MetadataTableId get tableId => MetadataTableId.moduleRef; 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/writer/table/type_spec.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../heap/metadata_heap.dart'; 7 | import '../helpers.dart'; 8 | import '../row.dart'; 9 | import '../table_stream.dart'; 10 | 11 | /// Represents a row in the `TypeSpec` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.39`. 15 | /// 16 | /// The `TypeSpec` table has the following columns: 17 | /// - **Signature** (Blob Heap Index) 18 | final class TypeSpec implements Row { 19 | const TypeSpec({required this.signature}); 20 | 21 | final BlobIndex signature; 22 | 23 | @override 24 | void serialize(BytesBuilder buffer, TableStream stream) { 25 | buffer.writeHeapIndex(signature, stream); 26 | } 27 | } 28 | 29 | @internal 30 | final class TypeSpecCompanion extends RowCompanion { 31 | const TypeSpecCompanion(); 32 | 33 | @override 34 | MetadataTableId get tableId => MetadataTableId.typeSpec; 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/writer/table/assembly_processor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | 10 | /// Represents a row in the `AssemblyProcessor` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.4`. 14 | /// 15 | /// The `AssemblyProcessor` table has the following columns: 16 | /// - **Processor** (4-byte value) 17 | final class AssemblyProcessor implements Row { 18 | const AssemblyProcessor({this.processor = 0}); 19 | 20 | final int processor; 21 | 22 | @override 23 | void serialize(BytesBuilder buffer, TableStream stream) { 24 | buffer.writeUint32(processor); 25 | } 26 | } 27 | 28 | @internal 29 | final class AssemblyProcessorCompanion extends RowCompanion { 30 | const AssemblyProcessorCompanion(); 31 | 32 | @override 33 | MetadataTableId get tableId => MetadataTableId.assemblyProcessor; 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/writer/table/stand_alone_sig.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../heap/metadata_heap.dart'; 7 | import '../helpers.dart'; 8 | import '../row.dart'; 9 | import '../table_stream.dart'; 10 | 11 | /// Represents a row in the `StandAloneSig` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.36`. 15 | /// 16 | /// The `StandAloneSig` table has the following columns: 17 | /// - **Signature** (Blob Heap Index) 18 | final class StandAloneSig implements Row { 19 | const StandAloneSig({required this.signature}); 20 | 21 | final BlobIndex signature; 22 | 23 | @override 24 | void serialize(BytesBuilder buffer, TableStream stream) { 25 | buffer.writeHeapIndex(signature, stream); 26 | } 27 | } 28 | 29 | @internal 30 | final class StandAloneSigCompanion extends RowCompanion { 31 | const StandAloneSigCompanion(); 32 | 33 | @override 34 | MetadataTableId get tableId => MetadataTableId.standAloneSig; 35 | } 36 | -------------------------------------------------------------------------------- /test/reader/table/assembly_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWin32Metadata( 10 | version: win32MetadataVersion, 11 | ); 12 | 13 | group('Assembly', () { 14 | test('Windows.Win32.winmd', () { 15 | check(index.assembly.length).equals(1); 16 | final assembly = index.assembly.first; 17 | check(assembly.token).equals(0x20000000); 18 | check(assembly.majorVersion).equals(0); 19 | check(assembly.minorVersion).equals(0); 20 | check(assembly.buildNumber).equals(0); 21 | check(assembly.revisionNumber).equals(0); 22 | check(assembly.flags).equals(const AssemblyFlags(0)); 23 | check(assembly.publicKey).isNull(); 24 | check(assembly.name).equals('Windows.Win32.winmd'); 25 | check(assembly.culture).isNull(); 26 | check(assembly.version).equals('0.0.0.0'); 27 | }); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/writer/table/field_rva.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | import 'index.dart'; 10 | 11 | /// Represents a row in the `FieldRVA` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.18`. 15 | /// 16 | /// The `FieldRVA` table has the following columns: 17 | /// - **RVA** (4-byte constant) 18 | /// - **Field** (Field Table Index) 19 | final class FieldRVA implements Row { 20 | const FieldRVA({required this.rva, required this.field}); 21 | 22 | final int rva; 23 | final FieldIndex field; 24 | 25 | @override 26 | void serialize(BytesBuilder buffer, TableStream stream) { 27 | buffer 28 | ..writeUint32(rva) 29 | ..writeTableIndex(field, stream); 30 | } 31 | } 32 | 33 | @internal 34 | final class FieldRVACompanion extends RowCompanion { 35 | const FieldRVACompanion(); 36 | 37 | @override 38 | MetadataTableId get tableId => MetadataTableId.fieldRVA; 39 | } 40 | -------------------------------------------------------------------------------- /test/versions.dart: -------------------------------------------------------------------------------- 1 | // This file defines the specific versions of Windows Metadata packages used 2 | // during testing. These constants ensure consistent and reproducible test 3 | // results by locking the test environment to known versions of the Windows 4 | // Driver Kit (WDK), Win32 API, and Windows Runtime (WinRT) metadata. 5 | 6 | import 'package:winmd/windows_metadata.dart'; 7 | 8 | /// The version of the [Windows Driver Kit (WDK) metadata]( 9 | /// https://www.nuget.org/packages/Microsoft.Windows.WDK.Win32Metadata/) 10 | /// package. 11 | const wdkMetadataVersion = '0.13.25-experimental'; 12 | 13 | /// The version of the [Windows API metadata]( 14 | /// https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata/) 15 | /// package. 16 | const win32MetadataVersion = '63.0.31-preview'; 17 | 18 | /// The version of the [Windows Runtime (WinRT) metadata]( 19 | /// https://www.nuget.org/packages/Microsoft.Windows.SDK.Contracts/) 20 | /// package. 21 | const winrtMetadataVersion = '10.0.26100.1742'; 22 | 23 | const metadataVersions = WindowsMetadataVersions( 24 | wdk: wdkMetadataVersion, 25 | win32: win32MetadataVersion, 26 | winrt: winrtMetadataVersion, 27 | ); 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/build.yaml: -------------------------------------------------------------------------------- 1 | name: 🔧 Build System 2 | description: Changes that affect the build system or external dependencies 3 | title: "build: " 4 | labels: [build] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Describe what changes need to be done to the build system and why 11 | placeholder: "Describe the build system change." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] All CI/CD checks are passing. 21 | - [ ] There is no drop in the test coverage percentage. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: additional-context 26 | attributes: 27 | label: Additional Context 28 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 29 | placeholder: "Provide context here." 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yaml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature Request 2 | description: A new feature to be added to the project 3 | title: "feat: " 4 | labels: [feature] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Clearly describe what you are looking to add. The more business/user context the better. 11 | placeholder: "Provide a description of the feature." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] All CI/CD checks are passing. 21 | - [ ] There is no drop in the test coverage percentage. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: additional-context 26 | attributes: 27 | label: Additional Context 28 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 29 | placeholder: "Provide context here." 30 | -------------------------------------------------------------------------------- /lib/src/writer/table/field_layout.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | import 'index.dart'; 10 | 11 | /// Represents a row in the `FieldLayout` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.16`. 15 | /// 16 | /// The `FieldLayout` table has the following columns: 17 | /// - **Offset** (4-byte constant) 18 | /// - **Field** (Field Table Index) 19 | final class FieldLayout implements Row { 20 | const FieldLayout({required this.offset, required this.field}); 21 | 22 | final int offset; 23 | final FieldIndex field; 24 | 25 | @override 26 | void serialize(BytesBuilder buffer, TableStream stream) { 27 | buffer 28 | ..writeUint32(offset) 29 | ..writeTableIndex(field, stream); 30 | } 31 | } 32 | 33 | @internal 34 | final class FieldLayoutCompanion extends RowCompanion { 35 | const FieldLayoutCompanion(); 36 | 37 | @override 38 | MetadataTableId get tableId => MetadataTableId.fieldLayout; 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/reader/type_category.dart: -------------------------------------------------------------------------------- 1 | /// @docImport 'table/type_def.dart'; 2 | library; 3 | 4 | /// Represents the semantic classification of a [TypeDef]. 5 | enum TypeCategory { 6 | /// A type that derives from `System.Attribute`, used to annotate program 7 | /// elements with declarative information. 8 | attribute, 9 | 10 | /// A reference type that derives from another class or directly from 11 | /// `System.Object`, typically supporting inheritance and polymorphism. 12 | class$, 13 | 14 | /// A value type that inherits from `System.Enum`, providing a set of named 15 | /// constants backed by an underlying primitive type. 16 | enum$, 17 | 18 | /// A reference type that encapsulates a method reference, serving a role 19 | /// similar to function pointers in languages such as C++. 20 | delegate, 21 | 22 | /// A contract-based type that defines a set of methods, properties, or events 23 | /// without providing implementations, specifying capabilities that a class or 24 | /// struct must fulfill. 25 | interface, 26 | 27 | /// A value type that derives implicitly from `System.ValueType`, which in 28 | /// turn is derived from `System.Object`. 29 | struct, 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/writer/table/event_map.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | import 'index.dart'; 10 | 11 | /// Represents a row in the `EventMap` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.12`. 15 | /// 16 | /// The `EventMap` table has the following columns: 17 | /// - **Parent** (TypeDef Table Index) 18 | /// - **EventList** (Event Table Index) 19 | final class EventMap implements Row { 20 | const EventMap({required this.parent, required this.eventList}); 21 | 22 | final TypeDefIndex parent; 23 | final EventIndex eventList; 24 | 25 | @override 26 | void serialize(BytesBuilder buffer, TableStream stream) { 27 | buffer 28 | ..writeTableIndex(parent, stream) 29 | ..writeTableIndex(eventList, stream); 30 | } 31 | } 32 | 33 | @internal 34 | final class EventMapCompanion extends RowCompanion { 35 | const EventMapCompanion(); 36 | 37 | @override 38 | MetadataTableId get tableId => MetadataTableId.eventMap; 39 | } 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ✅ Continuous Integration 2 | description: Changes to the CI configuration files and scripts 3 | title: "ci: " 4 | labels: [ci] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Describe what changes need to be done to the CI/CD system and why. 11 | placeholder: "Provide a description of the changes that need to be done to the CI/CD system." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] All CI/CD checks are passing. 21 | - [ ] There is no drop in the test coverage percentage. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: additional-context 26 | attributes: 27 | label: Additional Context 28 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 29 | placeholder: "Provide context here." 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/style.yaml: -------------------------------------------------------------------------------- 1 | name: 🖌️ Style 2 | description: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 3 | title: "style: " 4 | labels: [style] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Clearly describe what you are looking to change and why. 11 | placeholder: "Provide a description of the style changes." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] All CI/CD checks are passing. 21 | - [ ] There is no drop in the unit or widget test coverage percentage. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: additional-context 26 | attributes: 27 | label: Additional Context 28 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 29 | placeholder: "Provide context here." 30 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | ## Description 12 | 13 | 14 | 15 | ## Related Issue 16 | 17 | 18 | 19 | ## Type of Change 20 | 21 | 22 | 23 | - [ ] 🚀 `feat` – New feature (non-breaking change that adds functionality) 24 | - [ ] 🛠️ `fix` – Bug fix (non-breaking change that fixes an issue) 25 | - [ ] ❌ `!` – Breaking change (fix or feature that causes existing functionality to change) 26 | - [ ] ⚡ `perf` – Performance improvement 27 | - [ ] 🧹 `refactor` – Code refactor (no functionality change) 28 | - [ ] 📝 `docs` – Documentation update 29 | - [ ] 🎨 `style` – Code style changes (formatting, renaming, etc.) 30 | - [ ] 🧪 `test` – Test update or addition 31 | - [ ] 🔧 `build` – Build related changes 32 | - [ ] ✅ `ci` – CI related changes 33 | - [ ] 🗑️ `chore` – Chore (maintenance, non-production code change) 34 | - [ ] ◀️ `revert` – Revert a previous commit 35 | -------------------------------------------------------------------------------- /lib/src/writer/table/property_map.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | import 'index.dart'; 10 | 11 | /// Represents a row in the `PropertyMap` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.35`. 15 | /// 16 | /// The `PropertyMap` table has the following columns: 17 | /// - **Parent** (TypeDef Table Index) 18 | /// - **PropertyList** (Property Table Index) 19 | final class PropertyMap implements Row { 20 | const PropertyMap({required this.parent, required this.propertyList}); 21 | 22 | final TypeDefIndex parent; 23 | final PropertyIndex propertyList; 24 | 25 | @override 26 | void serialize(BytesBuilder buffer, TableStream stream) { 27 | buffer 28 | ..writeTableIndex(parent, stream) 29 | ..writeTableIndex(propertyList, stream); 30 | } 31 | } 32 | 33 | @internal 34 | final class PropertyMapCompanion extends RowCompanion { 35 | const PropertyMapCompanion(); 36 | 37 | @override 38 | MetadataTableId get tableId => MetadataTableId.propertyMap; 39 | } 40 | -------------------------------------------------------------------------------- /test/reader/table/type_spec_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWinrtMetadata( 10 | version: winrtMetadataVersion, 11 | ); 12 | 13 | group('TypeSpec', () { 14 | test('IMap', () { 15 | final stringMap = index.findSingleType( 16 | 'Windows.Foundation.Collections', 17 | 'StringMap', 18 | ); 19 | final iMap = stringMap.interfaceImpls.first; 20 | final interface = iMap.decode(1); 21 | check(interface).isA(); 22 | final typeSpec = (interface as TypeDefOrRefTypeSpec).value; 23 | check(typeSpec.token).equals(0x1B000073); 24 | check(typeSpec.attributes).isEmpty(); 25 | check(typeSpec.signature).equals( 26 | const NamedClassType( 27 | TypeName( 28 | 'Windows.Foundation.Collections', 29 | 'IMap`2', 30 | generics: [StringType(), StringType()], 31 | ), 32 | ), 33 | ); 34 | }); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/chore.yaml: -------------------------------------------------------------------------------- 1 | name: 🗑️ Chore 2 | description: Other changes that don't modify source or test files 3 | title: "chore: " 4 | labels: [chore] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Clearly describe what change is needed and why. If this changes code then please use another issue type. 11 | placeholder: "Provide a description of the chore." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] No functional changes to the code. 21 | - [ ] All CI/CD checks are passing. 22 | - [ ] There is no drop in the test coverage percentage. 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: additional-context 27 | attributes: 28 | label: Additional Context 29 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 30 | placeholder: "Provide context here." 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/test.yaml: -------------------------------------------------------------------------------- 1 | name: 🧪 Test 2 | description: Adding missing tests or correcting existing tests 3 | title: "test: " 4 | labels: [test] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: List out the tests that need to be added or changed. Please also include any information as to why this was not covered in the past. 11 | placeholder: "Provide a description of the tests that need to be added or changed." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] All CI/CD checks are passing. 21 | - [ ] There is no drop in the test coverage percentage. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: additional-context 26 | attributes: 27 | label: Additional Context 28 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 29 | placeholder: "Provide context here." 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/refactor.yaml: -------------------------------------------------------------------------------- 1 | name: 🧹 Refactor 2 | description: A code change that neither fixes a bug nor adds a feature 3 | title: "refactor: " 4 | labels: [refactor] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Clearly describe what needs to be refactored and why. Please provide links to related issues (bugs or upcoming features) in order to help prioritize. 11 | placeholder: "Provide a description of the refactor." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] All CI/CD checks are passing. 21 | - [ ] There is no drop in the test coverage percentage. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: additional-context 26 | attributes: 27 | label: Additional Context 28 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 29 | placeholder: "Provide context here." 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/revert.yaml: -------------------------------------------------------------------------------- 1 | name: 🔂 Revert 2 | description: Revert a previous commit 3 | title: "revert: " 4 | labels: [revert] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Provide a link to a PR/Commit that you are looking to revert and why. 11 | placeholder: "Provide a description of and link to the commit that needs to be reverted." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] Change has been reverted. 21 | - [ ] No change in unit/widget test coverage has happened. 22 | - [ ] A new ticket is created for any follow on work that needs to happen. 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: additional-context 27 | attributes: 28 | label: Additional Context 29 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 30 | placeholder: "Provide context here." 31 | -------------------------------------------------------------------------------- /lib/src/writer/table/nested_class.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | import 'index.dart'; 10 | 11 | /// Represents a row in the `NestedClass` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.32`. 15 | /// 16 | /// The `NestedClass` table has the following columns: 17 | /// - **NestedClass** (TypeDef Table Index) 18 | /// - **EnclosingClass** (TypeDef Table Index) 19 | final class NestedClass implements Row { 20 | const NestedClass({required this.nestedClass, required this.enclosingClass}); 21 | 22 | final TypeDefIndex nestedClass; 23 | final TypeDefIndex enclosingClass; 24 | 25 | @override 26 | void serialize(BytesBuilder buffer, TableStream stream) { 27 | buffer 28 | ..writeTableIndex(nestedClass, stream) 29 | ..writeTableIndex(enclosingClass, stream); 30 | } 31 | } 32 | 33 | @internal 34 | final class NestedClassCompanion extends RowCompanion { 35 | const NestedClassCompanion(); 36 | 37 | @override 38 | MetadataTableId get tableId => MetadataTableId.nestedClass; 39 | } 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/performance.yaml: -------------------------------------------------------------------------------- 1 | name: ⚡ Performance Update 2 | description: A code change that improves performance 3 | title: "perf: " 4 | labels: [performance] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Clearly describe what code needs to be changed and what the performance impact is going to be. Bonus point's if you can tie this directly to user experience. 11 | placeholder: " Provide a description of the performance update." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] All CI/CD checks are passing. 21 | - [ ] There is no drop in the test coverage percentage. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: additional-context 26 | attributes: 27 | label: Additional Context 28 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 29 | placeholder: "Provide context here." 30 | -------------------------------------------------------------------------------- /test/reader/table/file_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:checks/checks.dart'; 4 | import 'package:test/scaffolding.dart'; 5 | import 'package:winmd/winmd.dart' show MetadataIndex, MetadataReader; 6 | import 'package:winmd/writer.dart'; 7 | 8 | void main() { 9 | test('File', () { 10 | final writer = MetadataWriter(name: 'MyMetadata') 11 | ..writeFile( 12 | name: 'foo.dll', 13 | hashValue: Uint8List.fromList([1, 2, 3]), 14 | flags: FileAttributes.containsNoMetadata, 15 | ) 16 | ..writeFile(name: 'bar.dll', hashValue: Uint8List.fromList([4, 5, 6])); 17 | final reader = MetadataReader.read(writer.toBytes()); 18 | final index = MetadataIndex.fromReader(reader); 19 | final files = index.file.toList(); 20 | check(files.length).equals(2); 21 | final [file1, file2] = files; 22 | 23 | check(file1.token).equals(0x26000000); 24 | check(file1.name).equals('foo.dll'); 25 | check(file1.hashValue.slice).deepEquals([1, 2, 3]); 26 | check(file1.flags).equals(FileAttributes.containsNoMetadata); 27 | 28 | check(file2.name).equals('bar.dll'); 29 | check(file2.hashValue.slice).deepEquals([4, 5, 6]); 30 | check(file2.flags).equals(FileAttributes.containsMetadata); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yaml: -------------------------------------------------------------------------------- 1 | name: 📝 Documentation 2 | description: Improve the documentation so all collaborators have a common understanding 3 | title: "docs: " 4 | labels: [documentation] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: Clearly describe what documentation you are looking to add or improve. 11 | placeholder: "Provide a description of the documentation changes." 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: requirements 16 | attributes: 17 | label: Requirements 18 | description: The list of requirements that need to be met in order to consider the ticket to be completed. Please be as explicit as possible. 19 | value: | 20 | - [ ] No functional changes to the code. 21 | - [ ] All CI/CD checks are passing. 22 | - [ ] There is no drop in the test coverage percentage. 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: additional-context 27 | attributes: 28 | label: Additional Context 29 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 30 | placeholder: "Provide context here." 31 | -------------------------------------------------------------------------------- /lib/src/writer/table.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'row.dart'; 4 | import 'table_stream.dart'; 5 | 6 | /// A strongly-typed table of rows used for metadata serialization. 7 | /// 8 | /// Each [Table] contains a list of [Row]s of a specific type [T], and provides 9 | /// methods to add rows and serialize the table into a binary format. 10 | final class Table { 11 | /// The list of rows in the table. 12 | final rows = []; 13 | 14 | /// Adds a single [row] to the table. 15 | void add(T row) => rows.add(row); 16 | 17 | /// Adds all rows from the given [iterable] to the table. 18 | void addAll(Iterable iterable) => rows.addAll(iterable); 19 | 20 | /// The number of rows currently in the table. 21 | int get length => rows.length; 22 | 23 | /// Whether the [rows] has no rows. 24 | bool get isEmpty => rows.isEmpty; 25 | 26 | /// Whether the [rows] has at least one row. 27 | bool get isNotEmpty => rows.isNotEmpty; 28 | 29 | /// Serializes all rows in the table into the provided [buffer], using the 30 | /// given [stream] for row-specific serialization logic. 31 | void serialize(BytesBuilder buffer, TableStream stream) { 32 | for (final row in rows) { 33 | row.serialize(buffer, stream); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/writer/table/interface_impl.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../codes.dart'; 7 | import '../helpers.dart'; 8 | import '../row.dart'; 9 | import '../table_stream.dart'; 10 | import 'index.dart'; 11 | 12 | /// Represents a row in the `InterfaceImpl` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.23`. 16 | /// 17 | /// The `InterfaceImpl` table has the following columns: 18 | /// - **Class** (TypeDef Table Index) 19 | /// - **Interface** (TypeDefOrRef Coded Index) 20 | final class InterfaceImpl implements Row { 21 | const InterfaceImpl({required this.class$, required this.interface}); 22 | 23 | final TypeDefIndex class$; 24 | final TypeDefOrRef interface; 25 | 26 | @override 27 | void serialize(BytesBuilder buffer, TableStream stream) { 28 | buffer 29 | ..writeTableIndex(class$, stream) 30 | ..writeCodedIndex(interface, stream); 31 | } 32 | } 33 | 34 | @internal 35 | final class InterfaceImplCompanion extends RowCompanion { 36 | const InterfaceImplCompanion(); 37 | 38 | @override 39 | MetadataTableId get tableId => MetadataTableId.interfaceImpl; 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/writer/table/method_spec.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../codes.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `MethodSpec` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.29`. 16 | /// 17 | /// The `MethodSpec` table has the following columns: 18 | /// - **Method** (MethodDefOrRef Coded Index) 19 | /// - **Instantiation** (Blob Heap Index) 20 | final class MethodSpec implements Row { 21 | const MethodSpec({required this.method, required this.instantiation}); 22 | 23 | final MethodDefOrRef method; 24 | final BlobIndex instantiation; 25 | 26 | @override 27 | void serialize(BytesBuilder buffer, TableStream stream) { 28 | buffer 29 | ..writeCodedIndex(method, stream) 30 | ..writeHeapIndex(instantiation, stream); 31 | } 32 | } 33 | 34 | @internal 35 | final class MethodSpecCompanion extends RowCompanion { 36 | const MethodSpecCompanion(); 37 | 38 | @override 39 | MetadataTableId get tableId => MetadataTableId.methodSpec; 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/writer/table/field_marshal.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../codes.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `FieldMarshal` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.17`. 16 | /// 17 | /// The `FieldMarshal` table has the following columns: 18 | /// - **Parent** (HasFieldMarshal Coded Index) 19 | /// - **NativeType** (Blob Heap Index) 20 | final class FieldMarshal implements Row { 21 | const FieldMarshal({required this.parent, required this.nativeType}); 22 | 23 | final HasFieldMarshal parent; 24 | final BlobIndex nativeType; 25 | 26 | @override 27 | void serialize(BytesBuilder buffer, TableStream stream) { 28 | buffer 29 | ..writeCodedIndex(parent, stream) 30 | ..writeHeapIndex(nativeType, stream); 31 | } 32 | } 33 | 34 | @internal 35 | final class FieldMarshalCompanion extends RowCompanion { 36 | const FieldMarshalCompanion(); 37 | 38 | @override 39 | MetadataTableId get tableId => MetadataTableId.fieldMarshal; 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/reader/heap/guid.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import '../../guid.dart'; 4 | import 'metadata_heap.dart'; 5 | 6 | /// Provides indexed access to the `#GUID` heap in a metadata file. 7 | final class GuidHeap extends MetadataHeap { 8 | /// Creates a [GuidHeap] from the provided binary [data]. 9 | const GuidHeap(super.data); 10 | 11 | /// The number of GUIDs stored in this heap. 12 | int get count => data.length ~/ 16; 13 | 14 | /// Enumerates all GUIDs in the heap. 15 | Iterable get guids sync* { 16 | if (data.length < 16) return; 17 | 18 | for (var i = 0; i < data.length; i += 16) { 19 | yield this[i]; 20 | } 21 | } 22 | 23 | /// Retrieves the GUID located at the specified [offset]. 24 | Guid operator [](int offset) { 25 | assert( 26 | offset >= 0 && offset < data.length, 27 | 'Offset $offset out of bounds.', 28 | ); 29 | final byteData = ByteData.sublistView(data, offset, offset + 8); 30 | final data1 = byteData.getUint32(0, Endian.little); 31 | final data2 = byteData.getUint16(4, Endian.little); 32 | final data3 = byteData.getUint16(6, Endian.little); 33 | final data4 = Uint8List.sublistView(data, offset + 8, offset + 16); 34 | return Guid(data1, data2, data3, data4.asUnmodifiableView()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/writer/table/assembly_ref_processor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | import 'index.dart'; 10 | 11 | /// Represents a row in the `AssemblyRefProcessor` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.7`. 15 | /// 16 | /// The `AssemblyRefProcessor` table has the following columns: 17 | /// - **Processor** (4-byte value) 18 | /// - **AssemblyRef** (AssemblyRef Table Index) 19 | final class AssemblyRefProcessor implements Row { 20 | const AssemblyRefProcessor({required this.assemblyRef, this.processor = 0}); 21 | 22 | final int processor; 23 | final AssemblyRefIndex assemblyRef; 24 | 25 | @override 26 | void serialize(BytesBuilder buffer, TableStream stream) { 27 | buffer 28 | ..writeUint32(processor) 29 | ..writeTableIndex(assemblyRef, stream); 30 | } 31 | } 32 | 33 | @internal 34 | final class AssemblyRefProcessorCompanion 35 | extends RowCompanion { 36 | const AssemblyRefProcessorCompanion(); 37 | 38 | @override 39 | MetadataTableId get tableId => MetadataTableId.assemblyRefProcessor; 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/reader/table/type_spec.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../../metadata_type.dart'; 5 | import '../has_custom_attributes.dart'; 6 | import '../metadata_index.dart'; 7 | import '../metadata_table.dart'; 8 | import '../row.dart'; 9 | 10 | /// Represents a row in the `TypeSpec` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.39`. 14 | /// 15 | /// The `TypeSpec` table has the following columns: 16 | /// - **Signature** (Blob Heap Index) 17 | final class TypeSpec extends Row with HasCustomAttributes { 18 | TypeSpec(super.metadataIndex, super.readerIndex, super.index); 19 | 20 | @override 21 | MetadataTable get table => MetadataTable.typeSpec; 22 | 23 | @override 24 | int get token => (MetadataTableId.typeSpec << 24) | index; 25 | 26 | /// The signature of the type specification. 27 | late final MetadataType signature = readBlob(0).readTypeCode(); 28 | } 29 | 30 | @internal 31 | final class TypeSpecCompanion extends RowCompanion { 32 | const TypeSpecCompanion(); 33 | 34 | @override 35 | TypeSpec Function(MetadataIndex, int, int) get constructor => TypeSpec.new; 36 | 37 | @override 38 | MetadataTable get table => MetadataTable.typeSpec; 39 | } 40 | -------------------------------------------------------------------------------- /test/reader/table/impl_map_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final metadata = MetadataLookup( 10 | await WindowsMetadataLoader().loadWin32Metadata( 11 | version: win32MetadataVersion, 12 | ), 13 | ); 14 | 15 | group('ImplMap', () { 16 | test('CreateWindowExW', () { 17 | final function = metadata.findFunction( 18 | 'Windows.Win32.UI.WindowsAndMessaging', 19 | 'CreateWindowExW', 20 | ); 21 | final implMap = function.implMap; 22 | check(implMap).isNotNull(); 23 | check(implMap!.token).equals(0x1C0042E2); 24 | check(implMap.flags).equals( 25 | PInvokeAttributes.noMangle | 26 | PInvokeAttributes.supportsLastError | 27 | PInvokeAttributes.callConvPlatformApi, 28 | ); 29 | check(implMap.charSet).equals(CharSet.notSpecified); 30 | check(implMap.callConv).equals(CallConv.platformApi); 31 | check(implMap.memberForwarded.name).equals('CreateWindowExW'); 32 | check(implMap.importName).equals('CreateWindowExW'); 33 | check(implMap.importScope.name).equals('USER32.dll'); 34 | }); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/reader/table/module_ref.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../has_custom_attributes.dart'; 5 | import '../metadata_index.dart'; 6 | import '../metadata_table.dart'; 7 | import '../row.dart'; 8 | 9 | /// Represents a row in the `ModuleRef` metadata table. 10 | /// 11 | /// The fields are populated by interpreting the binary metadata as specified in 12 | /// ECMA-335 `§II.22.31`. 13 | /// 14 | /// The `ModuleRef` table has the following columns: 15 | /// - **Name** (String Heap Index) 16 | final class ModuleRef extends Row with HasCustomAttributes { 17 | ModuleRef(super.metadataIndex, super.readerIndex, super.index); 18 | 19 | @override 20 | MetadataTable get table => MetadataTable.moduleRef; 21 | 22 | @override 23 | int get token => (MetadataTableId.moduleRef << 24) | index; 24 | 25 | /// The name of the module referenced by the assembly. 26 | late final String name = readString(0); 27 | 28 | @override 29 | String toString() => 'ModuleRef(name: $name)'; 30 | } 31 | 32 | @internal 33 | final class ModuleRefCompanion extends RowCompanion { 34 | const ModuleRefCompanion(); 35 | 36 | @override 37 | ModuleRef Function(MetadataIndex, int, int) get constructor => ModuleRef.new; 38 | 39 | @override 40 | MetadataTable get table => MetadataTable.moduleRef; 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/writer/table/assembly_os.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | 10 | /// Represents a row in the `AssemblyOS` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.3`. 14 | /// 15 | /// The `AssemblyOS` table has the following columns: 16 | /// - **OSPlatformID** (4-byte value) 17 | /// - **OSMajorVersion** (4-byte value) 18 | /// - **OSMinorVersion** (4-byte value) 19 | final class AssemblyOS implements Row { 20 | const AssemblyOS({ 21 | this.osPlatformId = 0, 22 | this.osMajorVersion = 0, 23 | this.osMinorVersion = 0, 24 | }); 25 | 26 | final int osPlatformId; 27 | final int osMajorVersion; 28 | final int osMinorVersion; 29 | 30 | @override 31 | void serialize(BytesBuilder buffer, TableStream stream) { 32 | buffer 33 | ..writeUint32(osPlatformId) 34 | ..writeUint32(osMajorVersion) 35 | ..writeUint32(osMinorVersion); 36 | } 37 | } 38 | 39 | @internal 40 | final class AssemblyOSCompanion extends RowCompanion { 41 | const AssemblyOSCompanion(); 42 | 43 | @override 44 | MetadataTableId get tableId => MetadataTableId.assemblyOS; 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/writer/table/class_layout.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | import 'index.dart'; 10 | 11 | /// Represents a row in the `ClassLayout` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.8`. 15 | /// 16 | /// The `ClassLayout` table has the following columns: 17 | /// - **PackingSize** (2-byte constant) 18 | /// - **ClassSize** (4-byte constant) 19 | /// - **Parent** (TypeDef Table Index) 20 | final class ClassLayout implements Row { 21 | const ClassLayout({ 22 | required this.parent, 23 | this.packingSize = 0, 24 | this.classSize = 0, 25 | }); 26 | 27 | final int packingSize; 28 | final int classSize; 29 | final TypeDefIndex parent; 30 | 31 | @override 32 | void serialize(BytesBuilder buffer, TableStream stream) { 33 | buffer 34 | ..writeUint16(packingSize) 35 | ..writeUint32(classSize) 36 | ..writeTableIndex(parent, stream); 37 | } 38 | } 39 | 40 | @internal 41 | final class ClassLayoutCompanion extends RowCompanion { 42 | const ClassLayoutCompanion(); 43 | 44 | @override 45 | MetadataTableId get tableId => MetadataTableId.classLayout; 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/writer/table/generic_param_constraint.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../codes.dart'; 7 | import '../helpers.dart'; 8 | import '../row.dart'; 9 | import '../table_stream.dart'; 10 | import 'index.dart'; 11 | 12 | /// Represents a row in the `GenericParamConstraint` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.21`. 16 | /// 17 | /// The `GenericParamConstraint` table has the following columns: 18 | /// - **Owner** (GenericParam Table Index) 19 | /// - **Constraint** (TypeDefOrRef Coded Index) 20 | final class GenericParamConstraint implements Row { 21 | const GenericParamConstraint({required this.owner, required this.constraint}); 22 | 23 | final GenericParamIndex owner; 24 | final TypeDefOrRef constraint; 25 | 26 | @override 27 | void serialize(BytesBuilder buffer, TableStream stream) { 28 | buffer 29 | ..writeTableIndex(owner, stream) 30 | ..writeCodedIndex(constraint, stream); 31 | } 32 | } 33 | 34 | @internal 35 | final class GenericParamConstraintCompanion 36 | extends RowCompanion { 37 | const GenericParamConstraintCompanion(); 38 | 39 | @override 40 | MetadataTableId get tableId => MetadataTableId.genericParamConstraint; 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/writer/table/field.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `Field` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.15`. 16 | /// 17 | /// The `Field` table has the following columns: 18 | /// - **Flags** (2-byte bitmask of FieldAttributes) 19 | /// - **Name** (String Heap Index) 20 | /// - **Signature** (Blob Heap Index) 21 | final class Field implements Row { 22 | const Field({ 23 | required this.flags, 24 | required this.name, 25 | required this.signature, 26 | }); 27 | 28 | final FieldAttributes flags; 29 | final StringIndex name; 30 | final BlobIndex signature; 31 | 32 | @override 33 | void serialize(BytesBuilder buffer, TableStream stream) { 34 | buffer 35 | ..writeUint16(flags) 36 | ..writeHeapIndex(name, stream) 37 | ..writeHeapIndex(signature, stream); 38 | } 39 | } 40 | 41 | @internal 42 | final class FieldCompanion extends RowCompanion { 43 | const FieldCompanion(); 44 | 45 | @override 46 | MetadataTableId get tableId => MetadataTableId.field; 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/writer/table/param.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `Param` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.33`. 16 | /// 17 | /// The `Param` table has the following columns: 18 | /// - **Flags** (2-byte bitmask of ParamAttributes) 19 | /// - **Sequence** (2-byte constant) 20 | /// - **Name** (String Heap Index) 21 | final class Param implements Row { 22 | const Param({ 23 | required this.sequence, 24 | required this.name, 25 | this.flags = const ParamAttributes(0), 26 | }); 27 | 28 | final ParamAttributes flags; 29 | final int sequence; 30 | final StringIndex name; 31 | 32 | @override 33 | void serialize(BytesBuilder buffer, TableStream stream) { 34 | buffer 35 | ..writeUint16(flags) 36 | ..writeUint16(sequence) 37 | ..writeHeapIndex(name, stream); 38 | } 39 | } 40 | 41 | @internal 42 | final class ParamCompanion extends RowCompanion { 43 | const ParamCompanion(); 44 | 45 | @override 46 | MetadataTableId get tableId => MetadataTableId.param; 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/reader/table/assembly_processor.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | 8 | /// Represents a row in the `AssemblyProcessor` metadata table. 9 | /// 10 | /// The fields are populated by interpreting the binary metadata as specified in 11 | /// ECMA-335 `§II.22.4`. 12 | /// 13 | /// The `AssemblyProcessor` table has the following columns: 14 | /// - **Processor** (4-byte value) 15 | final class AssemblyProcessor extends Row { 16 | AssemblyProcessor(super.metadataIndex, super.readerIndex, super.index); 17 | 18 | @override 19 | MetadataTable get table => MetadataTable.assemblyProcessor; 20 | 21 | @override 22 | int get token => (MetadataTableId.assemblyProcessor << 24) | index; 23 | 24 | /// Always returns zero, per ECMA-335 `§II.22.4`. 25 | int get processor => 0; 26 | 27 | @override 28 | String toString() => 'AssemblyProcessor(processor: $processor)'; 29 | } 30 | 31 | @internal 32 | final class AssemblyProcessorCompanion extends RowCompanion { 33 | const AssemblyProcessorCompanion(); 34 | 35 | @override 36 | AssemblyProcessor Function(MetadataIndex, int, int) get constructor => 37 | AssemblyProcessor.new; 38 | 39 | @override 40 | MetadataTable get table => MetadataTable.assemblyProcessor; 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/writer/table/file.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `File` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.19`. 16 | /// 17 | /// The `File` table has the following columns: 18 | /// - **Flags** (4-byte bitmask of FileAttributes) 19 | /// - **Name** (String Heap Index) 20 | /// - **HashValue** (Blob Heap Index) 21 | final class File implements Row { 22 | const File({ 23 | required this.name, 24 | required this.hashValue, 25 | this.flags = const FileAttributes(0), 26 | }); 27 | 28 | final FileAttributes flags; 29 | final StringIndex name; 30 | final BlobIndex hashValue; 31 | 32 | @override 33 | void serialize(BytesBuilder buffer, TableStream stream) { 34 | buffer 35 | ..writeUint32(flags) 36 | ..writeHeapIndex(name, stream) 37 | ..writeHeapIndex(hashValue, stream); 38 | } 39 | } 40 | 41 | @internal 42 | final class FileCompanion extends RowCompanion { 43 | const FileCompanion(); 44 | 45 | @override 46 | MetadataTableId get tableId => MetadataTableId.file; 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/writer/heap/guid.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import '../../guid.dart'; 4 | import 'metadata_heap.dart'; 5 | 6 | /// A metadata heap that stores GUIDs (Globally Unique Identifiers) in 16-byte 7 | /// little-endian format, as required by the ECMA-335 standard. 8 | /// 9 | /// Each GUID is inserted only once and is assigned a 1-based index. This index 10 | /// is later used in metadata tables to reference GUIDs compactly. 11 | /// 12 | /// Unlike other heaps, this one does not include a placeholder entry at 13 | /// index 0. Instead, the first inserted GUID is assigned index 1. 14 | final class GuidHeap extends MetadataHeap { 15 | /// Creates a [GuidHeap] with the given [map] and [buffer]. 16 | const GuidHeap(super.map, super.buffer); 17 | 18 | /// Creates an empty [GuidHeap] with no initial entries. 19 | /// 20 | /// GUIDs inserted into the heap are stored consecutively in 16-byte blocks. 21 | /// The index of each GUID is determined by its index in the heap, with the 22 | /// first GUID assigned index 1. 23 | GuidHeap.empty() : super({}, BytesBuilder(copy: false)); 24 | 25 | @override 26 | GuidIndex insert(Guid key) { 27 | if (map[key] case final existing?) return existing; 28 | final index = GuidIndex((buffer.length >> 4) + 1); // 1-based index 29 | map[key] = index; 30 | buffer.add(key.toBytes()); 31 | return index; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/writer/table/property.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `Property` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.34`. 16 | /// 17 | /// The `Property` table has the following columns: 18 | /// - **Flags** (2-byte bitmask of PropertyAttributes) 19 | /// - **Name** (String Heap Index) 20 | /// - **Type** (Blob Heap Index) 21 | final class Property implements Row { 22 | const Property({ 23 | required this.name, 24 | required this.type, 25 | this.flags = const PropertyAttributes(0), 26 | }); 27 | 28 | final PropertyAttributes flags; 29 | final StringIndex name; 30 | final BlobIndex type; 31 | 32 | @override 33 | void serialize(BytesBuilder buffer, TableStream stream) { 34 | buffer 35 | ..writeUint16(flags) 36 | ..writeHeapIndex(name, stream) 37 | ..writeHeapIndex(type, stream); 38 | } 39 | } 40 | 41 | @internal 42 | final class PropertyCompanion extends RowCompanion { 43 | const PropertyCompanion(); 44 | 45 | @override 46 | MetadataTableId get tableId => MetadataTableId.property; 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/type_name.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import 'common.dart'; 4 | import 'metadata_type.dart'; 5 | 6 | /// Represents a fully qualified type name within a metadata context, 7 | /// optionally including generic type parameters. 8 | final class TypeName { 9 | /// Creates a [TypeName] with the given [namespace], [name], and optional 10 | /// [generics]. 11 | const TypeName(this.namespace, this.name, {List? generics}) 12 | : _generics = generics ?? const []; 13 | 14 | /// The namespace part of the type name. 15 | final String namespace; 16 | 17 | /// The simple name part of the type. 18 | final String name; 19 | 20 | final List _generics; 21 | 22 | /// The list of generic type arguments for this type, if any. 23 | List get generics => UnmodifiableListView(_generics); 24 | 25 | @override 26 | bool operator ==(Object other) => 27 | identical(this, other) || 28 | other is TypeName && 29 | namespace == other.namespace && 30 | name == other.name && 31 | listEqual(_generics, other._generics); 32 | 33 | @override 34 | int get hashCode => Object.hash(namespace, name, Object.hashAll(_generics)); 35 | 36 | @override 37 | String toString() => 38 | 'TypeName(' 39 | '${namespace.isNotEmpty ? '$namespace.$name' : name}' 40 | '${_generics.isNotEmpty ? ', generics: $_generics' : ''})'; 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | paths: 7 | - CHANGELOG.md 8 | - pubspec.yaml 9 | workflow_dispatch: 10 | 11 | jobs: 12 | check-release: 13 | if: ${{ github.event_name != 'workflow_dispatch' }} 14 | 15 | runs-on: ubuntu-latest 16 | 17 | outputs: 18 | should_publish: ${{ steps.check_release.outputs.should_publish }} 19 | 20 | steps: 21 | - name: 📚 Git Checkout 22 | uses: actions/checkout@v6 23 | 24 | - name: 🔍 Check if There is a Release Commit 25 | id: check_release 26 | run: | 27 | latest_commit_msg=$(git log -1 --pretty=format:%s) 28 | echo "📜 Latest commit: $latest_commit_msg" 29 | 30 | if [[ "$latest_commit_msg" =~ ^chore\(release\):\ v.* ]]; then 31 | echo "🎉 Release commit detected. Proceeding with the publish step." 32 | echo "should_publish=true" >> $GITHUB_OUTPUT 33 | else 34 | echo "❌ No release commit detected. Skipping the publish step." 35 | echo "should_publish=false" >> $GITHUB_OUTPUT 36 | fi 37 | 38 | release: 39 | needs: [check-release] 40 | if: ${{ github.event_name == 'workflow_dispatch' || needs.check-release.outputs.should_publish == 'true' }} 41 | uses: halildurmus/workflows/.github/workflows/dart_release.yml@main 42 | secrets: 43 | token: ${{ secrets.PAT }} 44 | -------------------------------------------------------------------------------- /lib/src/writer/table/constant.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../bindings.dart'; 6 | import '../../common.dart'; 7 | import '../codes.dart'; 8 | import '../heap/metadata_heap.dart'; 9 | import '../helpers.dart'; 10 | import '../row.dart'; 11 | import '../table_stream.dart'; 12 | 13 | /// Represents a row in the `Constant` metadata table. 14 | /// 15 | /// The fields are populated by interpreting the binary metadata as specified in 16 | /// ECMA-335 `§II.22.9`. 17 | /// 18 | /// The `Constant` table has the following columns: 19 | /// - **Type** (1-byte, 1-byte padding zero) 20 | /// - **Parent** (HasConstant Coded Index) 21 | /// - **Value** (Blob Heap Index) 22 | final class Constant implements Row { 23 | const Constant({ 24 | required this.type, 25 | required this.parent, 26 | required this.value, 27 | }); 28 | 29 | final ElementType type; 30 | final HasConstant parent; 31 | final BlobIndex value; 32 | 33 | @override 34 | void serialize(BytesBuilder buffer, TableStream stream) { 35 | buffer 36 | ..addByte(type) 37 | ..addByte(0) 38 | ..writeCodedIndex(parent, stream) 39 | ..writeHeapIndex(value, stream); 40 | } 41 | } 42 | 43 | @internal 44 | final class ConstantCompanion extends RowCompanion { 45 | const ConstantCompanion(); 46 | 47 | @override 48 | MetadataTableId get tableId => MetadataTableId.constant; 49 | } 50 | -------------------------------------------------------------------------------- /test/reader/heap/string_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:checks/checks.dart'; 4 | import 'package:test/scaffolding.dart'; 5 | import 'package:winmd/winmd.dart'; 6 | 7 | void main() { 8 | group('StringHeap', () { 9 | test('reads null-terminated string correctly', () { 10 | // "Hello" + null terminator 11 | final bytes = Uint8List.fromList([0x00, 72, 101, 108, 108, 111, 0]); 12 | final heap = StringHeap(bytes); 13 | check(heap.count).equals(2); 14 | check(heap.strings) 15 | ..length.equals(2) 16 | ..first.isEmpty() 17 | ..last.equals('Hello'); 18 | check(heap[1]).equals('Hello'); 19 | }); 20 | 21 | test('returns empty string for only null terminator', () { 22 | final bytes = Uint8List.fromList([0]); 23 | final heap = StringHeap(bytes); 24 | check(heap[0]).isEmpty(); 25 | }); 26 | 27 | test('asserts when accessing beyond bounds', () { 28 | final bytes = Uint8List.fromList([0x00, 65, 66, 0]); // "AB\0" 29 | final heap = StringHeap(bytes); 30 | // Offset out of range 31 | check(() => heap[4]).throws().isA(); 32 | // Missing null terminator case (truncated string) 33 | final incomplete = Uint8List.fromList([0x00, 67, 68]); // "CD" (no null) 34 | final heap2 = StringHeap(incomplete); 35 | check(() => heap2[1]).throws().isA(); 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/reader/table/stand_alone_sig.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../../stand_alone_signature.dart'; 5 | import '../has_custom_attributes.dart'; 6 | import '../metadata_index.dart'; 7 | import '../metadata_table.dart'; 8 | import '../row.dart'; 9 | 10 | /// Represents a row in the `StandAloneSig` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.36`. 14 | /// 15 | /// The `StandAloneSig` table has the following columns: 16 | /// - **Signature** (Blob Heap Index) 17 | final class StandAloneSig extends Row with HasCustomAttributes { 18 | StandAloneSig(super.metadataIndex, super.readerIndex, super.index); 19 | 20 | @override 21 | MetadataTable get table => MetadataTable.standAloneSig; 22 | 23 | @override 24 | int get token => (MetadataTableId.standAloneSig << 24) | index; 25 | 26 | /// The signature of a standalone method or local variable. 27 | late final StandAloneSignature signature = readBlob( 28 | 0, 29 | ).readStandAloneSignature(); 30 | } 31 | 32 | @internal 33 | final class StandAloneSigCompanion extends RowCompanion { 34 | const StandAloneSigCompanion(); 35 | 36 | @override 37 | StandAloneSig Function(MetadataIndex, int, int) get constructor => 38 | StandAloneSig.new; 39 | 40 | @override 41 | MetadataTable get table => MetadataTable.standAloneSig; 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/writer/table/type_ref.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../codes.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `TypeRef` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.38`. 16 | /// 17 | /// The `TypeRef` table has the following columns: 18 | /// - **ResolutionScope** (ResolutionScope Coded Index) 19 | /// - **TypeName** (String Heap Index) 20 | /// - **TypeNamespace** (String Heap Index) 21 | final class TypeRef implements Row { 22 | TypeRef({ 23 | required this.resolutionScope, 24 | required this.name, 25 | this.namespace = const StringIndex(0), 26 | }); 27 | 28 | ResolutionScope resolutionScope; 29 | final StringIndex name; 30 | final StringIndex namespace; 31 | 32 | @override 33 | void serialize(BytesBuilder buffer, TableStream stream) { 34 | buffer 35 | ..writeCodedIndex(resolutionScope, stream) 36 | ..writeHeapIndex(name, stream) 37 | ..writeHeapIndex(namespace, stream); 38 | } 39 | } 40 | 41 | @internal 42 | final class TypeRefCompanion extends RowCompanion { 43 | const TypeRefCompanion(); 44 | 45 | @override 46 | MetadataTableId get tableId => MetadataTableId.typeRef; 47 | } 48 | -------------------------------------------------------------------------------- /test/reader/table/assembly_ref_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:checks/checks.dart'; 4 | import 'package:test/scaffolding.dart'; 5 | import 'package:winmd/windows_metadata.dart'; 6 | import 'package:winmd/winmd.dart'; 7 | 8 | import '../../versions.dart'; 9 | 10 | void main() async { 11 | final index = await WindowsMetadataLoader().loadWin32Metadata( 12 | version: win32MetadataVersion, 13 | ); 14 | 15 | group('AssemblyRef', () { 16 | test('netstandard', () { 17 | check(index.assemblyRef.length).isGreaterOrEqual(4); 18 | final assemblyRef = index.assemblyRef.first; 19 | check(assemblyRef.token).equals(0x23000000); 20 | check(assemblyRef.majorVersion).equals(2); 21 | check(assemblyRef.minorVersion).equals(1); 22 | check(assemblyRef.buildNumber).equals(0); 23 | check(assemblyRef.revisionNumber).equals(0); 24 | check(assemblyRef.flags).equals(const AssemblyFlags(0)); 25 | check(assemblyRef.publicKeyOrToken) 26 | .isNotNull() 27 | .has((it) => it.slice, 'slice') 28 | .deepEquals( 29 | Uint8List.fromList([ 30 | 0xCC, 0x7B, 0x13, 0xFF, 0xCD, 0x2D, 0xDD, 0x51, // 31 | ]), 32 | ); 33 | check(assemblyRef.name).equals('netstandard'); 34 | check(assemblyRef.culture).isNull(); 35 | check(assemblyRef.hashValue).isNull(); 36 | check(assemblyRef.version).equals('2.1.0.0'); 37 | }); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/writer/table/event.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../codes.dart'; 8 | import '../heap/metadata_heap.dart'; 9 | import '../helpers.dart'; 10 | import '../row.dart'; 11 | import '../table_stream.dart'; 12 | 13 | /// Represents a row in the `Event` metadata table. 14 | /// 15 | /// The fields are populated by interpreting the binary metadata as specified in 16 | /// ECMA-335 `§II.22.13`. 17 | /// 18 | /// The `Event` table has the following columns: 19 | /// - **EventFlags** (2-byte bitmask of EventAttributes) 20 | /// - **Name** (String Heap Index) 21 | /// - **EventType** (TypeDefOrRef Coded Index) 22 | final class Event implements Row { 23 | const Event({ 24 | required this.name, 25 | this.eventFlags = const EventAttributes(0), 26 | this.eventType = TypeDefOrRef.none, 27 | }); 28 | 29 | final EventAttributes eventFlags; 30 | final StringIndex name; 31 | final TypeDefOrRef eventType; 32 | 33 | @override 34 | void serialize(BytesBuilder buffer, TableStream stream) { 35 | buffer 36 | ..writeUint16(eventFlags) 37 | ..writeHeapIndex(name, stream) 38 | ..writeCodedIndex(eventType, stream); 39 | } 40 | } 41 | 42 | @internal 43 | final class EventCompanion extends RowCompanion { 44 | const EventCompanion(); 45 | 46 | @override 47 | MetadataTableId get tableId => MetadataTableId.event; 48 | } 49 | -------------------------------------------------------------------------------- /test/reader/table/event_map_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | 5 | import '../../versions.dart'; 6 | 7 | void main() async { 8 | final index = await WindowsMetadataLoader().loadWinrtMetadata( 9 | version: winrtMetadataVersion, 10 | ); 11 | 12 | group('EventMap', () { 13 | test('ISplashScreen', () { 14 | final eventMap = index.eventMap.first; 15 | check(eventMap.token).equals(0x12000000); 16 | check(eventMap.parent.namespace).equals('Windows.ApplicationModel'); 17 | check(eventMap.parent.name).equals('IPackageCatalog'); 18 | final events = eventMap.events; 19 | check(events.length).equals(5); 20 | check(events[0].name).equals('PackageInstalling'); 21 | check(events[1].name).equals('PackageStaging'); 22 | check(events[2].name).equals('PackageStatusChanged'); 23 | check(events[3].name).equals('PackageUninstalling'); 24 | check(events[4].name).equals('PackageUpdating'); 25 | }); 26 | 27 | test('IHttpDiagnosticProvider', () { 28 | final eventMap = index.eventMap.last; 29 | check(eventMap.parent.namespace).equals('Windows.Web.UI.Interop'); 30 | check(eventMap.parent.name).equals('WebViewControlProcess'); 31 | final events = eventMap.events; 32 | check(events.length).equals(1); 33 | check(events[0].name).equals('ProcessExited'); 34 | }); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/writer/table/custom_attribute.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../codes.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `CustomAttribute` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.10`. 16 | /// 17 | /// The `CustomAttribute` table has the following columns: 18 | /// - **Parent** (HasCustomAttribute Coded Index) 19 | /// - **Type** (CustomAttributeType Coded Index) 20 | /// - **Value** (Blob Heap Index) 21 | final class CustomAttribute implements Row { 22 | const CustomAttribute({ 23 | required this.parent, 24 | required this.type, 25 | required this.value, 26 | }); 27 | 28 | final HasCustomAttribute parent; 29 | final CustomAttributeType type; 30 | final BlobIndex value; 31 | 32 | @override 33 | void serialize(BytesBuilder buffer, TableStream stream) { 34 | buffer 35 | ..writeCodedIndex(parent, stream) 36 | ..writeCodedIndex(type, stream) 37 | ..writeHeapIndex(value, stream); 38 | } 39 | } 40 | 41 | @internal 42 | final class CustomAttributeCompanion extends RowCompanion { 43 | const CustomAttributeCompanion(); 44 | 45 | @override 46 | MetadataTableId get tableId => MetadataTableId.customAttribute; 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/writer/table/method_impl.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../codes.dart'; 7 | import '../helpers.dart'; 8 | import '../row.dart'; 9 | import '../table_stream.dart'; 10 | import 'index.dart'; 11 | 12 | /// Represents a row in the `MethodImpl` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.27`. 16 | /// 17 | /// The `MethodImpl` table has the following columns: 18 | /// - **Class** (TypeDef Table Index) 19 | /// - **MethodBody** (MethodDefOrRef Coded Index) 20 | /// - **MethodDeclaration** (MethodDefOrRef Coded Index) 21 | final class MethodImpl implements Row { 22 | const MethodImpl({ 23 | required this.class$, 24 | required this.methodBody, 25 | required this.methodDeclaration, 26 | }); 27 | 28 | final TypeDefIndex class$; 29 | final MethodDefOrRef methodBody; 30 | final MethodDefOrRef methodDeclaration; 31 | 32 | @override 33 | void serialize(BytesBuilder buffer, TableStream stream) { 34 | buffer 35 | ..writeTableIndex(class$, stream) 36 | ..writeCodedIndex(methodBody, stream) 37 | ..writeCodedIndex(methodDeclaration, stream); 38 | } 39 | } 40 | 41 | @internal 42 | final class MethodImplCompanion extends RowCompanion { 43 | const MethodImplCompanion(); 44 | 45 | @override 46 | MetadataTableId get tableId => MetadataTableId.methodImpl; 47 | } 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yaml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: Report a bug or unexpected behavior 3 | title: "fix: " 4 | labels: [bug] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: Thanks for taking the time to fill out this bug report! 9 | - type: textarea 10 | id: description 11 | attributes: 12 | label: Description 13 | description: A clear and concise description of what the bug is. 14 | placeholder: "Describe the bug." 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: setps-to-reproduce 19 | attributes: 20 | label: Steps To Reproduce 21 | description: A set of instructions, step by step, explaining how to reproduce the bug. 22 | placeholder: | 23 | 1. Go to '...' 24 | 2. Click on '....' 25 | 3. Scroll down to '....' 26 | 4. See error 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: expected-behavior 31 | attributes: 32 | label: Expected Behavior 33 | description: A clear and concise description of what you expected to happen. 34 | placeholder: "Describe what you expected to happen." 35 | validations: 36 | required: true 37 | - type: textarea 38 | id: additional-context 39 | attributes: 40 | label: Additional Context 41 | description: Add any other context, including links/screenshots/video recordings/etc about the problem here. 42 | placeholder: "Provide context here." 43 | -------------------------------------------------------------------------------- /lib/src/property_sig.dart: -------------------------------------------------------------------------------- 1 | import 'bindings.dart'; 2 | import 'common.dart'; 3 | import 'method_signature.dart'; 4 | 5 | /// Represents the signature of a property (essentially, the signature of its 6 | /// getter method) within metadata. 7 | final class PropertySig extends MethodSignature { 8 | /// Creates a [PropertySig] with the given [callingConvention], [returnType], 9 | /// and [types]. 10 | /// 11 | /// If [callingConvention] is not specified, it defaults to 12 | /// [CallingConvention.DEFAULT], indicating a getter method with default 13 | /// calling convention. 14 | /// 15 | /// If [types] is not specified, it defaults to an empty list, indicating that 16 | /// the getter method has no parameters. 17 | const PropertySig({ 18 | required super.returnType, 19 | super.callingConvention = CallingConvention.DEFAULT, 20 | super.types = const [], 21 | }); 22 | 23 | @override 24 | bool operator ==(Object other) => 25 | identical(this, other) || 26 | other is PropertySig && 27 | callingConvention == other.callingConvention && 28 | returnType == other.returnType && 29 | listEqual(types, other.types); 30 | 31 | @override 32 | int get hashCode => 33 | Object.hash(callingConvention, returnType, Object.hashAll(types)); 34 | 35 | @override 36 | String toString() => 37 | 'PropertySig(' 38 | 'callingConvention: 0x${callingConvention.toRadixString(16)}, ' 39 | 'returnType: $returnType, types: $types)'; 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/reader/table/field_rva.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | import 'field.dart'; 8 | 9 | /// Represents a row in the `FieldRVA` metadata table. 10 | /// 11 | /// The fields are populated by interpreting the binary metadata as specified in 12 | /// ECMA-335 `§II.22.18`. 13 | /// 14 | /// The `FieldRVA` table has the following columns: 15 | /// - **RVA** (4-byte constant) 16 | /// - **Field** (Field Table Index) 17 | final class FieldRVA extends Row { 18 | FieldRVA(super.metadataIndex, super.readerIndex, super.index); 19 | 20 | @override 21 | MetadataTable get table => MetadataTable.fieldRVA; 22 | 23 | @override 24 | int get token => (MetadataTableId.fieldRVA << 24) | index; 25 | 26 | /// The relative virtual address (RVA) within the image file where the field's 27 | /// data is stored. 28 | late final int rva = readUint32(0); 29 | 30 | /// The associated field that has an explicit RVA. 31 | late final Field field = readRow(1); 32 | 33 | @override 34 | String toString() => 'FieldRVA(rva: $rva, field: $field)'; 35 | } 36 | 37 | @internal 38 | final class FieldRVACompanion extends RowCompanion { 39 | const FieldRVACompanion(); 40 | 41 | @override 42 | FieldRVA Function(MetadataIndex, int, int) get constructor => FieldRVA.new; 43 | 44 | @override 45 | MetadataTable get table => MetadataTable.fieldRVA; 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/writer/stream.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | /// See ECMA-335 `§II.24.2.2`. 4 | final class BlobStreamHeader extends Struct { 5 | @Uint32() 6 | external int offset; 7 | 8 | @Uint32() 9 | external int size; 10 | 11 | @Array(8) 12 | external Array name; 13 | 14 | int get nextOffset => offset + size; 15 | } 16 | 17 | /// See ECMA-335 `§II.24.2.2`. 18 | final class GuidStreamHeader extends Struct { 19 | @Uint32() 20 | external int offset; 21 | 22 | @Uint32() 23 | external int size; 24 | 25 | @Array(8) 26 | external Array name; 27 | 28 | int get nextOffset => offset + size; 29 | } 30 | 31 | /// See ECMA-335 `§II.24.2.2`. 32 | final class StringStreamHeader extends Struct { 33 | @Uint32() 34 | external int offset; 35 | 36 | @Uint32() 37 | external int size; 38 | 39 | @Array(12) 40 | external Array name; 41 | 42 | int get nextOffset => offset + size; 43 | } 44 | 45 | /// See ECMA-335 `§II.24.2.2`. 46 | final class TableStreamHeader extends Struct { 47 | @Uint32() 48 | external int offset; 49 | 50 | @Uint32() 51 | external int size; 52 | 53 | @Array(4) 54 | external Array name; 55 | 56 | int get nextOffset => offset + size; 57 | } 58 | 59 | /// See ECMA-335 `§II.24.2.2`. 60 | final class UserStringStreamHeader extends Struct { 61 | @Uint32() 62 | external int offset; 63 | 64 | @Uint32() 65 | external int size; 66 | 67 | @Array(4) 68 | external Array name; 69 | 70 | int get nextOffset => offset + size; 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/writer/table/decl_security.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../codes.dart'; 8 | import '../heap/metadata_heap.dart'; 9 | import '../helpers.dart'; 10 | import '../row.dart'; 11 | import '../table_stream.dart'; 12 | 13 | /// Represents a row in the `DeclSecurity` metadata table. 14 | /// 15 | /// The fields are populated by interpreting the binary metadata as specified in 16 | /// ECMA-335 `§II.22.11`. 17 | /// 18 | /// The `DeclSecurity` table has the following columns: 19 | /// - **Action** (2-byte value, SecurityAction) 20 | /// - **Parent** (HasDeclSecurity Coded Index) 21 | /// - **PermissionSet** (Blob Heap Index) 22 | final class DeclSecurity implements Row { 23 | const DeclSecurity({ 24 | required this.action, 25 | required this.parent, 26 | required this.permissionSet, 27 | }); 28 | 29 | final SecurityAction action; 30 | final HasDeclSecurity parent; 31 | final BlobIndex permissionSet; 32 | 33 | @override 34 | void serialize(BytesBuilder buffer, TableStream stream) { 35 | buffer 36 | ..writeUint16(action.value) 37 | ..writeCodedIndex(parent, stream) 38 | ..writeHeapIndex(permissionSet, stream); 39 | } 40 | } 41 | 42 | @internal 43 | final class DeclSecurityCompanion extends RowCompanion { 44 | const DeclSecurityCompanion(); 45 | 46 | @override 47 | MetadataTableId get tableId => MetadataTableId.declSecurity; 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/reader/table/file.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../attributes.dart'; 4 | import '../../common.dart'; 5 | import '../blob.dart'; 6 | import '../has_custom_attributes.dart'; 7 | import '../metadata_index.dart'; 8 | import '../metadata_table.dart'; 9 | import '../row.dart'; 10 | 11 | /// Represents a row in the `File` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.19`. 15 | /// 16 | /// The `File` table has the following columns: 17 | /// - **Flags** (4-byte bitmask of FileAttributes) 18 | /// - **Name** (String Heap Index) 19 | /// - **HashValue** (Blob Heap Index) 20 | final class File extends Row with HasCustomAttributes { 21 | File(super.metadataIndex, super.readerIndex, super.index); 22 | 23 | @override 24 | MetadataTable get table => MetadataTable.file; 25 | 26 | @override 27 | int get token => (MetadataTableId.file << 24) | index; 28 | 29 | /// The attributes of the file. 30 | late final flags = FileAttributes(readUint32(0)); 31 | 32 | /// The name of the file. 33 | late final String name = readString(1); 34 | 35 | /// The hash value of the file. 36 | Blob get hashValue => readBlob(2); 37 | } 38 | 39 | @internal 40 | final class FileCompanion extends RowCompanion { 41 | const FileCompanion(); 42 | 43 | @override 44 | File Function(MetadataIndex, int, int) get constructor => File.new; 45 | 46 | @override 47 | MetadataTable get table => MetadataTable.file; 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/reader/table/nested_class.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | import 'type_def.dart'; 8 | 9 | /// Represents a row in the `NestedClass` metadata table. 10 | /// 11 | /// The fields are populated by interpreting the binary metadata as specified in 12 | /// ECMA-335 `§II.22.32`. 13 | /// 14 | /// The `NestedClass` table has the following columns: 15 | /// - **NestedClass** (TypeDef Table Index) 16 | /// - **EnclosingClass** (TypeDef Table Index) 17 | final class NestedClass extends Row { 18 | NestedClass(super.metadataIndex, super.readerIndex, super.index); 19 | 20 | @override 21 | MetadataTable get table => MetadataTable.nestedClass; 22 | 23 | @override 24 | int get token => (MetadataTableId.nestedClass << 24) | index; 25 | 26 | /// The [TypeDef] that is contained. 27 | late final TypeDef inner = readRow(0); 28 | 29 | /// The [TypeDef] that contains the nested type. 30 | late final TypeDef outer = readRow(1); 31 | 32 | @override 33 | String toString() => 'NestedClass(inner: $inner, outer: $outer)'; 34 | } 35 | 36 | @internal 37 | final class NestedClassCompanion extends RowCompanion { 38 | const NestedClassCompanion(); 39 | 40 | @override 41 | NestedClass Function(MetadataIndex, int, int) get constructor => 42 | NestedClass.new; 43 | 44 | @override 45 | MetadataTable get table => MetadataTable.nestedClass; 46 | } 47 | -------------------------------------------------------------------------------- /test/reader/table/method_impl_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWinrtMetadata( 10 | version: winrtMetadataVersion, 11 | ); 12 | 13 | group('MethodImpl', () { 14 | test('Calendar.Clone', () { 15 | final calendar = index.findSingleType( 16 | 'Windows.Globalization', 17 | 'Calendar', 18 | ); 19 | final clone = calendar.methodImpls.first; 20 | check(clone.token).equals(0x190022EB); 21 | check(clone.class$.namespace).equals('Windows.Globalization'); 22 | check(clone.class$.name).equals('Calendar'); 23 | check(clone.methodBody) 24 | .isA() 25 | .has((it) => it.name, 'name') 26 | .equals('Clone'); 27 | check(clone.methodDeclaration).isA(); 28 | final methodDeclaration = 29 | (clone.methodDeclaration as MethodDefOrRefMemberRef).value; 30 | check(methodDeclaration.parent).isA() 31 | ..has( 32 | (it) => it.value.namespace, 33 | 'value.namespace', 34 | ).equals('Windows.Globalization') 35 | ..has((it) => it.value.name, 'value.name').equals('ICalendar'); 36 | check(methodDeclaration.name).equals('Clone'); 37 | }); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/writer/heap/string.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'metadata_heap.dart'; 5 | 6 | /// A metadata heap that stores strings as UTF-8 encoded, null-terminated byte 7 | /// sequences, following the ECMA-335 specification. 8 | /// 9 | /// Each string is inserted only once and is assigned a zero-based index, 10 | /// which represents its byte offset in the underlying heap buffer. Strings are 11 | /// terminated with a single `0x00` byte. 12 | /// 13 | /// The empty string is always stored at index 0 and is inserted during heap 14 | /// initialization. 15 | final class StringHeap extends MetadataHeap { 16 | /// Creates a [StringHeap] with the given [map] and [buffer]. 17 | const StringHeap(super.map, super.buffer); 18 | 19 | /// Creates an empty [StringHeap] with a pre-inserted empty string at 20 | /// offset 0. 21 | /// 22 | /// This ensures that references to the empty string in metadata always 23 | /// resolve to a valid offset, as required by the metadata format. 24 | StringHeap.empty() : super({}, BytesBuilder(copy: false)) { 25 | buffer.addByte(0x00); // Add an empty string. 26 | } 27 | 28 | @override 29 | StringIndex insert(String key) { 30 | if (key.isEmpty) return const StringIndex(0); 31 | if (map[key] case final existing?) return existing; 32 | final index = StringIndex(buffer.length); 33 | map[key] = index; 34 | buffer 35 | ..add(utf8.encode(key)) 36 | ..addByte(0x00); 37 | return index; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/reader/table/event_map.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | import 'event.dart'; 8 | import 'type_def.dart'; 9 | 10 | /// Represents a row in the `EventMap` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.12`. 14 | /// 15 | /// The `EventMap` table has the following columns: 16 | /// - **Parent** (TypeDef Table Index) 17 | /// - **EventList** (Event Table Index) 18 | final class EventMap extends Row { 19 | EventMap(super.metadataIndex, super.readerIndex, super.index); 20 | 21 | @override 22 | MetadataTable get table => MetadataTable.eventMap; 23 | 24 | @override 25 | int get token => (MetadataTableId.eventMap << 24) | index; 26 | 27 | /// The [TypeDef] that owns the [events]. 28 | late final TypeDef parent = readRow(0); 29 | 30 | /// A list of [Event]s that belong to the type defined in [parent]. 31 | late final List events = getList(1).toList(growable: false); 32 | 33 | @override 34 | String toString() => 'EventMap(parent: $parent, events: $events)'; 35 | } 36 | 37 | @internal 38 | final class EventMapCompanion extends RowCompanion { 39 | const EventMapCompanion(); 40 | 41 | @override 42 | EventMap Function(MetadataIndex, int, int) get constructor => EventMap.new; 43 | 44 | @override 45 | MetadataTable get table => MetadataTable.eventMap; 46 | } 47 | -------------------------------------------------------------------------------- /test/reader/table/exported_type_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:checks/checks.dart'; 4 | import 'package:test/scaffolding.dart'; 5 | import 'package:winmd/winmd.dart' as winmd; 6 | import 'package:winmd/writer.dart'; 7 | 8 | void main() { 9 | test('ExportedType', () { 10 | final writer = MetadataWriter(name: 'MyMetadata'); 11 | final file = writer.writeFile( 12 | name: 'foo.dll', 13 | hashValue: Uint8List.fromList([1, 2, 3]), 14 | ); 15 | final typeDef = writer.writeTypeDef(namespace: 'Namespace', name: 'MyType'); 16 | writer.writeExportedType( 17 | flags: winmd.TypeAttributes.public, 18 | typeDefId: typeDef, 19 | namespace: 'Namespace', 20 | name: 'MyExportedType', 21 | implementation: Implementation.file(file), 22 | ); 23 | final reader = winmd.MetadataReader.read(writer.toBytes()); 24 | final index = winmd.MetadataIndex.fromReader(reader); 25 | final exportedType = index.exportedType.first; 26 | check(exportedType.token).equals(0x27000000); 27 | check(exportedType.flags).equals(winmd.TypeAttributes.public); 28 | check( 29 | exportedType.typeDefId, 30 | ).isNotNull().has((it) => it.name, 'name').equals('MyType'); 31 | check(exportedType.namespace).equals('Namespace'); 32 | check(exportedType.name).equals('MyExportedType'); 33 | check(exportedType.implementation) 34 | .isA() 35 | .has((it) => it.value.name, 'value.name') 36 | .equals('foo.dll'); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/writer/table/method_semantics.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../codes.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | import 'index.dart'; 12 | 13 | /// Represents a row in the `MethodSemantics` metadata table. 14 | /// 15 | /// The fields are populated by interpreting the binary metadata as specified in 16 | /// ECMA-335 `§II.22.28`. 17 | /// 18 | /// The `MethodSemantics` table has the following columns: 19 | /// - **Semantics** (2-byte bitmask of MethodSemanticsAttributes) 20 | /// - **Method** (MethodDef Table Index) 21 | /// - **Association** (HasSemantics Coded Index) 22 | final class MethodSemantics implements Row { 23 | const MethodSemantics({ 24 | required this.semantics, 25 | required this.method, 26 | required this.association, 27 | }); 28 | 29 | final MethodSemanticsAttributes semantics; 30 | final MethodDefIndex method; 31 | final HasSemantics association; 32 | 33 | @override 34 | void serialize(BytesBuilder buffer, TableStream stream) { 35 | buffer 36 | ..writeUint16(semantics) 37 | ..writeTableIndex(method, stream) 38 | ..writeCodedIndex(association, stream); 39 | } 40 | } 41 | 42 | @internal 43 | final class MethodSemanticsCompanion extends RowCompanion { 44 | const MethodSemanticsCompanion(); 45 | 46 | @override 47 | MetadataTableId get tableId => MetadataTableId.methodSemantics; 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/writer/heap/blob.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | import 'dart:typed_data'; 3 | 4 | import '../../common.dart'; 5 | import '../../compressed_integer.dart'; 6 | import 'metadata_heap.dart'; 7 | 8 | /// A metadata heap that stores binary blobs with compressed length prefixes. 9 | /// 10 | /// Each entry in the heap is a [Uint8List] of arbitrary binary data. When a 11 | /// new blob is inserted, it is prefixed with a compressed integer that encodes 12 | /// its length, as required by the ECMA-335 metadata format. 13 | /// 14 | /// The first byte of the heap is always `0x00`, representing an empty blob 15 | /// at index 0, which serves as the default or null-equivalent entry. 16 | final class BlobHeap extends MetadataHeap { 17 | /// Creates a [BlobHeap] with the given [map] and [buffer]. 18 | const BlobHeap(super.map, super.buffer); 19 | 20 | /// Creates an empty [BlobHeap] with a single `0x00` entry at offset 0. 21 | BlobHeap.empty() 22 | : super( 23 | HashMap(equals: listEqual, hashCode: Object.hashAll), 24 | BytesBuilder(copy: false), 25 | ) { 26 | buffer.addByte(0x00); // Add an empty blob. 27 | } 28 | 29 | @override 30 | BlobIndex insert(Uint8List key) { 31 | if (key.isEmpty) return const BlobIndex(0); 32 | if (map[key] case final existing?) return existing; 33 | final index = BlobIndex(buffer.length); 34 | final header = CompressedInteger.encode(key.length); 35 | map[key] = index; 36 | buffer 37 | ..add(header) 38 | ..add(key); 39 | return index; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/writer/table/assembly_ref_os.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../helpers.dart'; 7 | import '../row.dart'; 8 | import '../table_stream.dart'; 9 | import 'index.dart'; 10 | 11 | /// Represents a row in the `AssemblyRefOS` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.6`. 15 | /// 16 | /// The `AssemblyRefOS` table has the following columns: 17 | /// - **OSPlatformID** (4-byte value) 18 | /// - **OSMajorVersion** (4-byte value) 19 | /// - **OSMinorVersion** (4-byte value) 20 | /// - **AssemblyRef** (AssemblyRef Table Index) 21 | final class AssemblyRefOS implements Row { 22 | const AssemblyRefOS({ 23 | required this.assemblyRef, 24 | this.osPlatformId = 0, 25 | this.osMajorVersion = 0, 26 | this.osMinorVersion = 0, 27 | }); 28 | 29 | final int osPlatformId; 30 | final int osMajorVersion; 31 | final int osMinorVersion; 32 | final AssemblyRefIndex assemblyRef; 33 | 34 | @override 35 | void serialize(BytesBuilder buffer, TableStream stream) { 36 | buffer 37 | ..writeUint32(osPlatformId) 38 | ..writeUint32(osMajorVersion) 39 | ..writeUint32(osMinorVersion) 40 | ..writeTableIndex(assemblyRef, stream); 41 | } 42 | } 43 | 44 | @internal 45 | final class AssemblyRefOSCompanion extends RowCompanion { 46 | const AssemblyRefOSCompanion(); 47 | 48 | @override 49 | MetadataTableId get tableId => MetadataTableId.assemblyRefOS; 50 | } 51 | -------------------------------------------------------------------------------- /test/reader/table/property_map_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | 5 | import '../../versions.dart'; 6 | 7 | void main() async { 8 | final index = await WindowsMetadataLoader().loadWinrtMetadata( 9 | version: winrtMetadataVersion, 10 | ); 11 | 12 | group('PropertyMap', () { 13 | test('IImageFeatureDescriptor', () { 14 | final propertyMap = index.propertyMap.first; 15 | check(propertyMap.token).equals(0x15000000); 16 | check(propertyMap.parent.namespace).equals('Windows.AI.MachineLearning'); 17 | check(propertyMap.parent.name).equals('IImageFeatureDescriptor'); 18 | final properties = propertyMap.properties; 19 | check(properties.length).equals(4); 20 | check(properties[0].name).equals('BitmapAlphaMode'); 21 | check(properties[1].name).equals('BitmapPixelFormat'); 22 | check(properties[2].name).equals('Height'); 23 | check(properties[3].name).equals('Width'); 24 | }); 25 | 26 | test('IImageFeatureDescriptor', () { 27 | final propertyMap = index.propertyMap.last; 28 | check(propertyMap.parent.namespace).equals('Windows.Web.UI.Interop'); 29 | check(propertyMap.parent.name).equals('WebViewControlProcessOptions'); 30 | final properties = propertyMap.properties; 31 | check(properties.length).equals(2); 32 | check(properties[0].name).equals('PrivateNetworkClientServerCapability'); 33 | check(properties[1].name).equals('EnterpriseId'); 34 | }); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /example/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io' as io; 2 | 3 | import 'package:winmd/winmd.dart'; 4 | 5 | void main() { 6 | // Load WinRT metadata from a local .winmd file. 7 | const winmdPath = r'C:\WINDOWS\System32\WinMetadata\Windows.Storage.winmd'; 8 | final bytes = io.File(winmdPath).readAsBytesSync(); 9 | final reader = MetadataReader.read(bytes); 10 | final index = MetadataIndex.fromReader(reader); 11 | 12 | // Optional: Use MetadataLookup for efficient type resolution by name. 13 | // This is especially helpful when the namespace of the target type is 14 | // unknown. 15 | final metadata = MetadataLookup(index); 16 | 17 | // Lookup a WinRT class (e.g., StorageFile) and list its public methods. 18 | final storageFile = metadata.findSingleTypeByName('StorageFile'); 19 | print('WinRT class "${storageFile.name}" has the following methods:'); 20 | for (final method in storageFile.methods) { 21 | print(' ${method.name}'); 22 | } 23 | print(''); 24 | 25 | // Lookup a WinRT enum (e.g., FileAttributes) and display its members. 26 | final enumType = metadata.findSingleTypeByName('FileAttributes'); 27 | print('WinRT enum "${enumType.name}" has the following fields:'); 28 | 29 | // The first field represents the underlying integral type (e.g., Int32). 30 | final underlyingType = enumType.fields.first; 31 | print(' ${underlyingType.name} = ${underlyingType.signature}'); 32 | 33 | // Subsequent fields are named values within the enum. 34 | for (final field in enumType.fields.skip(1)) { 35 | print(' ${field.name} = ${field.constant?.value}'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Halil Durmus 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /test/reader/table/member_ref_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWin32Metadata( 10 | version: win32MetadataVersion, 11 | ); 12 | 13 | group('MemberRef', () { 14 | test('SupportedArchitectureAttribute', () { 15 | final typeDef = index 16 | .findTypes( 17 | 'Windows.Win32.Devices.DeviceAndDriverInstallation', 18 | 'SP_DEVICE_INTERFACE_DATA', 19 | ) 20 | .first; 21 | final attribute = typeDef.findAttribute('SupportedArchitectureAttribute'); 22 | check(attribute.type).isA(); 23 | final memberRef = (attribute.type as CustomAttributeTypeMemberRef).value; 24 | check(memberRef.token).equals(0x0A000013); 25 | check(memberRef.parent).isA(); 26 | check( 27 | (memberRef.parent as MemberRefParentTypeRef).value.name, 28 | ).equals('SupportedArchitectureAttribute'); 29 | check(memberRef.name).equals('.ctor'); 30 | check(memberRef.signature).equals( 31 | const MethodRefSig( 32 | callingConvention: CallingConvention.HASTHIS, 33 | types: [ 34 | NamedValueType( 35 | TypeName('Windows.Win32.Foundation.Metadata', 'Architecture'), 36 | ), 37 | ], 38 | ), 39 | ); 40 | }); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/reader/table/method_spec.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../../metadata_type.dart'; 5 | import '../codes.dart'; 6 | import '../has_custom_attributes.dart'; 7 | import '../metadata_index.dart'; 8 | import '../metadata_table.dart'; 9 | import '../row.dart'; 10 | 11 | /// Represents a row in the `MethodSpec` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.29`. 15 | /// 16 | /// The `MethodSpec` table has the following columns: 17 | /// - **Method** (MethodDefOrRef Coded Index) 18 | /// - **Instantiation** (Blob Heap Index) 19 | final class MethodSpec extends Row with HasCustomAttributes { 20 | MethodSpec(super.metadataIndex, super.readerIndex, super.index); 21 | 22 | @override 23 | MetadataTable get table => MetadataTable.methodSpec; 24 | 25 | @override 26 | int get token => (MetadataTableId.methodSpec << 24) | index; 27 | 28 | /// The method that is being specialized. 29 | late final MethodDefOrRef method = decode(0); 30 | 31 | /// The list of instantiated types for a generic method. 32 | late final List instantiation = readBlob( 33 | 1, 34 | ).readMethodSpecBlob(); 35 | } 36 | 37 | @internal 38 | final class MethodSpecCompanion extends RowCompanion { 39 | const MethodSpecCompanion(); 40 | 41 | @override 42 | MethodSpec Function(MetadataIndex, int, int) get constructor => 43 | MethodSpec.new; 44 | 45 | @override 46 | MetadataTable get table => MetadataTable.methodSpec; 47 | } 48 | -------------------------------------------------------------------------------- /PUBLISHING.md: -------------------------------------------------------------------------------- 1 | # 🚀 Publishing a New Release 2 | 3 | Follow these steps to publish a new release of the package: 4 | 5 | ## 1️⃣ Trigger the **Prepare Release** Workflow 6 | 7 | Go to the [GitHub Actions] tab and trigger the [Prepare Release] workflow. 8 | 9 | This workflow will: 10 | 11 | - 🔼 **Bump** the `version` in the `pubspec.yaml` file based on commit messages 12 | _(e.g., `6.0.0` ➡️ `6.1.0`)_. 13 | - 📝 **Update** the `CHANGELOG.md` file with the latest changes. 14 | 15 | After making these changes, the workflow will create a pull request (PR) 16 | _(e.g., **`chore(release): v6.1.0`**)_. 17 | 18 | ## 2️⃣ Review and Merge the PR 19 | 20 | Open the PR created by the workflow, review the changes, and **merge it** into 21 | the main branch. 22 | 23 | ## 3️⃣ Automatic Release and Publishing 24 | 25 | After merging the PR, the following workflows will automatically be triggered: 26 | 27 | ### 📦 **Release** Workflow 28 | 29 | - The [Release] workflow workflow will **create** a new release on GitHub with 30 | the corresponding Git tag. 31 | 32 | ### 🚀 **Publish** Workflow 33 | 34 | - After the Git tag is created, the [Publish] workflow will be triggered to 35 | **publish** the new package version to [pub.dev]. 36 | 37 | [GitHub Actions]: https://github.com/halildurmus/winmd/actions 38 | [Prepare Release]: https://github.com/halildurmus/winmd/blob/main/.github/workflows/prepare_release.yml 39 | [pub.dev]: https://pub.dev 40 | [Publish]: https://github.com/halildurmus/winmd/blob/main/.github/workflows/publish.yml 41 | [Release]: https://github.com/halildurmus/winmd/blob/main/.github/workflows/release.yml 42 | -------------------------------------------------------------------------------- /test/reader/table/property_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWinrtMetadata( 10 | version: winrtMetadataVersion, 11 | ); 12 | 13 | group('Property', () { 14 | test('StringMap.Size', () { 15 | final stringMap = index.findSingleType( 16 | 'Windows.Foundation.Collections', 17 | 'StringMap', 18 | ); 19 | final size = stringMap.findProperty('Size'); 20 | check(size.token).equals(0x17002572); 21 | check(size.flags).equals(const PropertyAttributes(0)); 22 | check(size.name).equals('Size'); 23 | check(size.signature).equals( 24 | const PropertySig( 25 | callingConvention: CallingConvention.HASTHIS, 26 | returnType: Uint32Type(), 27 | ), 28 | ); 29 | check(size.constant).isNull(); 30 | check(size.methodSemantics.length).equals(1); 31 | final getter = size.getter; 32 | check(getter).isNotNull(); 33 | check(getter!.name).equals('get_Size'); 34 | check(getter.signature).equals( 35 | const MethodSignature( 36 | callingConvention: CallingConvention.HASTHIS, 37 | returnType: Uint32Type(), 38 | ), 39 | ); 40 | check(size.setter).isNull(); 41 | check(size.parent.namespace).equals('Windows.Foundation.Collections'); 42 | check(size.parent.name).equals('StringMap'); 43 | }); 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /test/reader/table/stand_alone_sig_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart'; 4 | import 'package:winmd/writer.dart'; 5 | 6 | void main() { 7 | group('StandAloneSig', () { 8 | test('LocalVarSig', () { 9 | final writer = MetadataWriter(name: 'MyMetadata') 10 | ..writeStandAloneSig( 11 | signature: const StandAloneSignature.localVar([ 12 | Int32Type(), 13 | StringType(), 14 | ]), 15 | ); 16 | final reader = MetadataReader.read(writer.toBytes()); 17 | final index = MetadataIndex.fromReader(reader); 18 | final localVarSig = index.standAloneSig.first; 19 | check(localVarSig.token).equals(0x11000000); 20 | check( 21 | localVarSig.signature, 22 | ).equals(const LocalVarSig([Int32Type(), StringType()])); 23 | }); 24 | 25 | test('StandAloneMethodSig', () { 26 | final writer = MetadataWriter(name: 'MyMetadata') 27 | ..writeStandAloneSig( 28 | signature: const StandAloneSignature.method( 29 | returnType: BoolType(), 30 | types: [Int32Type(), StringType()], 31 | ), 32 | ); 33 | final reader = MetadataReader.read(writer.toBytes()); 34 | final index = MetadataIndex.fromReader(reader); 35 | final methodSig = index.standAloneSig.first; 36 | check(methodSig.signature).equals( 37 | const StandAloneMethodSig( 38 | returnType: BoolType(), 39 | types: [Int32Type(), StringType()], 40 | ), 41 | ); 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/reader/table/property_map.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | import 'property.dart'; 8 | import 'type_def.dart'; 9 | 10 | /// Represents a row in the `PropertyMap` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.35`. 14 | /// 15 | /// The `PropertyMap` table has the following columns: 16 | /// - **Parent** (TypeDef Table Index) 17 | /// - **PropertyList** (Property Table Index) 18 | final class PropertyMap extends Row { 19 | PropertyMap(super.metadataIndex, super.readerIndex, super.index); 20 | 21 | @override 22 | MetadataTable get table => MetadataTable.propertyMap; 23 | 24 | @override 25 | int get token => (MetadataTableId.propertyMap << 24) | index; 26 | 27 | /// The [TypeDef] that owns the [properties]. 28 | late final TypeDef parent = readRow(0); 29 | 30 | /// A list of [Property]s that belong to the type defined in [parent]. 31 | late final List properties = getList( 32 | 1, 33 | ).toList(growable: false); 34 | 35 | @override 36 | String toString() => 'PropertyMap(parent: $parent, properties: $properties)'; 37 | } 38 | 39 | @internal 40 | final class PropertyMapCompanion extends RowCompanion { 41 | const PropertyMapCompanion(); 42 | 43 | @override 44 | PropertyMap Function(MetadataIndex, int, int) get constructor => 45 | PropertyMap.new; 46 | 47 | @override 48 | MetadataTable get table => MetadataTable.propertyMap; 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/reader/table/field_marshal.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../../marshalling_descriptor.dart'; 5 | import '../codes.dart'; 6 | import '../metadata_index.dart'; 7 | import '../metadata_table.dart'; 8 | import '../row.dart'; 9 | 10 | /// Represents a row in the `FieldMarshal` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.17`. 14 | /// 15 | /// The `FieldMarshal` table has the following columns: 16 | /// - **Parent** (HasFieldMarshal Coded Index) 17 | /// - **NativeType** (Blob Heap Index) 18 | final class FieldMarshal extends Row { 19 | FieldMarshal(super.metadataIndex, super.readerIndex, super.index); 20 | 21 | @override 22 | MetadataTable get table => MetadataTable.fieldMarshal; 23 | 24 | @override 25 | int get token => (MetadataTableId.fieldMarshal << 24) | index; 26 | 27 | /// The entity being marshaled. 28 | late final HasFieldMarshal parent = decode(0); 29 | 30 | /// The native marshaling rules for the associated [parent]. 31 | late final MarshallingDescriptor nativeType = readBlob( 32 | 1, 33 | ).readMarshallingDescriptor(); 34 | 35 | @override 36 | String toString() => 'FieldMarshal(parent: $parent)'; 37 | } 38 | 39 | @internal 40 | final class FieldMarshalCompanion extends RowCompanion { 41 | const FieldMarshalCompanion(); 42 | 43 | @override 44 | FieldMarshal Function(MetadataIndex, int, int) get constructor => 45 | FieldMarshal.new; 46 | 47 | @override 48 | MetadataTable get table => MetadataTable.fieldMarshal; 49 | } 50 | -------------------------------------------------------------------------------- /test/reader/table/manifest_resource_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:checks/checks.dart'; 4 | import 'package:test/scaffolding.dart'; 5 | import 'package:winmd/winmd.dart' as winmd; 6 | import 'package:winmd/writer.dart'; 7 | 8 | void main() { 9 | group('ManifestResource', () { 10 | test('File', () { 11 | final writer = MetadataWriter(name: 'MyMetadata'); 12 | final file = writer.writeFile( 13 | name: 'foo.dll', 14 | hashValue: Uint8List.fromList([1, 2, 3]), 15 | flags: FileAttributes.containsNoMetadata, 16 | ); 17 | writer.writeManifestResource( 18 | offset: 0, 19 | flags: ManifestResourceAttributes.public, 20 | name: 'MyResource', 21 | implementation: Implementation.file(file), 22 | ); 23 | final reader = winmd.MetadataReader.read(writer.toBytes()); 24 | final index = winmd.MetadataIndex.fromReader(reader); 25 | final manifestResource = index.manifestResource.first; 26 | check(manifestResource.token).equals(0x28000000); 27 | check(manifestResource.offset).equals(0); 28 | check( 29 | manifestResource.flags, 30 | ).equals(winmd.ManifestResourceAttributes.public); 31 | check( 32 | manifestResource.resourceVisibility, 33 | ).equals(winmd.ManifestResourceVisibility.public); 34 | check(manifestResource.name).equals('MyResource'); 35 | check(manifestResource.implementation) 36 | .isNotNull() 37 | .isA() 38 | .has((it) => it.value.name, 'value.name') 39 | .equals('foo.dll'); 40 | }); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/reader/table/field_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | import 'field.dart'; 8 | 9 | /// Represents a row in the `FieldLayout` metadata table. 10 | /// 11 | /// The fields are populated by interpreting the binary metadata as specified in 12 | /// ECMA-335 `§II.22.16`. 13 | /// 14 | /// The `FieldLayout` table has the following columns: 15 | /// - **Offset** (4-byte constant) 16 | /// - **Field** (Field Table Index) 17 | final class FieldLayout extends Row { 18 | FieldLayout(super.metadataIndex, super.readerIndex, super.index); 19 | 20 | @override 21 | MetadataTable get table => MetadataTable.fieldLayout; 22 | 23 | @override 24 | int get token => (MetadataTableId.fieldLayout << 24) | index; 25 | 26 | /// The byte offset of the field within its containing type. 27 | /// 28 | /// This value specifies how many bytes from the start of the type's instance 29 | /// data the field's storage begins. 30 | late final int offset = readUint32(0); 31 | 32 | /// The [Field] to which this layout information applies. 33 | late final Field field = readRow(1); 34 | 35 | @override 36 | String toString() => 'FieldLayout(offset: $offset, field: $field)'; 37 | } 38 | 39 | @internal 40 | final class FieldLayoutCompanion extends RowCompanion { 41 | const FieldLayoutCompanion(); 42 | 43 | @override 44 | FieldLayout Function(MetadataIndex, int, int) get constructor => 45 | FieldLayout.new; 46 | 47 | @override 48 | MetadataTable get table => MetadataTable.fieldLayout; 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/writer/table/module.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../heap/metadata_heap.dart'; 7 | import '../helpers.dart'; 8 | import '../row.dart'; 9 | import '../table_stream.dart'; 10 | 11 | /// Represents a row in the `Module` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.30`. 15 | /// 16 | /// The `Module` table has the following columns: 17 | /// - **Generation** (2-byte value, reserved, MBZ) 18 | /// - **Name** (String Heap Index) 19 | /// - **Mvid** (Module Version ID) (GUID Heap Index) 20 | /// - **EncId** (GUID Heap Index, reserved, MBZ) 21 | /// - **EncBaseId** (GUID Heap Index, reserved, MBZ) 22 | final class Module implements Row { 23 | const Module({required this.name, required this.mvid}) 24 | : generation = 0, 25 | encId = const GuidIndex(0), 26 | encBaseId = const GuidIndex(0); 27 | 28 | final int generation; 29 | final StringIndex name; 30 | final GuidIndex mvid; 31 | final GuidIndex encId; 32 | final GuidIndex encBaseId; 33 | 34 | @override 35 | void serialize(BytesBuilder buffer, TableStream stream) { 36 | buffer 37 | ..writeUint16(generation) 38 | ..writeHeapIndex(name, stream) 39 | ..writeHeapIndex(mvid, stream) 40 | ..writeHeapIndex(encId, stream) 41 | ..writeHeapIndex(encBaseId, stream); 42 | } 43 | } 44 | 45 | @internal 46 | final class ModuleCompanion extends RowCompanion { 47 | const ModuleCompanion(); 48 | 49 | @override 50 | MetadataTableId get tableId => MetadataTableId.module; 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/reader/table/assembly_os.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | 8 | /// Represents a row in the `AssemblyOS` metadata table. 9 | /// 10 | /// The fields are populated by interpreting the binary metadata as specified in 11 | /// ECMA-335 `§II.22.3`. 12 | /// 13 | /// The `AssemblyOS` table has the following columns: 14 | /// - **OSPlatformID** (4-byte value) 15 | /// - **OSMajorVersion** (4-byte value) 16 | /// - **OSMinorVersion** (4-byte value) 17 | final class AssemblyOS extends Row { 18 | AssemblyOS(super.metadataIndex, super.readerIndex, super.index); 19 | 20 | @override 21 | MetadataTable get table => MetadataTable.assemblyOS; 22 | 23 | @override 24 | int get token => (MetadataTableId.assemblyOS << 24) | index; 25 | 26 | /// Always returns zero, per ECMA-335 `§II.22.3`. 27 | int get osPlatformId => 0; 28 | 29 | /// Always returns zero, per ECMA-335 `§II.22.3`. 30 | int get osMajorVersion => 0; 31 | 32 | /// Always returns zero, per ECMA-335 `§II.22.3`. 33 | int get osMinorVersion => 0; 34 | 35 | @override 36 | String toString() => 37 | 'AssemblyOS(osPlatformId: $osPlatformId, ' 38 | 'osMajorVersion: $osMajorVersion, osMinorVersion: $osMinorVersion)'; 39 | } 40 | 41 | @internal 42 | final class AssemblyOSCompanion extends RowCompanion { 43 | const AssemblyOSCompanion(); 44 | 45 | @override 46 | AssemblyOS Function(MetadataIndex, int, int) get constructor => 47 | AssemblyOS.new; 48 | 49 | @override 50 | MetadataTable get table => MetadataTable.assemblyOS; 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/reader/table/assembly_ref_processor.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | import 'assembly_ref.dart'; 8 | 9 | /// Represents a row in the `AssemblyRefProcessor` metadata table. 10 | /// 11 | /// The fields are populated by interpreting the binary metadata as specified in 12 | /// ECMA-335 `§II.22.7`. 13 | /// 14 | /// The `AssemblyRefProcessor` table has the following columns: 15 | /// - **Processor** (4-byte value) 16 | /// - **AssemblyRef** (AssemblyRef Table Index) 17 | final class AssemblyRefProcessor extends Row { 18 | AssemblyRefProcessor(super.metadataIndex, super.readerIndex, super.index); 19 | 20 | @override 21 | MetadataTable get table => MetadataTable.assemblyRefProcessor; 22 | 23 | @override 24 | int get token => (MetadataTableId.assemblyRefProcessor << 24) | index; 25 | 26 | /// Always returns zero, per ECMA-335 `§II.22.7`. 27 | int get processor => 0; 28 | 29 | /// Always returns null, per ECMA-335 `§II.22.7`. 30 | AssemblyRef? get assemblyRef => null; 31 | 32 | @override 33 | String toString() => 34 | 'AssemblyRefProcessor(processor: $processor, ' 35 | 'assemblyRef: $assemblyRef)'; 36 | } 37 | 38 | @internal 39 | final class AssemblyRefProcessorCompanion 40 | extends RowCompanion { 41 | const AssemblyRefProcessorCompanion(); 42 | 43 | @override 44 | AssemblyRefProcessor Function(MetadataIndex, int, int) get constructor => 45 | AssemblyRefProcessor.new; 46 | 47 | @override 48 | MetadataTable get table => MetadataTable.assemblyRefProcessor; 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/reader/extensions.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | /// Extension methods on [Uint8List] to simplify common metadata reading 4 | /// operations. 5 | extension Uint8ListExtension on Uint8List { 6 | /// Reads a null-terminated UTF-8 string starting at the given [offset]. 7 | /// 8 | /// Characters are read sequentially until a null terminator is encountered. 9 | /// 10 | /// The resulting string is returned. 11 | String readString(int offset) { 12 | final buffer = StringBuffer(); 13 | var i = 0; 14 | 15 | // Read the array until the null terminator is encountered. 16 | while (true) { 17 | final char = this[offset + i]; 18 | if (char == 0) return buffer.toString(); 19 | buffer.writeCharCode(char); 20 | i++; 21 | } 22 | } 23 | 24 | /// Reads an unsigned 8-bit integer at the given [offset]. 25 | @pragma('vm:prefer-inline') 26 | int readUint8(int offset) => this[offset]; 27 | 28 | /// Reads an unsigned 16-bit integer at the given [offset], assuming 29 | /// little-endian encoding. 30 | @pragma('vm:prefer-inline') 31 | int readUint16(int offset) => 32 | buffer.asByteData().getUint16(offset, Endian.little); 33 | 34 | /// Reads an unsigned 32-bit integer at the given [offset], assuming 35 | /// little-endian encoding. 36 | @pragma('vm:prefer-inline') 37 | int readUint32(int offset) => 38 | buffer.asByteData().getUint32(offset, Endian.little); 39 | 40 | /// Reads an unsigned 64-bit integer at the given [offset], assuming 41 | /// little-endian encoding. 42 | @pragma('vm:prefer-inline') 43 | int readUint64(int offset) => 44 | buffer.asByteData().getUint64(offset, Endian.little); 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/reader/table/module.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../../guid.dart'; 5 | import '../has_custom_attributes.dart'; 6 | import '../metadata_index.dart'; 7 | import '../metadata_table.dart'; 8 | import '../row.dart'; 9 | 10 | /// Represents a row in the `Module` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.30`. 14 | /// 15 | /// The `Module` table has the following columns: 16 | /// - **Generation** (2-byte value, reserved, MBZ) 17 | /// - **Name** (String Heap Index) 18 | /// - **Mvid** (Module Version ID) (GUID Heap Index) 19 | /// - **EncId** (GUID Heap Index, reserved, MBZ) 20 | /// - **EncBaseId** (GUID Heap Index, reserved, MBZ) 21 | final class Module extends Row with HasCustomAttributes { 22 | Module(super.metadataIndex, super.readerIndex, super.index); 23 | 24 | @override 25 | MetadataTable get table => MetadataTable.module; 26 | 27 | @override 28 | int get token => (MetadataTableId.module << 24) | index; 29 | 30 | /// The name of the module. 31 | late final String name = readString(1); 32 | 33 | /// The Module Version ID (MVID), a GUID that uniquely identifies the version 34 | /// of the module. 35 | late final Guid mvid = readGuid(2); 36 | 37 | @override 38 | String toString() => 'Module(name: $name, mvid: $mvid)'; 39 | } 40 | 41 | @internal 42 | final class ModuleCompanion extends RowCompanion { 43 | const ModuleCompanion(); 44 | 45 | @override 46 | Module Function(MetadataIndex, int, int) get constructor => Module.new; 47 | 48 | @override 49 | MetadataTable get table => MetadataTable.module; 50 | } 51 | -------------------------------------------------------------------------------- /test/reader/table/generic_param_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWinrtMetadata( 10 | version: winrtMetadataVersion, 11 | ); 12 | 13 | group('GenericParam', () { 14 | test('IAsyncOperationWithProgress', () { 15 | final typeDef = index.findSingleType( 16 | 'Windows.Foundation', 17 | 'IAsyncOperationWithProgress`2', 18 | ); 19 | final generics = typeDef.generics; 20 | check(generics.length).equals(2); 21 | final [tResult, tProgress] = generics; 22 | 23 | check(tResult.token).equals(0x2A000009); 24 | check(tResult.sequence).equals(0); 25 | check(tResult.flags).equals(GenericParamAttributes.none); 26 | check(tResult.variance).equals(Variance.none); 27 | check(tResult.specialConstraint).equals(SpecialConstraint.none); 28 | check(tResult.owner.name).equals('IAsyncOperationWithProgress`2'); 29 | check(tResult.name).equals('TResult'); 30 | check(tResult.constraints).isEmpty(); 31 | 32 | check(tProgress.sequence).equals(1); 33 | check(tProgress.flags).equals(GenericParamAttributes.none); 34 | check(tProgress.variance).equals(Variance.none); 35 | check(tProgress.specialConstraint).equals(SpecialConstraint.none); 36 | check(tProgress.owner.name).equals('IAsyncOperationWithProgress`2'); 37 | check(tProgress.name).equals('TProgress'); 38 | check(tProgress.constraints).isEmpty(); 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/winmd.yml: -------------------------------------------------------------------------------- 1 | name: winmd 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - .github/workflows/winmd.yml 7 | - lib/** 8 | - test/** 9 | - pubspec.yaml 10 | push: 11 | branches: [main] 12 | paths: 13 | - .github/workflows/winmd.yml 14 | - lib/** 15 | - test/** 16 | - pubspec.yaml 17 | 18 | jobs: 19 | build: 20 | strategy: 21 | # We want to see the results on stable even if main fails (and vice 22 | # versa). This prevents one failure from stopping the entire run. 23 | fail-fast: false 24 | 25 | # Free config supports up to 20 concurrent jobs, so pick a sparse matrix 26 | # that tiles the space well. 27 | matrix: 28 | include: 29 | - os: windows-2025 30 | sdk: main 31 | - os: windows-2022 32 | sdk: beta 33 | - os: macos-latest 34 | sdk: stable 35 | - os: ubuntu-latest 36 | sdk: stable 37 | - os: windows-11-arm 38 | sdk: stable 39 | - os: windows-2019 40 | sdk: stable 41 | 42 | uses: halildurmus/workflows/.github/workflows/dart.yml@main 43 | with: 44 | check_coverage: ${{ matrix.sdk == 'main' && matrix.os == 'windows-2025' }} 45 | concurrency: '1' 46 | dart_sdk: ${{ matrix.sdk }} 47 | format_directories: lib test 48 | randomize_execution_order_of_tests: true 49 | runs_on: ${{ matrix.os }} 50 | secrets: 51 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 52 | 53 | dartdoc: 54 | uses: halildurmus/workflows/.github/workflows/dartdoc.yml@main 55 | 56 | pana: 57 | uses: halildurmus/workflows/.github/workflows/pana.yml@main 58 | -------------------------------------------------------------------------------- /lib/src/writer/table/manifest_resource.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../codes.dart'; 8 | import '../heap/metadata_heap.dart'; 9 | import '../helpers.dart'; 10 | import '../row.dart'; 11 | import '../table_stream.dart'; 12 | 13 | /// Represents a row in the `ManifestResource` metadata table. 14 | /// 15 | /// The fields are populated by interpreting the binary metadata as specified in 16 | /// ECMA-335 `§II.22.24`. 17 | /// 18 | /// The `ManifestResource` table has the following columns: 19 | /// - **Offset** (4-byte constant) 20 | /// - **Flags** (4-byte bitmask of ManifestResourceAttributes) 21 | /// - **Name** (String Heap Index) 22 | /// - **Implementation** (Implementation Coded Index) 23 | final class ManifestResource implements Row { 24 | const ManifestResource({ 25 | required this.offset, 26 | required this.name, 27 | required this.implementation, 28 | this.flags = const ManifestResourceAttributes(0), 29 | }); 30 | 31 | final int offset; 32 | final ManifestResourceAttributes flags; 33 | final StringIndex name; 34 | final Implementation implementation; 35 | 36 | @override 37 | void serialize(BytesBuilder buffer, TableStream stream) { 38 | buffer 39 | ..writeUint32(offset) 40 | ..writeUint32(flags) 41 | ..writeHeapIndex(name, stream) 42 | ..writeCodedIndex(implementation, stream); 43 | } 44 | } 45 | 46 | @internal 47 | final class ManifestResourceCompanion extends RowCompanion { 48 | const ManifestResourceCompanion(); 49 | 50 | @override 51 | MetadataTableId get tableId => MetadataTableId.manifestResource; 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/reader/table/generic_param_constraint.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../codes.dart'; 5 | import '../has_custom_attributes.dart'; 6 | import '../metadata_index.dart'; 7 | import '../metadata_table.dart'; 8 | import '../row.dart'; 9 | import 'generic_param.dart'; 10 | 11 | /// Represents a row in the `GenericParamConstraint` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.21`. 15 | /// 16 | /// The `GenericParamConstraint` table has the following columns: 17 | /// - **Owner** (GenericParam Table Index) 18 | /// - **Constraint** (TypeDefOrRef Coded Index) 19 | final class GenericParamConstraint extends Row with HasCustomAttributes { 20 | GenericParamConstraint(super.metadataIndex, super.readerIndex, super.index); 21 | 22 | @override 23 | MetadataTable get table => MetadataTable.genericParamConstraint; 24 | 25 | @override 26 | int get token => (MetadataTableId.genericParamConstraint << 24) | index; 27 | 28 | /// The generic parameter to which this constraint applies. 29 | late final GenericParam owner = readRow(0); 30 | 31 | /// The type that constrains the generic parameter. 32 | late final TypeDefOrRef constraint = decode(1); 33 | } 34 | 35 | @internal 36 | final class GenericParamConstraintCompanion 37 | extends RowCompanion { 38 | const GenericParamConstraintCompanion(); 39 | 40 | @override 41 | GenericParamConstraint Function(MetadataIndex, int, int) get constructor => 42 | GenericParamConstraint.new; 43 | 44 | @override 45 | MetadataTable get table => MetadataTable.genericParamConstraint; 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/writer/table/impl_map.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../codes.dart'; 8 | import '../heap/metadata_heap.dart'; 9 | import '../helpers.dart'; 10 | import '../row.dart'; 11 | import '../table_stream.dart'; 12 | import 'index.dart'; 13 | 14 | /// Represents a row in the `ImplMap` metadata table. 15 | /// 16 | /// The fields are populated by interpreting the binary metadata as specified in 17 | /// ECMA-335 `§II.22.22`. 18 | /// 19 | /// The `ImplMap` table has the following columns: 20 | /// - **MappingFlags** (2-byte bitmask of PInvokeAttributes) 21 | /// - **MemberForwarded** (MemberForwarded Coded Index) 22 | /// - **ImportName** (String Heap Index) 23 | /// - **ImportScope** (ModuleRef Table Index) 24 | final class ImplMap implements Row { 25 | const ImplMap({ 26 | required this.memberForwarded, 27 | required this.importName, 28 | required this.importScope, 29 | this.mappingFlags = const PInvokeAttributes(0), 30 | }); 31 | 32 | final PInvokeAttributes mappingFlags; 33 | final MemberForwarded memberForwarded; 34 | final StringIndex importName; 35 | final ModuleRefIndex importScope; 36 | 37 | @override 38 | void serialize(BytesBuilder buffer, TableStream stream) { 39 | buffer 40 | ..writeUint16(mappingFlags) 41 | ..writeCodedIndex(memberForwarded, stream) 42 | ..writeHeapIndex(importName, stream) 43 | ..writeTableIndex(importScope, stream); 44 | } 45 | } 46 | 47 | @internal 48 | final class ImplMapCompanion extends RowCompanion { 49 | const ImplMapCompanion(); 50 | 51 | @override 52 | MetadataTableId get tableId => MetadataTableId.implMap; 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/writer/table/member_ref.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../common.dart'; 6 | import '../codes.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `MemberRef` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.25`. 16 | /// 17 | /// The `MemberRef` table has the following columns: 18 | /// - **Class** (MemberRefParent Coded Index) 19 | /// - **Name** (String Heap Index) 20 | /// - **Signature** (Blob Heap Index) 21 | final class MemberRef implements Row { 22 | const MemberRef({ 23 | required this.parent, 24 | required this.name, 25 | required this.signature, 26 | }); 27 | 28 | final MemberRefParent parent; 29 | final StringIndex name; 30 | final BlobIndex signature; 31 | 32 | @override 33 | void serialize(BytesBuilder buffer, TableStream stream) { 34 | buffer 35 | ..writeCodedIndex(parent, stream) 36 | ..writeHeapIndex(name, stream) 37 | ..writeHeapIndex(signature, stream); 38 | } 39 | 40 | @override 41 | bool operator ==(Object other) => 42 | identical(this, other) || 43 | other is MemberRef && 44 | parent == other.parent && 45 | name == other.name && 46 | signature == other.signature; 47 | 48 | @override 49 | int get hashCode => Object.hash(parent, name, signature); 50 | } 51 | 52 | @internal 53 | final class MemberRefCompanion extends RowCompanion { 54 | const MemberRefCompanion(); 55 | 56 | @override 57 | MetadataTableId get tableId => MetadataTableId.memberRef; 58 | } 59 | -------------------------------------------------------------------------------- /test/reader/table/generic_param_constraint_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart' as winmd; 4 | import 'package:winmd/writer.dart'; 5 | 6 | void main() { 7 | group('GenericParamConstraint', () { 8 | test('IIterable', () { 9 | final writer = MetadataWriter(name: 'MyMetadata'); 10 | final typeDef1 = writer.writeTypeDef( 11 | namespace: 'Namespace', 12 | name: 'Comparable', 13 | flags: 14 | TypeAttributes.interface | 15 | TypeAttributes.abstract | 16 | TypeAttributes.windowsRuntime, 17 | ); 18 | final typeDef2 = writer.writeTypeDef( 19 | namespace: 'Namespace', 20 | name: 'IIterable`1', 21 | flags: 22 | TypeAttributes.interface | 23 | TypeAttributes.abstract | 24 | TypeAttributes.windowsRuntime, 25 | ); 26 | writer.writeGenericParam( 27 | number: 0, 28 | owner: TypeOrMethodDef.typeDef(typeDef2), 29 | name: 'E', 30 | constraint: TypeDefOrRef.typeDef(typeDef1), 31 | ); 32 | final reader = winmd.MetadataReader.read(writer.toBytes()); 33 | final index = winmd.MetadataIndex.fromReader(reader); 34 | final genericParamConstraint = index.genericParamConstraint.first; 35 | check(genericParamConstraint.token).equals(0x2C000000); 36 | check(genericParamConstraint.owner.sequence).equals(0); 37 | check(genericParamConstraint.owner.name).equals('E'); 38 | check(genericParamConstraint.constraint) 39 | .isA() 40 | .has((it) => it.name, 'name') 41 | .equals('Comparable'); 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/reader/table/decl_security.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../attributes.dart'; 4 | import '../../common.dart'; 5 | import '../blob.dart'; 6 | import '../codes.dart'; 7 | import '../metadata_index.dart'; 8 | import '../metadata_table.dart'; 9 | import '../row.dart'; 10 | 11 | /// Represents a row in the `DeclSecurity` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.11`. 15 | /// 16 | /// The `DeclSecurity` table has the following columns: 17 | /// - **Action** (2-byte value, SecurityAction) 18 | /// - **Parent** (HasDeclSecurity Coded Index) 19 | /// - **PermissionSet** (Blob Heap Index) 20 | final class DeclSecurity extends Row { 21 | DeclSecurity(super.metadataIndex, super.readerIndex, super.index); 22 | 23 | @override 24 | MetadataTable get table => MetadataTable.declSecurity; 25 | 26 | @override 27 | int get token => (MetadataTableId.declSecurity << 24) | index; 28 | 29 | /// The security action (e.g., demand, assert, deny). 30 | late final action = SecurityAction.fromValue(readUint16(0)); 31 | 32 | /// The metadata element to which this security declaration applies. 33 | late final HasDeclSecurity parent = decode(1); 34 | 35 | /// A blob representing the serialized permission set to be applied for the 36 | /// given security action. 37 | Blob get permissionSet => readBlob(2); 38 | } 39 | 40 | @internal 41 | final class DeclSecurityCompanion extends RowCompanion { 42 | const DeclSecurityCompanion(); 43 | 44 | @override 45 | DeclSecurity Function(MetadataIndex, int, int) get constructor => 46 | DeclSecurity.new; 47 | 48 | @override 49 | MetadataTable get table => MetadataTable.declSecurity; 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/reader/table/member_ref.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../../member_ref_signature.dart'; 5 | import '../codes.dart'; 6 | import '../has_custom_attributes.dart'; 7 | import '../metadata_index.dart'; 8 | import '../metadata_table.dart'; 9 | import '../row.dart'; 10 | 11 | /// Represents a row in the `MemberRef` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.25`. 15 | /// 16 | /// The `MemberRef` table has the following columns: 17 | /// - **Class** (MemberRefParent Coded Index) 18 | /// - **Name** (String Heap Index) 19 | /// - **Signature** (Blob Heap Index) 20 | final class MemberRef extends Row with HasCustomAttributes { 21 | MemberRef(super.metadataIndex, super.readerIndex, super.index); 22 | 23 | @override 24 | MetadataTable get table => MetadataTable.memberRef; 25 | 26 | @override 27 | int get token => (MetadataTableId.memberRef << 24) | index; 28 | 29 | /// The type that contains the referenced member. 30 | late final MemberRefParent parent = decode(0); 31 | 32 | /// The name of the referenced member. 33 | late final String name = readString(1); 34 | 35 | /// The signature for the referenced member. 36 | late final MemberRefSignature signature = readBlob( 37 | 2, 38 | ).readMemberRefSignature(); 39 | 40 | @override 41 | String toString() => 'MemberRef(name: $name)'; 42 | } 43 | 44 | @internal 45 | final class MemberRefCompanion extends RowCompanion { 46 | const MemberRefCompanion(); 47 | 48 | @override 49 | MemberRef Function(MetadataIndex, int, int) get constructor => MemberRef.new; 50 | 51 | @override 52 | MetadataTable get table => MetadataTable.memberRef; 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/reader/table/type_ref.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../codes.dart'; 5 | import '../has_custom_attributes.dart'; 6 | import '../metadata_index.dart'; 7 | import '../metadata_table.dart'; 8 | import '../row.dart'; 9 | 10 | /// Represents a row in the `TypeRef` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.38`. 14 | /// 15 | /// The `TypeRef` table has the following columns: 16 | /// - **ResolutionScope** (ResolutionScope Coded Index) 17 | /// - **TypeName** (String Heap Index) 18 | /// - **TypeNamespace** (String Heap Index) 19 | final class TypeRef extends Row with HasCustomAttributes { 20 | TypeRef(super.metadataIndex, super.readerIndex, super.index); 21 | 22 | @override 23 | MetadataTable get table => MetadataTable.typeRef; 24 | 25 | @override 26 | int get token => (MetadataTableId.typeRef << 24) | index; 27 | 28 | /// The resolution scope of the referenced type, indicating the context or 29 | /// scope in which the type is defined. 30 | late final ResolutionScope scope = decode(0); 31 | 32 | /// The name of the referenced type. 33 | late final String name = readString(1); 34 | 35 | /// The namespace of the referenced type. 36 | late final String namespace = readString(2); 37 | 38 | @override 39 | String toString() => 40 | namespace.isEmpty ? 'TypeRef($name)' : 'TypeRef($namespace.$name)'; 41 | } 42 | 43 | @internal 44 | final class TypeRefCompanion extends RowCompanion { 45 | const TypeRefCompanion(); 46 | 47 | @override 48 | TypeRef Function(MetadataIndex, int, int) get constructor => TypeRef.new; 49 | 50 | @override 51 | MetadataTable get table => MetadataTable.typeRef; 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/reader/table/method_semantics.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../attributes.dart'; 4 | import '../../common.dart'; 5 | import '../codes.dart'; 6 | import '../metadata_index.dart'; 7 | import '../metadata_table.dart'; 8 | import '../row.dart'; 9 | import 'method_def.dart'; 10 | 11 | /// Represents a row in the `MethodSemantics` metadata table. 12 | /// 13 | /// The fields are populated by interpreting the binary metadata as specified in 14 | /// ECMA-335 `§II.22.28`. 15 | /// 16 | /// The `MethodSemantics` table has the following columns: 17 | /// - **Semantics** (2-byte bitmask of MethodSemanticsAttributes) 18 | /// - **Method** (MethodDef Table Index) 19 | /// - **Association** (HasSemantics Coded Index) 20 | final class MethodSemantics extends Row { 21 | MethodSemantics(super.metadataIndex, super.readerIndex, super.index); 22 | 23 | @override 24 | MetadataTable get table => MetadataTable.methodSemantics; 25 | 26 | @override 27 | int get token => (MetadataTableId.methodSemantics << 24) | index; 28 | 29 | /// The semantics of the method. 30 | late final semantics = MethodSemanticsAttributes(readUint16(0)); 31 | 32 | /// The method definition that is associated with these semantics. 33 | late final MethodDef method = readRow(1); 34 | 35 | /// The entity that is associated with the method. 36 | late final HasSemantics association = decode(2); 37 | 38 | @override 39 | String toString() => 'MethodSemantics(method: $method)'; 40 | } 41 | 42 | @internal 43 | final class MethodSemanticsCompanion extends RowCompanion { 44 | const MethodSemanticsCompanion(); 45 | 46 | @override 47 | MethodSemantics Function(MetadataIndex, int, int) get constructor => 48 | MethodSemantics.new; 49 | 50 | @override 51 | MetadataTable get table => MetadataTable.methodSemantics; 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/writer/heap/metadata_heap.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import '../helpers.dart'; 4 | 5 | /// Represents a contiguous region of memory used to store structured binary 6 | /// data, such as strings, blobs, or GUIDs in a metadata file. 7 | abstract base class MetadataHeap { 8 | /// Creates a new metadata heap from the given [map] and [buffer]. 9 | const MetadataHeap(this.map, this.buffer); 10 | 11 | /// A map from inserted values to their assigned indexes within the heap. 12 | /// 13 | /// Used to ensure that duplicate values are not inserted multiple times. 14 | final Map map; 15 | 16 | /// A buffer that holds the serialized binary contents of the heap. 17 | final BytesBuilder buffer; 18 | 19 | /// Inserts [key] into the heap if not already present, returning its index. 20 | V insert(K key); 21 | 22 | /// Returns a padded byte representation of the heap. 23 | Uint8List toBytes() => buffer.takeBytes().toBytesPadded(); 24 | } 25 | 26 | /// Represents an index into a heap. 27 | sealed class HeapIndex { 28 | const HeapIndex(this.index); 29 | 30 | /// The index into the heap. 31 | final int index; 32 | 33 | @override 34 | // ignore: no_runtimetype_tostring 35 | String toString() => '$runtimeType($index)'; 36 | } 37 | 38 | /// Represents an index into the `#Blob` heap. 39 | final class BlobIndex extends HeapIndex { 40 | const BlobIndex(super.index); 41 | } 42 | 43 | /// Represents an index into the `#GUID` heap. 44 | final class GuidIndex extends HeapIndex { 45 | const GuidIndex(super.index); 46 | } 47 | 48 | /// Represents an index into the `#Strings` heap. 49 | final class StringIndex extends HeapIndex { 50 | const StringIndex(super.index); 51 | } 52 | 53 | /// Represents an index into the `#US` heap. 54 | final class UserStringIndex extends HeapIndex { 55 | const UserStringIndex(super.index); 56 | } 57 | -------------------------------------------------------------------------------- /test/reader/table/method_semantics_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/windows_metadata.dart'; 4 | import 'package:winmd/winmd.dart'; 5 | 6 | import '../../versions.dart'; 7 | 8 | void main() async { 9 | final index = await WindowsMetadataLoader().loadWinrtMetadata( 10 | version: winrtMetadataVersion, 11 | ); 12 | 13 | group('MethodSemantics', () { 14 | test('StringMap.MapChanged Event', () { 15 | final stringMap = index.findSingleType( 16 | 'Windows.Foundation.Collections', 17 | 'StringMap', 18 | ); 19 | final mapChanged = stringMap.findEvent('MapChanged'); 20 | final methodSemantics = mapChanged.methodSemantics; 21 | check(methodSemantics.length).equals(2); 22 | final [add, remove] = methodSemantics; 23 | check(add.token).equals(0x180007DD); 24 | check(add.association).isA(); 25 | check(add.method.name).equals('add_MapChanged'); 26 | check(add.semantics).equals(MethodSemanticsAttributes.addOn); 27 | check(remove.association).isA(); 28 | check(remove.method.name).equals('remove_MapChanged'); 29 | check(remove.semantics).equals(MethodSemanticsAttributes.removeOn); 30 | }); 31 | 32 | test('StringMap.Size Property', () { 33 | final stringMap = index.findSingleType( 34 | 'Windows.Foundation.Collections', 35 | 'StringMap', 36 | ); 37 | final size = stringMap.findProperty('Size'); 38 | final methodSemantics = size.methodSemantics; 39 | check(methodSemantics.length).equals(1); 40 | final [get] = methodSemantics; 41 | check(get.association).isA(); 42 | check(get.method.name).equals('get_Size'); 43 | check(get.semantics).equals(MethodSemanticsAttributes.getter); 44 | }); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/reader/table/class_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | import 'type_def.dart'; 8 | 9 | /// Represents a row in the `ClassLayout` metadata table. 10 | /// 11 | /// The fields are populated by interpreting the binary metadata as specified in 12 | /// ECMA-335 `§II.22.8`. 13 | /// 14 | /// The `ClassLayout` table has the following columns: 15 | /// - **PackingSize** (2-byte constant) 16 | /// - **ClassSize** (4-byte constant) 17 | /// - **Parent** (TypeDef Table Index) 18 | final class ClassLayout extends Row { 19 | ClassLayout(super.metadataIndex, super.readerIndex, super.index); 20 | 21 | @override 22 | MetadataTable get table => MetadataTable.classLayout; 23 | 24 | @override 25 | int get token => (MetadataTableId.classLayout << 24) | index; 26 | 27 | /// The packing size (byte alignment) for the class fields. 28 | /// 29 | /// Common values are 1, 2, 4, 8, or 16 bytes. This influences how fields are 30 | /// laid out in memory. If not specified, a platform default is assumed. 31 | late final int packingSize = readUint16(0); 32 | 33 | /// The total size, in bytes, of an instance of the class. 34 | late final int classSize = readUint32(1); 35 | 36 | /// The [TypeDef] representing the class whose layout is specified. 37 | late final TypeDef parent = readRow(2); 38 | 39 | @override 40 | String toString() => 41 | 'ClassLayout(packingSize: $packingSize, classSize: $classSize)'; 42 | } 43 | 44 | @internal 45 | final class ClassLayoutCompanion extends RowCompanion { 46 | const ClassLayoutCompanion(); 47 | 48 | @override 49 | ClassLayout Function(MetadataIndex, int, int) get constructor => 50 | ClassLayout.new; 51 | 52 | @override 53 | MetadataTable get table => MetadataTable.classLayout; 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/writer/table/exported_type.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../codes.dart'; 8 | import '../heap/metadata_heap.dart'; 9 | import '../helpers.dart'; 10 | import '../row.dart'; 11 | import '../table_stream.dart'; 12 | import 'index.dart'; 13 | 14 | /// Represents a row in the `ExportedType` metadata table. 15 | /// 16 | /// The fields are populated by interpreting the binary metadata as specified in 17 | /// ECMA-335 `§II.22.14`. 18 | /// 19 | /// The `ExportedType` table has the following columns: 20 | /// - **Flags** (4-byte bitmask of TypeAttributes) 21 | /// - **TypeDefId** (TypeDef Table Index) 22 | /// - **TypeName** (String Heap Index) 23 | /// - **TypeNamespace** (String Heap Index) 24 | /// - **Implementation** (Implementation Coded Index) 25 | final class ExportedType implements Row { 26 | const ExportedType({ 27 | required this.typeDefId, 28 | required this.typeName, 29 | required this.implementation, 30 | this.flags = const TypeAttributes(0), 31 | this.typeNamespace = const StringIndex(0), 32 | }); 33 | 34 | final TypeAttributes flags; 35 | final TypeDefIndex typeDefId; 36 | final StringIndex typeName; 37 | final StringIndex typeNamespace; 38 | final Implementation implementation; 39 | 40 | @override 41 | void serialize(BytesBuilder buffer, TableStream stream) { 42 | buffer 43 | ..writeUint32(flags) 44 | ..writeTableIndex(typeDefId, stream) 45 | ..writeHeapIndex(typeName, stream) 46 | ..writeHeapIndex(typeNamespace, stream) 47 | ..writeCodedIndex(implementation, stream); 48 | } 49 | } 50 | 51 | @internal 52 | final class ExportedTypeCompanion extends RowCompanion { 53 | const ExportedTypeCompanion(); 54 | 55 | @override 56 | MetadataTableId get tableId => MetadataTableId.exportedType; 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/method_signature.dart: -------------------------------------------------------------------------------- 1 | import 'bindings.dart'; 2 | import 'common.dart'; 3 | import 'metadata_type.dart'; 4 | 5 | /// Represents the signature of a method within metadata. 6 | base class MethodSignature { 7 | /// Creates a [MethodSignature] with the given [callingConvention], 8 | /// [returnType], and [types]. 9 | /// 10 | /// If [callingConvention] is not specified, it defaults to 11 | /// [CallingConvention.DEFAULT], indicating a method with default calling 12 | /// convention. 13 | /// 14 | /// If [returnType] is not specified, it defaults to [VoidType], representing 15 | /// a method that returns no value. 16 | /// 17 | /// If [types] is not specified, it defaults to an empty list, indicating that 18 | /// the method has no parameters. 19 | const MethodSignature({ 20 | this.callingConvention = CallingConvention.DEFAULT, 21 | this.returnType = const VoidType(), 22 | this.types = const [], 23 | }); 24 | 25 | /// The method's calling convention. 26 | final CallingConvention callingConvention; 27 | 28 | /// The return type of the method. 29 | final MetadataType returnType; 30 | 31 | /// The list of parameter types for the method. 32 | /// 33 | /// Parameters are represented in the order they are declared. 34 | final List types; 35 | 36 | @override 37 | bool operator ==(Object other) => 38 | identical(this, other) || 39 | other is MethodSignature && 40 | callingConvention == other.callingConvention && 41 | returnType == other.returnType && 42 | listEqual(types, other.types); 43 | 44 | @override 45 | int get hashCode => 46 | Object.hash(callingConvention, returnType, Object.hashAll(types)); 47 | 48 | @override 49 | String toString() => 50 | 'MethodSignature(' 51 | 'callingConvention: 0x${callingConvention.toRadixString(16)}, ' 52 | 'returnType: $returnType, types: $types)'; 53 | } 54 | -------------------------------------------------------------------------------- /test/reader/table/decl_security_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:checks/checks.dart'; 4 | import 'package:test/scaffolding.dart'; 5 | import 'package:winmd/winmd.dart' as winmd; 6 | import 'package:winmd/writer.dart'; 7 | 8 | void main() { 9 | test('DeclSecurity', () { 10 | final writer = MetadataWriter(name: 'MyMetadata'); 11 | final typeDef = writer.writeTypeDef(namespace: 'Namespace', name: 'Name'); 12 | const attribute = 'System.Security.Permissions.SecurityAction'; 13 | const enumName = 'System.Security.Permissions.SecurityPermissionFlag'; 14 | const propertyName = 'Flags'; 15 | final permissionSet = Uint8List.fromList([ 16 | 0x2E, // '.' 17 | 1, // Number of attributes 18 | ...winmd.CompressedInteger.encode(attribute.length), 19 | ...attribute.codeUnits, 20 | 1, // NumNamed 21 | winmd.ELEMENT_TYPE_PROPERTY, 22 | winmd.ELEMENT_TYPE_ENUM, 23 | ...winmd.CompressedInteger.encode(propertyName.length), 24 | ...propertyName.codeUnits, 25 | ...winmd.CompressedInteger.encode(enumName.length), 26 | ...enumName.codeUnits, 27 | 0x8, 0, 0, 0, // enum value = Execution (0x8) 28 | ]); 29 | writer.writeDeclSecurity( 30 | action: SecurityAction.demand, 31 | parent: HasDeclSecurity.typeDef(typeDef), 32 | permissionSet: permissionSet, 33 | ); 34 | final reader = winmd.MetadataReader.read(writer.toBytes()); 35 | final index = winmd.MetadataIndex.fromReader(reader); 36 | final declSecurity = index.declSecurity.first; 37 | check(declSecurity.token).equals(0x0E000000); 38 | check(declSecurity.action).equals(winmd.SecurityAction.demand); 39 | check(declSecurity.parent) 40 | .isA() 41 | .has((it) => it.value.name, 'value.name') 42 | .equals('Name'); 43 | check(declSecurity.permissionSet.slice).deepEquals(permissionSet); 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/windows_metadata/package.dart: -------------------------------------------------------------------------------- 1 | /// Represents a NuGet package that provides Windows Metadata (`.winmd`) files. 2 | /// 3 | /// These metadata packages describe APIs exposed by Windows components, 4 | /// including the Windows Driver Kit (WDK), traditional Win32 APIs, and the 5 | /// Windows Runtime (WinRT). 6 | enum WindowsMetadataPackage { 7 | /// The [Windows Driver Kit (WDK) metadata]( 8 | /// https://www.nuget.org/packages/Microsoft.Windows.WDK.Win32Metadata/) 9 | /// package. 10 | wdk._('microsoft.windows.wdk.win32metadata', 'Windows.Wdk.winmd'), 11 | 12 | /// The [Windows API metadata]( 13 | /// https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata/) 14 | /// package. 15 | win32._('microsoft.windows.sdk.win32metadata', 'Windows.Win32.winmd'), 16 | 17 | /// The [Windows Runtime (WinRT) metadata]( 18 | /// https://www.nuget.org/packages/Microsoft.Windows.SDK.Contracts/) 19 | /// package. 20 | winrt._('microsoft.windows.sdk.contracts', 'Windows.winmd'); 21 | 22 | const WindowsMetadataPackage._(this.packageId, this.assetName); 23 | 24 | /// Retrieves a [WindowsMetadataPackage] based on the given NuGet [packageId]. 25 | factory WindowsMetadataPackage.fromPackageId(String packageId) => 26 | switch (packageId) { 27 | 'microsoft.windows.wdk.win32metadata' => wdk, 28 | 'microsoft.windows.sdk.win32metadata' => win32, 29 | 'microsoft.windows.sdk.contracts' => winrt, 30 | _ => throw ArgumentError.value( 31 | packageId, 32 | 'packageId', 33 | 'Unknown package ID.', 34 | ), 35 | }; 36 | 37 | /// The unique identifier of the NuGet package (e.g., 38 | /// `microsoft.windows.sdk.win32metadata`). 39 | final String packageId; 40 | 41 | /// The name of the asset file within the NuGet package (e.g., 42 | /// `Windows.Win32.winmd`). 43 | final String assetName; 44 | 45 | @override 46 | String toString() => packageId; 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/writer/table/method_def.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | import 'index.dart'; 12 | 13 | /// Represents a row in the `MethodDef` metadata table. 14 | /// 15 | /// The fields are populated by interpreting the binary metadata as specified in 16 | /// ECMA-335 `§II.22.26`. 17 | /// 18 | /// The `MethodDef` table has the following columns: 19 | /// - **RVA** (4-byte constant) 20 | /// - **ImplFlags** (2-byte bitmask of MethodImplAtttributes) 21 | /// - **Flags** (2-byte bitmask of MethodAttributes) 22 | /// - **Name** (String Heap Index) 23 | /// - **Signature** (Blob Heap Index) 24 | /// - **ParamList** (Param Table Index) 25 | final class MethodDef implements Row { 26 | const MethodDef({ 27 | required this.rva, 28 | required this.name, 29 | required this.signature, 30 | required this.paramList, 31 | this.implFlags = const MethodImplAttributes(0), 32 | this.flags = const MethodAttributes(0), 33 | }); 34 | 35 | final int rva; 36 | final MethodImplAttributes implFlags; 37 | final MethodAttributes flags; 38 | final StringIndex name; 39 | final BlobIndex signature; 40 | final ParamIndex paramList; 41 | 42 | @override 43 | void serialize(BytesBuilder buffer, TableStream stream) { 44 | buffer 45 | ..writeUint32(rva) 46 | ..writeUint16(implFlags) 47 | ..writeUint16(flags) 48 | ..writeHeapIndex(name, stream) 49 | ..writeHeapIndex(signature, stream) 50 | ..writeTableIndex(paramList, stream); 51 | } 52 | } 53 | 54 | @internal 55 | final class MethodDefCompanion extends RowCompanion { 56 | const MethodDefCompanion(); 57 | 58 | @override 59 | MetadataTableId get tableId => MetadataTableId.methodDef; 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/reader/table/assembly_ref_os.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../metadata_index.dart'; 5 | import '../metadata_table.dart'; 6 | import '../row.dart'; 7 | import 'assembly_ref.dart'; 8 | 9 | /// Represents a row in the `AssemblyRefOS` metadata table. 10 | /// 11 | /// The fields are populated by interpreting the binary metadata as specified in 12 | /// ECMA-335 `§II.22.6`. 13 | /// 14 | /// The `AssemblyRefOS` table has the following columns: 15 | /// - **OSPlatformID** (4-byte value) 16 | /// - **OSMajorVersion** (4-byte value) 17 | /// - **OSMinorVersion** (4-byte value) 18 | /// - **AssemblyRef** (AssemblyRef Table Index) 19 | final class AssemblyRefOS extends Row { 20 | AssemblyRefOS(super.metadataIndex, super.readerIndex, super.index); 21 | 22 | @override 23 | MetadataTable get table => MetadataTable.assemblyRefOS; 24 | 25 | @override 26 | int get token => (MetadataTableId.assemblyRefOS << 24) | index; 27 | 28 | /// Always returns zero, per ECMA-335 `§II.22.6`. 29 | int get osPlatformId => 0; 30 | 31 | /// Always returns zero, per ECMA-335 `§II.22.6`. 32 | int get osMajorVersion => 0; 33 | 34 | /// Always returns zero, per ECMA-335 `§II.22.6`. 35 | int get osMinorVersion => 0; 36 | 37 | /// Always returns null, per ECMA-335 `§II.22.6`. 38 | AssemblyRef? get assemblyRef => null; 39 | 40 | @override 41 | String toString() => 42 | 'AssemblyRefOS(osPlatformId: $osPlatformId, ' 43 | 'osMajorVersion: $osMajorVersion, osMinorVersion: $osMinorVersion, ' 44 | 'assemblyRef: $assemblyRef)'; 45 | } 46 | 47 | @internal 48 | final class AssemblyRefOSCompanion extends RowCompanion { 49 | const AssemblyRefOSCompanion(); 50 | 51 | @override 52 | AssemblyRefOS Function(MetadataIndex, int, int) get constructor => 53 | AssemblyRefOS.new; 54 | 55 | @override 56 | MetadataTable get table => MetadataTable.assemblyRefOS; 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/reader/table/method_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../codes.dart'; 5 | import '../metadata_index.dart'; 6 | import '../metadata_table.dart'; 7 | import '../row.dart'; 8 | import 'type_def.dart'; 9 | 10 | /// Represents a row in the `MethodImpl` metadata table. 11 | /// 12 | /// The fields are populated by interpreting the binary metadata as specified in 13 | /// ECMA-335 `§II.22.27`. 14 | /// 15 | /// The `MethodImpl` table has the following columns: 16 | /// - **Class** (TypeDef Table Index) 17 | /// - **MethodBody** (MethodDefOrRef Coded Index) 18 | /// - **MethodDeclaration** (MethodDefOrRef Coded Index) 19 | final class MethodImpl extends Row { 20 | MethodImpl(super.metadataIndex, super.readerIndex, super.index); 21 | 22 | @override 23 | MetadataTable get table => MetadataTable.methodImpl; 24 | 25 | @override 26 | int get token => (MetadataTableId.methodImpl << 24) | index; 27 | 28 | /// The [TypeDef] that contains the method implementation. 29 | late final TypeDef class$ = readRow(0); 30 | 31 | /// The method body, which could either be a method definition or a reference 32 | /// to a method. 33 | late final MethodDefOrRef methodBody = decode(1); 34 | 35 | /// The method declaration, referring to the method's signature and metadata. 36 | late final MethodDefOrRef methodDeclaration = decode(2); 37 | 38 | @override 39 | String toString() => 40 | 'MethodImpl(' 41 | 'class\$: ${class$}, ' 42 | 'methodBody: $methodBody, ' 43 | 'methodDeclaration: $methodDeclaration)'; 44 | } 45 | 46 | @internal 47 | final class MethodImplCompanion extends RowCompanion { 48 | const MethodImplCompanion(); 49 | 50 | @override 51 | MethodImpl Function(MetadataIndex, int, int) get constructor => 52 | MethodImpl.new; 53 | 54 | @override 55 | MetadataTable get table => MetadataTable.methodImpl; 56 | } 57 | -------------------------------------------------------------------------------- /test/method_signature_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart'; 4 | 5 | void main() { 6 | group('MethodDefSig', () { 7 | test('default constructor uses expected values', () { 8 | const sig = MethodSignature(); 9 | check(sig.callingConvention).equals(CallingConvention.DEFAULT); 10 | check(sig.returnType).equals(const VoidType()); 11 | check(sig.types).deepEquals(const []); 12 | check(sig.toString()).equals( 13 | 'MethodSignature(callingConvention: 0x0, returnType: VoidType, ' 14 | 'types: [])', 15 | ); 16 | }); 17 | 18 | test('equality: equal for same flags, return type, and types', () { 19 | const sig1 = MethodSignature( 20 | callingConvention: CallingConvention.HASTHIS, 21 | returnType: BoolType(), 22 | types: [Int32Type()], 23 | ); 24 | const sig2 = MethodSignature( 25 | callingConvention: CallingConvention.HASTHIS, 26 | returnType: BoolType(), 27 | types: [Int32Type()], 28 | ); 29 | check(sig1).equals(sig2); 30 | }); 31 | 32 | test('equality: instances with different flags are not equal', () { 33 | const sig1 = MethodSignature(); 34 | const sig2 = MethodSignature( 35 | callingConvention: CallingConvention.HASTHIS, 36 | ); 37 | check(sig1).not((it) => it.equals(sig2)); 38 | }); 39 | 40 | test('equality: instances with different return types are not equal', () { 41 | const sig1 = MethodSignature(); 42 | const sig2 = MethodSignature(returnType: BoolType()); 43 | check(sig1).not((it) => it.equals(sig2)); 44 | }); 45 | 46 | test('equality: instances with different types are not equal', () { 47 | const sig1 = MethodSignature(types: [BoolType()]); 48 | const sig2 = MethodSignature(types: [StringType()]); 49 | check(sig1).not((it) => it.equals(sig2)); 50 | }); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/writer/table/type_def.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../codes.dart'; 8 | import '../heap/metadata_heap.dart'; 9 | import '../helpers.dart'; 10 | import '../row.dart'; 11 | import '../table_stream.dart'; 12 | import 'index.dart'; 13 | 14 | /// Represents a row in the `TypeDef` metadata table. 15 | /// 16 | /// The fields are populated by interpreting the binary metadata as specified in 17 | /// ECMA-335 `§II.22.37`. 18 | /// 19 | /// The `TypeDef` table has the following columns: 20 | /// - **Flags** (4-byte bitmask of TypeAttributes) 21 | /// - **TypeName** (String Heap Index) 22 | /// - **TypeNamespace** (String Heap Index) 23 | /// - **Extends** (TypeDefOrRef Coded Index) 24 | /// - **FieldList** (Field Table Index) 25 | /// - **MethodList** (MethodDef Table Index) 26 | final class TypeDef implements Row { 27 | const TypeDef({ 28 | required this.name, 29 | required this.extends$, 30 | required this.fieldList, 31 | required this.methodList, 32 | this.flags = const TypeAttributes(0), 33 | this.namespace = const StringIndex(0), 34 | }); 35 | 36 | final TypeAttributes flags; 37 | final StringIndex name; 38 | final StringIndex namespace; 39 | final TypeDefOrRef extends$; 40 | final FieldIndex fieldList; 41 | final MethodDefIndex methodList; 42 | 43 | @override 44 | void serialize(BytesBuilder buffer, TableStream stream) { 45 | buffer 46 | ..writeUint32(flags) 47 | ..writeHeapIndex(name, stream) 48 | ..writeHeapIndex(namespace, stream) 49 | ..writeCodedIndex(extends$, stream) 50 | ..writeTableIndex(fieldList, stream) 51 | ..writeTableIndex(methodList, stream); 52 | } 53 | } 54 | 55 | @internal 56 | final class TypeDefCompanion extends RowCompanion { 57 | const TypeDefCompanion(); 58 | 59 | @override 60 | MetadataTableId get tableId => MetadataTableId.typeDef; 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/reader/has_custom_attributes.dart: -------------------------------------------------------------------------------- 1 | import '../attribute_arg.dart'; 2 | import '../exception.dart'; 3 | import 'codes.dart'; 4 | import 'row.dart'; 5 | import 'table/custom_attribute.dart'; 6 | 7 | /// Provides support for querying custom attributes attached to a metadata 8 | /// [Row]. 9 | base mixin HasCustomAttributes on Row { 10 | /// Enumerates all [CustomAttribute]s associated with this row. 11 | late final List attributes = getEqualRange( 12 | 0, 13 | HasCustomAttribute(this).encode(), 14 | ).toList(growable: false); 15 | 16 | /// Retrieves the first argument of the custom attribute with the given 17 | /// [attributeName], if it is a string. 18 | /// 19 | /// Returns the string value if the attribute exists and has a single string 20 | /// argument. Otherwise, returns `null`. 21 | String? attributeAsString(String attributeName) { 22 | final attr = tryFindAttribute(attributeName); 23 | if (attr?.fixedArgs case [final arg]) return arg.valueAsString; 24 | if (attr?.namedArgs case [ 25 | final arg, 26 | ] when attr?.fixedArgs.isEmpty ?? false) { 27 | return arg.valueAsString; 28 | } 29 | return null; 30 | } 31 | 32 | /// Finds the first [CustomAttribute] with the specified [name]. 33 | /// 34 | /// Throws a [WinmdException] if no matching attribute is found. 35 | CustomAttribute findAttribute(String name) => 36 | attributes.where((attr) => attr.name == name).firstOrNull ?? 37 | (throw WinmdException('Attribute not found: $name')); 38 | 39 | /// Attempts to find the first [CustomAttribute] with the specified [name]. 40 | /// 41 | /// Returns `null` if no matching attribute is found. 42 | CustomAttribute? tryFindAttribute(String name) => 43 | attributes.where((attr) => attr.name == name).firstOrNull; 44 | 45 | /// Determines whether an attribute with the specified [name] is attached to 46 | /// this row. 47 | bool hasAttribute(String name) => tryFindAttribute(name) != null; 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/writer/table/generic_param.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../codes.dart'; 8 | import '../heap/metadata_heap.dart'; 9 | import '../helpers.dart'; 10 | import '../row.dart'; 11 | import '../table_stream.dart'; 12 | 13 | /// Represents a row in the `GenericParam` metadata table. 14 | /// 15 | /// The fields are populated by interpreting the binary metadata as specified in 16 | /// ECMA-335 `§II.22.20`. 17 | /// 18 | /// The `GenericParam` table has the following columns: 19 | /// - **Number** (2-byte value) 20 | /// - **Flags** (2-byte bitmask of GenericParamAttributes) 21 | /// - **Owner** (TypeOrMethodDef Coded Index) 22 | /// - **Name** (String Heap Index) 23 | final class GenericParam implements Row { 24 | const GenericParam({ 25 | required this.number, 26 | required this.owner, 27 | required this.name, 28 | this.flags = const GenericParamAttributes(0), 29 | }); 30 | 31 | final int number; 32 | final GenericParamAttributes flags; 33 | final TypeOrMethodDef owner; 34 | final StringIndex name; 35 | 36 | @override 37 | void serialize(BytesBuilder buffer, TableStream stream) { 38 | buffer 39 | ..writeUint16(number) 40 | ..writeUint16(flags) 41 | ..writeCodedIndex(owner, stream) 42 | ..writeHeapIndex(name, stream); 43 | } 44 | 45 | @override 46 | bool operator ==(Object other) => 47 | identical(this, other) || 48 | other is GenericParam && 49 | number == other.number && 50 | flags == other.flags && 51 | owner == other.owner && 52 | name == other.name; 53 | 54 | @override 55 | int get hashCode => Object.hash(number, flags, owner, name); 56 | } 57 | 58 | @internal 59 | final class GenericParamCompanion extends RowCompanion { 60 | const GenericParamCompanion(); 61 | 62 | @override 63 | MetadataTableId get tableId => MetadataTableId.genericParam; 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/reader/table/interface_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../common.dart'; 4 | import '../../metadata_type.dart'; 5 | import '../../type_name.dart'; 6 | import '../codes.dart'; 7 | import '../has_custom_attributes.dart'; 8 | import '../metadata_index.dart'; 9 | import '../metadata_table.dart'; 10 | import '../row.dart'; 11 | import 'type_def.dart'; 12 | 13 | /// Represents a row in the `InterfaceImpl` metadata table. 14 | /// 15 | /// The fields are populated by interpreting the binary metadata as specified in 16 | /// ECMA-335 `§II.22.23`. 17 | /// 18 | /// The `InterfaceImpl` table has the following columns: 19 | /// - **Class** (TypeDef Table Index) 20 | /// - **Interface** (TypeDefOrRef Coded Index) 21 | final class InterfaceImpl extends Row with HasCustomAttributes { 22 | InterfaceImpl(super.metadataIndex, super.readerIndex, super.index); 23 | 24 | @override 25 | MetadataTable get table => MetadataTable.interfaceImpl; 26 | 27 | @override 28 | int get token => (MetadataTableId.interfaceImpl << 24) | index; 29 | 30 | /// The type that implements the interface. 31 | late final TypeDef class$ = readRow(0); 32 | 33 | /// Returns the interface type implemented by [class$]. 34 | late final NamedClassType interface = switch (decode(1)) { 35 | TypeDefOrRefTypeDef(:final value) => NamedClassType( 36 | TypeName(value.namespace, value.name), 37 | ), 38 | TypeDefOrRefTypeRef(:final value) => NamedClassType( 39 | TypeName(value.namespace, value.name), 40 | ), 41 | TypeDefOrRefTypeSpec(:final value) => value.signature as NamedClassType, 42 | }; 43 | 44 | @override 45 | String toString() => 'InterfaceImpl(class\$: ${class$})'; 46 | } 47 | 48 | @internal 49 | final class InterfaceImplCompanion extends RowCompanion { 50 | const InterfaceImplCompanion(); 51 | 52 | @override 53 | InterfaceImpl Function(MetadataIndex, int, int) get constructor => 54 | InterfaceImpl.new; 55 | 56 | @override 57 | MetadataTable get table => MetadataTable.interfaceImpl; 58 | } 59 | -------------------------------------------------------------------------------- /test/type_name_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart'; 4 | 5 | void main() { 6 | group('TypeName', () { 7 | test('constructs with no generics by default', () { 8 | const type = TypeName('System', 'Guid'); 9 | check(type.namespace).equals('System'); 10 | check(type.name).equals('Guid'); 11 | check(type.generics).isEmpty(); 12 | }); 13 | 14 | test('returns unmodifiable generics list', () { 15 | const type = TypeName( 16 | 'Windows.Foundation.Collections', 17 | 'IIterable`1', 18 | generics: [Int32Type()], 19 | ); 20 | check( 21 | () => type.generics.add(const Int16Type()), 22 | ).throws(); 23 | }); 24 | 25 | test('equality: equal for same namespace, name, and generics', () { 26 | const type1 = TypeName( 27 | 'Windows.Foundation.Collections', 28 | 'IIterable`1', 29 | generics: [Int32Type()], 30 | ); 31 | const type2 = TypeName( 32 | 'Windows.Foundation.Collections', 33 | 'IIterable`1', 34 | generics: [Int32Type()], 35 | ); 36 | check(type1).equals(type2); 37 | }); 38 | 39 | test('equality: not equal if generics differ', () { 40 | const type1 = TypeName( 41 | 'Windows.Foundation.Collections', 42 | 'IIterable`1', 43 | generics: [Int16Type()], 44 | ); 45 | const type2 = TypeName( 46 | 'Windows.Foundation.Collections', 47 | 'IIterable`1', 48 | generics: [Int32Type()], 49 | ); 50 | check(type1).not((it) => it.equals(type2)); 51 | }); 52 | 53 | test('equality: not equal if name or namespace differs', () { 54 | const typeName = TypeName('System', 'Guid'); 55 | check( 56 | typeName, 57 | ).not((it) => it.equals(const TypeName('System', 'Object'))); 58 | check( 59 | typeName, 60 | ).not((it) => it.equals(const TypeName('Windows.Foundation', 'Point'))); 61 | }); 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /test/property_sig_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart'; 4 | 5 | void main() { 6 | group('PropertySig', () { 7 | test('default constructor uses expected values', () { 8 | const sig = PropertySig(returnType: Int32Type()); 9 | check(sig.callingConvention).equals(CallingConvention.DEFAULT); 10 | check(sig.returnType).equals(const Int32Type()); 11 | check(sig.types).deepEquals(const []); 12 | check(sig.toString()).equals( 13 | 'PropertySig(callingConvention: 0x0, returnType: Int32Type, ' 14 | 'types: [])', 15 | ); 16 | }); 17 | 18 | test('equality: equal for same flags, return type, and types', () { 19 | const sig1 = PropertySig( 20 | callingConvention: CallingConvention.HASTHIS, 21 | returnType: BoolType(), 22 | types: [Int32Type()], 23 | ); 24 | const sig2 = PropertySig( 25 | callingConvention: CallingConvention.HASTHIS, 26 | returnType: BoolType(), 27 | types: [Int32Type()], 28 | ); 29 | check(sig1).equals(sig2); 30 | }); 31 | 32 | test('equality: instances with different flags are not equal', () { 33 | const sig1 = PropertySig(returnType: BoolType()); 34 | const sig2 = PropertySig( 35 | returnType: BoolType(), 36 | callingConvention: CallingConvention.HASTHIS, 37 | ); 38 | check(sig1).not((it) => it.equals(sig2)); 39 | }); 40 | 41 | test('equality: instances with different return types are not equal', () { 42 | const sig1 = PropertySig(returnType: Int32Type()); 43 | const sig2 = PropertySig(returnType: BoolType()); 44 | check(sig1).not((it) => it.equals(sig2)); 45 | }); 46 | 47 | test('equality: instances with different types are not equal', () { 48 | const sig1 = PropertySig(returnType: Int32Type(), types: [BoolType()]); 49 | const sig2 = PropertySig(returnType: Int32Type(), types: [StringType()]); 50 | check(sig1).not((it) => it.equals(sig2)); 51 | }); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/reader/table/exported_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../../attributes.dart'; 4 | import '../../common.dart'; 5 | import '../codes.dart'; 6 | import '../has_custom_attributes.dart'; 7 | import '../metadata_index.dart'; 8 | import '../metadata_table.dart'; 9 | import '../row.dart'; 10 | import 'type_def.dart'; 11 | 12 | /// Represents a row in the `ExportedType` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.14`. 16 | /// 17 | /// The `ExportedType` table has the following columns: 18 | /// - **Flags** (4-byte bitmask of TypeAttributes) 19 | /// - **TypeDefId** (TypeDef Table Index) 20 | /// - **TypeName** (String Heap Index) 21 | /// - **TypeNamespace** (String Heap Index) 22 | /// - **Implementation** (Implementation Coded Index) 23 | final class ExportedType extends Row with HasCustomAttributes { 24 | ExportedType(super.metadataIndex, super.readerIndex, super.index); 25 | 26 | @override 27 | MetadataTable get table => MetadataTable.exportedType; 28 | 29 | @override 30 | int get token => (MetadataTableId.exportedType << 24) | index; 31 | 32 | /// The attributes of the exported type. 33 | late final flags = TypeAttributes(readUint32(0)); 34 | 35 | /// The exported type. 36 | late final TypeDef? typeDefId = () { 37 | if (readUint(1) == 0) return null; 38 | return readRow(1); 39 | }(); 40 | 41 | /// The name of the exported type. 42 | late final String name = readString(2); 43 | 44 | /// The namespace of the exported type. 45 | late final String namespace = readString(3); 46 | 47 | /// The source of the exported type. 48 | late final Implementation implementation = decode(4); 49 | } 50 | 51 | @internal 52 | final class ExportedTypeCompanion extends RowCompanion { 53 | const ExportedTypeCompanion(); 54 | 55 | @override 56 | ExportedType Function(MetadataIndex, int, int) get constructor => 57 | ExportedType.new; 58 | 59 | @override 60 | MetadataTable get table => MetadataTable.exportedType; 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/culture.dart: -------------------------------------------------------------------------------- 1 | /// Represents a valid culture identifier as defined in ECMA-335 `§II.23.1.3`. 2 | extension type const Culture._(String _) implements String { 3 | /// Creates a [Culture] from a [culture] string (e.g., `en-US`). 4 | factory Culture(String culture) { 5 | if (isValidCulture(culture)) return Culture._(culture); 6 | throw ArgumentError.value(culture, 'culture', 'Invalid culture'); 7 | } 8 | 9 | /// Whether the given [culture] is valid. 10 | static bool isValidCulture(String culture) => 11 | validCultures.contains(culture.toLowerCase()); 12 | 13 | /// The set of valid culture identifiers as defined in ECMA-335 `§II.23.1.3`. 14 | static const validCultures = { 15 | 'ar-sa', 'ar-iq', 'ar-eg', 'ar-ly', 'ar-dz', 'ar-ma', 'ar-tn', 'ar-om', // 16 | 'ar-ye', 'ar-sy', 'ar-jo', 'ar-lb', 'ar-kw', 'ar-ae', 'ar-bh', 'ar-qa', // 17 | 'bg-bg', 'ca-es', 'zh-tw', 'zh-cn', 'zh-hk', 'zh-sg', 'zh-mo', 'cs-cz', // 18 | 'da-dk', 'de-de', 'de-ch', 'de-at', 'de-lu', 'de-li', 'el-gr', 'en-us', // 19 | 'en-gb', 'en-au', 'en-ca', 'en-nz', 'en-ie', 'en-za', 'en-jm', 'en-cb', // 20 | 'en-bz', 'en-tt', 'en-zw', 'en-ph', 'es-es-ts', 'es-mx', 'es-es-is', // 21 | 'es-gt', 'es-cr', 'es-pa', 'es-do', 'es-ve', 'es-co', 'es-pe', 'es-ar', // 22 | 'es-ec', 'es-cl', 'es-uy', 'es-py', 'es-bo', 'es-sv', 'es-hn', 'es-ni', // 23 | 'es-pr', 'fi-fi', 'fr-fr', 'fr-be', 'fr-ca', 'fr-ch', 'fr-lu', 'fr-mc', // 24 | 'he-il', 'hu-hu', 'is-is', 'it-it', 'it-ch', 'ja-jp', 'ko-kr', 'nl-nl', // 25 | 'nl-be', 'nb-no', 'nn-no', 'pl-pl', 'pt-br', 'pt-pt', 'ro-ro', 'ru-ru', // 26 | 'hr-hr', 'lt-sr-sp', 'cy-sr-sp', 'sk-sk', 'sq-al', 'sv-se', 'sv-fi', // 27 | 'th-th', 'tr-tr', 'ur-pk', 'id-id', 'uk-ua', 'be-by', 'sl-si', 'et-ee', // 28 | 'lv-lv', 'lt-lt', 'fa-ir', 'vi-vn', 'hy-am', 'lt-az-az', 'cy-az-az', // 29 | 'eu-es', 'mk-mk', 'af-za', 'ka-ge', 'fo-fo', 'hi-in', 'ms-my', 'ms-bn', // 30 | 'kk-kz', 'ky-kz', 'sw-ke', 'lt-uz-uz', 'cy-uz-uz', 'tt-ta', 'pa-in', // 31 | 'gu-in', 'ta-in', 'te-in', 'kn-in', 'mr-in', 'sa-in', 'mn-mn', 'gl-es', // 32 | 'kok-in', 'syr-sy', 'div-mv', 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /example/writer.dart: -------------------------------------------------------------------------------- 1 | import 'package:winmd/winmd.dart'; 2 | import 'package:winmd/writer.dart'; 3 | 4 | void main() { 5 | // Initialize an in-memory metadata writer. 6 | final writer = MetadataWriter(name: 'Windows.winmd') 7 | // Write a WinRT interface: "Windows.Example.IExample" 8 | ..writeTypeDef( 9 | namespace: 'Windows.Example', 10 | name: 'IExample', 11 | flags: 12 | TypeAttributes.public | 13 | TypeAttributes.interface | 14 | TypeAttributes.abstract | 15 | TypeAttributes.windowsRuntime, 16 | ) 17 | // Write a method: Int32 Sum(Int32 a, Int32 b) 18 | ..writeMethodDef( 19 | name: 'Sum', 20 | flags: 21 | MethodAttributes.public | 22 | MethodAttributes.hideBySig | 23 | MethodAttributes.abstract | 24 | MethodAttributes.newSlot | 25 | MethodAttributes.virtual, 26 | signature: const MethodSignature( 27 | returnType: Int32Type(), 28 | types: [Int32Type(), Int32Type()], 29 | ), 30 | ) 31 | ..writeParam(name: 'a', sequence: 1, flags: ParamAttributes.in$) 32 | ..writeParam(name: 'b', sequence: 2, flags: ParamAttributes.in$); 33 | 34 | // Serialize the metadata into a Uint8List. 35 | final bytes = writer.toBytes(); 36 | 37 | // Load the serialized metadata for introspection. 38 | final reader = MetadataReader.read(bytes); 39 | final index = MetadataIndex.fromReader(reader); 40 | 41 | // Retrieve the "IExample" interface type definition. 42 | final typeDef = index.findSingleType('Windows.Example', 'IExample'); 43 | print('Interface "${typeDef.name}" defines the following methods:'); 44 | 45 | // Display each method with its return type and parameters. 46 | for (final method in typeDef.methods) { 47 | final MethodSignature(:returnType, :types) = method.signature; 48 | final params = method.params; 49 | final paramList = types.indexed 50 | .map((entry) { 51 | final (idx, type) = entry; 52 | final name = params[idx].name; 53 | return '$type $name'; 54 | }) 55 | .join(', '); 56 | print(' $returnType ${method.name}($paramList)'); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /example/windows_metadata.dart: -------------------------------------------------------------------------------- 1 | import 'package:winmd/windows_metadata.dart'; 2 | import 'package:winmd/winmd.dart'; 3 | 4 | void main() async { 5 | // Load Win32 metadata from the "Microsoft.Windows.SDK.Win32Metadata" NuGet 6 | // package. If the package is not already downloaded, it will be automatically 7 | // downloaded and cached in the local storage directory. To customize the 8 | // cache directory, provide a "LocalStorageManager" to the constructor. 9 | final metadataLoader = WindowsMetadataLoader(); 10 | // By default, the latest available version is loaded. To specify a particular 11 | // version, pass it as an argument (e.g., version: '63.0.31-preview'). 12 | final index = await metadataLoader.loadWin32Metadata(); 13 | 14 | // Optional: Use MetadataLookup for efficient type resolution by name. 15 | // This is especially helpful when the namespace of the target type is 16 | // unknown. 17 | final metadata = MetadataLookup(index); 18 | 19 | // Lookup a Win32 constant (e.g., E_FAIL) and print its type and value. 20 | final constant = metadata.findConstantByName('E_FAIL'); 21 | print('Win32 constant "${constant.name}":'); 22 | print(' Type: ${constant.signature}'); 23 | print(' Value: ${constant.constant?.value}'); 24 | print(''); 25 | 26 | // Lookup a Win32 function (e.g., MessageBoxW) and display its signature. 27 | final function = metadata.findFunctionByName('MessageBoxW'); 28 | print('Win32 function "${function.name}":'); 29 | final MethodSignature(:returnType, :types) = function.signature; 30 | print(' Return type: $returnType'); 31 | 32 | final parameters = function.params; 33 | for (final (i, type) in types.indexed) { 34 | final parameter = parameters[i + 1]; // skip return value 35 | print(' Parameter #${i + 1}:'); 36 | print(' Type: $type'); 37 | print(' Name: ${parameter.name}'); 38 | } 39 | print(''); 40 | 41 | // Lookup a COM interface (e.g., INetwork) and list its method names. 42 | final type = metadata.findSingleTypeByName('INetwork'); 43 | print('COM interface "${type.name}" exposes the following methods:'); 44 | for (final method in type.methods) { 45 | print(' ${method.name}'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/reader/heap/string.dart: -------------------------------------------------------------------------------- 1 | import 'metadata_heap.dart'; 2 | 3 | /// Provides indexed access to the `#Strings` heap in a metadata file. 4 | final class StringHeap extends MetadataHeap { 5 | /// Creates a [StringHeap] from the provided binary [data]. 6 | const StringHeap(super.data); 7 | 8 | /// The number of strings in the heap. 9 | int get count { 10 | var count = 0; 11 | var offset = 0; 12 | 13 | while (offset < data.length) { 14 | while (offset < data.length && data[offset] != 0) { 15 | offset++; 16 | } 17 | 18 | // Found a null terminator → valid string 19 | if (offset < data.length) { 20 | count++; 21 | offset++; // Skip null terminator 22 | } else { 23 | // If no null terminator, likely corrupted or incomplete 24 | break; 25 | } 26 | 27 | // Skip padded zeroes 28 | while (offset < data.length && data[offset] == 0) { 29 | offset++; 30 | } 31 | } 32 | 33 | return count; 34 | } 35 | 36 | /// Enumerates all strings in the heap. 37 | Iterable get strings sync* { 38 | var offset = 0; 39 | 40 | while (offset < data.length) { 41 | final start = offset; 42 | 43 | // Find the null terminator. 44 | while (offset < data.length && data[offset] != 0) { 45 | offset++; 46 | } 47 | 48 | if (offset >= data.length) break; 49 | 50 | // Extract the string from the start to the null terminator. 51 | yield this[start]; 52 | 53 | offset++; // Skip null terminator. 54 | 55 | // Skip padded zeroes. 56 | while (offset < data.length && data[offset] == 0) { 57 | offset++; 58 | } 59 | } 60 | } 61 | 62 | /// Retrieves the string located at the specified [offset]. 63 | String operator [](int offset) { 64 | assert( 65 | offset >= 0 && offset < data.length, 66 | 'Offset $offset out of bounds.', 67 | ); 68 | final buffer = StringBuffer(); 69 | var i = 0; 70 | 71 | // Read the array until the null terminator is encountered. 72 | while (true) { 73 | final char = data[offset + i]; 74 | if (char == 0) return buffer.toString(); 75 | buffer.writeCharCode(char); 76 | i++; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/reader/table/method_spec_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart' 4 | show MetadataIndex, MetadataReader, MethodSignature; 5 | import 'package:winmd/writer.dart'; 6 | 7 | void main() { 8 | group('MethodSpec', () { 9 | test('IMap.Insert(K key, V value)', () { 10 | final writer = MetadataWriter(name: 'MyMetadata'); 11 | final typeDef = writer.writeTypeDef( 12 | namespace: 'Namespace', 13 | name: 'IMap`2', 14 | flags: 15 | TypeAttributes.interface | 16 | TypeAttributes.abstract | 17 | TypeAttributes.windowsRuntime, 18 | ); 19 | writer 20 | ..writeGenericParam( 21 | number: 0, 22 | owner: TypeOrMethodDef.typeDef(typeDef), 23 | name: 'K', 24 | ) 25 | ..writeGenericParam( 26 | number: 1, 27 | owner: TypeOrMethodDef.typeDef(typeDef), 28 | name: 'V', 29 | ); 30 | final method = writer.writeMethodDef( 31 | name: 'Insert', 32 | flags: 33 | MethodAttributes.public | 34 | MethodAttributes.virtual | 35 | MethodAttributes.hideBySig | 36 | MethodAttributes.newSlot | 37 | MethodAttributes.abstract, 38 | implFlags: MethodImplAttributes.runtime, 39 | signature: const MethodSignature( 40 | returnType: BoolType(), 41 | types: [GenericParameterType(0), GenericParameterType(1)], 42 | ), 43 | ); 44 | writer 45 | ..writeParam(sequence: 1, name: 'key', flags: ParamAttributes.in$) 46 | ..writeParam(sequence: 2, name: 'value', flags: ParamAttributes.in$) 47 | ..writeMethodSpec( 48 | method: MethodDefOrRef.methodDef(method), 49 | generics: [const StringType(), const Int32Type()], 50 | ); 51 | final reader = MetadataReader.read(writer.toBytes()); 52 | final index = MetadataIndex.fromReader(reader); 53 | final methodSpec = index.methodSpec.first; 54 | check(methodSpec.token).equals(0x2B000000); 55 | check(methodSpec.method.name).equals('Insert'); 56 | check( 57 | methodSpec.instantiation, 58 | ).deepEquals([const StringType(), const Int32Type()]); 59 | }); 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/writer/table/assembly.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `Assembly` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.2`. 16 | /// 17 | /// The `Assembly` table has the following columns: 18 | /// - **HashAlgId** (4-byte value, AssemblyHashAlgorithm) 19 | /// - **MajorVersion** (2-byte value) 20 | /// - **MinorVersion** (2-byte value) 21 | /// - **BuildNumber** (2-byte value) 22 | /// - **RevisionNumber** (2-byte value) 23 | /// - **Flags** (4-byte value, AssemblyFlags) 24 | /// - **PublicKey** (Blob Heap Index) 25 | /// - **Name** (String Heap Index) 26 | /// - **Culture** (String Heap Index) 27 | final class Assembly implements Row { 28 | const Assembly({ 29 | required this.name, 30 | this.hashAlgId = AssemblyHashAlgorithm.none, 31 | this.majorVersion = 0, 32 | this.minorVersion = 0, 33 | this.buildNumber = 0, 34 | this.revisionNumber = 0, 35 | this.flags = const AssemblyFlags(0), 36 | this.publicKey = const BlobIndex(0), 37 | this.culture = const StringIndex(0), 38 | }); 39 | 40 | final AssemblyHashAlgorithm hashAlgId; 41 | final int majorVersion; 42 | final int minorVersion; 43 | final int buildNumber; 44 | final int revisionNumber; 45 | final AssemblyFlags flags; 46 | final BlobIndex publicKey; 47 | final StringIndex name; 48 | final StringIndex culture; 49 | 50 | @override 51 | void serialize(BytesBuilder buffer, TableStream stream) { 52 | buffer 53 | ..writeUint32(hashAlgId) 54 | ..writeUint16(majorVersion) 55 | ..writeUint16(minorVersion) 56 | ..writeUint16(buildNumber) 57 | ..writeUint16(revisionNumber) 58 | ..writeUint32(flags) 59 | ..writeHeapIndex(publicKey, stream) 60 | ..writeHeapIndex(name, stream) 61 | ..writeHeapIndex(culture, stream); 62 | } 63 | } 64 | 65 | @internal 66 | final class AssemblyCompanion extends RowCompanion { 67 | const AssemblyCompanion(); 68 | 69 | @override 70 | MetadataTableId get tableId => MetadataTableId.assembly; 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/writer/table/assembly_ref.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../../attributes.dart'; 6 | import '../../common.dart'; 7 | import '../heap/metadata_heap.dart'; 8 | import '../helpers.dart'; 9 | import '../row.dart'; 10 | import '../table_stream.dart'; 11 | 12 | /// Represents a row in the `AssemblyRef` metadata table. 13 | /// 14 | /// The fields are populated by interpreting the binary metadata as specified in 15 | /// ECMA-335 `§II.22.5`. 16 | /// 17 | /// The `AssemblyRef` table has the following columns: 18 | /// - **MajorVersion** (2-byte value) 19 | /// - **MinorVersion** (2-byte value) 20 | /// - **BuildNumber** (2-byte value) 21 | /// - **RevisionNumber** (2-byte value) 22 | /// - **Flags** (4-byte value, AssemblyFlags) 23 | /// - **PublicKeyOrToken** (Blob Heap Index) 24 | /// - **Name** (String Heap Index) 25 | /// - **Culture** (String Heap Index) 26 | /// - **HashValue** (Blob Heap Index) 27 | final class AssemblyRef implements Row { 28 | const AssemblyRef({ 29 | required this.name, 30 | this.majorVersion = 0, 31 | this.minorVersion = 0, 32 | this.buildNumber = 0, 33 | this.revisionNumber = 0, 34 | this.flags = const AssemblyFlags(0), 35 | this.publicKeyOrToken = const BlobIndex(0), 36 | this.culture = const StringIndex(0), 37 | this.hashValue = const BlobIndex(0), 38 | }); 39 | 40 | final int majorVersion; 41 | final int minorVersion; 42 | final int buildNumber; 43 | final int revisionNumber; 44 | final AssemblyFlags flags; 45 | final BlobIndex publicKeyOrToken; 46 | final StringIndex name; 47 | final StringIndex culture; 48 | final BlobIndex hashValue; 49 | 50 | @override 51 | void serialize(BytesBuilder buffer, TableStream stream) { 52 | buffer 53 | ..writeUint16(majorVersion) 54 | ..writeUint16(minorVersion) 55 | ..writeUint16(buildNumber) 56 | ..writeUint16(revisionNumber) 57 | ..writeUint32(flags) 58 | ..writeHeapIndex(publicKeyOrToken, stream) 59 | ..writeHeapIndex(name, stream) 60 | ..writeHeapIndex(culture, stream) 61 | ..writeHeapIndex(hashValue, stream); 62 | } 63 | } 64 | 65 | @internal 66 | final class AssemblyRefCompanion extends RowCompanion { 67 | const AssemblyRefCompanion(); 68 | 69 | @override 70 | MetadataTableId get tableId => MetadataTableId.assemblyRef; 71 | } 72 | -------------------------------------------------------------------------------- /test/reader/table/field_marshal_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:checks/checks.dart'; 2 | import 'package:test/scaffolding.dart'; 3 | import 'package:winmd/winmd.dart' as winmd; 4 | import 'package:winmd/writer.dart'; 5 | 6 | void main() { 7 | test('FieldMarshal', () { 8 | final writer = MetadataWriter(name: 'MyMetadata'); 9 | final someArrayFieldIndex = writer.writeField( 10 | flags: FieldAttributes.public, 11 | name: 'SomeArray', 12 | signature: const ArrayType(Int32Type()), 13 | ); 14 | writer.writeFieldMarshal( 15 | parent: HasFieldMarshal.field(someArrayFieldIndex), 16 | descriptor: const winmd.MarshallingDescriptor.array( 17 | elementType: winmd.NATIVE_TYPE_I4, 18 | ), 19 | ); 20 | final someStringFieldIndex = writer.writeField( 21 | flags: FieldAttributes.public, 22 | name: 'SomeString', 23 | signature: const NamedValueType(winmd.TypeName('System', 'String')), 24 | ); 25 | writer.writeFieldMarshal( 26 | parent: HasFieldMarshal.field(someStringFieldIndex), 27 | descriptor: const winmd.MarshallingDescriptor.simple( 28 | winmd.NATIVE_TYPE_LPWSTR, 29 | ), 30 | ); 31 | final reader = winmd.MetadataReader.read(writer.toBytes()); 32 | final index = winmd.MetadataIndex.fromReader(reader); 33 | 34 | final [someArrayField, someStringField] = index.field.toList(); 35 | 36 | final someArrayFieldMarshal = someArrayField.fieldMarshal; 37 | check(someArrayFieldMarshal).isNotNull(); 38 | check(someArrayFieldMarshal!.token).equals(0x0D000000); 39 | check(someArrayFieldMarshal.parent) 40 | .isA() 41 | .has((it) => it.value.name, 'value.name') 42 | .equals('SomeArray'); 43 | check(someArrayFieldMarshal.nativeType).equals( 44 | const winmd.MarshallingDescriptor.array( 45 | elementType: winmd.NATIVE_TYPE_I4, 46 | ), 47 | ); 48 | 49 | final someStringFieldMarshal = someStringField.fieldMarshal; 50 | check(someStringFieldMarshal).isNotNull(); 51 | check(someStringFieldMarshal!.parent) 52 | .isA() 53 | .has((it) => it.value.name, 'value.name') 54 | .equals('SomeString'); 55 | check(someStringFieldMarshal.nativeType).equals( 56 | const winmd.MarshallingDescriptor.simple(winmd.NATIVE_TYPE_LPWSTR), 57 | ); 58 | }); 59 | } 60 | --------------------------------------------------------------------------------