From 3267a0a60a5c76092209f60d53bb3a8a1ddc5faa Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Mon, 19 Jun 2023 21:09:10 -0500 Subject: [PATCH] Consolidate code generation (#2544) --- .gitattributes | 1 - .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/workflows/clippy.yml | 2 - .github/workflows/test.yml | 4 +- .gitignore | 8 +- crates/libs/bindgen/Cargo.toml | 17 - crates/libs/bindgen/license-apache-2.0 | 201 -- crates/libs/bindgen/license-mit | 21 - crates/libs/core/src/imp/bindings.rs | 2 +- crates/libs/core/src/imp/com_bindings.rs | 2 +- crates/libs/core/src/runtime_type.rs | 1 - crates/libs/core/src/type.rs | 20 - crates/libs/metadata/Cargo.toml | 13 - crates/libs/metadata/src/attributes.rs | 40 +- crates/libs/metadata/src/{reader => }/blob.rs | 0 .../libs/metadata/src/{reader => }/codes.rs | 0 crates/libs/metadata/src/error.rs | 86 - crates/libs/metadata/src/{reader => }/file.rs | 142 +- crates/libs/metadata/src/filter.rs | 10 +- crates/libs/metadata/src/{reader => }/guid.rs | 0 .../libs/metadata/src/{ => imp}/bindings.rs | 2 +- .../libs/metadata/src/{imp.rs => imp/mod.rs} | 3 + crates/libs/metadata/src/lib.rs | 1925 ++++++++++++++++- crates/libs/metadata/src/reader/mod.rs | 1912 ---------------- crates/libs/metadata/src/reader/tree.rs | 31 - crates/libs/metadata/src/{reader => }/row.rs | 0 crates/libs/metadata/src/{reader => }/type.rs | 32 +- .../metadata/src/{reader => }/type_name.rs | 0 crates/libs/metadata/src/writer/extend.rs | 16 - crates/libs/metadata/src/writer/idl/mod.rs | 52 - crates/libs/metadata/src/writer/idl/parse.rs | 120 - crates/libs/metadata/src/writer/idl/read.rs | 333 --- crates/libs/metadata/src/writer/idl/write.rs | 338 --- crates/libs/metadata/src/writer/mod.rs | 263 --- crates/libs/metadata/src/writer/validate.rs | 9 - crates/libs/metadata/src/writer/winmd/mod.rs | 6 - crates/libs/metadata/src/writer/winmd/read.rs | 156 -- .../metadata/src/writer/winmd/write/mod.rs | 361 ---- crates/libs/sys/Cargo.toml | 1 + crates/libs/tokens/Cargo.toml | 12 - crates/libs/tokens/license-apache-2.0 | 201 -- crates/libs/tokens/license-mit | 21 - crates/libs/windows/Cargo.toml | 1 + crates/tests/component/Cargo.toml | 5 - crates/tests/component/build.rs | 42 +- crates/tests/component/src/bindings.rs | 93 + crates/tests/component/src/component.idl | 8 + crates/tests/component_client/Cargo.toml | 6 +- crates/tests/component_client/build.rs | 33 +- crates/tests/component_client/src/bindings.rs | 253 +-- crates/tests/component_client/src/lib.rs | 2 +- crates/tests/metadata/Cargo.toml | 5 +- crates/tests/metadata/tests/attribute_enum.rs | 4 +- crates/tests/metadata/tests/fn_call_size.rs | 4 +- crates/tests/riddle/Cargo.toml | 3 + crates/tests/riddle/src/lib.rs | 28 +- crates/tests/riddle/tests/struct.idl | 19 +- crates/tests/riddle/tests/struct.rs | 45 +- crates/tests/standalone/Cargo.toml | 3 - crates/tests/standalone/build.rs | 41 +- crates/tests/standalone/src/b_arch.rs | 2 +- .../standalone/src/b_arch_dependencies.rs | 2 +- crates/tests/standalone/src/b_bstr.rs | 2 +- crates/tests/standalone/src/b_calendar.rs | 2 +- .../tests/standalone/src/b_constant_types.rs | 2 +- crates/tests/standalone/src/b_depends.rs | 2 +- crates/tests/standalone/src/b_enumeration.rs | 2 +- crates/tests/standalone/src/b_enumerator.rs | 2 +- crates/tests/standalone/src/b_guid.rs | 2 +- crates/tests/standalone/src/b_hresult.rs | 2 +- crates/tests/standalone/src/b_hstring.rs | 2 +- crates/tests/standalone/src/b_inspectable.rs | 2 +- crates/tests/standalone/src/b_nested.rs | 2 +- crates/tests/standalone/src/b_none.rs | 2 +- crates/tests/standalone/src/b_overloads.rs | 2 +- crates/tests/standalone/src/b_pcstr.rs | 2 +- crates/tests/standalone/src/b_pcwstr.rs | 2 +- crates/tests/standalone/src/b_pstr.rs | 2 +- crates/tests/standalone/src/b_pwstr.rs | 2 +- crates/tests/standalone/src/b_std.rs | 2 +- crates/tests/standalone/src/b_stringable.rs | 2 +- crates/tests/standalone/src/b_test.rs | 2 +- crates/tests/standalone/src/b_unknown.rs | 2 +- crates/tests/standalone/src/b_uri.rs | 2 +- .../tests/standalone/src/b_win_enumerator.rs | 2 +- crates/tools/core/Cargo.toml | 3 - crates/tools/core/bindings.txt | 28 + crates/tools/core/com_bindings.txt | 26 + crates/tools/core/src/main.rs | 74 +- crates/tools/gnu/src/main.rs | 48 +- crates/tools/lib/src/lib.rs | 59 +- crates/tools/metadata/Cargo.toml | 3 - crates/tools/metadata/bindings.txt | 52 + crates/tools/metadata/src/main.rs | 62 +- crates/tools/msvc/src/main.rs | 32 +- crates/tools/riddle/Cargo.toml | 10 + crates/tools/riddle/src/args.rs | 39 + crates/tools/riddle/src/error.rs | 65 + crates/tools/riddle/src/idl/mod.rs | 268 +++ crates/tools/riddle/src/idl/to_idl.rs | 221 ++ crates/tools/riddle/src/idl/to_winmd.rs | 399 ++++ .../riddle/src/idl/writer.rs} | 76 +- crates/tools/riddle/src/main.rs | 215 +- .../src => tools/riddle/src/rust}/classes.rs | 10 +- .../riddle/src/rust}/com_methods.rs | 0 .../riddle/src/rust}/constants.rs | 4 +- .../riddle/src/rust}/delegates.rs | 0 .../src => tools/riddle/src/rust}/enums.rs | 2 +- .../impl/Foundation/Collections/Iterable.rs | 0 .../impl/Foundation/Collections/MapView.rs | 0 .../impl/Foundation/Collections/VectorView.rs | 0 .../riddle/src/rust}/extensions/mod.rs | 0 .../mod/Foundation/Numerics/Matrix3x2.rs | 0 .../mod/Foundation/Numerics/Matrix4x4.rs | 0 .../mod/Foundation/Numerics/Vector2.rs | 0 .../mod/Foundation/Numerics/Vector3.rs | 0 .../mod/Foundation/Numerics/Vector4.rs | 0 .../extensions/mod/Foundation/TimeSpan.rs | 0 .../extensions/mod/Win32/Foundation/BOOL.rs | 0 .../mod/Win32/Foundation/BOOLEAN.rs | 0 .../mod/Win32/Foundation/NTSTATUS.rs | 0 .../mod/Win32/Foundation/VARIANT_BOOL.rs | 0 .../mod/Win32/Foundation/WIN32_ERROR.rs | 0 .../mod/Win32/Networking/WinSock/IN6_ADDR.rs | 0 .../mod/Win32/Networking/WinSock/IN_ADDR.rs | 0 .../Win32/Networking/WinSock/SOCKADDR_IN.rs | 0 .../Win32/Networking/WinSock/SOCKADDR_IN6.rs | 0 .../Win32/Networking/WinSock/SOCKADDR_INET.rs | 0 .../UI/WindowsAndMessaging/WindowLong.rs | 0 .../riddle/src/rust}/functions.rs | 2 +- .../src => tools/riddle/src/rust}/gen.rs | 79 +- .../src => tools/riddle/src/rust}/handles.rs | 0 .../riddle/src/rust}/implements.rs | 12 +- .../riddle/src/rust}/interfaces.rs | 6 +- .../riddle/src/rust}/iterators.rs | 4 +- .../riddle/src/rust}/method_names.rs | 2 +- .../lib.rs => tools/riddle/src/rust/mod.rs} | 188 +- .../riddle/src/rust}/standalone.rs | 61 +- .../src => tools/riddle/src/rust}/structs.rs | 2 +- .../riddle/src/rust}/try_format.rs | 0 .../riddle/src/rust}/winrt_methods.rs | 0 .../lib.rs => tools/riddle/src/tokens/mod.rs} | 155 +- .../riddle/src/tokens}/runtime.rs | 4 +- .../riddle/src/tokens}/to_tokens.rs | 2 +- .../riddle/src/tokens}/token_stream.rs | 11 +- crates/tools/riddle/src/tree.rs | 46 + crates/tools/riddle/src/winmd/mod.rs | 4 + crates/tools/riddle/src/winmd/to_winmd.rs | 87 + .../riddle/src/winmd/writer}/blobs.rs | 5 +- .../riddle/src/winmd/writer}/codes.rs | 0 .../riddle/src/winmd/writer}/file.rs | 59 +- crates/tools/riddle/src/winmd/writer/mod.rs | 390 ++++ .../riddle/src/winmd/writer}/strings.rs | 17 +- .../riddle/src/winmd/writer}/tables.rs | 46 +- .../riddle/src/winmd/writer}/traits.rs | 5 +- crates/tools/riddle/src/winmd/writer/type.rs | 68 + crates/{libs/bindgen => tools}/rustfmt.toml | 0 crates/tools/sys/Cargo.toml | 6 - crates/tools/sys/bindings.txt | 114 + crates/tools/sys/src/main.rs | 144 +- crates/tools/windows/Cargo.toml | 6 - crates/tools/windows/bindings.txt | 20 + crates/tools/windows/src/main.rs | 120 +- crates/tools/yml/Cargo.toml | 1 - crates/tools/yml/src/main.rs | 6 +- docs/publish.cmd | 4 +- 166 files changed, 5048 insertions(+), 5335 deletions(-) delete mode 100644 crates/libs/bindgen/Cargo.toml delete mode 100644 crates/libs/bindgen/license-apache-2.0 delete mode 100644 crates/libs/bindgen/license-mit rename crates/libs/metadata/src/{reader => }/blob.rs (100%) rename crates/libs/metadata/src/{reader => }/codes.rs (100%) delete mode 100644 crates/libs/metadata/src/error.rs rename crates/libs/metadata/src/{reader => }/file.rs (87%) rename crates/libs/metadata/src/{reader => }/guid.rs (100%) rename crates/libs/metadata/src/{ => imp}/bindings.rs (99%) rename crates/libs/metadata/src/{imp.rs => imp/mod.rs} (96%) delete mode 100644 crates/libs/metadata/src/reader/mod.rs delete mode 100644 crates/libs/metadata/src/reader/tree.rs rename crates/libs/metadata/src/{reader => }/row.rs (100%) rename crates/libs/metadata/src/{reader => }/type.rs (80%) rename crates/libs/metadata/src/{reader => }/type_name.rs (100%) delete mode 100644 crates/libs/metadata/src/writer/extend.rs delete mode 100644 crates/libs/metadata/src/writer/idl/mod.rs delete mode 100644 crates/libs/metadata/src/writer/idl/parse.rs delete mode 100644 crates/libs/metadata/src/writer/idl/read.rs delete mode 100644 crates/libs/metadata/src/writer/idl/write.rs delete mode 100644 crates/libs/metadata/src/writer/mod.rs delete mode 100644 crates/libs/metadata/src/writer/validate.rs delete mode 100644 crates/libs/metadata/src/writer/winmd/mod.rs delete mode 100644 crates/libs/metadata/src/writer/winmd/read.rs delete mode 100644 crates/libs/metadata/src/writer/winmd/write/mod.rs delete mode 100644 crates/libs/tokens/Cargo.toml delete mode 100644 crates/libs/tokens/license-apache-2.0 delete mode 100644 crates/libs/tokens/license-mit create mode 100644 crates/tools/core/bindings.txt create mode 100644 crates/tools/core/com_bindings.txt create mode 100644 crates/tools/metadata/bindings.txt create mode 100644 crates/tools/riddle/src/args.rs create mode 100644 crates/tools/riddle/src/error.rs create mode 100644 crates/tools/riddle/src/idl/mod.rs create mode 100644 crates/tools/riddle/src/idl/to_idl.rs create mode 100644 crates/tools/riddle/src/idl/to_winmd.rs rename crates/{libs/metadata/src/writer/idl/format.rs => tools/riddle/src/idl/writer.rs} (81%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/classes.rs (95%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/com_methods.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/constants.rs (98%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/delegates.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/enums.rs (99%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/impl/Foundation/Collections/Iterable.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/impl/Foundation/Collections/MapView.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/impl/Foundation/Collections/VectorView.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Foundation/Numerics/Matrix3x2.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Foundation/Numerics/Matrix4x4.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Foundation/Numerics/Vector2.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Foundation/Numerics/Vector3.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Foundation/Numerics/Vector4.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Foundation/TimeSpan.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Foundation/BOOL.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Foundation/BOOLEAN.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Foundation/NTSTATUS.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Foundation/VARIANT_BOOL.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Foundation/WIN32_ERROR.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Networking/WinSock/IN6_ADDR.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Networking/WinSock/IN_ADDR.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN6.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/Networking/WinSock/SOCKADDR_INET.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/extensions/mod/Win32/UI/WindowsAndMessaging/WindowLong.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/functions.rs (99%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/gen.rs (96%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/handles.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/implements.rs (95%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/interfaces.rs (97%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/iterators.rs (98%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/method_names.rs (93%) rename crates/{libs/bindgen/src/lib.rs => tools/riddle/src/rust/mod.rs} (52%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/standalone.rs (86%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/structs.rs (99%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/try_format.rs (100%) rename crates/{libs/bindgen/src => tools/riddle/src/rust}/winrt_methods.rs (100%) rename crates/{libs/tokens/src/lib.rs => tools/riddle/src/tokens/mod.rs} (65%) rename crates/{libs/tokens/src => tools/riddle/src/tokens}/runtime.rs (99%) rename crates/{libs/tokens/src => tools/riddle/src/tokens}/to_tokens.rs (99%) rename crates/{libs/tokens/src => tools/riddle/src/tokens}/token_stream.rs (93%) create mode 100644 crates/tools/riddle/src/tree.rs create mode 100644 crates/tools/riddle/src/winmd/mod.rs create mode 100644 crates/tools/riddle/src/winmd/to_winmd.rs rename crates/{libs/metadata/src/writer/winmd/write => tools/riddle/src/winmd/writer}/blobs.rs (93%) rename crates/{libs/metadata/src/writer/winmd/write => tools/riddle/src/winmd/writer}/codes.rs (100%) rename crates/{libs/metadata/src/writer/winmd/write => tools/riddle/src/winmd/writer}/file.rs (72%) create mode 100644 crates/tools/riddle/src/winmd/writer/mod.rs rename crates/{libs/metadata/src/writer/winmd/write => tools/riddle/src/winmd/writer}/strings.rs (66%) rename crates/{libs/metadata/src/writer/winmd/write => tools/riddle/src/winmd/writer}/tables.rs (85%) rename crates/{libs/metadata/src/writer/winmd/write => tools/riddle/src/winmd/writer}/traits.rs (91%) create mode 100644 crates/tools/riddle/src/winmd/writer/type.rs rename crates/{libs/bindgen => tools}/rustfmt.toml (100%) create mode 100644 crates/tools/sys/bindings.txt create mode 100644 crates/tools/windows/bindings.txt diff --git a/.gitattributes b/.gitattributes index b9535379b1..a9567e6535 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,5 @@ *.png -text *.jpg -text *.winmd -text -*.dll -text *.lib -text *.a -text diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d834858a41..3d08f629fb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -14,7 +14,7 @@ body: options: - "windows" - "windows-sys" - - "windows-bindgen" + - "windows-metadata" - "windows-core" - "windows-targets" - "other (please share in the comments)" diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 76d122148e..0a74cc6034 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -134,14 +134,12 @@ jobs: cargo clippy -p tool_windows && cargo clippy -p tool_yml && cargo clippy -p windows && - cargo clippy -p windows-bindgen && cargo clippy -p windows-core && cargo clippy -p windows-implement && cargo clippy -p windows-interface && cargo clippy -p windows-metadata && cargo clippy -p windows-sys && cargo clippy -p windows-targets && - cargo clippy -p windows-tokens && cargo clippy -p windows_aarch64_gnullvm && cargo clippy -p windows_aarch64_msvc && cargo clippy -p windows_i686_gnu && diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4aade96da2..55162cf8ac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -96,8 +96,8 @@ jobs: cargo test -p test_enums && cargo test -p test_error && cargo test -p test_event && - cargo test -p test_extensions && cargo clean && + cargo test -p test_extensions && cargo test -p test_handles && cargo test -p test_helpers && cargo test -p test_implement && @@ -141,14 +141,12 @@ jobs: cargo test -p tool_windows && cargo test -p tool_yml && cargo test -p windows && - cargo test -p windows-bindgen && cargo test -p windows-core && cargo test -p windows-implement && cargo test -p windows-interface && cargo test -p windows-metadata && cargo test -p windows-sys && cargo test -p windows-targets && - cargo test -p windows-tokens && cargo test -p windows_aarch64_gnullvm && cargo test -p windows_aarch64_msvc && cargo test -p windows_i686_gnu && diff --git a/.gitignore b/.gitignore index b473848567..04af890f55 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,6 @@ .vscode .vs -.windows target -packages -obj -bin +temp *.lock -*.user -*.filters -*.bin *.winmd diff --git a/crates/libs/bindgen/Cargo.toml b/crates/libs/bindgen/Cargo.toml deleted file mode 100644 index c5afd252c3..0000000000 --- a/crates/libs/bindgen/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "windows-bindgen" -version = "0.49.0" -authors = ["Microsoft"] -edition = "2018" -license = "MIT OR Apache-2.0" -description = "Code gen support for the windows and windows-sys crates" -repository = "https://github.com/microsoft/windows-rs" -readme = "../../../docs/readme.md" - -[package.metadata.docs.rs] -default-target = "x86_64-pc-windows-msvc" -targets = [] - -[dependencies] -tokens = { package = "windows-tokens", path = "../tokens", version = "0.48.0" } -metadata = { package = "windows-metadata", path = "../metadata", version = "0.49.0" } diff --git a/crates/libs/bindgen/license-apache-2.0 b/crates/libs/bindgen/license-apache-2.0 deleted file mode 100644 index b5ed4ecec2..0000000000 --- a/crates/libs/bindgen/license-apache-2.0 +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright (c) Microsoft Corporation. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/crates/libs/bindgen/license-mit b/crates/libs/bindgen/license-mit deleted file mode 100644 index 9e841e7a26..0000000000 --- a/crates/libs/bindgen/license-mit +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/crates/libs/core/src/imp/bindings.rs b/crates/libs/core/src/imp/bindings.rs index ffeff2684d..39575282e0 100644 --- a/crates/libs/core/src/imp/bindings.rs +++ b/crates/libs/core/src/imp/bindings.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] ::windows_targets::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL); diff --git a/crates/libs/core/src/imp/com_bindings.rs b/crates/libs/core/src/imp/com_bindings.rs index d20b26c220..15d0905d80 100644 --- a/crates/libs/core/src/imp/com_bindings.rs +++ b/crates/libs/core/src/imp/com_bindings.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] #[inline] diff --git a/crates/libs/core/src/runtime_type.rs b/crates/libs/core/src/runtime_type.rs index 1d0d383b7b..c85a3889bc 100644 --- a/crates/libs/core/src/runtime_type.rs +++ b/crates/libs/core/src/runtime_type.rs @@ -2,7 +2,6 @@ use super::*; #[doc(hidden)] pub trait RuntimeType: Type { - // TODO: hidden? const SIGNATURE: crate::imp::ConstBuffer; } diff --git a/crates/libs/core/src/type.rs b/crates/libs/core/src/type.rs index 1847004388..89285cab8b 100644 --- a/crates/libs/core/src/type.rs +++ b/crates/libs/core/src/type.rs @@ -26,10 +26,6 @@ pub trait Type::TypeKind>: TypeKind + Sized { /// # Safety unsafe fn from_abi(abi: Self::Abi) -> Result; fn from_default(default: &Self::Default) -> Result; - - // TODO: this only used by ManuallyDrop - /// # Safety - unsafe fn from_abi_ref(abi: &Self::Abi) -> Option<&Self>; } impl Type for T @@ -47,14 +43,6 @@ where } } - unsafe fn from_abi_ref(abi: &Self::Abi) -> Option<&Self> { - if !abi.is_null() { - Some(std::mem::transmute(abi)) - } else { - None - } - } - fn from_default(default: &Self::Default) -> Result { default.as_ref().cloned().ok_or(Error::OK) } @@ -71,10 +59,6 @@ where Ok(abi.assume_init()) } - unsafe fn from_abi_ref(abi: &Self::Abi) -> Option<&Self> { - Some(std::mem::transmute(abi)) - } - fn from_default(default: &Self::Default) -> Result { Ok(default.clone()) } @@ -91,10 +75,6 @@ where Ok(abi) } - unsafe fn from_abi_ref(abi: &Self::Abi) -> Option<&Self> { - Some(abi) - } - fn from_default(default: &Self) -> Result { Ok(default.clone()) } diff --git a/crates/libs/metadata/Cargo.toml b/crates/libs/metadata/Cargo.toml index 3dac891b7f..acaa5e3a74 100644 --- a/crates/libs/metadata/Cargo.toml +++ b/crates/libs/metadata/Cargo.toml @@ -10,16 +10,3 @@ repository = "https://github.com/microsoft/windows-rs" [package.metadata.docs.rs] default-target = "x86_64-pc-windows-msvc" targets = [] - -[dependencies.tokens] -package = "windows-tokens" -path = "../tokens" -version = "0.48.0" - -[dependencies.syn] -version = "2.0" -features = ["full", "extra-traits"] - -[dependencies.proc-macro2] -version = "1.0" -features = ["span-locations"] diff --git a/crates/libs/metadata/src/attributes.rs b/crates/libs/metadata/src/attributes.rs index d28bedc816..2bfaa67446 100644 --- a/crates/libs/metadata/src/attributes.rs +++ b/crates/libs/metadata/src/attributes.rs @@ -1,4 +1,42 @@ -use super::*; +macro_rules! flags { + ($name:ident, $size:ty) => { + #[derive(Default, Copy, Clone, PartialEq, Eq, Debug)] + pub struct $name(pub $size); + impl $name { + pub fn contains(&self, contains: Self) -> bool { + *self & contains == contains + } + } + impl std::ops::BitOr for $name { + type Output = Self; + fn bitor(self, other: Self) -> Self { + Self(self.0 | other.0) + } + } + impl std::ops::BitAnd for $name { + type Output = Self; + fn bitand(self, other: Self) -> Self { + Self(self.0 & other.0) + } + } + impl std::ops::BitOrAssign for $name { + fn bitor_assign(&mut self, other: Self) { + self.0.bitor_assign(other.0) + } + } + impl std::ops::BitAndAssign for $name { + fn bitand_assign(&mut self, other: Self) { + self.0.bitand_assign(other.0) + } + } + impl std::ops::Not for $name { + type Output = Self; + fn not(self) -> Self { + Self(self.0.not()) + } + } + }; +} flags!(FieldAttributes, u16); impl FieldAttributes { diff --git a/crates/libs/metadata/src/reader/blob.rs b/crates/libs/metadata/src/blob.rs similarity index 100% rename from crates/libs/metadata/src/reader/blob.rs rename to crates/libs/metadata/src/blob.rs diff --git a/crates/libs/metadata/src/reader/codes.rs b/crates/libs/metadata/src/codes.rs similarity index 100% rename from crates/libs/metadata/src/reader/codes.rs rename to crates/libs/metadata/src/codes.rs diff --git a/crates/libs/metadata/src/error.rs b/crates/libs/metadata/src/error.rs deleted file mode 100644 index 66f9143d62..0000000000 --- a/crates/libs/metadata/src/error.rs +++ /dev/null @@ -1,86 +0,0 @@ -pub type Result = std::result::Result; - -#[derive(Default, Debug)] -pub struct Error { - message: String, - path: String, - span: Option<(usize, usize)>, -} - -impl std::error::Error for Error {} - -impl From for std::io::Error { - fn from(error: Error) -> Self { - std::io::Error::new(std::io::ErrorKind::Other, error.message.as_str()) - } -} - -impl From for Error { - fn from(error: syn::Error) -> Self { - let start = error.span().start(); - Self { message: error.to_string(), span: Some((start.line, start.column)), ..Self::default() } - } -} - -impl std::fmt::Display for Error { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(fmt, "error: {}", self.message)?; - if !self.path.is_empty() { - if let Some((line, column)) = self.span { - writeln!(fmt, " --> {}:{line}:{column}", self.path)?; - } else { - writeln!(fmt, " --> {}", self.path)?; - } - } - Ok(()) - } -} - -impl Error { - pub fn new(message: &str) -> Self { - Self { message: message.to_string(), ..Self::default() } - } - - pub fn with_path(self, path: &str) -> Self { - Self { path: path.to_string(), ..self } - } - - pub fn with_span(self, span: proc_macro2::Span) -> Self { - let start = span.start(); - Self { span: Some((start.line, start.column)), ..self } - } -} - -pub fn read_file(path: &str) -> Result> { - std::fs::read(path).map_err(|_| Error::new("failed to read file").with_path(path)) -} - -pub fn read_to_string(path: &str) -> Result { - std::fs::read_to_string(path).map_err(|_| Error::new("failed to read file").with_path(path)) -} - -pub fn write_to_file>(path: &str, contents: C) -> Result<()> { - if let Some(parent) = std::path::Path::new(path).parent() { - std::fs::create_dir_all(parent).map_err(|_| Error::new("failed to create directory").with_path(path))?; - } - - std::fs::write(path, contents).map_err(|_| Error::new("failed to write file").with_path(path)) -} - -pub fn canonicalize(path: &str) -> Result { - let path = std::fs::canonicalize(path).map_err(|_| Error::new("failed to find path").with_path(path))?; - let path = path.to_string_lossy().trim_start_matches(r#"\\?\"#).to_string(); - - match extension(&path) { - (_, "") => Ok(path), - (file, extension) => Ok(format!("{file}.{}", extension.to_lowercase())), - } -} - -pub fn extension(path: &str) -> (&str, &str) { - if let Some((file, extension)) = path.rsplit_once('.') { - (file, extension) - } else { - ("", "") - } -} diff --git a/crates/libs/metadata/src/reader/file.rs b/crates/libs/metadata/src/file.rs similarity index 87% rename from crates/libs/metadata/src/reader/file.rs rename to crates/libs/metadata/src/file.rs index 3d987a327d..e6614b21ad 100644 --- a/crates/libs/metadata/src/reader/file.rs +++ b/crates/libs/metadata/src/file.rs @@ -42,72 +42,60 @@ pub const TABLE_ASSEMBLYREF: usize = 15; pub const TABLE_CLASSLAYOUT: usize = 16; pub const TABLE_LEN: usize = 17; -fn error_invalid_winmd() -> Error { - Error::new("not a valid `winmd` file") -} +type Result = std::result::Result; impl File { - pub fn with_default(paths: &[&str]) -> Result> { - let mut files = vec![Self::from_buffer(std::include_bytes!("../../default/Windows.winmd").to_vec())?, Self::from_buffer(std::include_bytes!("../../default/Windows.Wdk.winmd").to_vec())?, Self::from_buffer(std::include_bytes!("../../default/Windows.Win32.winmd").to_vec())?]; - - for path in paths { - files.push(Self::new(path)?); - } - - Ok(files) - } - - pub fn new(path: &str) -> Result { - Self::from_buffer(read_file(path)?).map_err(|error| error.with_path(path)) + pub fn new(bytes: Vec) -> Option { + Self::ok(bytes).ok() } - pub fn from_buffer(bytes: Vec) -> Result { + fn ok(bytes: Vec) -> Result { let mut result = File { bytes, ..Default::default() }; - let dos = result.bytes.view_as::(0); + let dos = result.bytes.view_as::(0)?; - if dos.e_magic != IMAGE_DOS_SIGNATURE as _ || result.bytes.copy_as::(dos.e_lfanew as _) != IMAGE_NT_SIGNATURE { - return Err(error_invalid_winmd()); + if dos.e_magic != IMAGE_DOS_SIGNATURE as _ || result.bytes.copy_as::(dos.e_lfanew as _)? != IMAGE_NT_SIGNATURE { + return Err(()); } let file_offset = dos.e_lfanew as usize + std::mem::size_of::(); - let file = result.bytes.view_as::(file_offset); + let file = result.bytes.view_as::(file_offset)?; let optional_offset = file_offset + std::mem::size_of::(); - let (com_virtual_address, sections) = match result.bytes.copy_as::(optional_offset) { + let (com_virtual_address, sections) = match result.bytes.copy_as::(optional_offset)? { IMAGE_NT_OPTIONAL_HDR32_MAGIC => { - let optional = result.bytes.view_as::(optional_offset); - (optional.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR as usize].VirtualAddress, result.bytes.view_as_slice_of::(optional_offset + std::mem::size_of::(), file.NumberOfSections as usize)) + let optional = result.bytes.view_as::(optional_offset)?; + (optional.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR as usize].VirtualAddress, result.bytes.view_as_slice_of::(optional_offset + std::mem::size_of::(), file.NumberOfSections as usize)?) } IMAGE_NT_OPTIONAL_HDR64_MAGIC => { - let optional = result.bytes.view_as::(optional_offset); - (optional.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR as usize].VirtualAddress, result.bytes.view_as_slice_of::(optional_offset + std::mem::size_of::(), file.NumberOfSections as usize)) + let optional = result.bytes.view_as::(optional_offset)?; + (optional.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR as usize].VirtualAddress, result.bytes.view_as_slice_of::(optional_offset + std::mem::size_of::(), file.NumberOfSections as usize)?) } - _ => return Err(error_invalid_winmd()), + _ => return Err(()), }; - let clr = result.bytes.view_as::(offset_from_rva(section_from_rva(sections, com_virtual_address)?, com_virtual_address) as _); + let clr = result.bytes.view_as::(offset_from_rva(section_from_rva(sections, com_virtual_address)?, com_virtual_address) as _)?; if clr.cb != std::mem::size_of::() as _ { - return Err(error_invalid_winmd()); + return Err(()); } let metadata_offset = offset_from_rva(section_from_rva(sections, clr.MetaData.VirtualAddress)?, clr.MetaData.VirtualAddress); - let metadata = result.bytes.view_as::(metadata_offset as _); + let metadata = result.bytes.view_as::(metadata_offset as _)?; if metadata.signature != METADATA_SIGNATURE { - return Err(error_invalid_winmd()); + return Err(()); } // The METADATA_HEADER struct is not a fixed size so have to offset a little more carefully. let mut view = metadata_offset + metadata.length as usize + 20; let mut tables_data: (usize, usize) = (0, 0); - for _ in 0..result.bytes.copy_as::(metadata_offset + metadata.length as usize + 18) { - let stream_offset = result.bytes.copy_as::(view) as usize; - let stream_len = result.bytes.copy_as::(view + 4) as usize; - let stream_name = result.bytes.view_as_str(view + 8); + for _ in 0..result.bytes.copy_as::(metadata_offset + metadata.length as usize + 18)? { + let stream_offset = result.bytes.copy_as::(view)? as usize; + let stream_len = result.bytes.copy_as::(view + 4)? as usize; + let stream_name = result.bytes.view_as_str(view + 8)?; match stream_name { b"#Strings" => result.strings = metadata_offset + stream_offset, b"#Blob" => result.blobs = metadata_offset + stream_offset, @@ -123,11 +111,11 @@ impl File { view += 8 + stream_name.len() + padding; } - let heap_sizes = result.bytes.copy_as::(tables_data.0 + 6); + let heap_sizes = result.bytes.copy_as::(tables_data.0 + 6)?; let string_index_size = if (heap_sizes & 1) == 1 { 4 } else { 2 }; let guid_index_size = if (heap_sizes >> 1 & 1) == 1 { 4 } else { 2 }; let blob_index_size = if (heap_sizes >> 2 & 1) == 1 { 4 } else { 2 }; - let valid_bits = result.bytes.copy_as::(tables_data.0 + 8); + let valid_bits = result.bytes.copy_as::(tables_data.0 + 8)?; view = tables_data.0 + 24; // These tables are unused by the reader, but needed temporarily to calculate sizes and offsets for subsequent tables. @@ -159,7 +147,7 @@ impl File { continue; } - let len = result.bytes.copy_as::(view) as _; + let len = result.bytes.copy_as::(view)? as _; view += 4; match i { @@ -327,10 +315,10 @@ impl File { let column = &table.columns[column]; let offset = table.offset + row * table.width + column.offset; match column.width { - 1 => self.bytes.copy_as::(offset) as _, - 2 => self.bytes.copy_as::(offset) as _, - 4 => self.bytes.copy_as::(offset) as _, - _ => self.bytes.copy_as::(offset) as _, + 1 => self.bytes.copy_as::(offset).map_or(0, |v| v as usize), + 2 => self.bytes.copy_as::(offset).map_or(0, |v| v as usize), + 4 => self.bytes.copy_as::(offset).map_or(0, |v| v as usize), + _ => self.bytes.copy_as::(offset).map_or(0, |v| v as usize), } } @@ -473,59 +461,63 @@ impl Column { } } -macro_rules! assert_proper_length { - ($self:expr, $t:ty, $offset:expr, $size:expr) => { - let enough_room = $offset + $size <= $self.len(); - assert!(enough_room, "Invalid file: not enough bytes at offset {} to represent T", $offset); - }; -} - -macro_rules! assert_proper_length_and_alignment { - ($self:expr, $t:ty, $offset:expr, $size:expr) => {{ - assert_proper_length!($self, $t, $offset, $size); - let ptr = &$self[$offset] as *const u8 as *const $t; - let properly_aligned = ptr.align_offset(std::mem::align_of::<$t>()) == 0; - assert!(properly_aligned, "Invalid file: offset {} is not properly aligned to T", $offset); - ptr - }}; -} - trait View { - fn view_as(&self, offset: usize) -> &T; - fn view_as_slice_of(&self, offset: usize, len: usize) -> &[T]; - fn copy_as(&self, offset: usize) -> T; - fn view_as_str(&self, offset: usize) -> &[u8]; + fn view_as(&self, offset: usize) -> Result<&T>; + fn view_as_slice_of(&self, offset: usize, len: usize) -> Result<&[T]>; + fn copy_as(&self, offset: usize) -> Result; + fn view_as_str(&self, offset: usize) -> Result<&[u8]>; + fn is_proper_length(&self, offset: usize) -> Result<()>; + fn is_proper_length_and_alignment(&self, offset: usize, count: usize) -> Result<*const T>; } +// TODO: maybe use std::ptr::read_unaligned here impl View for [u8] { - fn view_as(&self, offset: usize) -> &T { - let ptr = assert_proper_length_and_alignment!(self, T, offset, std::mem::size_of::()); - unsafe { &*ptr } + fn view_as(&self, offset: usize) -> Result<&T> { + unsafe { Ok(&*self.is_proper_length_and_alignment(offset, 1)?) } } - fn view_as_slice_of(&self, offset: usize, len: usize) -> &[T] { - let ptr = assert_proper_length_and_alignment!(self, T, offset, std::mem::size_of::() * len); - unsafe { std::slice::from_raw_parts(ptr, len) } + fn view_as_slice_of(&self, offset: usize, len: usize) -> Result<&[T]> { + unsafe { Ok(std::slice::from_raw_parts(self.is_proper_length_and_alignment(offset, len)?, len)) } } - fn copy_as(&self, offset: usize) -> T { - assert_proper_length!(self, T, offset, std::mem::size_of::()); + fn copy_as(&self, offset: usize) -> Result { + self.is_proper_length::(offset)?; + unsafe { let mut data = std::mem::MaybeUninit::zeroed().assume_init(); std::ptr::copy_nonoverlapping(self[offset..].as_ptr(), &mut data as *mut T as *mut u8, std::mem::size_of::()); - data + Ok(data) } } - fn view_as_str(&self, offset: usize) -> &[u8] { + fn view_as_str(&self, offset: usize) -> Result<&[u8]> { let buffer = &self[offset..]; - let index = buffer.iter().position(|c| *c == b'\0').expect("Invalid file"); - &self[offset..offset + index] + let index = buffer.iter().position(|c| *c == b'\0').ok_or(())?; + Ok(&self[offset..offset + index]) + } + + fn is_proper_length(&self, offset: usize) -> Result<()> { + if offset + std::mem::size_of::() <= self.len() { + Ok(()) + } else { + Err(()) + } + } + + fn is_proper_length_and_alignment(&self, offset: usize, count: usize) -> Result<*const T> { + self.is_proper_length::(offset * count)?; + let ptr = &self[offset] as *const u8 as *const T; + + if ptr.align_offset(std::mem::align_of::()) == 0 { + Ok(ptr) + } else { + Err(()) + } } } fn section_from_rva(sections: &[IMAGE_SECTION_HEADER], rva: u32) -> Result<&IMAGE_SECTION_HEADER> { - sections.iter().find(|&s| rva >= s.VirtualAddress && rva < s.VirtualAddress + unsafe { s.Misc.VirtualSize }).ok_or_else(error_invalid_winmd) + sections.iter().find(|&s| rva >= s.VirtualAddress && rva < s.VirtualAddress + unsafe { s.Misc.VirtualSize }).ok_or(()) } fn offset_from_rva(section: &IMAGE_SECTION_HEADER, rva: u32) -> usize { diff --git a/crates/libs/metadata/src/filter.rs b/crates/libs/metadata/src/filter.rs index eb9e97e79d..fe3fee9453 100644 --- a/crates/libs/metadata/src/filter.rs +++ b/crates/libs/metadata/src/filter.rs @@ -49,11 +49,11 @@ impl<'a> Filter<'a> { false } - pub fn includes_type(&self, reader: &reader::Reader, ty: reader::TypeDef) -> bool { + pub fn includes_type(&self, reader: &Reader, ty: TypeDef) -> bool { self.includes_type_name(reader.type_def_type_name(ty)) } - pub fn includes_type_name(&self, type_name: reader::TypeName) -> bool { + pub fn includes_type_name(&self, type_name: TypeName) -> bool { if self.is_empty() { return true; } @@ -67,6 +67,10 @@ impl<'a> Filter<'a> { false } + pub fn includes(&self) -> impl Iterator + '_ { + self.0.iter().filter_map(|(name, include)| if *include { Some(*name) } else { None }) + } + fn is_empty(&self) -> bool { self.0.is_empty() } @@ -93,7 +97,7 @@ mod tests { use super::*; fn includes_type_name(filter: &Filter, full_name: &str) -> bool { - filter.includes_type_name(reader::TypeName::parse(full_name)) + filter.includes_type_name(TypeName::parse(full_name)) } #[test] diff --git a/crates/libs/metadata/src/reader/guid.rs b/crates/libs/metadata/src/guid.rs similarity index 100% rename from crates/libs/metadata/src/reader/guid.rs rename to crates/libs/metadata/src/guid.rs diff --git a/crates/libs/metadata/src/bindings.rs b/crates/libs/metadata/src/imp/bindings.rs similarity index 99% rename from crates/libs/metadata/src/bindings.rs rename to crates/libs/metadata/src/imp/bindings.rs index 667f12b63e..37ec08325d 100644 --- a/crates/libs/metadata/src/bindings.rs +++ b/crates/libs/metadata/src/imp/bindings.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] pub type CorElementType = i32; diff --git a/crates/libs/metadata/src/imp.rs b/crates/libs/metadata/src/imp/mod.rs similarity index 96% rename from crates/libs/metadata/src/imp.rs rename to crates/libs/metadata/src/imp/mod.rs index 27a735a451..571d0f0633 100644 --- a/crates/libs/metadata/src/imp.rs +++ b/crates/libs/metadata/src/imp/mod.rs @@ -1,3 +1,6 @@ +mod bindings; +pub use bindings::*; + #[repr(C)] #[derive(Default)] pub struct METADATA_HEADER { diff --git a/crates/libs/metadata/src/lib.rs b/crates/libs/metadata/src/lib.rs index 2f4e1fb6c1..81eccc02db 100644 --- a/crates/libs/metadata/src/lib.rs +++ b/crates/libs/metadata/src/lib.rs @@ -1,58 +1,1911 @@ -#![allow(dead_code, non_upper_case_globals)] +#![allow(non_upper_case_globals)] + +#[doc(hidden)] +pub mod imp; -use std::collections::*; mod attributes; -mod bindings; -mod error; +mod blob; +mod codes; +mod file; mod filter; -mod imp; -pub mod reader; -pub mod writer; +mod guid; +mod row; +mod r#type; +mod type_name; pub use attributes::*; -use bindings::*; -pub use error::*; +pub use blob::*; +pub use codes::*; +pub use file::*; pub use filter::*; +pub use guid::*; +pub use r#type::*; +pub use row::*; +pub use type_name::*; + use imp::*; +use std::collections::*; + +macro_rules! tables { + ($($name:ident,)*) => ($( + #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)] + pub struct $name(pub Row); + )*) +} + +tables! { + Attribute, + ClassLayout, + Constant, + Field, + GenericParam, + ImplMap, + InterfaceImpl, + MemberRef, + MethodDef, + Module, + ModuleRef, + AssemblyRef, + Param, + TypeDef, + TypeRef, + TypeSpec, +} + +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)] +pub struct Interface { + pub ty: Type, + pub kind: InterfaceKind, +} + +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)] +pub enum InterfaceKind { + None, + Default, + Overridable, + Static, + Base, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct QueryPosition { + pub object: usize, + pub guid: usize, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum SignatureKind { + Query(QueryPosition), + QueryOptional(QueryPosition), + ResultValue, + ResultVoid, + ReturnStruct, + ReturnValue, + ReturnVoid, + PreserveSig, +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum SignatureParamKind { + ArrayFixed(usize), + ArrayRelativeLen(usize), + ArrayRelativeByteLen(usize), + ArrayRelativePtr(usize), + TryInto, + IntoParam, + OptionalPointer, + ValueType, + Blittable, + Other, +} + +impl SignatureParamKind { + fn is_array(&self) -> bool { + matches!(self, Self::ArrayFixed(_) | Self::ArrayRelativeLen(_) | Self::ArrayRelativeByteLen(_) | Self::ArrayRelativePtr(_)) + } +} + +#[derive(PartialEq, Eq)] +pub enum AsyncKind { + None, + Action, + ActionWithProgress, + Operation, + OperationWithProgress, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] +pub enum TypeKind { + Interface, + Class, + Enum, + Struct, + Delegate, +} + +#[derive(Debug)] +pub enum Value { + Bool(bool), + U8(u8), + I8(i8), + U16(u16), + I16(i16), + U32(u32), + I32(i32), + U64(u64), + I64(i64), + F32(f32), + F64(f64), + String(String), + TypeDef(TypeDef), + TypeRef(TypeDefOrRef), + EnumDef(TypeDef, Box), + EnumRef(TypeDefOrRef, Box), +} + +pub struct Signature { + pub def: MethodDef, + pub params: Vec, + pub return_type: Type, + pub call_flags: MethodCallAttributes, +} + +pub struct SignatureParam { + pub def: Param, + pub ty: Type, + pub kind: SignatureParamKind, +} + +#[derive(Default, Clone)] +pub struct Cfg<'a> { + pub types: BTreeMap<&'a str, BTreeSet>, + pub core_types: BTreeSet, + pub arches: BTreeSet<&'static str>, + pub implement: bool, +} + +impl<'a> Cfg<'a> { + pub fn add_feature(&mut self, feature: &'a str) { + self.types.entry(feature).or_default(); + } + pub fn union(&self, other: &Self) -> Self { + let mut union = Self::default(); + self.types.keys().for_each(|feature| { + union.types.entry(feature).or_default(); + }); + other.types.keys().for_each(|feature| { + union.types.entry(feature).or_default(); + }); + self.arches.iter().for_each(|arch| { + union.arches.insert(arch); + }); + other.arches.iter().for_each(|arch| { + union.arches.insert(arch); + }); + union + } +} + +pub struct Reader<'a> { + files: &'a [File], + types: BTreeMap<&'a str, BTreeMap<&'a str, Vec>>, + nested: HashMap>, +} + +impl<'a> Reader<'a> { + pub fn new(files: &'a [File]) -> Self { + let mut types = BTreeMap::<&'a str, BTreeMap<&'a str, Vec>>::new(); + let mut nested = HashMap::>::new(); + for (file_index, file) in files.iter().enumerate() { + for row in 0..file.tables[TABLE_TYPEDEF].len { + let key = Row::new(row, TABLE_TYPEDEF, file_index); + let namespace = file.str(key.row as _, key.table as _, 2); + if namespace.is_empty() { + continue; + } + let name = trim_tick(file.str(key.row as _, key.table as _, 1)); + types.entry(namespace).or_default().entry(name).or_default().push(TypeDef(key)); + } + for row in 0..file.tables[TABLE_NESTEDCLASS].len { + let key = Row::new(row, TABLE_NESTEDCLASS, file_index); + let inner = Row::new(file.usize(key.row as _, key.table as _, 0) - 1, TABLE_TYPEDEF, file_index); + let outer = Row::new(file.usize(key.row as _, key.table as _, 1) - 1, TABLE_TYPEDEF, file_index); + let name = file.str(inner.row as _, inner.table as _, 1); + nested.entry(TypeDef(outer)).or_default().insert(name, TypeDef(inner)); + } + } + Self { files, types, nested } + } + + // + // Hash functions for fast type lookup + // + + pub fn namespaces(&self) -> impl Iterator + '_ { + self.types.keys().copied() + } + pub fn types(&'a self, filter: &'a Filter) -> impl Iterator + '_ { + self.types.iter().filter(move |(namespace, _)| filter.includes_namespace(namespace)).flat_map(move |(_, types)| types.values().flatten().copied().filter(move |def| filter.includes_type(self, *def))) + } + pub fn namespace_types(&'a self, namespace: &str, filter: &'a Filter) -> impl Iterator + '_ { + self.types.get(namespace).map(move |types| types.values().flatten().copied().filter(move |ty| filter.includes_type(self, *ty))).into_iter().flatten() + } + pub fn nested_types(&self, type_def: TypeDef) -> impl Iterator + '_ { + self.nested.get(&type_def).map(|map| map.values().copied()).into_iter().flatten() + } + pub fn get(&self, type_name: TypeName) -> impl Iterator + '_ { + if let Some(types) = self.types.get(type_name.namespace) { + if let Some(definitions) = types.get(type_name.name) { + return Some(definitions.iter().copied()).into_iter().flatten(); + } + } + None.into_iter().flatten() + } + pub fn namespace_functions(&self, namespace: &str) -> impl Iterator + '_ { + self.get(TypeName::new(namespace, "Apis")).flat_map(move |apis| self.type_def_methods(apis)).filter(move |method| { + // The ImplMap table contains import information, without which the function cannot be linked. + let Some(impl_map) = self.method_def_impl_map(*method) else { + return false; + }; + + // Skip functions exported by ordinal. + if self.impl_map_import_name(impl_map).starts_with('#') { + return false; + } + + // Skip "inline" function constants. + self.module_ref_name(self.impl_map_scope(impl_map)) != "FORCEINLINE" + }) + } + pub fn namespace_constants(&self, namespace: &str) -> impl Iterator + '_ { + self.get(TypeName::new(namespace, "Apis")).flat_map(move |apis| self.type_def_fields(apis)) + } + + // + // Row functions providing low-level file access + // + + fn row_usize(&self, key: Row, column: usize) -> usize { + self.files[key.file as usize].usize(key.row as _, key.table as _, column) + } + fn row_str(&self, key: Row, column: usize) -> &str { + self.files[key.file as usize].str(key.row as _, key.table as _, column) + } + pub fn row_blob(&self, key: Row, column: usize) -> Blob { + let file = key.file as usize; + Blob::new(file, self.files[file].blob(key.row as _, key.table as _, column)) + } + fn row_equal_range(&self, key: Row, table: usize, column: usize, value: usize) -> impl Iterator { + let (first, last) = self.files[key.file as usize].equal_range(table, column, value); + (first..last).map(move |row| Row::new(row, table, key.file as _)) + } + fn row_attributes(&self, key: Row, source: HasAttribute) -> impl Iterator { + self.row_equal_range(key, TABLE_CUSTOMATTRIBUTE, 0, source.encode()).map(Attribute) + } + fn row_list(&self, key: Row, table: usize, column: usize) -> impl Iterator { + let file = key.file as usize; + let first = self.row_usize(key, column) - 1; + let last = if key.row + 1 < self.files[file].tables[key.table as usize].len as _ { self.row_usize(key.next(), column) - 1 } else { self.files[file].tables[table].len }; + (first..last).map(move |row| Row::new(row, table, file)) + } + fn row_decode(&self, key: Row, column: usize) -> T { + T::decode(key.file as _, self.row_usize(key, column)) + } + + // + // Attribute table queries + // + + pub fn attribute_type_name(&self, row: Attribute) -> TypeName { + let AttributeType::MemberRef(row) = self.row_decode(row.0, 1); + let MemberRefParent::TypeRef(row) = self.row_decode(row.0, 0); + self.type_ref_type_name(row) + } + pub fn attribute_name(&self, row: Attribute) -> &str { + let AttributeType::MemberRef(row) = self.row_decode(row.0, 1); + let MemberRefParent::TypeRef(row) = self.row_decode(row.0, 0); + self.type_ref_name(row) + } + pub fn attribute_args(&self, row: Attribute) -> Vec<(String, Value)> { + let AttributeType::MemberRef(member) = self.row_decode(row.0, 1); + let mut sig = self.member_ref_signature(member); + let mut values = self.row_blob(row.0, 2); + let _prolog = values.read_u16(); + let _this_and_gen_param_count = sig.read_usize(); + let fixed_arg_count = sig.read_usize(); + let _ret_type = sig.read_usize(); + let mut args: Vec<(String, Value)> = Vec::with_capacity(fixed_arg_count); + + for _ in 0..fixed_arg_count { + let arg = match self.type_from_blob(&mut sig, None, &[]) { + Type::Bool => Value::Bool(values.read_bool()), + Type::I8 => Value::I8(values.read_i8()), + Type::U8 => Value::U8(values.read_u8()), + Type::I16 => Value::I16(values.read_i16()), + Type::U16 => Value::U16(values.read_u16()), + Type::I32 => Value::I32(values.read_i32()), + Type::U32 => Value::U32(values.read_u32()), + Type::I64 => Value::I64(values.read_i64()), + Type::U64 => Value::U64(values.read_u64()), + Type::String => Value::String(values.read_str().to_string()), + Type::TypeName => Value::TypeDef(self.get(TypeName::parse(values.read_str())).next().expect("Type not found")), + Type::TypeDef(def, _) => Value::EnumDef(def, Box::new(values.read_integer(self.type_def_underlying_type(def)))), + // It's impossible to know the type of a TypeRef so we just assume 32-bit integer which covers System.* attribute args + // reasonably well but the solution is to follow the WinRT metadata and define replacements for those attribute types. + Type::TypeRef(code) => Value::EnumRef(code, Box::new(values.read_integer(Type::I32))), + rest => todo!("{:?}", rest), + }; + + args.push((String::new(), arg)); + } + + let named_arg_count = values.read_u16(); + args.reserve(named_arg_count as usize); + + for _ in 0..named_arg_count { + let _id = values.read_u8(); + let arg_type = values.read_u8(); + let mut name = values.read_str().to_string(); + let arg = match arg_type as _ { + ELEMENT_TYPE_BOOLEAN => Value::Bool(values.read_bool()), + ELEMENT_TYPE_I2 => Value::I16(values.read_i16()), + ELEMENT_TYPE_I4 => Value::I32(values.read_i32()), + ELEMENT_TYPE_U4 => Value::U32(values.read_u32()), + ELEMENT_TYPE_STRING => Value::String(values.read_str().to_string()), + 0x50 => Value::TypeDef(self.get(TypeName::parse(values.read_str())).next().expect("Type not found")), + 0x55 => { + let def = self.get(TypeName::parse(&name)).next().expect("Type not found"); + name = values.read_str().into(); + Value::EnumDef(def, Box::new(values.read_integer(self.type_def_underlying_type(def)))) + } + _ => todo!("{:?}", arg_type), + }; + args.push((name, arg)); + } + + assert_eq!(sig.slice.len(), 0); + assert_eq!(values.slice.len(), 0); + + args + } + + // + // ClassLayout table queries + // + + pub fn class_layout_packing_size(&self, row: ClassLayout) -> usize { + self.row_usize(row.0, 0) + } + + // + // Constant table queries + // + + pub fn constant_type(&self, row: Constant) -> Type { + let code = self.row_usize(row.0, 0); + Type::from_code(code).expect("Type not found") + } + pub fn constant_value(&self, row: Constant) -> Value { + let mut blob = self.row_blob(row.0, 2); + match self.constant_type(row) { + Type::I8 => Value::I8(blob.read_i8()), + Type::U8 => Value::U8(blob.read_u8()), + Type::I16 => Value::I16(blob.read_i16()), + Type::U16 => Value::U16(blob.read_u16()), + Type::I32 => Value::I32(blob.read_i32()), + Type::U32 => Value::U32(blob.read_u32()), + Type::I64 => Value::I64(blob.read_i64()), + Type::U64 => Value::U64(blob.read_u64()), + Type::F32 => Value::F32(blob.read_f32()), + Type::F64 => Value::F64(blob.read_f64()), + Type::String => Value::String(blob.read_string()), + _ => unimplemented!(), + } + } + + // + // Field table queries + // + + pub fn field_flags(&self, row: Field) -> FieldAttributes { + FieldAttributes(self.row_usize(row.0, 0) as _) + } + pub fn field_name(&self, row: Field) -> &str { + self.row_str(row.0, 1) + } + pub fn field_constant(&self, row: Field) -> Option { + self.row_equal_range(row.0, TABLE_CONSTANT, 1, HasConstant::Field(row).encode()).map(Constant).next() + } + pub fn field_attributes(&self, row: Field) -> impl Iterator { + self.row_attributes(row.0, HasAttribute::Field(row)) + } + pub fn field_is_const(&self, row: Field) -> bool { + self.field_attributes(row).any(|attribute| self.attribute_name(attribute) == "ConstAttribute") + } + pub fn field_type(&self, row: Field, enclosing: Option) -> Type { + let mut blob = self.row_blob(row.0, 2); + blob.read_usize(); + blob.read_modifiers(); + let def = self.type_from_blob(&mut blob, enclosing, &[]); + + if self.field_is_const(row) { + def.to_const_type().to_const_ptr() + } else { + def + } + } + pub fn field_is_blittable(&self, row: Field, enclosing: TypeDef) -> bool { + self.type_is_blittable(&self.field_type(row, Some(enclosing))) + } + pub fn field_is_copyable(&self, row: Field, enclosing: TypeDef) -> bool { + self.type_is_copyable(&self.field_type(row, Some(enclosing))) + } + pub fn field_guid(&self, row: Field) -> Option { + for attribute in self.field_attributes(row) { + if self.attribute_name(attribute) == "GuidAttribute" { + return Some(GUID::from_args(&self.attribute_args(attribute))); + } + } + None + } + pub fn field_cfg(&self, row: Field) -> Cfg { + let mut cfg = Cfg::default(); + self.field_cfg_combine(row, None, &mut cfg); + cfg + } + fn field_cfg_combine(&'a self, row: Field, enclosing: Option, cfg: &mut Cfg<'a>) { + self.type_cfg_combine(&self.field_type(row, enclosing), cfg) + } + pub fn field_is_ansi(&self, row: Field) -> bool { + for attribute in self.field_attributes(row) { + if self.attribute_name(attribute) == "NativeEncodingAttribute" { + if let Some((_, Value::String(encoding))) = self.attribute_args(attribute).get(0) { + if encoding == "ansi" { + return true; + } + } + } + } + false + } + + // + // GenericParam table queries + // + + pub fn generic_param_name(&self, row: GenericParam) -> &str { + self.row_str(row.0, 3) + } + + // + // ImplMap table queries + // + + pub fn impl_map_flags(&self, row: ImplMap) -> PInvokeAttributes { + PInvokeAttributes(self.row_usize(row.0, 0)) + } + pub fn impl_map_scope(&self, row: ImplMap) -> ModuleRef { + ModuleRef(Row::new(self.row_usize(row.0, 3) - 1, TABLE_MODULEREF, row.0.file as _)) + } + pub fn impl_map_import_name(&self, row: ImplMap) -> &str { + self.row_str(row.0, 2) + } + + // + // InterfaceImpl table queries + // + + pub fn interface_impl_attributes(&self, row: InterfaceImpl) -> impl Iterator { + self.row_attributes(row.0, HasAttribute::InterfaceImpl(row)) + } + pub fn interface_impl_is_default(&self, row: InterfaceImpl) -> bool { + self.interface_impl_attributes(row).any(|attribute| self.attribute_name(attribute) == "DefaultAttribute") + } + pub fn interface_impl_is_overridable(&self, row: InterfaceImpl) -> bool { + self.interface_impl_attributes(row).any(|attribute| self.attribute_name(attribute) == "OverridableAttribute") + } + pub fn interface_impl_type(&self, row: InterfaceImpl, generics: &[Type]) -> Interface { + let mut kind = InterfaceKind::None; + for attribute in self.interface_impl_attributes(row) { + match self.attribute_name(attribute) { + "DefaultAttribute" => kind = InterfaceKind::Default, + "OverridableAttribute" => kind = InterfaceKind::Overridable, + _ => {} + } + } + Interface { ty: self.type_from_ref(self.row_decode(row.0, 1), None, generics), kind } + } + + // + // MemberRef table queries + // + + pub fn member_ref_parent(&self, row: MemberRef) -> MemberRefParent { + self.row_decode(row.0, 0) + } + pub fn member_ref_signature(&self, row: MemberRef) -> Blob { + self.row_blob(row.0, 2) + } + + // + // MethodDef table queries + // + + pub fn method_def_impl_flags(&self, row: MethodDef) -> MethodImplAttributes { + MethodImplAttributes(self.row_usize(row.0, 1)) + } + pub fn method_def_flags(&self, row: MethodDef) -> MethodAttributes { + MethodAttributes(self.row_usize(row.0, 2) as _) + } + pub fn method_def_name(&self, row: MethodDef) -> &str { + self.row_str(row.0, 3) + } + pub fn method_def_params(&self, row: MethodDef) -> impl Iterator { + self.row_list(row.0, TABLE_PARAM, 5).map(Param) + } + pub fn method_def_attributes(&self, row: MethodDef) -> impl Iterator { + self.row_attributes(row.0, HasAttribute::MethodDef(row)) + } + pub fn method_def_is_deprecated(&self, row: MethodDef) -> bool { + self.method_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "DeprecatedAttribute") + } + pub fn method_def_does_not_return(&self, row: MethodDef) -> bool { + self.method_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "DoesNotReturnAttribute") + } + pub fn method_def_can_return_multiple_success_values(&self, row: MethodDef) -> bool { + self.method_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "CanReturnMultipleSuccessValuesAttribute") + } + pub fn method_def_special_name(&self, row: MethodDef) -> String { + let name = self.method_def_name(row); + if self.method_def_flags(row).contains(MethodAttributes::SpecialName) { + if name.starts_with("get") { + name[4..].to_string() + } else if name.starts_with("put") { + format!("Set{}", &name[4..]) + } else if name.starts_with("add") { + name[4..].to_string() + } else if name.starts_with("remove") { + format!("Remove{}", &name[7..]) + } else { + name.to_string() + } + } else { + for attribute in self.method_def_attributes(row) { + if self.attribute_name(attribute) == "OverloadAttribute" { + for (_, arg) in self.attribute_args(attribute) { + if let Value::String(name) = arg { + return name; + } + } + } + } + name.to_string() + } + } + pub fn method_def_static_lib(&self, row: MethodDef) -> Option { + for attribute in self.method_def_attributes(row) { + if self.attribute_name(attribute) == "StaticLibraryAttribute" { + let args = self.attribute_args(attribute); + if let Value::String(value) = &args[0].1 { + return Some(value.clone()); + } + } + } + None + } + pub fn method_def_impl_map(&self, row: MethodDef) -> Option { + self.row_equal_range(row.0, TABLE_IMPLMAP, 1, MemberForwarded::MethodDef(row).encode()).map(ImplMap).next() + } + pub fn method_def_module_name(&self, row: MethodDef) -> String { + let Some(impl_map) = self.method_def_impl_map(row) else { + return String::new(); + }; + self.module_ref_name(self.impl_map_scope(impl_map)).to_lowercase() + } + pub fn method_def_last_error(&self, row: MethodDef) -> bool { + if let Some(map) = self.method_def_impl_map(row) { + self.impl_map_flags(map).contains(PInvokeAttributes::SupportsLastError) + } else { + false + } + } + pub fn method_def_signature(&self, row: MethodDef, generics: &[Type]) -> Signature { + let mut blob = self.row_blob(row.0, 4); + let call_flags = MethodCallAttributes(blob.read_usize() as _); + let _param_count = blob.read_usize(); + let mut return_type = self.type_from_blob(&mut blob, None, generics); + + let mut params: Vec = self + .method_def_params(row) + .filter_map(|param| { + if self.param_sequence(param) == 0 { + if self.param_is_const(param) { + return_type = return_type.clone().to_const_type(); + } + None + } else { + let is_output = self.param_flags(param).contains(ParamAttributes::Out); + let mut ty = self.type_from_blob(&mut blob, None, generics); + if self.param_is_const(param) || !is_output { + ty = ty.to_const_type(); + } + if !is_output { + ty = ty.to_const_ptr(); + } + let kind = self.param_kind(param); + Some(SignatureParam { def: param, ty, kind }) + } + }) + .collect(); + + for position in 0..params.len() { + // Point len params back to the corresponding ptr params. + match params[position].kind { + SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { + // The len params must be input only. + if !self.param_flags(params[relative].def).contains(ParamAttributes::Out) && position != relative && !params[relative].ty.is_pointer() { + params[relative].kind = SignatureParamKind::ArrayRelativePtr(position); + } else { + params[position].kind = SignatureParamKind::Other; + } + } + SignatureParamKind::ArrayFixed(_) => { + if self.param_free_with(params[position].def).is_some() { + params[position].kind = SignatureParamKind::Other; + } + } + _ => {} + } + } + + let mut sets = BTreeMap::>::new(); + + // Finds sets of ptr params pointing at the same len param. + for (position, param) in params.iter().enumerate() { + match param.kind { + SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { + sets.entry(relative).or_default().push(position); + } + _ => {} + } + } + + // Remove all sets. + for (len, ptrs) in sets { + if ptrs.len() > 1 { + params[len].kind = SignatureParamKind::Other; + for ptr in ptrs { + params[ptr].kind = SignatureParamKind::Other; + } + } + } + + // Remove any byte arrays that aren't byte-sized types. + for position in 0..params.len() { + if let SignatureParamKind::ArrayRelativeByteLen(relative) = params[position].kind { + if !params[position].ty.is_byte_size() { + params[position].kind = SignatureParamKind::Other; + params[relative].kind = SignatureParamKind::Other; + } + } + } + + for param in &mut params { + if param.kind == SignatureParamKind::Other { + if self.signature_param_is_convertible(param) { + if self.signature_param_is_failible_param(param) { + param.kind = SignatureParamKind::TryInto; + } else { + param.kind = SignatureParamKind::IntoParam; + } + } else { + let flags = self.param_flags(param.def); + if param.ty.is_pointer() && (flags.contains(ParamAttributes::Optional) || self.param_is_reserved(param.def)) { + param.kind = SignatureParamKind::OptionalPointer; + } else if self.type_is_primitive(¶m.ty) && (!param.ty.is_pointer() || self.type_is_blittable(¶m.ty.deref())) { + param.kind = SignatureParamKind::ValueType; + } else if self.type_is_blittable(¶m.ty) { + param.kind = SignatureParamKind::Blittable; + } + } + } + } + + Signature { def: row, params, return_type, call_flags } + } + pub fn method_def_extern_abi(&self, def: MethodDef) -> &'static str { + let impl_map = self.method_def_impl_map(def).expect("ImplMap not found"); + let flags = self.impl_map_flags(impl_map); + + if flags.contains(PInvokeAttributes::CallConvPlatformapi) { + "system" + } else if flags.contains(PInvokeAttributes::CallConvCdecl) { + "cdecl" + } else { + unimplemented!() + } + } + pub fn method_def_size(&self, method: MethodDef) -> usize { + let signature = self.method_def_signature(method, &[]); + signature.params.iter().fold(0, |sum, param| sum + std::cmp::max(4, self.type_size(¶m.ty))) + } + pub fn type_def_size(&self, def: TypeDef) -> usize { + match self.type_def_kind(def) { + TypeKind::Struct => { + if self.type_def_flags(def).contains(TypeAttributes::ExplicitLayout) { + self.type_def_fields(def).map(|field| self.type_size(&self.field_type(field, Some(def)))).max().unwrap_or(1) + } else { + let mut sum = 0; + for field in self.type_def_fields(def) { + let size = self.type_size(&self.field_type(field, Some(def))); + let align = self.type_align(&self.field_type(field, Some(def))); + sum = (sum + (align - 1)) & !(align - 1); + sum += size; + } + sum + } + } + TypeKind::Enum => self.type_size(&self.type_def_underlying_type(def)), + _ => 4, + } + } + fn type_size(&self, ty: &Type) -> usize { + match ty { + Type::I8 | Type::U8 => 1, + Type::I16 | Type::U16 => 2, + Type::I64 | Type::U64 | Type::F64 => 8, + Type::GUID => 16, + Type::TypeDef(def, _) => self.type_def_size(*def), + Type::Win32Array(ty, len) => self.type_size(ty) * len, + _ => 4, + } + } + fn type_def_align(&self, def: TypeDef) -> usize { + match self.type_def_kind(def) { + TypeKind::Struct => self.type_def_fields(def).map(|field| self.type_align(&self.field_type(field, Some(def)))).max().unwrap_or(1), + TypeKind::Enum => self.type_align(&self.type_def_underlying_type(def)), + _ => 4, + } + } + fn type_align(&self, ty: &Type) -> usize { + match ty { + Type::I8 | Type::U8 => 1, + Type::I16 | Type::U16 => 2, + Type::I64 | Type::U64 | Type::F64 => 8, + Type::GUID => 4, + Type::TypeDef(def, _) => self.type_def_align(*def), + Type::Win32Array(ty, len) => self.type_align(ty) * len, + _ => 4, + } + } + + // + // ModuleRef table queries + // + + fn module_ref_name(&self, row: ModuleRef) -> &str { + self.row_str(row.0, 0) + } + + // + // Param table queries + // + + pub fn param_flags(&self, row: Param) -> ParamAttributes { + ParamAttributes(self.row_usize(row.0, 0) as _) + } + pub fn param_sequence(&self, row: Param) -> usize { + self.row_usize(row.0, 1) + } + pub fn param_name(&self, row: Param) -> &str { + self.row_str(row.0, 2) + } + pub fn param_attributes(&self, row: Param) -> impl Iterator { + self.row_attributes(row.0, HasAttribute::Param(row)) + } + pub fn param_is_com_out_ptr(&self, row: Param) -> bool { + self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "ComOutPtrAttribute") + } + fn param_kind(&self, row: Param) -> SignatureParamKind { + for attribute in self.param_attributes(row) { + match self.attribute_name(attribute) { + "NativeArrayInfoAttribute" => { + for (_, value) in self.attribute_args(attribute) { + match value { + Value::I16(value) => return SignatureParamKind::ArrayRelativeLen(value as _), + Value::I32(value) => return SignatureParamKind::ArrayFixed(value as _), + _ => {} + } + } + } + "MemorySizeAttribute" => { + for (_, value) in self.attribute_args(attribute) { + if let Value::I16(value) = value { + return SignatureParamKind::ArrayRelativeByteLen(value as _); + } + } + } + _ => {} + } + } + SignatureParamKind::Other + } + pub fn param_is_retval(&self, row: Param) -> bool { + self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "RetValAttribute") + } + pub fn param_is_reserved(&self, row: Param) -> bool { + self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "ReservedAttribute") + } + pub fn param_free_with(&self, row: Param) -> Option { + for attribute in self.param_attributes(row) { + if self.attribute_name(attribute) == "FreeWithAttribute" { + for (_, arg) in self.attribute_args(attribute) { + if let Value::String(name) = arg { + return Some(name); + } + } + } + } + None + } + pub fn param_is_const(&self, row: Param) -> bool { + self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "ConstAttribute") + } + + // + // TypeDef table queries + // + + pub fn type_def_flags(&self, row: TypeDef) -> TypeAttributes { + TypeAttributes(self.row_usize(row.0, 0) as _) + } + pub fn type_def_name(&self, row: TypeDef) -> &str { + self.row_str(row.0, 1) + } + pub fn type_def_namespace(&self, row: TypeDef) -> &str { + self.row_str(row.0, 2) + } + pub fn type_def_type_name(&self, row: TypeDef) -> TypeName { + TypeName::new(self.type_def_namespace(row), self.type_def_name(row)) + } + pub fn type_def_extends(&self, row: TypeDef) -> Option { + match self.row_usize(row.0, 3) { + 0 => None, + code => Some(self.type_def_or_ref(TypeDefOrRef::decode(row.0.file as _, code))), + } + } + pub fn type_def_fields(&self, row: TypeDef) -> impl Iterator { + self.row_list(row.0, TABLE_FIELD, 4).map(Field) + } + pub fn type_def_methods(&self, row: TypeDef) -> impl Iterator { + self.row_list(row.0, TABLE_METHODDEF, 5).map(MethodDef) + } + pub fn type_def_attributes(&self, row: TypeDef) -> impl Iterator { + self.row_attributes(row.0, HasAttribute::TypeDef(row)) + } + pub fn type_def_generics(&self, row: TypeDef) -> impl Iterator { + self.row_equal_range(row.0, TABLE_GENERICPARAM, 2, TypeOrMethodDef::TypeDef(row).encode()).map(|row| Type::GenericParam(GenericParam(row))) + } + pub fn type_def_interface_impls(&self, row: TypeDef) -> impl Iterator { + self.row_equal_range(row.0, TABLE_INTERFACEIMPL, 0, (row.0.row + 1) as _).map(InterfaceImpl) + } + pub fn type_def_enclosing_type(&self, row: TypeDef) -> Option { + self.row_equal_range(row.0, TABLE_NESTEDCLASS, 0, (row.0.row + 1) as _).next().map(|row| TypeDef(Row::new(self.files[row.file as usize].usize(row.row as _, row.table as _, 1) - 1, TABLE_TYPEDEF, row.file as _))) + } + pub fn type_def_class_layout(&self, row: TypeDef) -> Option { + self.row_equal_range(row.0, TABLE_CLASSLAYOUT, 2, (row.0.row + 1) as _).map(ClassLayout).next() + } + pub fn type_def_underlying_type(&self, row: TypeDef) -> Type { + let field = self.type_def_fields(row).next().expect("Field not found"); + if let Some(constant) = self.field_constant(field) { + self.constant_type(constant) + } else { + self.field_type(field, Some(row)) + } + } + pub fn type_def_kind(&self, row: TypeDef) -> TypeKind { + match self.type_def_extends(row) { + None => TypeKind::Interface, + Some(TypeName::Enum) => TypeKind::Enum, + Some(TypeName::Delegate) => TypeKind::Delegate, + Some(TypeName::Struct) => TypeKind::Struct, + Some(_) => TypeKind::Class, + } + } + pub fn type_def_stdcall(&self, row: TypeDef) -> usize { + if self.type_def_kind(row) == TypeKind::Struct { + if self.type_def_flags(row).contains(TypeAttributes::ExplicitLayout) { + self.type_def_fields(row).map(|field| self.type_stdcall(&self.field_type(field, Some(row)))).max().unwrap_or(1) + } else { + self.type_def_fields(row).fold(0, |sum, field| sum + self.type_stdcall(&self.field_type(field, Some(row)))) + } + } else { + 4 + } + } + pub fn type_def_is_blittable(&self, row: TypeDef) -> bool { + match self.type_def_kind(row) { + TypeKind::Struct => { + if self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) { + self.type_def_fields(row).all(|field| self.field_is_blittable(field, row)) + } else { + true + } + } + TypeKind::Enum => true, + TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), + _ => false, + } + } + pub fn type_def_is_copyable(&self, row: TypeDef) -> bool { + match self.type_def_kind(row) { + TypeKind::Struct => self.type_def_fields(row).all(|field| self.field_is_copyable(field, row)), + TypeKind::Enum => true, + TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), + _ => false, + } + } + pub fn type_def_is_callback(&self, row: TypeDef) -> bool { + !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) && self.type_def_kind(row) == TypeKind::Delegate + } + pub fn type_def_has_default_constructor(&self, row: TypeDef) -> bool { + for attribute in self.type_def_attributes(row) { + if self.attribute_name(attribute) == "ActivatableAttribute" { + if self.attribute_args(attribute).iter().any(|arg| matches!(arg.1, Value::TypeDef(_))) { + continue; + } else { + return true; + } + } + } + false + } + // TODO: consider removing all the expects and just return Option and let the caller expect it + // that way the metadata reader is a little more schema-agnostic... + pub fn type_def_invoke_method(&self, row: TypeDef) -> MethodDef { + self.type_def_methods(row).find(|method| self.method_def_name(*method) == "Invoke").expect("`Invoke` method not found") + } + pub fn type_def_interfaces(&'a self, row: TypeDef, generics: &'a [Type]) -> impl Iterator + '_ { + self.type_def_interface_impls(row).map(move |row| self.interface_impl_type(row, generics)) + } + pub fn type_def_default_interface(&self, row: TypeDef) -> Option { + self.type_def_interfaces(row, &[]).find(|interface| interface.kind == InterfaceKind::Default).map(|interface| interface.ty) + } + pub fn type_def_has_default_interface(&self, row: TypeDef) -> bool { + self.type_def_interface_impls(row).any(|imp| self.interface_impl_is_default(imp)) + } + pub fn type_def_is_deprecated(&self, row: TypeDef) -> bool { + self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "DeprecatedAttribute") + } + pub fn type_def_is_handle(&self, row: TypeDef) -> bool { + self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "NativeTypedefAttribute") + } + pub fn type_def_is_exclusive(&self, row: TypeDef) -> bool { + self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ExclusiveToAttribute") + } + pub fn type_def_is_scoped(&self, row: TypeDef) -> bool { + self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) || self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ScopedEnumAttribute") + } + pub fn type_def_is_contract(&self, row: TypeDef) -> bool { + self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ApiContractAttribute") + } + fn type_def_is_composable(&self, row: TypeDef) -> bool { + self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ComposableAttribute") + } + fn type_def_is_struct(&self, row: TypeDef) -> bool { + // This check is used to detect virtual functions that return C-style PODs that affect how the stack is packed for x86. + // It could be defined as a struct with more than one field but that check is complicated as it would have to detect + // nested structs. Fortunately, this is rare enough that this check is sufficient. + self.type_def_kind(row) == TypeKind::Struct && !self.type_def_is_handle(row) + } + pub fn type_def_is_trivially_convertible(&self, row: TypeDef) -> bool { + match self.type_def_kind(row) { + TypeKind::Struct => self.type_def_is_handle(row), + _ => false, + } + } + pub fn type_def_is_primitive(&self, row: TypeDef) -> bool { + match self.type_def_kind(row) { + TypeKind::Enum => true, + TypeKind::Struct => self.type_def_is_handle(row), + TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), + _ => false, + } + } + pub fn type_def_has_explicit_layout(&self, row: TypeDef) -> bool { + if self.type_def_kind(row) != TypeKind::Struct { + return false; + } + fn check(reader: &Reader, row: TypeDef) -> bool { + if reader.type_def_flags(row).contains(TypeAttributes::ExplicitLayout) { + return true; + } + if reader.type_def_fields(row).any(|field| reader.type_has_explicit_layout(&reader.field_type(field, Some(row)))) { + return true; + } + false + } + let type_name = self.type_def_type_name(row); + if type_name.namespace.is_empty() { + check(self, row) + } else { + for row in self.get(type_name) { + if check(self, row) { + return true; + } + } + false + } + } + pub fn type_def_has_packing(&self, row: TypeDef) -> bool { + if self.type_def_kind(row) != TypeKind::Struct { + return false; + } + fn check(reader: &Reader, row: TypeDef) -> bool { + if reader.type_def_class_layout(row).is_some() { + return true; + } + if reader.type_def_fields(row).any(|field| reader.type_has_packing(&reader.field_type(field, Some(row)))) { + return true; + } + false + } + let type_name = self.type_def_type_name(row); + if type_name.namespace.is_empty() { + check(self, row) + } else { + for row in self.get(type_name) { + if check(self, row) { + return true; + } + } + false + } + } + pub fn type_def_has_callback(&self, row: TypeDef) -> bool { + if self.type_def_is_callback(row) { + return true; + } + if self.type_def_kind(row) != TypeKind::Struct { + return false; + } + fn check(reader: &Reader, row: TypeDef) -> bool { + if reader.type_def_fields(row).any(|field| reader.type_has_callback(&reader.field_type(field, Some(row)))) { + return true; + } + false + } + let type_name = self.type_def_type_name(row); + if type_name.namespace.is_empty() { + check(self, row) + } else { + for row in self.get(type_name) { + if check(self, row) { + return true; + } + } + false + } + } + pub fn type_def_guid(&self, row: TypeDef) -> Option { + for attribute in self.type_def_attributes(row) { + if self.attribute_name(attribute) == "GuidAttribute" { + return Some(GUID::from_args(&self.attribute_args(attribute))); + } + } + None + } + pub fn type_def_bases(&self, mut row: TypeDef) -> Vec { + let mut bases = Vec::new(); + loop { + match self.type_def_extends(row) { + Some(base) if base != TypeName::Object => { + row = self.get(base).next().expect("Type not found"); + bases.push(row); + } + _ => break, + } + } + bases + } + pub fn type_def_is_flags(&self, row: TypeDef) -> bool { + // Win32 enums use the Flags attribute. WinRT enums don't have the Flags attribute but are paritioned merely based + // on whether they are signed. + self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "FlagsAttribute") || (self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) && self.type_def_underlying_type(row) == Type::U32) + } + pub fn type_def_is_agile(&self, row: TypeDef) -> bool { + for attribute in self.type_def_attributes(row) { + match self.attribute_name(attribute) { + "AgileAttribute" => return true, + "MarshalingBehaviorAttribute" => { + if let Some((_, Value::EnumDef(_, value))) = self.attribute_args(attribute).get(0) { + if let Value::I32(2) = **value { + return true; + } + } + } + _ => {} + } + } + matches!(self.type_def_type_name(row), TypeName::IAsyncAction | TypeName::IAsyncActionWithProgress | TypeName::IAsyncOperation | TypeName::IAsyncOperationWithProgress) + } + pub fn type_def_invalid_values(&self, row: TypeDef) -> Vec { + let mut values = Vec::new(); + for attribute in self.type_def_attributes(row) { + if self.attribute_name(attribute) == "InvalidHandleValueAttribute" { + if let Some((_, Value::I64(value))) = self.attribute_args(attribute).get(0) { + values.push(*value); + } + } + } + values + } + pub fn type_def_usable_for(&self, row: TypeDef) -> Option { + for attribute in self.type_def_attributes(row) { + if self.attribute_name(attribute) == "AlsoUsableForAttribute" { + if let Some((_, Value::String(name))) = self.attribute_args(attribute).get(0) { + return self.get(TypeName::new(self.type_def_namespace(row), name.as_str())).next(); + } + } + } + None + } + pub fn type_def_is_nullable(&self, row: TypeDef) -> bool { + match self.type_def_kind(row) { + TypeKind::Interface | TypeKind::Class => true, + // Win32 callbacks are defined as `Option` so we don't include them here to avoid them + // from being doubly wrapped in `Option`. + TypeKind::Delegate => self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), + _ => false, + } + } + pub fn type_def_can_implement(&self, row: TypeDef) -> bool { + for attribute in self.type_def_attributes(row) { + if self.attribute_name(attribute) == "ExclusiveToAttribute" { + for (_, arg) in self.attribute_args(attribute) { + if let Value::TypeDef(def) = arg { + for child in self.type_def_interfaces(def, &[]) { + if child.kind == InterfaceKind::Overridable { + if let Type::TypeDef(def, _) = child.ty { + if self.type_def_type_name(def) == self.type_def_type_name(row) { + return true; + } + } + } + } + } + } + return false; + } + } + true + } + pub fn type_def_async_kind(&self, row: TypeDef) -> AsyncKind { + match self.type_def_type_name(row) { + TypeName::IAsyncAction => AsyncKind::Action, + TypeName::IAsyncActionWithProgress => AsyncKind::ActionWithProgress, + TypeName::IAsyncOperation => AsyncKind::Operation, + TypeName::IAsyncOperationWithProgress => AsyncKind::OperationWithProgress, + _ => AsyncKind::None, + } + } + pub fn type_def_signature(&self, row: TypeDef, generics: &[Type]) -> String { + match self.type_def_kind(row) { + TypeKind::Interface => self.type_def_interface_signature(row, generics), + TypeKind::Class => { + if let Type::TypeDef(default, generics) = self.type_def_interfaces(row, generics).find(|row| row.kind == InterfaceKind::Default).expect("Default interface not found").ty { + format!("rc({};{})", self.type_def_type_name(row), self.type_def_interface_signature(default, &generics)) + } else { + unimplemented!(); + } + } + TypeKind::Enum => format!("enum({};{})", self.type_def_type_name(row), self.type_signature(&self.type_def_underlying_type(row))), + TypeKind::Struct => { + let mut result = format!("struct({}", self.type_def_type_name(row)); + for field in self.type_def_fields(row) { + result.push(';'); + result.push_str(&self.type_signature(&self.field_type(field, Some(row)))); + } + result.push(')'); + result + } + TypeKind::Delegate => { + if generics.is_empty() { + format!("delegate({})", self.type_def_interface_signature(row, generics)) + } else { + self.type_def_interface_signature(row, generics) + } + } + } + } + fn type_def_interface_signature(&self, row: TypeDef, generics: &[Type]) -> String { + let guid = self.type_def_guid(row).unwrap(); + if generics.is_empty() { + format!("{{{guid:#?}}}") + } else { + let mut result = format!("pinterface({{{guid:#?}}}"); + for generic in generics { + result.push(';'); + result.push_str(&self.type_signature(generic)); + } + result.push(')'); + result + } + } + pub fn type_def_cfg(&self, row: TypeDef, generics: &[Type]) -> Cfg { + let mut cfg = Cfg::default(); + self.type_def_cfg_combine(row, generics, &mut cfg); + self.cfg_add_attributes(&mut cfg, self.type_def_attributes(row)); + cfg + } + pub fn type_def_cfg_impl(&self, def: TypeDef, generics: &[Type]) -> Cfg { + let mut cfg = Cfg { implement: true, ..Default::default() }; + + fn combine<'a>(reader: &'a Reader, def: TypeDef, generics: &[Type], cfg: &mut Cfg<'a>) { + reader.type_def_cfg_combine(def, generics, cfg); + + for method in reader.type_def_methods(def) { + reader.signature_cfg_combine(&reader.method_def_signature(method, generics), cfg); + } + } + + combine(self, def, generics, &mut cfg); + + for def in self.type_def_vtables(def) { + if let Type::TypeDef(def, generics) = def { + combine(self, def, &generics, &mut cfg); + } + } + + if self.type_def_flags(def).contains(TypeAttributes::WindowsRuntime) { + for interface in self.type_def_interfaces(def, generics) { + if let Type::TypeDef(def, generics) = interface.ty { + combine(self, def, &generics, &mut cfg); + } + } + } + + self.cfg_add_attributes(&mut cfg, self.type_def_attributes(def)); + cfg + } + pub fn type_def_cfg_combine(&'a self, row: TypeDef, generics: &[Type], cfg: &mut Cfg<'a>) { + let type_name = self.type_def_type_name(row); + + for generic in generics { + self.type_cfg_combine(generic, cfg); + } + + if cfg.types.entry(type_name.namespace).or_default().insert(row) { + match self.type_def_kind(row) { + TypeKind::Class => { + if let Some(default_interface) = self.type_def_default_interface(row) { + self.type_cfg_combine(&default_interface, cfg); + } + } + TypeKind::Interface => { + if !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) { + for def in self.type_def_vtables(row) { + if let Type::TypeDef(def, _) = def { + cfg.add_feature(self.type_def_namespace(def)); + } + } + } + } + TypeKind::Struct => { + self.type_def_fields(row).for_each(|field| self.field_cfg_combine(field, Some(row), cfg)); + if !type_name.namespace.is_empty() { + for def in self.get(type_name) { + if def != row { + self.type_def_cfg_combine(def, &[], cfg); + } + } + } + } + TypeKind::Delegate => self.signature_cfg_combine(&self.method_def_signature(self.type_def_invoke_method(row), generics), cfg), + _ => {} + } + } + } + pub fn type_def_vtables(&self, row: TypeDef) -> Vec { + let mut result = Vec::new(); + if self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) { + result.push(Type::IUnknown); + if self.type_def_kind(row) != TypeKind::Delegate { + result.push(Type::IInspectable); + } + } else { + let mut next = row; + while let Some(base) = self.type_def_interfaces(next, &[]).next() { + match base.ty { + Type::TypeDef(row, _) => { + next = row; + result.insert(0, base.ty); + } + Type::IInspectable => { + result.insert(0, Type::IUnknown); + result.insert(1, Type::IInspectable); + break; + } + Type::IUnknown => { + result.insert(0, Type::IUnknown); + break; + } + _ => unimplemented!(), + } + } + } + result + } + + // + // TypeRef table queries + // + + pub fn type_ref_name(&self, row: TypeRef) -> &str { + self.row_str(row.0, 1) + } + pub fn type_ref_namespace(&self, row: TypeRef) -> &str { + self.row_str(row.0, 2) + } + pub fn type_ref_type_name(&self, row: TypeRef) -> TypeName { + TypeName::new(self.type_ref_namespace(row), self.type_ref_name(row)) + } + pub fn type_ref_resolution_scope(&self, row: TypeRef) -> ResolutionScope { + self.row_decode(row.0, 0) + } + + // + // TypeSpec table queries + // + + pub fn type_spec_signature(&self, row: TypeSpec) -> Blob { + self.row_blob(row.0, 0) + } + + // + // Signature queries + // + + pub fn signature_cfg(&self, signature: &Signature) -> Cfg { + let mut cfg = Cfg::default(); + self.signature_cfg_combine(signature, &mut cfg); + self.cfg_add_attributes(&mut cfg, self.method_def_attributes(signature.def)); + cfg + } + fn signature_cfg_combine(&'a self, signature: &Signature, cfg: &mut Cfg<'a>) { + self.type_cfg_combine(&signature.return_type, cfg); + signature.params.iter().for_each(|param| self.type_cfg_combine(¶m.ty, cfg)); + } + pub fn signature_param_is_borrowed(&self, param: &SignatureParam) -> bool { + self.type_is_borrowed(¶m.ty) + } + pub fn signature_param_is_failible_param(&self, param: &SignatureParam) -> bool { + self.type_is_non_exclusive_winrt_interface(¶m.ty) + } + pub fn signature_param_is_trivially_convertible(&self, param: &SignatureParam) -> bool { + self.type_is_trivially_convertible(¶m.ty) + } + pub fn signature_param_is_convertible(&self, param: &SignatureParam) -> bool { + !self.param_flags(param.def).contains(ParamAttributes::Out) && !param.ty.is_winrt_array() && !param.ty.is_pointer() && !param.kind.is_array() && (self.type_is_borrowed(¶m.ty) || self.type_is_non_exclusive_winrt_interface(¶m.ty) || self.type_is_trivially_convertible(¶m.ty)) + } + pub fn signature_param_is_retval(&self, param: &SignatureParam) -> bool { + // The Win32 metadata uses `RetValAttribute` to call out retval methods but it is employed + // very sparingly, so this heuristic is used to apply the transformation more uniformly. + if self.param_is_retval(param.def) { + return true; + } + if !param.ty.is_pointer() { + return false; + } + if param.ty.is_void() { + return false; + } + let flags = self.param_flags(param.def); + if flags.contains(ParamAttributes::In) || !flags.contains(ParamAttributes::Out) || flags.contains(ParamAttributes::Optional) || param.kind.is_array() { + return false; + } + if self.param_kind(param.def).is_array() { + return false; + } + // If it's bigger than 128 bits, best to pass as a reference. + if self.type_size(¶m.ty.deref()) > 16 { + return false; + } + // Win32 callbacks are defined as `Option` so we don't include them here to avoid + // producing the `Result>` anti-pattern. + !self.type_is_callback(¶m.ty.deref()) + } + pub fn signature_kind(&self, signature: &Signature) -> SignatureKind { + if self.method_def_can_return_multiple_success_values(signature.def) { + return SignatureKind::PreserveSig; + } + match &signature.return_type { + Type::Void if self.signature_is_retval(signature) => SignatureKind::ReturnValue, + Type::Void => SignatureKind::ReturnVoid, + Type::HRESULT => { + if signature.params.len() >= 2 { + if let Some(guid) = self.signature_param_is_query_guid(&signature.params) { + if let Some(object) = self.signature_param_is_query_object(&signature.params) { + if self.param_flags(signature.params[object].def).contains(ParamAttributes::Optional) { + return SignatureKind::QueryOptional(QueryPosition { object, guid }); + } else { + return SignatureKind::Query(QueryPosition { object, guid }); + } + } + } + } + if self.signature_is_retval(signature) { + SignatureKind::ResultValue + } else { + SignatureKind::ResultVoid + } + } + Type::TypeDef(def, _) if self.type_def_type_name(*def) == TypeName::NTSTATUS => SignatureKind::ResultVoid, + Type::TypeDef(def, _) if self.type_def_type_name(*def) == TypeName::WIN32_ERROR => SignatureKind::ResultVoid, + Type::TypeDef(def, _) if self.type_def_type_name(*def) == TypeName::BOOL && self.method_def_last_error(signature.def) => SignatureKind::ResultVoid, + _ if self.type_is_struct(&signature.return_type) => SignatureKind::ReturnStruct, + _ => SignatureKind::PreserveSig, + } + } + fn signature_is_retval(&self, signature: &Signature) -> bool { + signature.params.last().map_or(false, |param| self.signature_param_is_retval(param)) + && signature.params[..signature.params.len() - 1].iter().all(|param| { + let flags = self.param_flags(param.def); + !flags.contains(ParamAttributes::Out) + }) + } + fn signature_param_is_query_guid(&self, params: &[SignatureParam]) -> Option { + params.iter().rposition(|param| param.ty == Type::ConstPtr(Box::new(Type::GUID), 1) && !self.param_flags(param.def).contains(ParamAttributes::Out)) + } + fn signature_param_is_query_object(&self, params: &[SignatureParam]) -> Option { + params.iter().rposition(|param| param.ty == Type::MutPtr(Box::new(Type::Void), 2) && self.param_is_com_out_ptr(param.def)) + } -macro_rules! flags { - ($name:ident, $size:ty) => { - #[derive(Default, Copy, Clone, PartialEq, Eq, Debug)] - pub struct $name(pub $size); - impl $name { - pub fn contains(&self, contains: Self) -> bool { - *self & contains == contains + // + // Other type queries + // + + fn cfg_add_attributes(&self, cfg: &mut Cfg, attributes: impl Iterator) { + for attribute in attributes { + match self.attribute_name(attribute) { + "SupportedArchitectureAttribute" => { + if let Some((_, Value::EnumDef(_, value))) = self.attribute_args(attribute).get(0) { + if let Value::I32(value) = **value { + if value & 1 == 1 { + cfg.arches.insert("x86"); + } + if value & 2 == 2 { + cfg.arches.insert("x86_64"); + } + if value & 4 == 4 { + cfg.arches.insert("aarch64"); + } + } + } + } + "DeprecatedAttribute" => { + cfg.add_feature("deprecated"); + } + _ => {} + } + } + } + pub fn type_collect_standalone(&self, ty: &Type, set: &mut BTreeSet) { + let ty = ty.to_underlying_type(); + if !set.insert(ty.clone()) { + return; + } + + let Type::TypeDef(def, generics) = &ty else { return; }; + let def = *def; + + // Ensure that we collect all the typedefs of the same name. We need to + // do this in the case where the user specifies a top level item that + // references a typedef by name, but that name resolves to more than 1 + // Type based on target architecture (typically) + // + // Note this is a bit overeager as we can collect a typedef that is used + // by one architecture but not by another + let type_name = self.type_def_type_name(def); + if !type_name.namespace.is_empty() { + for row in self.get(type_name) { + if def != row { + self.type_collect_standalone(&Type::TypeDef(row, Vec::new()), set); + } + } + } + + for generic in generics { + self.type_collect_standalone(generic, set); + } + for field in self.type_def_fields(def) { + let ty = self.field_type(field, Some(def)); + if let Type::TypeDef(def, _) = &ty { + if self.type_def_namespace(*def).is_empty() { + continue; + } + } + self.type_collect_standalone(&ty, set); + } + for method in self.type_def_methods(def) { + // Skip delegate pseudo-constructors. + if self.method_def_name(method) == ".ctor" { + continue; + } + let signature = self.method_def_signature(method, generics); + self.type_collect_standalone(&signature.return_type, set); + signature.params.iter().for_each(|param| self.type_collect_standalone(¶m.ty, set)); + } + for interface in self.type_interfaces(&ty) { + self.type_collect_standalone(&interface.ty, set); + } + if self.type_def_kind(def) == TypeKind::Struct && self.type_def_fields(def).next().is_none() && self.type_def_guid(def).is_some() { + set.insert(Type::GUID); + } + + self.type_collect_standalone_nested(def, set); + } + + fn type_collect_standalone_nested(&self, td: TypeDef, set: &mut BTreeSet) { + for nested in self.nested_types(td) { + self.type_collect_standalone_nested(nested, set); + + for field in self.type_def_fields(nested) { + let ty = self.field_type(field, Some(nested)); + if let Type::TypeDef(def, _) = &ty { + // Skip the fields that actually refer to the anonymous nested + // type, otherwise it will get added to the typeset and emitted + if self.type_def_namespace(*def).is_empty() { + continue; + } + + self.type_collect_standalone(&ty, set); + } + } + } + } + + pub fn type_cfg(&self, ty: &Type) -> Cfg { + let mut cfg = Cfg::default(); + self.type_cfg_combine(ty, &mut cfg); + cfg + } + pub fn type_cfg_combine(&'a self, ty: &Type, cfg: &mut Cfg<'a>) { + match ty { + Type::TypeDef(row, generics) => self.type_def_cfg_combine(*row, generics, cfg), + Type::Win32Array(ty, _) => self.type_cfg_combine(ty, cfg), + Type::ConstPtr(ty, _) => self.type_cfg_combine(ty, cfg), + Type::MutPtr(ty, _) => self.type_cfg_combine(ty, cfg), + Type::WinrtArray(ty) => self.type_cfg_combine(ty, cfg), + Type::WinrtArrayRef(ty) => self.type_cfg_combine(ty, cfg), + ty => _ = cfg.core_types.insert(ty.clone()), + } + } + pub fn type_interfaces(&self, ty: &Type) -> Vec { + // TODO: collect into btree map and then return collected vec + // This will both sort the results and should make finding dupes faster + fn walk(reader: &Reader, result: &mut Vec, parent: &Type, is_base: bool) { + if let Type::TypeDef(row, generics) = parent { + for mut child in reader.type_def_interfaces(*row, generics) { + child.kind = if !is_base && child.kind == InterfaceKind::Default { + InterfaceKind::Default + } else if child.kind == InterfaceKind::Overridable { + continue; + } else if is_base { + InterfaceKind::Base + } else { + InterfaceKind::None + }; + let mut found = false; + for existing in result.iter_mut() { + if existing.ty == child.ty { + found = true; + if child.kind == InterfaceKind::Default { + existing.kind = child.kind + } + } + } + if !found { + walk(reader, result, &child.ty, is_base); + result.push(child); + } + } + } + } + let mut result = Vec::new(); + walk(self, &mut result, ty, false); + if let Type::TypeDef(row, _) = ty { + if self.type_def_kind(*row) == TypeKind::Class { + for base in self.type_def_bases(*row) { + walk(self, &mut result, &Type::TypeDef(base, Vec::new()), true); + } + for attribute in self.type_def_attributes(*row) { + match self.attribute_name(attribute) { + "StaticAttribute" | "ActivatableAttribute" => { + for (_, arg) in self.attribute_args(attribute) { + if let Value::TypeDef(row) = arg { + result.push(Interface { ty: Type::TypeDef(row, Vec::new()), kind: InterfaceKind::Static }); + break; + } + } + } + _ => {} + } + } + } + } + result.sort_by(|a, b| self.type_name(&a.ty).cmp(self.type_name(&b.ty))); + result + } + pub fn type_def_or_ref(&self, code: TypeDefOrRef) -> TypeName { + match code { + TypeDefOrRef::TypeDef(row) => TypeName::new(self.type_def_namespace(row), self.type_def_name(row)), + TypeDefOrRef::TypeRef(row) => TypeName::new(self.type_ref_namespace(row), self.type_ref_name(row)), + _ => unimplemented!(), + } + } + fn type_stdcall(&self, ty: &Type) -> usize { + match ty { + Type::I8 | Type::U8 => 1, + Type::I16 | Type::U16 => 2, + Type::I64 | Type::U64 | Type::F64 => 8, + Type::GUID => 16, + Type::TypeDef(row, _) => self.type_def_stdcall(*row), + _ => 4, + } + } + pub fn type_is_exclusive(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_is_exclusive(*row), + _ => false, + } + } + pub fn type_is_blittable(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_is_blittable(*row), + Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, + Type::Win32Array(kind, _) => self.type_is_blittable(kind), + Type::WinrtArray(kind) => self.type_is_blittable(kind), + _ => true, + } + } + pub fn type_is_copyable(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_is_copyable(*row), + Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, + Type::Win32Array(kind, _) => self.type_is_copyable(kind), + Type::WinrtArray(kind) => self.type_is_copyable(kind), + _ => true, + } + } + pub fn type_has_explicit_layout(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_has_explicit_layout(*row), + Type::Win32Array(ty, _) => self.type_has_explicit_layout(ty), + _ => false, + } + } + pub fn type_has_packing(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_has_packing(*row), + Type::Win32Array(ty, _) => self.type_has_packing(ty), + _ => false, + } + } + pub fn type_has_callback(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_has_callback(*row), + Type::Win32Array(ty, _) => self.type_has_callback(ty), + _ => false, + } + } + fn type_from_ref(&self, code: TypeDefOrRef, enclosing: Option, generics: &[Type]) -> Type { + if let TypeDefOrRef::TypeSpec(def) = code { + let mut blob = self.type_spec_signature(def); + return self.type_from_blob_impl(&mut blob, None, generics); + } + + let mut full_name = self.type_def_or_ref(code); + + for (known_name, kind) in CORE_TYPES { + if full_name == known_name { + return kind; } } - impl std::ops::BitOr for $name { - type Output = Self; - fn bitor(self, other: Self) -> Self { - Self(self.0 | other.0) + + for (from, to) in REMAP_TYPES { + if full_name == from { + full_name = to; + break; } } - impl std::ops::BitAnd for $name { - type Output = Self; - fn bitand(self, other: Self) -> Self { - Self(self.0 & other.0) + + if let Some(outer) = enclosing { + if full_name.namespace.is_empty() { + let nested = &self.nested[&outer]; + let Some(inner) = nested.get(full_name.name) else { + panic!("Nested type not found: {}.{}", self.type_def_type_name(outer), full_name.name); + }; + return Type::TypeDef(*inner, Vec::new()); } } - impl std::ops::BitOrAssign for $name { - fn bitor_assign(&mut self, other: Self) { - self.0.bitor_assign(other.0) + + if let Some(ty) = self.get(full_name).next() { + Type::TypeDef(ty, Vec::new()) + } else { + Type::TypeRef(code) + } + } + fn type_from_blob(&self, blob: &mut Blob, enclosing: Option, generics: &[Type]) -> Type { + // Used by WinRT to indicate that a struct input parameter is passed by reference rather than by value on the ABI. + let is_const = blob.read_modifiers().iter().any(|def| self.type_def_or_ref(*def) == TypeName::IsConst); + + // Used by WinRT to indicate an output parameter, but there are other ways to determine this direction so here + // it is only used to distinguish between slices and heap-allocated arrays. + let is_ref = blob.read_expected(ELEMENT_TYPE_BYREF as _); + + if blob.read_expected(ELEMENT_TYPE_VOID as _) { + return Type::Void; + } + + let is_array = blob.read_expected(ELEMENT_TYPE_SZARRAY as _); // Used by WinRT to indicate an array + + let mut pointers = 0; + + while blob.read_expected(ELEMENT_TYPE_PTR as _) { + pointers += 1; + } + + let kind = self.type_from_blob_impl(blob, enclosing, generics); + + if pointers > 0 { + Type::MutPtr(Box::new(kind), pointers) + } else if is_const { + Type::ConstRef(Box::new(kind)) + } else if is_array { + if is_ref { + Type::WinrtArrayRef(Box::new(kind)) + } else { + Type::WinrtArray(Box::new(kind)) } + } else { + kind } - impl std::ops::BitAndAssign for $name { - fn bitand_assign(&mut self, other: Self) { - self.0.bitand_assign(other.0) + } + fn type_from_blob_impl(&self, blob: &mut Blob, enclosing: Option, generics: &[Type]) -> Type { + let code = blob.read_usize(); + + if let Some(code) = Type::from_code(code) { + return code; + } + + match code as _ { + ELEMENT_TYPE_VALUETYPE | ELEMENT_TYPE_CLASS => self.type_from_ref(TypeDefOrRef::decode(blob.file, blob.read_usize()), enclosing, generics), + ELEMENT_TYPE_VAR => generics.get(blob.read_usize()).unwrap_or(&Type::Void).clone(), + ELEMENT_TYPE_ARRAY => { + let kind = self.type_from_blob(blob, enclosing, generics); + let _rank = blob.read_usize(); + let _count = blob.read_usize(); + let bounds = blob.read_usize(); + Type::Win32Array(Box::new(kind), bounds) } + ELEMENT_TYPE_GENERICINST => { + blob.read_usize(); + + let def = self.get(self.type_def_or_ref(TypeDefOrRef::decode(blob.file, blob.read_usize()))).next().expect("Type not found"); + let mut args = Vec::with_capacity(blob.read_usize()); + + for _ in 0..args.capacity() { + args.push(self.type_from_blob_impl(blob, enclosing, generics)); + } + + Type::TypeDef(def, args) + } + _ => unimplemented!(), + } + } + pub fn type_name(&self, ty: &Type) -> &str { + match ty { + Type::TypeDef(row, _) => self.type_def_name(*row), + _ => "", + } + } + pub fn type_signature(&self, ty: &Type) -> String { + match ty { + Type::Bool => "b1".to_string(), + Type::Char => "c2".to_string(), + Type::I8 => "i1".to_string(), + Type::U8 => "u1".to_string(), + Type::I16 => "i2".to_string(), + Type::U16 => "u2".to_string(), + Type::I32 => "i4".to_string(), + Type::U32 => "u4".to_string(), + Type::I64 => "i8".to_string(), + Type::U64 => "u8".to_string(), + Type::F32 => "f4".to_string(), + Type::F64 => "f8".to_string(), + Type::String => "string".to_string(), + Type::IInspectable => "cinterface(IInspectable)".to_string(), + Type::GUID => "g16".to_string(), + Type::HRESULT => "struct(Windows.Foundation.HResult;i4)".to_string(), + Type::TypeDef(row, generics) => self.type_def_signature(*row, generics), + _ => unimplemented!(), + } + } + pub fn type_is_nullable(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_is_nullable(*row), + Type::IInspectable | Type::IUnknown => true, + _ => false, } - impl std::ops::Not for $name { - type Output = Self; - fn not(self) -> Self { - Self(self.0.not()) + } + fn type_is_borrowed(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => !self.type_def_is_blittable(*row), + Type::BSTR | Type::PCSTR | Type::PCWSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => true, + _ => false, + } + } + pub fn type_is_non_exclusive_winrt_interface(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => { + let flags = self.type_def_flags(*row); + if !flags.contains(TypeAttributes::WindowsRuntime) { + false + } else { + match self.type_def_kind(*row) { + TypeKind::Interface => !self.type_def_is_exclusive(*row), + TypeKind::Class => self.type_def_is_composable(*row), + _ => false, + } + } } + _ => false, + } + } + pub fn type_is_trivially_convertible(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_is_trivially_convertible(*row), + Type::PCSTR | Type::PCWSTR => true, + _ => false, + } + } + pub fn type_is_callback(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_is_callback(*row), + _ => false, + } + } + pub fn type_is_primitive(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_is_primitive(*row), + Type::Bool | Type::Char | Type::I8 | Type::U8 | Type::I16 | Type::U16 | Type::I32 | Type::U32 | Type::I64 | Type::U64 | Type::F32 | Type::F64 | Type::ISize | Type::USize | Type::HRESULT | Type::ConstPtr(_, _) | Type::MutPtr(_, _) => true, + _ => false, } - }; + } + pub fn type_is_struct(&self, ty: &Type) -> bool { + match ty { + Type::TypeDef(row, _) => self.type_def_is_struct(*row), + Type::GUID => true, + _ => false, + } + } + pub fn type_underlying_type(&self, ty: &Type) -> Type { + match ty { + Type::TypeDef(row, _) => self.type_def_underlying_type(*row), + Type::HRESULT => Type::I32, + _ => ty.clone(), + } + } + pub fn type_has_replacement(&self, ty: &Type) -> bool { + match ty { + Type::HRESULT | Type::PCSTR | Type::PCWSTR => true, + Type::TypeDef(row, _) => self.type_def_is_handle(*row) || self.type_def_kind(*row) == TypeKind::Enum, + _ => false, + } + } } -pub(crate) use flags; +pub const REMAP_TYPES: [(TypeName, TypeName); 2] = [(TypeName::D2D_MATRIX_3X2_F, TypeName::Matrix3x2), (TypeName::D3DMATRIX, TypeName::Matrix4x4)]; + +pub const CORE_TYPES: [(TypeName, Type); 11] = [(TypeName::GUID, Type::GUID), (TypeName::IUnknown, Type::IUnknown), (TypeName::HResult, Type::HRESULT), (TypeName::HRESULT, Type::HRESULT), (TypeName::HSTRING, Type::String), (TypeName::BSTR, Type::BSTR), (TypeName::IInspectable, Type::IInspectable), (TypeName::PSTR, Type::PSTR), (TypeName::PWSTR, Type::PWSTR), (TypeName::Type, Type::TypeName), (TypeName::CHAR, Type::U8)]; diff --git a/crates/libs/metadata/src/reader/mod.rs b/crates/libs/metadata/src/reader/mod.rs deleted file mode 100644 index 37b5ff668c..0000000000 --- a/crates/libs/metadata/src/reader/mod.rs +++ /dev/null @@ -1,1912 +0,0 @@ -mod blob; -mod codes; -mod file; -mod guid; -mod row; -mod tree; -mod r#type; -mod type_name; - -pub use super::*; -pub use blob::*; -pub use codes::*; -pub use file::*; -pub use guid::*; -pub use r#type::*; -pub use row::*; -pub use tree::*; -pub use type_name::*; - -macro_rules! tables { - ($($name:ident,)*) => ($( - #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)] - pub struct $name(pub Row); - )*) -} - -tables! { - Attribute, - ClassLayout, - Constant, - Field, - GenericParam, - ImplMap, - InterfaceImpl, - MemberRef, - MethodDef, - Module, - ModuleRef, - AssemblyRef, - Param, - TypeDef, - TypeRef, - TypeSpec, -} - -#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct Interface { - pub ty: Type, - pub kind: InterfaceKind, -} - -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)] -pub enum InterfaceKind { - None, - Default, - Overridable, - Static, - Base, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct QueryPosition { - pub object: usize, - pub guid: usize, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum SignatureKind { - Query(QueryPosition), - QueryOptional(QueryPosition), - ResultValue, - ResultVoid, - ReturnStruct, - ReturnValue, - ReturnVoid, - PreserveSig, -} - -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum SignatureParamKind { - ArrayFixed(usize), - ArrayRelativeLen(usize), - ArrayRelativeByteLen(usize), - ArrayRelativePtr(usize), - TryInto, - IntoParam, - OptionalPointer, - ValueType, - Blittable, - Other, -} - -impl SignatureParamKind { - fn is_array(&self) -> bool { - matches!(self, Self::ArrayFixed(_) | Self::ArrayRelativeLen(_) | Self::ArrayRelativeByteLen(_) | Self::ArrayRelativePtr(_)) - } -} - -#[derive(PartialEq, Eq)] -pub enum AsyncKind { - None, - Action, - ActionWithProgress, - Operation, - OperationWithProgress, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] -pub enum TypeKind { - Interface, - Class, - Enum, - Struct, - Delegate, -} - -#[derive(Debug)] -pub enum Value { - Bool(bool), - U8(u8), - I8(i8), - U16(u16), - I16(i16), - U32(u32), - I32(i32), - U64(u64), - I64(i64), - F32(f32), - F64(f64), - String(String), - TypeDef(TypeDef), - TypeRef(TypeDefOrRef), - EnumDef(TypeDef, Box), - EnumRef(TypeDefOrRef, Box), -} - -pub struct Signature { - pub def: MethodDef, - pub params: Vec, - pub return_type: Type, - pub call_flags: MethodCallAttributes, -} - -pub struct SignatureParam { - pub def: Param, - pub ty: Type, - pub kind: SignatureParamKind, -} - -#[derive(Default, Clone)] -pub struct Cfg<'a> { - pub types: BTreeMap<&'a str, BTreeSet>, - pub core_types: BTreeSet, - pub arches: BTreeSet<&'static str>, - pub implement: bool, -} - -impl<'a> Cfg<'a> { - pub fn add_feature(&mut self, feature: &'a str) { - self.types.entry(feature).or_default(); - } - pub fn union(&self, other: &Self) -> Self { - let mut union = Self::default(); - self.types.keys().for_each(|feature| { - union.types.entry(feature).or_default(); - }); - other.types.keys().for_each(|feature| { - union.types.entry(feature).or_default(); - }); - self.arches.iter().for_each(|arch| { - union.arches.insert(arch); - }); - other.arches.iter().for_each(|arch| { - union.arches.insert(arch); - }); - union - } -} - -pub struct Reader<'a> { - files: &'a [File], - types: HashMap<&'a str, BTreeMap<&'a str, Vec>>, - nested: HashMap>, -} - -impl<'a> Reader<'a> { - pub fn new(files: &'a [File]) -> Self { - let mut types = HashMap::<&'a str, BTreeMap<&'a str, Vec>>::new(); - let mut nested = HashMap::>::new(); - for (file_index, file) in files.iter().enumerate() { - for row in 0..file.tables[TABLE_TYPEDEF].len { - let key = Row::new(row, TABLE_TYPEDEF, file_index); - let namespace = file.str(key.row as _, key.table as _, 2); - if namespace.is_empty() { - continue; - } - let name = trim_tick(file.str(key.row as _, key.table as _, 1)); - types.entry(namespace).or_default().entry(name).or_default().push(TypeDef(key)); - } - for row in 0..file.tables[TABLE_NESTEDCLASS].len { - let key = Row::new(row, TABLE_NESTEDCLASS, file_index); - let inner = Row::new(file.usize(key.row as _, key.table as _, 0) - 1, TABLE_TYPEDEF, file_index); - let outer = Row::new(file.usize(key.row as _, key.table as _, 1) - 1, TABLE_TYPEDEF, file_index); - let name = file.str(inner.row as _, inner.table as _, 1); - nested.entry(TypeDef(outer)).or_default().insert(name, TypeDef(inner)); - } - } - Self { files, types, nested } - } - pub fn tree(&'a self, root: &'a str, filter: &Filter) -> Tree { - let mut tree = Tree::from_namespace(""); - for ns in self.types.keys() { - if filter.includes_namespace(ns) { - tree.insert_namespace(ns, 0); - } - } - if root.is_empty() { - tree - } else { - tree.seek(root).expect("Namespace not found") - } - } - - // - // Hash functions for fast type lookup - // - - pub fn namespaces(&self) -> impl Iterator + '_ { - self.types.keys().copied() - } - pub fn namespace_types(&'a self, namespace: &str, filter: &'a Filter) -> impl Iterator + '_ { - self.types.get(namespace).map(move |types| types.values().flatten().copied().filter(move |ty| filter.includes_type(self, *ty))).into_iter().flatten() - } - pub fn nested_types(&self, type_def: TypeDef) -> impl Iterator + '_ { - self.nested.get(&type_def).map(|map| map.values().copied()).into_iter().flatten() - } - pub fn get(&self, type_name: TypeName) -> impl Iterator + '_ { - if let Some(types) = self.types.get(type_name.namespace) { - if let Some(definitions) = types.get(type_name.name) { - return Some(definitions.iter().copied()).into_iter().flatten(); - } - } - None.into_iter().flatten() - } - pub fn namespace_functions(&self, namespace: &str) -> impl Iterator + '_ { - self.get(TypeName::new(namespace, "Apis")).flat_map(move |apis| self.type_def_methods(apis)).filter(move |method| { - // The ImplMap table contains import information, without which the function cannot be linked. - let Some(impl_map) = self.method_def_impl_map(*method) else { - return false; - }; - - // Skip functions exported by ordinal. - if self.impl_map_import_name(impl_map).starts_with('#') { - return false; - } - - // Skip "inline" function constants. - self.module_ref_name(self.impl_map_scope(impl_map)) != "FORCEINLINE" - }) - } - pub fn namespace_constants(&self, namespace: &str) -> impl Iterator + '_ { - self.get(TypeName::new(namespace, "Apis")).flat_map(move |apis| self.type_def_fields(apis)) - } - - // - // Row functions providing low-level file access - // - - fn row_usize(&self, key: Row, column: usize) -> usize { - self.files[key.file as usize].usize(key.row as _, key.table as _, column) - } - fn row_str(&self, key: Row, column: usize) -> &str { - self.files[key.file as usize].str(key.row as _, key.table as _, column) - } - pub fn row_blob(&self, key: Row, column: usize) -> Blob { - let file = key.file as usize; - Blob::new(file, self.files[file].blob(key.row as _, key.table as _, column)) - } - fn row_equal_range(&self, key: Row, table: usize, column: usize, value: usize) -> impl Iterator { - let (first, last) = self.files[key.file as usize].equal_range(table, column, value); - (first..last).map(move |row| Row::new(row, table, key.file as _)) - } - fn row_attributes(&self, key: Row, source: HasAttribute) -> impl Iterator { - self.row_equal_range(key, TABLE_CUSTOMATTRIBUTE, 0, source.encode()).map(Attribute) - } - fn row_list(&self, key: Row, table: usize, column: usize) -> impl Iterator { - let file = key.file as usize; - let first = self.row_usize(key, column) - 1; - let last = if key.row + 1 < self.files[file].tables[key.table as usize].len as _ { self.row_usize(key.next(), column) - 1 } else { self.files[file].tables[table].len }; - (first..last).map(move |row| Row::new(row, table, file)) - } - fn row_decode(&self, key: Row, column: usize) -> T { - T::decode(key.file as _, self.row_usize(key, column)) - } - - // - // Attribute table queries - // - - pub fn attribute_type_name(&self, row: Attribute) -> TypeName { - let AttributeType::MemberRef(row) = self.row_decode(row.0, 1); - let MemberRefParent::TypeRef(row) = self.row_decode(row.0, 0); - self.type_ref_type_name(row) - } - pub fn attribute_name(&self, row: Attribute) -> &str { - let AttributeType::MemberRef(row) = self.row_decode(row.0, 1); - let MemberRefParent::TypeRef(row) = self.row_decode(row.0, 0); - self.type_ref_name(row) - } - pub fn attribute_args(&self, row: Attribute) -> Vec<(String, Value)> { - let AttributeType::MemberRef(member) = self.row_decode(row.0, 1); - let mut sig = self.member_ref_signature(member); - let mut values = self.row_blob(row.0, 2); - let _prolog = values.read_u16(); - let _this_and_gen_param_count = sig.read_usize(); - let fixed_arg_count = sig.read_usize(); - let _ret_type = sig.read_usize(); - let mut args: Vec<(String, Value)> = Vec::with_capacity(fixed_arg_count); - - for _ in 0..fixed_arg_count { - let arg = match self.type_from_blob(&mut sig, None, &[]) { - Type::Bool => Value::Bool(values.read_bool()), - Type::I8 => Value::I8(values.read_i8()), - Type::U8 => Value::U8(values.read_u8()), - Type::I16 => Value::I16(values.read_i16()), - Type::U16 => Value::U16(values.read_u16()), - Type::I32 => Value::I32(values.read_i32()), - Type::U32 => Value::U32(values.read_u32()), - Type::I64 => Value::I64(values.read_i64()), - Type::U64 => Value::U64(values.read_u64()), - Type::String => Value::String(values.read_str().to_string()), - Type::TypeName => Value::TypeDef(self.get(TypeName::parse(values.read_str())).next().expect("Type not found")), - Type::TypeDef((def, _)) => Value::EnumDef(def, Box::new(values.read_integer(self.type_def_underlying_type(def)))), - // It's impossible to know the type of a TypeRef so we just assume 32-bit integer which covers System.* attribute args - // reasonably well but the solution is to follow the WinRT metadata and define replacements for those attribute types. - Type::TypeRef(code) => Value::EnumRef(code, Box::new(values.read_integer(Type::I32))), - rest => todo!("{:?}", rest), - }; - - args.push((String::new(), arg)); - } - - let named_arg_count = values.read_u16(); - args.reserve(named_arg_count as usize); - - for _ in 0..named_arg_count { - let _id = values.read_u8(); - let arg_type = values.read_u8(); - let mut name = values.read_str().to_string(); - let arg = match arg_type as _ { - ELEMENT_TYPE_BOOLEAN => Value::Bool(values.read_bool()), - ELEMENT_TYPE_I2 => Value::I16(values.read_i16()), - ELEMENT_TYPE_I4 => Value::I32(values.read_i32()), - ELEMENT_TYPE_U4 => Value::U32(values.read_u32()), - ELEMENT_TYPE_STRING => Value::String(values.read_str().to_string()), - 0x50 => Value::TypeDef(self.get(TypeName::parse(values.read_str())).next().expect("Type not found")), - 0x55 => { - let def = self.get(TypeName::parse(&name)).next().expect("Type not found"); - name = values.read_str().into(); - Value::EnumDef(def, Box::new(values.read_integer(self.type_def_underlying_type(def)))) - } - _ => todo!("{:?}", arg_type), - }; - args.push((name, arg)); - } - - assert_eq!(sig.slice.len(), 0); - assert_eq!(values.slice.len(), 0); - - args - } - - // - // ClassLayout table queries - // - - pub fn class_layout_packing_size(&self, row: ClassLayout) -> usize { - self.row_usize(row.0, 0) - } - - // - // Constant table queries - // - - pub fn constant_type(&self, row: Constant) -> Type { - let code = self.row_usize(row.0, 0); - Type::from_code(code).expect("Type not found") - } - pub fn constant_value(&self, row: Constant) -> Value { - let mut blob = self.row_blob(row.0, 2); - match self.constant_type(row) { - Type::I8 => Value::I8(blob.read_i8()), - Type::U8 => Value::U8(blob.read_u8()), - Type::I16 => Value::I16(blob.read_i16()), - Type::U16 => Value::U16(blob.read_u16()), - Type::I32 => Value::I32(blob.read_i32()), - Type::U32 => Value::U32(blob.read_u32()), - Type::I64 => Value::I64(blob.read_i64()), - Type::U64 => Value::U64(blob.read_u64()), - Type::F32 => Value::F32(blob.read_f32()), - Type::F64 => Value::F64(blob.read_f64()), - Type::String => Value::String(blob.read_string()), - _ => unimplemented!(), - } - } - - // - // Field table queries - // - - pub fn field_flags(&self, row: Field) -> FieldAttributes { - FieldAttributes(self.row_usize(row.0, 0) as _) - } - pub fn field_name(&self, row: Field) -> &str { - self.row_str(row.0, 1) - } - pub fn field_constant(&self, row: Field) -> Option { - self.row_equal_range(row.0, TABLE_CONSTANT, 1, HasConstant::Field(row).encode()).map(Constant).next() - } - pub fn field_attributes(&self, row: Field) -> impl Iterator { - self.row_attributes(row.0, HasAttribute::Field(row)) - } - pub fn field_is_const(&self, row: Field) -> bool { - self.field_attributes(row).any(|attribute| self.attribute_name(attribute) == "ConstAttribute") - } - pub fn field_type(&self, row: Field, enclosing: Option) -> Type { - let mut blob = self.row_blob(row.0, 2); - blob.read_usize(); - blob.read_modifiers(); - let def = self.type_from_blob(&mut blob, enclosing, &[]); - - if self.field_is_const(row) { - def.to_const_type().to_const_ptr() - } else { - def - } - } - pub fn field_is_blittable(&self, row: Field, enclosing: TypeDef) -> bool { - self.type_is_blittable(&self.field_type(row, Some(enclosing))) - } - pub fn field_is_copyable(&self, row: Field, enclosing: TypeDef) -> bool { - self.type_is_copyable(&self.field_type(row, Some(enclosing))) - } - pub fn field_guid(&self, row: Field) -> Option { - for attribute in self.field_attributes(row) { - if self.attribute_name(attribute) == "GuidAttribute" { - return Some(GUID::from_args(&self.attribute_args(attribute))); - } - } - None - } - pub fn field_cfg(&self, row: Field) -> Cfg { - let mut cfg = Cfg::default(); - self.field_cfg_combine(row, None, &mut cfg); - cfg - } - fn field_cfg_combine(&'a self, row: Field, enclosing: Option, cfg: &mut Cfg<'a>) { - self.type_cfg_combine(&self.field_type(row, enclosing), cfg) - } - pub fn field_is_ansi(&self, row: Field) -> bool { - for attribute in self.field_attributes(row) { - if self.attribute_name(attribute) == "NativeEncodingAttribute" { - if let Some((_, Value::String(encoding))) = self.attribute_args(attribute).get(0) { - if encoding == "ansi" { - return true; - } - } - } - } - false - } - - // - // GenericParam table queries - // - - pub fn generic_param_name(&self, row: GenericParam) -> &str { - self.row_str(row.0, 3) - } - - // - // ImplMap table queries - // - - pub fn impl_map_flags(&self, row: ImplMap) -> PInvokeAttributes { - PInvokeAttributes(self.row_usize(row.0, 0)) - } - pub fn impl_map_scope(&self, row: ImplMap) -> ModuleRef { - ModuleRef(Row::new(self.row_usize(row.0, 3) - 1, TABLE_MODULEREF, row.0.file as _)) - } - pub fn impl_map_import_name(&self, row: ImplMap) -> &str { - self.row_str(row.0, 2) - } - - // - // InterfaceImpl table queries - // - - pub fn interface_impl_attributes(&self, row: InterfaceImpl) -> impl Iterator { - self.row_attributes(row.0, HasAttribute::InterfaceImpl(row)) - } - pub fn interface_impl_is_default(&self, row: InterfaceImpl) -> bool { - self.interface_impl_attributes(row).any(|attribute| self.attribute_name(attribute) == "DefaultAttribute") - } - pub fn interface_impl_is_overridable(&self, row: InterfaceImpl) -> bool { - self.interface_impl_attributes(row).any(|attribute| self.attribute_name(attribute) == "OverridableAttribute") - } - pub fn interface_impl_type(&self, row: InterfaceImpl, generics: &[Type]) -> Interface { - let mut kind = InterfaceKind::None; - for attribute in self.interface_impl_attributes(row) { - match self.attribute_name(attribute) { - "DefaultAttribute" => kind = InterfaceKind::Default, - "OverridableAttribute" => kind = InterfaceKind::Overridable, - _ => {} - } - } - Interface { ty: self.type_from_ref(self.row_decode(row.0, 1), None, generics), kind } - } - - // - // MemberRef table queries - // - - pub fn member_ref_parent(&self, row: MemberRef) -> MemberRefParent { - self.row_decode(row.0, 0) - } - pub fn member_ref_signature(&self, row: MemberRef) -> Blob { - self.row_blob(row.0, 2) - } - - // - // MethodDef table queries - // - - pub fn method_def_impl_flags(&self, row: MethodDef) -> MethodImplAttributes { - MethodImplAttributes(self.row_usize(row.0, 1)) - } - pub fn method_def_flags(&self, row: MethodDef) -> MethodAttributes { - MethodAttributes(self.row_usize(row.0, 2) as _) - } - pub fn method_def_name(&self, row: MethodDef) -> &str { - self.row_str(row.0, 3) - } - pub fn method_def_params(&self, row: MethodDef) -> impl Iterator { - self.row_list(row.0, TABLE_PARAM, 5).map(Param) - } - pub fn method_def_attributes(&self, row: MethodDef) -> impl Iterator { - self.row_attributes(row.0, HasAttribute::MethodDef(row)) - } - pub fn method_def_is_deprecated(&self, row: MethodDef) -> bool { - self.method_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "DeprecatedAttribute") - } - pub fn method_def_does_not_return(&self, row: MethodDef) -> bool { - self.method_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "DoesNotReturnAttribute") - } - pub fn method_def_can_return_multiple_success_values(&self, row: MethodDef) -> bool { - self.method_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "CanReturnMultipleSuccessValuesAttribute") - } - pub fn method_def_special_name(&self, row: MethodDef) -> String { - let name = self.method_def_name(row); - if self.method_def_flags(row).contains(MethodAttributes::SpecialName) { - if name.starts_with("get") { - name[4..].to_string() - } else if name.starts_with("put") { - format!("Set{}", &name[4..]) - } else if name.starts_with("add") { - name[4..].to_string() - } else if name.starts_with("remove") { - format!("Remove{}", &name[7..]) - } else { - name.to_string() - } - } else { - for attribute in self.method_def_attributes(row) { - if self.attribute_name(attribute) == "OverloadAttribute" { - for (_, arg) in self.attribute_args(attribute) { - if let Value::String(name) = arg { - return name; - } - } - } - } - name.to_string() - } - } - pub fn method_def_static_lib(&self, row: MethodDef) -> Option { - for attribute in self.method_def_attributes(row) { - if self.attribute_name(attribute) == "StaticLibraryAttribute" { - let args = self.attribute_args(attribute); - if let Value::String(value) = &args[0].1 { - return Some(value.clone()); - } - } - } - None - } - pub fn method_def_impl_map(&self, row: MethodDef) -> Option { - self.row_equal_range(row.0, TABLE_IMPLMAP, 1, MemberForwarded::MethodDef(row).encode()).map(ImplMap).next() - } - pub fn method_def_module_name(&self, row: MethodDef) -> String { - let Some(impl_map) = self.method_def_impl_map(row) else { - return String::new(); - }; - self.module_ref_name(self.impl_map_scope(impl_map)).to_lowercase() - } - pub fn method_def_last_error(&self, row: MethodDef) -> bool { - if let Some(map) = self.method_def_impl_map(row) { - self.impl_map_flags(map).contains(PInvokeAttributes::SupportsLastError) - } else { - false - } - } - pub fn method_def_signature(&self, row: MethodDef, generics: &[Type]) -> Signature { - let mut blob = self.row_blob(row.0, 4); - let call_flags = MethodCallAttributes(blob.read_usize() as _); - let _param_count = blob.read_usize(); - let mut return_type = self.type_from_blob(&mut blob, None, generics); - - let mut params: Vec = self - .method_def_params(row) - .filter_map(|param| { - if self.param_sequence(param) == 0 { - if self.param_is_const(param) { - return_type = return_type.clone().to_const_type(); - } - None - } else { - let is_output = self.param_flags(param).contains(ParamAttributes::Out); - let mut ty = self.type_from_blob(&mut blob, None, generics); - if self.param_is_const(param) || !is_output { - ty = ty.to_const_type(); - } - if !is_output { - ty = ty.to_const_ptr(); - } - let kind = self.param_kind(param); - Some(SignatureParam { def: param, ty, kind }) - } - }) - .collect(); - - for position in 0..params.len() { - // Point len params back to the corresponding ptr params. - match params[position].kind { - SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { - // The len params must be input only. - if !self.param_flags(params[relative].def).contains(ParamAttributes::Out) && position != relative && !params[relative].ty.is_pointer() { - params[relative].kind = SignatureParamKind::ArrayRelativePtr(position); - } else { - params[position].kind = SignatureParamKind::Other; - } - } - SignatureParamKind::ArrayFixed(_) => { - if self.param_free_with(params[position].def).is_some() { - params[position].kind = SignatureParamKind::Other; - } - } - _ => {} - } - } - - let mut sets = BTreeMap::>::new(); - - // Finds sets of ptr params pointing at the same len param. - for (position, param) in params.iter().enumerate() { - match param.kind { - SignatureParamKind::ArrayRelativeLen(relative) | SignatureParamKind::ArrayRelativeByteLen(relative) => { - sets.entry(relative).or_default().push(position); - } - _ => {} - } - } - - // Remove all sets. - for (len, ptrs) in sets { - if ptrs.len() > 1 { - params[len].kind = SignatureParamKind::Other; - for ptr in ptrs { - params[ptr].kind = SignatureParamKind::Other; - } - } - } - - // Remove any byte arrays that aren't byte-sized types. - for position in 0..params.len() { - if let SignatureParamKind::ArrayRelativeByteLen(relative) = params[position].kind { - if !params[position].ty.is_byte_size() { - params[position].kind = SignatureParamKind::Other; - params[relative].kind = SignatureParamKind::Other; - } - } - } - - for param in &mut params { - if param.kind == SignatureParamKind::Other { - if self.signature_param_is_convertible(param) { - if self.signature_param_is_failible_param(param) { - param.kind = SignatureParamKind::TryInto; - } else { - param.kind = SignatureParamKind::IntoParam; - } - } else { - let flags = self.param_flags(param.def); - if param.ty.is_pointer() && (flags.contains(ParamAttributes::Optional) || self.param_is_reserved(param.def)) { - param.kind = SignatureParamKind::OptionalPointer; - } else if self.type_is_primitive(¶m.ty) && (!param.ty.is_pointer() || self.type_is_blittable(¶m.ty.deref())) { - param.kind = SignatureParamKind::ValueType; - } else if self.type_is_blittable(¶m.ty) { - param.kind = SignatureParamKind::Blittable; - } - } - } - } - - Signature { def: row, params, return_type, call_flags } - } - pub fn method_def_extern_abi(&self, def: MethodDef) -> &'static str { - let impl_map = self.method_def_impl_map(def).expect("ImplMap not found"); - let flags = self.impl_map_flags(impl_map); - - if flags.contains(PInvokeAttributes::CallConvPlatformapi) { - "system" - } else if flags.contains(PInvokeAttributes::CallConvCdecl) { - "cdecl" - } else { - unimplemented!() - } - } - pub fn method_def_size(&self, method: MethodDef) -> usize { - let signature = self.method_def_signature(method, &[]); - signature.params.iter().fold(0, |sum, param| sum + std::cmp::max(4, self.type_size(¶m.ty))) - } - pub fn type_def_size(&self, def: TypeDef) -> usize { - match self.type_def_kind(def) { - TypeKind::Struct => { - if self.type_def_flags(def).contains(TypeAttributes::ExplicitLayout) { - self.type_def_fields(def).map(|field| self.type_size(&self.field_type(field, Some(def)))).max().unwrap_or(1) - } else { - let mut sum = 0; - for field in self.type_def_fields(def) { - let size = self.type_size(&self.field_type(field, Some(def))); - let align = self.type_align(&self.field_type(field, Some(def))); - sum = (sum + (align - 1)) & !(align - 1); - sum += size; - } - sum - } - } - TypeKind::Enum => self.type_size(&self.type_def_underlying_type(def)), - _ => 4, - } - } - fn type_size(&self, ty: &Type) -> usize { - match ty { - Type::I8 | Type::U8 => 1, - Type::I16 | Type::U16 => 2, - Type::I64 | Type::U64 | Type::F64 => 8, - Type::GUID => 16, - Type::TypeDef((def, _)) => self.type_def_size(*def), - Type::Win32Array((ty, len)) => self.type_size(ty) * len, - _ => 4, - } - } - fn type_def_align(&self, def: TypeDef) -> usize { - match self.type_def_kind(def) { - TypeKind::Struct => self.type_def_fields(def).map(|field| self.type_align(&self.field_type(field, Some(def)))).max().unwrap_or(1), - TypeKind::Enum => self.type_align(&self.type_def_underlying_type(def)), - _ => 4, - } - } - fn type_align(&self, ty: &Type) -> usize { - match ty { - Type::I8 | Type::U8 => 1, - Type::I16 | Type::U16 => 2, - Type::I64 | Type::U64 | Type::F64 => 8, - Type::GUID => 4, - Type::TypeDef((def, _)) => self.type_def_align(*def), - Type::Win32Array((ty, len)) => self.type_align(ty) * len, - _ => 4, - } - } - - // - // ModuleRef table queries - // - - fn module_ref_name(&self, row: ModuleRef) -> &str { - self.row_str(row.0, 0) - } - - // - // Param table queries - // - - pub fn param_flags(&self, row: Param) -> ParamAttributes { - ParamAttributes(self.row_usize(row.0, 0) as _) - } - pub fn param_sequence(&self, row: Param) -> usize { - self.row_usize(row.0, 1) - } - pub fn param_name(&self, row: Param) -> &str { - self.row_str(row.0, 2) - } - pub fn param_attributes(&self, row: Param) -> impl Iterator { - self.row_attributes(row.0, HasAttribute::Param(row)) - } - pub fn param_is_com_out_ptr(&self, row: Param) -> bool { - self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "ComOutPtrAttribute") - } - fn param_kind(&self, row: Param) -> SignatureParamKind { - for attribute in self.param_attributes(row) { - match self.attribute_name(attribute) { - "NativeArrayInfoAttribute" => { - for (_, value) in self.attribute_args(attribute) { - match value { - Value::I16(value) => return SignatureParamKind::ArrayRelativeLen(value as _), - Value::I32(value) => return SignatureParamKind::ArrayFixed(value as _), - _ => {} - } - } - } - "MemorySizeAttribute" => { - for (_, value) in self.attribute_args(attribute) { - if let Value::I16(value) = value { - return SignatureParamKind::ArrayRelativeByteLen(value as _); - } - } - } - _ => {} - } - } - SignatureParamKind::Other - } - pub fn param_is_retval(&self, row: Param) -> bool { - self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "RetValAttribute") - } - pub fn param_is_reserved(&self, row: Param) -> bool { - self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "ReservedAttribute") - } - pub fn param_free_with(&self, row: Param) -> Option { - for attribute in self.param_attributes(row) { - if self.attribute_name(attribute) == "FreeWithAttribute" { - for (_, arg) in self.attribute_args(attribute) { - if let Value::String(name) = arg { - return Some(name); - } - } - } - } - None - } - pub fn param_is_const(&self, row: Param) -> bool { - self.param_attributes(row).any(|attribute| self.attribute_name(attribute) == "ConstAttribute") - } - - // - // TypeDef table queries - // - - pub fn type_def_flags(&self, row: TypeDef) -> TypeAttributes { - TypeAttributes(self.row_usize(row.0, 0) as _) - } - pub fn type_def_name(&self, row: TypeDef) -> &str { - self.row_str(row.0, 1) - } - pub fn type_def_namespace(&self, row: TypeDef) -> &str { - self.row_str(row.0, 2) - } - pub fn type_def_type_name(&self, row: TypeDef) -> TypeName { - TypeName::new(self.type_def_namespace(row), self.type_def_name(row)) - } - pub fn type_def_extends(&self, row: TypeDef) -> Option { - match self.row_usize(row.0, 3) { - 0 => None, - code => Some(self.type_def_or_ref(TypeDefOrRef::decode(row.0.file as _, code))), - } - } - pub fn type_def_fields(&self, row: TypeDef) -> impl Iterator { - self.row_list(row.0, TABLE_FIELD, 4).map(Field) - } - pub fn type_def_methods(&self, row: TypeDef) -> impl Iterator { - self.row_list(row.0, TABLE_METHODDEF, 5).map(MethodDef) - } - pub fn type_def_attributes(&self, row: TypeDef) -> impl Iterator { - self.row_attributes(row.0, HasAttribute::TypeDef(row)) - } - pub fn type_def_generics(&self, row: TypeDef) -> impl Iterator { - self.row_equal_range(row.0, TABLE_GENERICPARAM, 2, TypeOrMethodDef::TypeDef(row).encode()).map(|row| Type::GenericParam(GenericParam(row))) - } - pub fn type_def_interface_impls(&self, row: TypeDef) -> impl Iterator { - self.row_equal_range(row.0, TABLE_INTERFACEIMPL, 0, (row.0.row + 1) as _).map(InterfaceImpl) - } - pub fn type_def_enclosing_type(&self, row: TypeDef) -> Option { - self.row_equal_range(row.0, TABLE_NESTEDCLASS, 0, (row.0.row + 1) as _).next().map(|row| TypeDef(Row::new(self.files[row.file as usize].usize(row.row as _, row.table as _, 1) - 1, TABLE_TYPEDEF, row.file as _))) - } - pub fn type_def_class_layout(&self, row: TypeDef) -> Option { - self.row_equal_range(row.0, TABLE_CLASSLAYOUT, 2, (row.0.row + 1) as _).map(ClassLayout).next() - } - pub fn type_def_underlying_type(&self, row: TypeDef) -> Type { - let field = self.type_def_fields(row).next().expect("Field not found"); - if let Some(constant) = self.field_constant(field) { - self.constant_type(constant) - } else { - self.field_type(field, Some(row)) - } - } - pub fn type_def_kind(&self, row: TypeDef) -> TypeKind { - match self.type_def_extends(row) { - None => TypeKind::Interface, - Some(TypeName::Enum) => TypeKind::Enum, - Some(TypeName::Delegate) => TypeKind::Delegate, - Some(TypeName::Struct) => TypeKind::Struct, - Some(_) => TypeKind::Class, - } - } - pub fn type_def_stdcall(&self, row: TypeDef) -> usize { - if self.type_def_kind(row) == TypeKind::Struct { - if self.type_def_flags(row).contains(TypeAttributes::ExplicitLayout) { - self.type_def_fields(row).map(|field| self.type_stdcall(&self.field_type(field, Some(row)))).max().unwrap_or(1) - } else { - self.type_def_fields(row).fold(0, |sum, field| sum + self.type_stdcall(&self.field_type(field, Some(row)))) - } - } else { - 4 - } - } - pub fn type_def_is_blittable(&self, row: TypeDef) -> bool { - match self.type_def_kind(row) { - TypeKind::Struct => { - if self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) { - self.type_def_fields(row).all(|field| self.field_is_blittable(field, row)) - } else { - true - } - } - TypeKind::Enum => true, - TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), - _ => false, - } - } - pub fn type_def_is_copyable(&self, row: TypeDef) -> bool { - match self.type_def_kind(row) { - TypeKind::Struct => self.type_def_fields(row).all(|field| self.field_is_copyable(field, row)), - TypeKind::Enum => true, - TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), - _ => false, - } - } - pub fn type_def_is_callback(&self, row: TypeDef) -> bool { - !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) && self.type_def_kind(row) == TypeKind::Delegate - } - pub fn type_def_has_default_constructor(&self, row: TypeDef) -> bool { - for attribute in self.type_def_attributes(row) { - if self.attribute_name(attribute) == "ActivatableAttribute" { - if self.attribute_args(attribute).iter().any(|arg| matches!(arg.1, Value::TypeDef(_))) { - continue; - } else { - return true; - } - } - } - false - } - // TODO: consider removing all the expects and just return Option and let the bindgen crate expect it - // that way the metadata reader is a little more schema-agnostic... - pub fn type_def_invoke_method(&self, row: TypeDef) -> MethodDef { - self.type_def_methods(row).find(|method| self.method_def_name(*method) == "Invoke").expect("`Invoke` method not found") - } - pub fn type_def_interfaces(&'a self, row: TypeDef, generics: &'a [Type]) -> impl Iterator + '_ { - self.type_def_interface_impls(row).map(move |row| self.interface_impl_type(row, generics)) - } - pub fn type_def_default_interface(&self, row: TypeDef) -> Option { - self.type_def_interfaces(row, &[]).find(|interface| interface.kind == InterfaceKind::Default).map(|interface| interface.ty) - } - pub fn type_def_has_default_interface(&self, row: TypeDef) -> bool { - self.type_def_interface_impls(row).any(|imp| self.interface_impl_is_default(imp)) - } - pub fn type_def_is_deprecated(&self, row: TypeDef) -> bool { - self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "DeprecatedAttribute") - } - pub fn type_def_is_handle(&self, row: TypeDef) -> bool { - self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "NativeTypedefAttribute") - } - pub fn type_def_is_exclusive(&self, row: TypeDef) -> bool { - self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ExclusiveToAttribute") - } - pub fn type_def_is_scoped(&self, row: TypeDef) -> bool { - self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) || self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ScopedEnumAttribute") - } - pub fn type_def_is_contract(&self, row: TypeDef) -> bool { - self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ApiContractAttribute") - } - fn type_def_is_composable(&self, row: TypeDef) -> bool { - self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "ComposableAttribute") - } - fn type_def_is_struct(&self, row: TypeDef) -> bool { - // This check is used to detect virtual functions that return C-style PODs that affect how the stack is packed for x86. - // It could be defined as a struct with more than one field but that check is complicated as it would have to detect - // nested structs. Fortunately, this is rare enough that this check is sufficient. - self.type_def_kind(row) == TypeKind::Struct && !self.type_def_is_handle(row) - } - pub fn type_def_is_trivially_convertible(&self, row: TypeDef) -> bool { - match self.type_def_kind(row) { - TypeKind::Struct => self.type_def_is_handle(row), - _ => false, - } - } - pub fn type_def_is_primitive(&self, row: TypeDef) -> bool { - match self.type_def_kind(row) { - TypeKind::Enum => true, - TypeKind::Struct => self.type_def_is_handle(row), - TypeKind::Delegate => !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), - _ => false, - } - } - pub fn type_def_has_explicit_layout(&self, row: TypeDef) -> bool { - if self.type_def_kind(row) != TypeKind::Struct { - return false; - } - fn check(reader: &Reader, row: TypeDef) -> bool { - if reader.type_def_flags(row).contains(TypeAttributes::ExplicitLayout) { - return true; - } - if reader.type_def_fields(row).any(|field| reader.type_has_explicit_layout(&reader.field_type(field, Some(row)))) { - return true; - } - false - } - let type_name = self.type_def_type_name(row); - if type_name.namespace.is_empty() { - check(self, row) - } else { - for row in self.get(type_name) { - if check(self, row) { - return true; - } - } - false - } - } - pub fn type_def_has_packing(&self, row: TypeDef) -> bool { - if self.type_def_kind(row) != TypeKind::Struct { - return false; - } - fn check(reader: &Reader, row: TypeDef) -> bool { - if reader.type_def_class_layout(row).is_some() { - return true; - } - if reader.type_def_fields(row).any(|field| reader.type_has_packing(&reader.field_type(field, Some(row)))) { - return true; - } - false - } - let type_name = self.type_def_type_name(row); - if type_name.namespace.is_empty() { - check(self, row) - } else { - for row in self.get(type_name) { - if check(self, row) { - return true; - } - } - false - } - } - pub fn type_def_has_callback(&self, row: TypeDef) -> bool { - if self.type_def_is_callback(row) { - return true; - } - if self.type_def_kind(row) != TypeKind::Struct { - return false; - } - fn check(reader: &Reader, row: TypeDef) -> bool { - if reader.type_def_fields(row).any(|field| reader.type_has_callback(&reader.field_type(field, Some(row)))) { - return true; - } - false - } - let type_name = self.type_def_type_name(row); - if type_name.namespace.is_empty() { - check(self, row) - } else { - for row in self.get(type_name) { - if check(self, row) { - return true; - } - } - false - } - } - pub fn type_def_guid(&self, row: TypeDef) -> Option { - for attribute in self.type_def_attributes(row) { - if self.attribute_name(attribute) == "GuidAttribute" { - return Some(GUID::from_args(&self.attribute_args(attribute))); - } - } - None - } - pub fn type_def_bases(&self, mut row: TypeDef) -> Vec { - let mut bases = Vec::new(); - loop { - match self.type_def_extends(row) { - Some(base) if base != TypeName::Object => { - row = self.get(base).next().expect("Type not found"); - bases.push(row); - } - _ => break, - } - } - bases - } - pub fn type_def_is_flags(&self, row: TypeDef) -> bool { - // Win32 enums use the Flags attribute. WinRT enums don't have the Flags attribute but are paritioned merely based - // on whether they are signed. - self.type_def_attributes(row).any(|attribute| self.attribute_name(attribute) == "FlagsAttribute") || (self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) && self.type_def_underlying_type(row) == Type::U32) - } - pub fn type_def_is_agile(&self, row: TypeDef) -> bool { - for attribute in self.type_def_attributes(row) { - match self.attribute_name(attribute) { - "AgileAttribute" => return true, - "MarshalingBehaviorAttribute" => { - if let Some((_, Value::EnumDef(_, value))) = self.attribute_args(attribute).get(0) { - if let Value::I32(2) = **value { - return true; - } - } - } - _ => {} - } - } - matches!(self.type_def_type_name(row), TypeName::IAsyncAction | TypeName::IAsyncActionWithProgress | TypeName::IAsyncOperation | TypeName::IAsyncOperationWithProgress) - } - pub fn type_def_invalid_values(&self, row: TypeDef) -> Vec { - let mut values = Vec::new(); - for attribute in self.type_def_attributes(row) { - if self.attribute_name(attribute) == "InvalidHandleValueAttribute" { - if let Some((_, Value::I64(value))) = self.attribute_args(attribute).get(0) { - values.push(*value); - } - } - } - values - } - pub fn type_def_usable_for(&self, row: TypeDef) -> Option { - for attribute in self.type_def_attributes(row) { - if self.attribute_name(attribute) == "AlsoUsableForAttribute" { - if let Some((_, Value::String(name))) = self.attribute_args(attribute).get(0) { - return self.get(TypeName::new(self.type_def_namespace(row), name.as_str())).next(); - } - } - } - None - } - pub fn type_def_is_nullable(&self, row: TypeDef) -> bool { - match self.type_def_kind(row) { - TypeKind::Interface | TypeKind::Class => true, - // Win32 callbacks are defined as `Option` so we don't include them here to avoid them - // from being doubly wrapped in `Option`. - TypeKind::Delegate => self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime), - _ => false, - } - } - pub fn type_def_can_implement(&self, row: TypeDef) -> bool { - for attribute in self.type_def_attributes(row) { - if self.attribute_name(attribute) == "ExclusiveToAttribute" { - for (_, arg) in self.attribute_args(attribute) { - if let Value::TypeDef(def) = arg { - for child in self.type_def_interfaces(def, &[]) { - if child.kind == InterfaceKind::Overridable { - if let Type::TypeDef((def, _)) = child.ty { - if self.type_def_type_name(def) == self.type_def_type_name(row) { - return true; - } - } - } - } - } - } - return false; - } - } - true - } - pub fn type_def_async_kind(&self, row: TypeDef) -> AsyncKind { - match self.type_def_type_name(row) { - TypeName::IAsyncAction => AsyncKind::Action, - TypeName::IAsyncActionWithProgress => AsyncKind::ActionWithProgress, - TypeName::IAsyncOperation => AsyncKind::Operation, - TypeName::IAsyncOperationWithProgress => AsyncKind::OperationWithProgress, - _ => AsyncKind::None, - } - } - pub fn type_def_signature(&self, row: TypeDef, generics: &[Type]) -> String { - match self.type_def_kind(row) { - TypeKind::Interface => self.type_def_interface_signature(row, generics), - TypeKind::Class => { - if let Type::TypeDef((default, generics)) = self.type_def_interfaces(row, generics).find(|row| row.kind == InterfaceKind::Default).expect("Default interface not found").ty { - format!("rc({};{})", self.type_def_type_name(row), self.type_def_interface_signature(default, &generics)) - } else { - unimplemented!(); - } - } - TypeKind::Enum => format!("enum({};{})", self.type_def_type_name(row), self.type_signature(&self.type_def_underlying_type(row))), - TypeKind::Struct => { - let mut result = format!("struct({}", self.type_def_type_name(row)); - for field in self.type_def_fields(row) { - result.push(';'); - result.push_str(&self.type_signature(&self.field_type(field, Some(row)))); - } - result.push(')'); - result - } - TypeKind::Delegate => { - if generics.is_empty() { - format!("delegate({})", self.type_def_interface_signature(row, generics)) - } else { - self.type_def_interface_signature(row, generics) - } - } - } - } - fn type_def_interface_signature(&self, row: TypeDef, generics: &[Type]) -> String { - let guid = self.type_def_guid(row).unwrap(); - if generics.is_empty() { - format!("{{{guid:#?}}}") - } else { - let mut result = format!("pinterface({{{guid:#?}}}"); - for generic in generics { - result.push(';'); - result.push_str(&self.type_signature(generic)); - } - result.push(')'); - result - } - } - pub fn type_def_cfg(&self, row: TypeDef, generics: &[Type]) -> Cfg { - let mut cfg = Cfg::default(); - self.type_def_cfg_combine(row, generics, &mut cfg); - self.cfg_add_attributes(&mut cfg, self.type_def_attributes(row)); - cfg - } - pub fn type_def_cfg_impl(&self, def: TypeDef, generics: &[Type]) -> Cfg { - let mut cfg = Cfg { implement: true, ..Default::default() }; - - fn combine<'a>(reader: &'a Reader, def: TypeDef, generics: &[Type], cfg: &mut Cfg<'a>) { - reader.type_def_cfg_combine(def, generics, cfg); - - for method in reader.type_def_methods(def) { - reader.signature_cfg_combine(&reader.method_def_signature(method, generics), cfg); - } - } - - combine(self, def, generics, &mut cfg); - - for def in self.type_def_vtables(def) { - if let Type::TypeDef((def, generics)) = def { - combine(self, def, &generics, &mut cfg); - } - } - - if self.type_def_flags(def).contains(TypeAttributes::WindowsRuntime) { - for interface in self.type_def_interfaces(def, generics) { - if let Type::TypeDef((def, generics)) = interface.ty { - combine(self, def, &generics, &mut cfg); - } - } - } - - self.cfg_add_attributes(&mut cfg, self.type_def_attributes(def)); - cfg - } - pub fn type_def_cfg_combine(&'a self, row: TypeDef, generics: &[Type], cfg: &mut Cfg<'a>) { - let type_name = self.type_def_type_name(row); - - for generic in generics { - self.type_cfg_combine(generic, cfg); - } - - if cfg.types.entry(type_name.namespace).or_default().insert(row) { - match self.type_def_kind(row) { - TypeKind::Class => { - if let Some(default_interface) = self.type_def_default_interface(row) { - self.type_cfg_combine(&default_interface, cfg); - } - } - TypeKind::Interface => { - if !self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) { - for def in self.type_def_vtables(row) { - if let Type::TypeDef((def, _)) = def { - cfg.add_feature(self.type_def_namespace(def)); - } - } - } - } - TypeKind::Struct => { - self.type_def_fields(row).for_each(|field| self.field_cfg_combine(field, Some(row), cfg)); - if !type_name.namespace.is_empty() { - for def in self.get(type_name) { - if def != row { - self.type_def_cfg_combine(def, &[], cfg); - } - } - } - } - TypeKind::Delegate => self.signature_cfg_combine(&self.method_def_signature(self.type_def_invoke_method(row), generics), cfg), - _ => {} - } - } - } - pub fn type_def_vtables(&self, row: TypeDef) -> Vec { - let mut result = Vec::new(); - if self.type_def_flags(row).contains(TypeAttributes::WindowsRuntime) { - result.push(Type::IUnknown); - if self.type_def_kind(row) != TypeKind::Delegate { - result.push(Type::IInspectable); - } - } else { - let mut next = row; - while let Some(base) = self.type_def_interfaces(next, &[]).next() { - match base.ty { - Type::TypeDef((row, _)) => { - next = row; - result.insert(0, base.ty); - } - Type::IInspectable => { - result.insert(0, Type::IUnknown); - result.insert(1, Type::IInspectable); - break; - } - Type::IUnknown => { - result.insert(0, Type::IUnknown); - break; - } - _ => unimplemented!(), - } - } - } - result - } - - // - // TypeRef table queries - // - - pub fn type_ref_name(&self, row: TypeRef) -> &str { - self.row_str(row.0, 1) - } - pub fn type_ref_namespace(&self, row: TypeRef) -> &str { - self.row_str(row.0, 2) - } - pub fn type_ref_type_name(&self, row: TypeRef) -> TypeName { - TypeName::new(self.type_ref_namespace(row), self.type_ref_name(row)) - } - pub fn type_ref_resolution_scope(&self, row: TypeRef) -> ResolutionScope { - self.row_decode(row.0, 0) - } - - // - // TypeSpec table queries - // - - pub fn type_spec_signature(&self, row: TypeSpec) -> Blob { - self.row_blob(row.0, 0) - } - - // - // Signature queries - // - - pub fn signature_cfg(&self, signature: &Signature) -> Cfg { - let mut cfg = Cfg::default(); - self.signature_cfg_combine(signature, &mut cfg); - self.cfg_add_attributes(&mut cfg, self.method_def_attributes(signature.def)); - cfg - } - fn signature_cfg_combine(&'a self, signature: &Signature, cfg: &mut Cfg<'a>) { - self.type_cfg_combine(&signature.return_type, cfg); - signature.params.iter().for_each(|param| self.type_cfg_combine(¶m.ty, cfg)); - } - pub fn signature_param_is_borrowed(&self, param: &SignatureParam) -> bool { - self.type_is_borrowed(¶m.ty) - } - pub fn signature_param_is_failible_param(&self, param: &SignatureParam) -> bool { - self.type_is_non_exclusive_winrt_interface(¶m.ty) - } - pub fn signature_param_is_trivially_convertible(&self, param: &SignatureParam) -> bool { - self.type_is_trivially_convertible(¶m.ty) - } - pub fn signature_param_is_convertible(&self, param: &SignatureParam) -> bool { - !self.param_flags(param.def).contains(ParamAttributes::Out) && !param.ty.is_winrt_array() && !param.ty.is_pointer() && !param.kind.is_array() && (self.type_is_borrowed(¶m.ty) || self.type_is_non_exclusive_winrt_interface(¶m.ty) || self.type_is_trivially_convertible(¶m.ty)) - } - pub fn signature_param_is_retval(&self, param: &SignatureParam) -> bool { - // The Win32 metadata uses `RetValAttribute` to call out retval methods but it is employed - // very sparingly, so this heuristic is used to apply the transformation more uniformly. - if self.param_is_retval(param.def) { - return true; - } - if !param.ty.is_pointer() { - return false; - } - if param.ty.is_void() { - return false; - } - let flags = self.param_flags(param.def); - if flags.contains(ParamAttributes::In) || !flags.contains(ParamAttributes::Out) || flags.contains(ParamAttributes::Optional) || param.kind.is_array() { - return false; - } - if self.param_kind(param.def).is_array() { - return false; - } - // If it's bigger than 128 bits, best to pass as a reference. - if self.type_size(¶m.ty.deref()) > 16 { - return false; - } - // Win32 callbacks are defined as `Option` so we don't include them here to avoid - // producing the `Result>` anti-pattern. - !self.type_is_callback(¶m.ty.deref()) - } - pub fn signature_kind(&self, signature: &Signature) -> SignatureKind { - if self.method_def_can_return_multiple_success_values(signature.def) { - return SignatureKind::PreserveSig; - } - match &signature.return_type { - Type::Void if self.signature_is_retval(signature) => SignatureKind::ReturnValue, - Type::Void => SignatureKind::ReturnVoid, - Type::HRESULT => { - if signature.params.len() >= 2 { - if let Some(guid) = self.signature_param_is_query_guid(&signature.params) { - if let Some(object) = self.signature_param_is_query_object(&signature.params) { - if self.param_flags(signature.params[object].def).contains(ParamAttributes::Optional) { - return SignatureKind::QueryOptional(QueryPosition { object, guid }); - } else { - return SignatureKind::Query(QueryPosition { object, guid }); - } - } - } - } - if self.signature_is_retval(signature) { - SignatureKind::ResultValue - } else { - SignatureKind::ResultVoid - } - } - Type::TypeDef((def, _)) if self.type_def_type_name(*def) == TypeName::NTSTATUS => SignatureKind::ResultVoid, - Type::TypeDef((def, _)) if self.type_def_type_name(*def) == TypeName::WIN32_ERROR => SignatureKind::ResultVoid, - Type::TypeDef((def, _)) if self.type_def_type_name(*def) == TypeName::BOOL && self.method_def_last_error(signature.def) => SignatureKind::ResultVoid, - _ if self.type_is_struct(&signature.return_type) => SignatureKind::ReturnStruct, - _ => SignatureKind::PreserveSig, - } - } - fn signature_is_retval(&self, signature: &Signature) -> bool { - signature.params.last().map_or(false, |param| self.signature_param_is_retval(param)) - && signature.params[..signature.params.len() - 1].iter().all(|param| { - let flags = self.param_flags(param.def); - !flags.contains(ParamAttributes::Out) - }) - } - fn signature_param_is_query_guid(&self, params: &[SignatureParam]) -> Option { - params.iter().rposition(|param| param.ty == Type::ConstPtr((Box::new(Type::GUID), 1)) && !self.param_flags(param.def).contains(ParamAttributes::Out)) - } - fn signature_param_is_query_object(&self, params: &[SignatureParam]) -> Option { - params.iter().rposition(|param| param.ty == Type::MutPtr((Box::new(Type::Void), 2)) && self.param_is_com_out_ptr(param.def)) - } - - // - // Other type queries - // - - fn cfg_add_attributes(&self, cfg: &mut Cfg, attributes: impl Iterator) { - for attribute in attributes { - match self.attribute_name(attribute) { - "SupportedArchitectureAttribute" => { - if let Some((_, Value::EnumDef(_, value))) = self.attribute_args(attribute).get(0) { - if let Value::I32(value) = **value { - if value & 1 == 1 { - cfg.arches.insert("x86"); - } - if value & 2 == 2 { - cfg.arches.insert("x86_64"); - } - if value & 4 == 4 { - cfg.arches.insert("aarch64"); - } - } - } - } - "DeprecatedAttribute" => { - cfg.add_feature("deprecated"); - } - _ => {} - } - } - } - pub fn type_collect_standalone(&self, ty: &Type, set: &mut BTreeSet) { - let ty = ty.to_underlying_type(); - if !set.insert(ty.clone()) { - return; - } - - let Type::TypeDef((def, generics)) = &ty else { return; }; - let def = *def; - - // Ensure that we collect all the typedefs of the same name. We need to - // do this in the case where the user specifies a top level item that - // references a typedef by name, but that name resolves to more than 1 - // Type based on target architecture (typically) - // - // Note this is a bit overeager as we can collect a typedef that is used - // by one architecture but not by another - let type_name = self.type_def_type_name(def); - if !type_name.namespace.is_empty() { - for row in self.get(type_name) { - if def != row { - self.type_collect_standalone(&Type::TypeDef((row, Vec::new())), set); - } - } - } - - for generic in generics { - self.type_collect_standalone(generic, set); - } - for field in self.type_def_fields(def) { - let ty = self.field_type(field, Some(def)); - if let Type::TypeDef((def, _)) = &ty { - if self.type_def_namespace(*def).is_empty() { - continue; - } - } - self.type_collect_standalone(&ty, set); - } - for method in self.type_def_methods(def) { - // Skip delegate pseudo-constructors. - if self.method_def_name(method) == ".ctor" { - continue; - } - let signature = self.method_def_signature(method, generics); - self.type_collect_standalone(&signature.return_type, set); - signature.params.iter().for_each(|param| self.type_collect_standalone(¶m.ty, set)); - } - for interface in self.type_interfaces(&ty) { - self.type_collect_standalone(&interface.ty, set); - } - if self.type_def_kind(def) == TypeKind::Struct && self.type_def_fields(def).next().is_none() && self.type_def_guid(def).is_some() { - set.insert(Type::GUID); - } - - self.type_collect_standalone_nested(def, set); - } - - fn type_collect_standalone_nested(&self, td: TypeDef, set: &mut BTreeSet) { - for nested in self.nested_types(td) { - self.type_collect_standalone_nested(nested, set); - - for field in self.type_def_fields(nested) { - let ty = self.field_type(field, Some(nested)); - if let Type::TypeDef((def, _)) = &ty { - // Skip the fields that actually refer to the anonymous nested - // type, otherwise it will get added to the typeset and emitted - if self.type_def_namespace(*def).is_empty() { - continue; - } - - self.type_collect_standalone(&ty, set); - } - } - } - } - - pub fn type_cfg(&self, ty: &Type) -> Cfg { - let mut cfg = Cfg::default(); - self.type_cfg_combine(ty, &mut cfg); - cfg - } - pub fn type_cfg_combine(&'a self, ty: &Type, cfg: &mut Cfg<'a>) { - match ty { - Type::TypeDef((row, generics)) => self.type_def_cfg_combine(*row, generics, cfg), - Type::Win32Array((ty, _)) => self.type_cfg_combine(ty, cfg), - Type::ConstPtr((ty, _)) => self.type_cfg_combine(ty, cfg), - Type::MutPtr((ty, _)) => self.type_cfg_combine(ty, cfg), - Type::WinrtArray(ty) => self.type_cfg_combine(ty, cfg), - Type::WinrtArrayRef(ty) => self.type_cfg_combine(ty, cfg), - ty => _ = cfg.core_types.insert(ty.clone()), - } - } - pub fn type_interfaces(&self, ty: &Type) -> Vec { - // TODO: collect into btree map and then return collected vec - // This will both sort the results and should make finding dupes faster - fn walk(reader: &Reader, result: &mut Vec, parent: &Type, is_base: bool) { - if let Type::TypeDef((row, generics)) = parent { - for mut child in reader.type_def_interfaces(*row, generics) { - child.kind = if !is_base && child.kind == InterfaceKind::Default { - InterfaceKind::Default - } else if child.kind == InterfaceKind::Overridable { - continue; - } else if is_base { - InterfaceKind::Base - } else { - InterfaceKind::None - }; - let mut found = false; - for existing in result.iter_mut() { - if existing.ty == child.ty { - found = true; - if child.kind == InterfaceKind::Default { - existing.kind = child.kind - } - } - } - if !found { - walk(reader, result, &child.ty, is_base); - result.push(child); - } - } - } - } - let mut result = Vec::new(); - walk(self, &mut result, ty, false); - if let Type::TypeDef((row, _)) = ty { - if self.type_def_kind(*row) == TypeKind::Class { - for base in self.type_def_bases(*row) { - walk(self, &mut result, &Type::TypeDef((base, Vec::new())), true); - } - for attribute in self.type_def_attributes(*row) { - match self.attribute_name(attribute) { - "StaticAttribute" | "ActivatableAttribute" => { - for (_, arg) in self.attribute_args(attribute) { - if let Value::TypeDef(row) = arg { - result.push(Interface { ty: Type::TypeDef((row, Vec::new())), kind: InterfaceKind::Static }); - break; - } - } - } - _ => {} - } - } - } - } - result.sort_by(|a, b| self.type_name(&a.ty).cmp(self.type_name(&b.ty))); - result - } - pub fn type_def_or_ref(&self, code: TypeDefOrRef) -> TypeName { - match code { - TypeDefOrRef::TypeDef(row) => TypeName::new(self.type_def_namespace(row), self.type_def_name(row)), - TypeDefOrRef::TypeRef(row) => TypeName::new(self.type_ref_namespace(row), self.type_ref_name(row)), - _ => unimplemented!(), - } - } - fn type_stdcall(&self, ty: &Type) -> usize { - match ty { - Type::I8 | Type::U8 => 1, - Type::I16 | Type::U16 => 2, - Type::I64 | Type::U64 | Type::F64 => 8, - Type::GUID => 16, - Type::TypeDef((row, _)) => self.type_def_stdcall(*row), - _ => 4, - } - } - pub fn type_is_exclusive(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_is_exclusive(*row), - _ => false, - } - } - pub fn type_is_blittable(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_is_blittable(*row), - Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, - Type::Win32Array((kind, _)) => self.type_is_blittable(kind), - Type::WinrtArray(kind) => self.type_is_blittable(kind), - _ => true, - } - } - pub fn type_is_copyable(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_is_copyable(*row), - Type::String | Type::BSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => false, - Type::Win32Array((kind, _)) => self.type_is_copyable(kind), - Type::WinrtArray(kind) => self.type_is_copyable(kind), - _ => true, - } - } - pub fn type_has_explicit_layout(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_has_explicit_layout(*row), - Type::Win32Array((ty, _)) => self.type_has_explicit_layout(ty), - _ => false, - } - } - pub fn type_has_packing(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_has_packing(*row), - Type::Win32Array((ty, _)) => self.type_has_packing(ty), - _ => false, - } - } - pub fn type_has_callback(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_has_callback(*row), - Type::Win32Array((ty, _)) => self.type_has_callback(ty), - _ => false, - } - } - fn type_from_ref(&self, code: TypeDefOrRef, enclosing: Option, generics: &[Type]) -> Type { - if let TypeDefOrRef::TypeSpec(def) = code { - let mut blob = self.type_spec_signature(def); - return self.type_from_blob_impl(&mut blob, None, generics); - } - - let mut full_name = self.type_def_or_ref(code); - - for (known_name, kind) in CORE_TYPES { - if full_name == known_name { - return kind; - } - } - - for (from, to) in REMAP_TYPES { - if full_name == from { - full_name = to; - break; - } - } - - if let Some(outer) = enclosing { - if full_name.namespace.is_empty() { - let nested = &self.nested[&outer]; - let Some(inner) = nested.get(full_name.name) else { - panic!("Nested type not found: {}.{}", self.type_def_type_name(outer), full_name.name); - }; - return Type::TypeDef((*inner, Vec::new())); - } - } - - if let Some(ty) = self.get(full_name).next() { - Type::TypeDef((ty, Vec::new())) - } else { - Type::TypeRef(code) - } - } - fn type_from_blob(&self, blob: &mut Blob, enclosing: Option, generics: &[Type]) -> Type { - // Used by WinRT to indicate that a struct input parameter is passed by reference rather than by value on the ABI. - let is_const = blob.read_modifiers().iter().any(|def| self.type_def_or_ref(*def) == TypeName::IsConst); - - // Used by WinRT to indicate an output parameter, but there are other ways to determine this direction so here - // it is only used to distinguish between slices and heap-allocated arrays. - let is_ref = blob.read_expected(ELEMENT_TYPE_BYREF as _); - - if blob.read_expected(ELEMENT_TYPE_VOID as _) { - return Type::Void; - } - - let is_array = blob.read_expected(ELEMENT_TYPE_SZARRAY as _); // Used by WinRT to indicate an array - - let mut pointers = 0; - - while blob.read_expected(ELEMENT_TYPE_PTR as _) { - pointers += 1; - } - - let kind = self.type_from_blob_impl(blob, enclosing, generics); - - if pointers > 0 { - Type::MutPtr((Box::new(kind), pointers)) - } else if is_const { - Type::ConstRef(Box::new(kind)) - } else if is_array { - if is_ref { - Type::WinrtArrayRef(Box::new(kind)) - } else { - Type::WinrtArray(Box::new(kind)) - } - } else { - kind - } - } - fn type_from_blob_impl(&self, blob: &mut Blob, enclosing: Option, generics: &[Type]) -> Type { - let code = blob.read_usize(); - - if let Some(code) = Type::from_code(code) { - return code; - } - - match code as _ { - ELEMENT_TYPE_VALUETYPE | ELEMENT_TYPE_CLASS => self.type_from_ref(TypeDefOrRef::decode(blob.file, blob.read_usize()), enclosing, generics), - ELEMENT_TYPE_VAR => generics.get(blob.read_usize()).unwrap_or(&Type::Void).clone(), - ELEMENT_TYPE_ARRAY => { - let kind = self.type_from_blob(blob, enclosing, generics); - let _rank = blob.read_usize(); - let _count = blob.read_usize(); - let bounds = blob.read_usize(); - Type::Win32Array((Box::new(kind), bounds)) - } - ELEMENT_TYPE_GENERICINST => { - blob.read_usize(); - - let def = self.get(self.type_def_or_ref(TypeDefOrRef::decode(blob.file, blob.read_usize()))).next().expect("Type not found"); - let mut args = Vec::with_capacity(blob.read_usize()); - - for _ in 0..args.capacity() { - args.push(self.type_from_blob_impl(blob, enclosing, generics)); - } - - Type::TypeDef((def, args)) - } - _ => unimplemented!(), - } - } - pub fn type_name(&self, ty: &Type) -> &str { - match ty { - Type::TypeDef((row, _)) => self.type_def_name(*row), - _ => "", - } - } - pub fn type_signature(&self, ty: &Type) -> String { - match ty { - Type::Bool => "b1".to_string(), - Type::Char => "c2".to_string(), - Type::I8 => "i1".to_string(), - Type::U8 => "u1".to_string(), - Type::I16 => "i2".to_string(), - Type::U16 => "u2".to_string(), - Type::I32 => "i4".to_string(), - Type::U32 => "u4".to_string(), - Type::I64 => "i8".to_string(), - Type::U64 => "u8".to_string(), - Type::F32 => "f4".to_string(), - Type::F64 => "f8".to_string(), - Type::String => "string".to_string(), - Type::IInspectable => "cinterface(IInspectable)".to_string(), - Type::GUID => "g16".to_string(), - Type::HRESULT => "struct(Windows.Foundation.HResult;i4)".to_string(), - Type::TypeDef((row, generics)) => self.type_def_signature(*row, generics), - _ => unimplemented!(), - } - } - pub fn type_is_nullable(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_is_nullable(*row), - Type::IInspectable | Type::IUnknown => true, - _ => false, - } - } - fn type_is_borrowed(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => !self.type_def_is_blittable(*row), - Type::BSTR | Type::PCSTR | Type::PCWSTR | Type::IInspectable | Type::IUnknown | Type::GenericParam(_) => true, - _ => false, - } - } - pub fn type_is_non_exclusive_winrt_interface(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => { - let flags = self.type_def_flags(*row); - if !flags.contains(TypeAttributes::WindowsRuntime) { - false - } else { - match self.type_def_kind(*row) { - TypeKind::Interface => !self.type_def_is_exclusive(*row), - TypeKind::Class => self.type_def_is_composable(*row), - _ => false, - } - } - } - _ => false, - } - } - pub fn type_is_trivially_convertible(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_is_trivially_convertible(*row), - Type::PCSTR | Type::PCWSTR => true, - _ => false, - } - } - pub fn type_is_callback(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_is_callback(*row), - _ => false, - } - } - pub fn type_is_primitive(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_is_primitive(*row), - Type::Bool | Type::Char | Type::I8 | Type::U8 | Type::I16 | Type::U16 | Type::I32 | Type::U32 | Type::I64 | Type::U64 | Type::F32 | Type::F64 | Type::ISize | Type::USize | Type::HRESULT | Type::ConstPtr(_) | Type::MutPtr(_) => true, - _ => false, - } - } - pub fn type_is_struct(&self, ty: &Type) -> bool { - match ty { - Type::TypeDef((row, _)) => self.type_def_is_struct(*row), - Type::GUID => true, - _ => false, - } - } - pub fn type_underlying_type(&self, ty: &Type) -> Type { - match ty { - Type::TypeDef((row, _)) => self.type_def_underlying_type(*row), - Type::HRESULT => Type::I32, - _ => ty.clone(), - } - } - pub fn type_has_replacement(&self, ty: &Type) -> bool { - match ty { - Type::HRESULT | Type::PCSTR | Type::PCWSTR => true, - Type::TypeDef((row, _)) => self.type_def_is_handle(*row) || self.type_def_kind(*row) == TypeKind::Enum, - _ => false, - } - } -} - -pub const REMAP_TYPES: [(TypeName, TypeName); 2] = [(TypeName::D2D_MATRIX_3X2_F, TypeName::Matrix3x2), (TypeName::D3DMATRIX, TypeName::Matrix4x4)]; - -pub const CORE_TYPES: [(TypeName, Type); 11] = [(TypeName::GUID, Type::GUID), (TypeName::IUnknown, Type::IUnknown), (TypeName::HResult, Type::HRESULT), (TypeName::HRESULT, Type::HRESULT), (TypeName::HSTRING, Type::String), (TypeName::BSTR, Type::BSTR), (TypeName::IInspectable, Type::IInspectable), (TypeName::PSTR, Type::PSTR), (TypeName::PWSTR, Type::PWSTR), (TypeName::Type, Type::TypeName), (TypeName::CHAR, Type::U8)]; diff --git a/crates/libs/metadata/src/reader/tree.rs b/crates/libs/metadata/src/reader/tree.rs deleted file mode 100644 index 3d576ea413..0000000000 --- a/crates/libs/metadata/src/reader/tree.rs +++ /dev/null @@ -1,31 +0,0 @@ -use super::*; - -#[derive(Debug)] -pub struct Tree<'a> { - pub namespace: &'a str, - pub nested: BTreeMap<&'a str, Tree<'a>>, -} - -impl<'a> Tree<'a> { - pub(crate) fn from_namespace(namespace: &'a str) -> Self { - Self { namespace, nested: BTreeMap::new() } - } - pub(crate) fn insert_namespace(&mut self, namespace: &'a str, pos: usize) -> &mut Self { - if let Some(next) = namespace[pos..].find('.') { - let next = pos + next; - self.nested.entry(&namespace[pos..next]).or_insert_with(|| Self::from_namespace(&namespace[..next])).insert_namespace(namespace, next + 1) - } else { - self.nested.entry(&namespace[pos..]).or_insert_with(|| Self::from_namespace(namespace)) - } - } - pub fn flatten(&self) -> Vec<&Self> { - std::iter::once(self).chain(self.nested.values().flat_map(|tree| tree.flatten())).collect() - } - pub fn seek(mut self, namespace: &'a str) -> Option { - if let Some(next) = namespace.find('.') { - self.nested.remove(&namespace[..next]).and_then(|tree| tree.seek(&namespace[next + 1..])) - } else { - self.nested.remove(namespace) - } - } -} diff --git a/crates/libs/metadata/src/reader/row.rs b/crates/libs/metadata/src/row.rs similarity index 100% rename from crates/libs/metadata/src/reader/row.rs rename to crates/libs/metadata/src/row.rs diff --git a/crates/libs/metadata/src/reader/type.rs b/crates/libs/metadata/src/type.rs similarity index 80% rename from crates/libs/metadata/src/reader/type.rs rename to crates/libs/metadata/src/type.rs index 7abed866ef..27e2555ce4 100644 --- a/crates/libs/metadata/src/reader/type.rs +++ b/crates/libs/metadata/src/type.rs @@ -30,10 +30,10 @@ pub enum Type { TypeName, TypeRef(TypeDefOrRef), GenericParam(GenericParam), - TypeDef((TypeDef, Vec)), - MutPtr((Box, usize)), - ConstPtr((Box, usize)), - Win32Array((Box, usize)), + TypeDef(TypeDef, Vec), + MutPtr(Box, usize), + ConstPtr(Box, usize), + Win32Array(Box, usize), WinrtArray(Box), WinrtArrayRef(Box), ConstRef(Box), @@ -68,8 +68,8 @@ impl Type { /// Converts the `Type` to an equivalent `const` variant if appropriate. pub fn to_const_type(self) -> Self { match self { - Self::MutPtr((kind, pointers)) => Self::MutPtr((Box::new(kind.to_const_type()), pointers)), - Self::ConstPtr((kind, pointers)) => Self::ConstPtr((Box::new(kind.to_const_type()), pointers)), + Self::MutPtr(kind, pointers) => Self::MutPtr(Box::new(kind.to_const_type()), pointers), + Self::ConstPtr(kind, pointers) => Self::ConstPtr(Box::new(kind.to_const_type()), pointers), Self::PSTR => Self::PCSTR, Self::PWSTR => Self::PCWSTR, _ => self, @@ -78,9 +78,9 @@ impl Type { pub fn to_underlying_type(&self) -> Self { match self { - Type::MutPtr((ty, _)) => *ty.clone(), - Type::ConstPtr((ty, _)) => *ty.clone(), - Type::Win32Array((ty, _)) => *ty.clone(), + Type::MutPtr(ty, _) => *ty.clone(), + Type::ConstPtr(ty, _) => *ty.clone(), + Type::Win32Array(ty, _) => *ty.clone(), Type::WinrtArray(ty) => *ty.clone(), Type::WinrtArrayRef(ty) => *ty.clone(), Type::ConstRef(ty) => *ty.clone(), @@ -91,7 +91,7 @@ impl Type { /// Converts a mutable pointer type, if appropriate, to a const pointer type. pub fn to_const_ptr(self) -> Self { match self { - Self::MutPtr((kind, pointers)) => Self::ConstPtr((kind, pointers)), + Self::MutPtr(kind, pointers) => Self::ConstPtr(kind, pointers), _ => self, } } @@ -100,15 +100,15 @@ impl Type { /// from its underlying type signature. pub fn deref(&self) -> Self { match self { - Self::ConstPtr((kind, 1)) | Self::MutPtr((kind, 1)) => { + Self::ConstPtr(kind, 1) | Self::MutPtr(kind, 1) => { if **kind == Self::Void { Self::U8 } else { *kind.clone() } } - Self::ConstPtr((kind, pointers)) => Self::ConstPtr((kind.clone(), pointers - 1)), - Self::MutPtr((kind, pointers)) => Self::MutPtr((kind.clone(), pointers - 1)), + Self::ConstPtr(kind, pointers) => Self::ConstPtr(kind.clone(), pointers - 1), + Self::MutPtr(kind, pointers) => Self::MutPtr(kind.clone(), pointers - 1), Self::PSTR | Self::PCSTR => Self::U8, Self::PWSTR | Self::PCWSTR => Self::U16, _ => panic!("`deref` can only be called on pointer types"), @@ -137,7 +137,7 @@ impl Type { /// Returns `true` if the `Type` is a pointer. pub fn is_pointer(&self) -> bool { - matches!(self, Type::ConstPtr(_) | Type::MutPtr(_)) + matches!(self, Type::ConstPtr(_, _) | Type::MutPtr(_, _)) } /// Returns `true` if the `Type` is unsigned. @@ -148,7 +148,7 @@ impl Type { /// Returns `true` if the `Type` is incomplete. pub fn is_void(&self) -> bool { match self { - Type::ConstPtr((kind, _)) | Type::MutPtr((kind, _)) => kind.is_void(), + Type::ConstPtr(kind, _) | Type::MutPtr(kind, _) => kind.is_void(), Type::Void => true, _ => false, } @@ -157,7 +157,7 @@ impl Type { /// Returns `true` if the `Type` has a byte-sized address. pub fn is_byte_size(&self) -> bool { match self { - Type::ConstPtr((kind, _)) | Type::MutPtr((kind, _)) => kind.is_byte_size(), + Type::ConstPtr(kind, _) | Type::MutPtr(kind, _) => kind.is_byte_size(), Type::I8 | Type::U8 | Type::PSTR | Type::PCSTR => true, _ => false, } diff --git a/crates/libs/metadata/src/reader/type_name.rs b/crates/libs/metadata/src/type_name.rs similarity index 100% rename from crates/libs/metadata/src/reader/type_name.rs rename to crates/libs/metadata/src/type_name.rs diff --git a/crates/libs/metadata/src/writer/extend.rs b/crates/libs/metadata/src/writer/extend.rs deleted file mode 100644 index 74d2a487fc..0000000000 --- a/crates/libs/metadata/src/writer/extend.rs +++ /dev/null @@ -1,16 +0,0 @@ -use super::*; - -// TODO: for the scraper we need a per-module toml file that can contain the mapping of headers and other info -// used to filter/transform the data for that module. This can be used by third parties to generate idl/metadata -// for their APIs too. - -pub fn extend(_base: &mut Module, _other: Module) { - // Adds any `new` declarations to `prev` leaving existing declarations intact. - // May be used by tool that parses other sources, like .h and .idl files from the Windows SDK - // to keep a canonical rs/idl style repo updated. - // The resulting `Module` can then be written out as idl with `to_idl` and to update the repo - // in place. - // Maybe return a list of differences that were ignored - - todo!() -} diff --git a/crates/libs/metadata/src/writer/idl/mod.rs b/crates/libs/metadata/src/writer/idl/mod.rs deleted file mode 100644 index b9ab56ee52..0000000000 --- a/crates/libs/metadata/src/writer/idl/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -mod format; -mod parse; -mod read; -mod write; - -use super::*; -pub use format::*; -pub use read::*; -pub use write::*; - -// The value of the IDL-specific memory representation is that it allows for constructs that are not modeled in the abstract Module -// tree such as the use declarations and if we get rid of it we'd always "format" IDL by stripping out any of that into a single -// canonical form which would not be very friendly to developers. -pub struct IdlFile { - references: Vec, - modules: Vec, -} - -pub struct IdlModule { - attributes: Vec, // winrt/win32 - ident: syn::Ident, - members: Vec, -} - -pub enum IdlModuleMember { - Module(IdlModule), - Interface(IdlInterface), - Struct(IdlStruct), - Enum(IdlEnum), - Class(IdlClass), - // Function and Delegate -} - -pub struct IdlEnum { - item: syn::ItemEnum, -} - -pub struct IdlStruct { - item: syn::ItemStruct, -} - -pub struct IdlClass { - attributes: Vec, - ident: syn::Ident, - extends: Vec, -} - -pub struct IdlInterface { - attributes: Vec, - ident: syn::Ident, - methods: Vec, -} diff --git a/crates/libs/metadata/src/writer/idl/parse.rs b/crates/libs/metadata/src/writer/idl/parse.rs deleted file mode 100644 index 99fffd317c..0000000000 --- a/crates/libs/metadata/src/writer/idl/parse.rs +++ /dev/null @@ -1,120 +0,0 @@ -use super::*; -use syn::spanned::Spanned; - -// TODO: always set the winrt bit on the assembly but only set the winrt bit on the TypeDef if its a WinRT type. -// Also, use a file-level attribute in the IDL file to indicate whether it contains WinRT or Win32 types -// e.g. #![win32|winrt] - with default being winrt - that way Win32 and WinRT types could conceivably share a -// namespace but live in separate IDL files to simplify the IDL syntax. - -syn::custom_keyword!(interface); -syn::custom_keyword!(class); - -impl IdlFile { - pub fn parse_str(source: &str) -> Result { - Ok(syn::parse_str::(source)?) - } -} - -impl syn::parse::Parse for IdlFile { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut references = vec![]; - let mut modules = vec![]; - while !input.is_empty() { - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![mod]) { - modules.push(input.parse()?); - } else if lookahead.peek(syn::Token![use]) { - references.push(input.parse()?); - } else { - return Err(lookahead.error()); - } - } - Ok(Self { references, modules }) - } -} - -impl syn::parse::Parse for IdlModule { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let ident = input.parse::()?; - let content; - syn::braced!(content in input); - let mut members = vec![]; - while !content.is_empty() { - members.push(content.parse::()?); - } - Ok(Self { attributes: vec![], ident, members }) - } -} - -impl syn::parse::Parse for IdlModuleMember { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let attributes: Vec = input.call(syn::Attribute::parse_outer)?; - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![mod]) { - if let Some(attribute) = attributes.first() { - return Err(syn::Error::new(attribute.span(), "module attribute are not supported")); - } - Ok(IdlModuleMember::Module(input.parse()?)) - } else if lookahead.peek(interface) { - Ok(IdlModuleMember::Interface(IdlInterface::parse(attributes, input)?)) - } else if lookahead.peek(syn::Token![struct]) { - Ok(IdlModuleMember::Struct(IdlStruct::parse(attributes, input)?)) - } else if lookahead.peek(syn::Token![enum]) { - Ok(IdlModuleMember::Enum(IdlEnum::parse(attributes, input)?)) - } else if lookahead.peek(class) { - Ok(IdlModuleMember::Class(IdlClass::parse(attributes, input)?)) - } else { - Err(lookahead.error()) - } - } -} - -impl IdlClass { - fn parse(attributes: Vec, input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let ident = input.parse::()?; - let mut extends = Vec::new(); - - if input.peek(syn::Token![:]) { - input.parse::()?; - while input.peek(syn::Ident) { - extends.push(input.parse::()?); - _ = input.parse::(); - } - } - - input.parse::()?; - Ok(Self { attributes, ident, extends }) - } -} - -impl IdlInterface { - fn parse(attributes: Vec, input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let ident = input.parse::()?; - let content; - syn::braced!(content in input); - let mut methods = vec![]; - while !content.is_empty() { - methods.push(content.parse::()?); - } - Ok(Self { attributes, ident, methods }) - } -} - -impl IdlStruct { - fn parse(attributes: Vec, input: syn::parse::ParseStream) -> syn::Result { - let mut item: syn::ItemStruct = input.parse()?; - item.attrs = attributes; - Ok(Self { item }) - } -} - -impl IdlEnum { - fn parse(attributes: Vec, input: syn::parse::ParseStream) -> syn::Result { - let mut item: syn::ItemEnum = input.parse()?; - item.attrs = attributes; - Ok(Self { item }) - } -} diff --git a/crates/libs/metadata/src/writer/idl/read.rs b/crates/libs/metadata/src/writer/idl/read.rs deleted file mode 100644 index 1b078effd8..0000000000 --- a/crates/libs/metadata/src/writer/idl/read.rs +++ /dev/null @@ -1,333 +0,0 @@ -use super::*; -use syn::spanned::Spanned; - -// Phases are needed to read use declarations from IDL files before resolving those types -// in the second pass. -#[derive(PartialEq, Copy, Clone)] -enum ReadPhase { - Index, - Define, -} - -impl Module { - pub fn read_idl(&mut self, paths: &[String], filter: &Filter) -> Result<()> { - let mut files = vec![]; - - for path in paths { - if extension(path).1 == "idl" { - files.push((path.as_str(), IdlFile::parse_str(&read_to_string(path)?).map_err(|error| error.with_path(path))?)); - } - } - - for (path, file) in &files { - self.read_file(file, filter, ReadPhase::Index).map_err(|error| error.with_path(path))?; - } - - for (path, file) in &files { - self.read_file(file, filter, ReadPhase::Define).map_err(|error| error.with_path(path))?; - } - - Ok(()) - } - - fn read_file(&mut self, file: &IdlFile, filter: &Filter, phase: ReadPhase) -> Result<()> { - for module in &file.modules { - self.read_module(file, module, &module.ident.to_string(), filter, phase)?; - } - - Ok(()) - } - - fn read_module(&mut self, file: &IdlFile, module: &IdlModule, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - if filter.includes_namespace(namespace) { - for member in &module.members { - self.read_member(file, member, namespace, filter, phase)?; - } - } - - Ok(()) - } - - fn read_member(&mut self, file: &IdlFile, member: &IdlModuleMember, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - match member { - IdlModuleMember::Module(member) => self.read_module(file, member, &format!("{namespace}.{}", member.ident), filter, phase), - IdlModuleMember::Interface(member) => self.read_interface(file, member, namespace, filter, phase), - IdlModuleMember::Struct(member) => self.read_struct(file, member, namespace, filter, phase), - IdlModuleMember::Enum(member) => self.read_enum(file, member, namespace, filter, phase), - IdlModuleMember::Class(member) => self.read_class(file, member, namespace, filter, phase), - } - } - - fn read_interface(&mut self, _file: &IdlFile, ty: &IdlInterface, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - let ident = ty.ident.to_string(); - - if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { - match phase { - ReadPhase::Index => { - self.insert(namespace, 0).types.entry(ident).or_default(); - } - ReadPhase::Define => { - let flags = TypeAttributes::Public | TypeAttributes::Interface | TypeAttributes::WindowsRuntime | TypeAttributes::Abstract; - let mut def = TypeDef { flags, extends: None, ..Default::default() }; - - for method in &ty.methods { - let name = method.sig.ident.to_string(); - let mut params = vec![]; - - for input in &method.sig.inputs { - let syn::FnArg::Typed(pat_type) = input else { - todo!(); - }; - - let syn::Pat::Ident(ref pat_ident) = *pat_type.pat else { - todo!(); - }; - - let name = pat_ident.ident.to_string(); - let ty = self.read_ty(namespace, &pat_type.ty)?; - params.push(Param { name, ty, ..Default::default() }); - } - - let ty = if let syn::ReturnType::Type(_, ty) = &method.sig.output { self.read_ty(namespace, ty)? } else { Type::Void }; - let return_type = Param { ty, ..Default::default() }; - let flags = MethodAttributes::Public; - - def.methods.push(Method { flags, name, params, return_type, ..Default::default() }); - } - - self.insert(namespace, 0).types.entry(ident).or_default().push(def); - } - } - } - - Ok(()) - } - - fn read_struct(&mut self, _file: &IdlFile, ty: &IdlStruct, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - let ident = ty.item.ident.to_string(); - - if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { - match phase { - ReadPhase::Index => { - self.insert(namespace, 0).types.entry(ident).or_default(); - } - ReadPhase::Define => { - let flags = TypeAttributes::Public | TypeAttributes::WindowsRuntime | TypeAttributes::Sealed | TypeAttributes::Import | TypeAttributes::SequentialLayout; - let mut def = TypeDef { flags, extends: Some(TypeRef { namespace: "System".to_string(), name: "ValueType".to_string(), ..Default::default() }), ..Default::default() }; - - let syn::Fields::Named(fields) = &ty.item.fields else { - unimplemented!(); - }; - - for field in &fields.named { - let Some(ref ident) = field.ident else { - unimplemented!(); - }; - - let flags = FieldAttributes::Public; - let name = ident.to_string(); - let ty = self.read_ty(namespace, &field.ty)?; - def.fields.push(Field { flags, name, ty, ..Default::default() }); - } - - self.insert(namespace, 0).types.entry(ident).or_default().push(def); - } - } - } - - Ok(()) - } - - fn read_enum(&mut self, _file: &IdlFile, ty: &IdlEnum, namespace: &str, filter: &Filter, phase: ReadPhase) -> Result<()> { - let ident = ty.item.ident.to_string(); - - if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { - match phase { - ReadPhase::Index => { - self.insert(namespace, 0).types.entry(ident).or_default(); - } - ReadPhase::Define => { - let mut def = TypeDef { extends: Some(TypeRef { namespace: "System".to_string(), name: "Enum".to_string(), ..Default::default() }), ..Default::default() }; - let enum_type = Type::TypeRef(TypeRef { namespace: namespace.to_string(), name: ident.clone(), ..Default::default() }); - - for variant in &ty.item.variants { - if let Some((_, expr)) = &variant.discriminant { - let flags = FieldAttributes::Public; - let name = variant.ident.to_string(); - let value = self.read_expr(expr, false)?; - - def.fields.push(Field { flags, name, ty: enum_type.clone(), value: Some(value) }); - } - } - - self.insert(namespace, 0).types.entry(ident).or_default().push(def); - } - } - } - - Ok(()) - } - - fn read_class(&mut self, _file: &IdlFile, ty: &IdlClass, namespace: &str, filter: &Filter, _phase: ReadPhase) -> Result<()> { - let ident = ty.ident.to_string(); - - if filter.includes_type_name(reader::TypeName::new(namespace, &ident)) { - self.insert(namespace, 0).types.entry(ident).or_default(); - } - - Ok(()) - } - - fn read_expr(&mut self, expr: &syn::Expr, neg: bool) -> Result { - match expr { - syn::Expr::Lit(lit) => self.read_expr_lit(lit, neg), - syn::Expr::Unary(unary) => self.read_expr_unary(unary), - _ => todo!("{:?}", expr), - } - } - - fn read_expr_unary(&mut self, unary: &syn::ExprUnary) -> Result { - self.read_expr(&unary.expr, true) - } - - fn read_expr_lit(&mut self, expr: &syn::ExprLit, neg: bool) -> Result { - self.read_lit(&expr.lit, neg) - } - - fn read_lit(&mut self, lit: &syn::Lit, neg: bool) -> Result { - match lit { - syn::Lit::Int(lit) => self.read_lit_int(lit, neg), - syn::Lit::Str(lit) => self.read_lit_str(lit), - _ => todo!("{:?}", lit), - } - } - - fn read_lit_str(&mut self, lit: &syn::LitStr) -> Result { - Ok(Value::String(lit.value())) - } - - fn read_lit_int(&mut self, lit: &syn::LitInt, neg: bool) -> Result { - fn parse(lit: &syn::LitInt, neg: bool) -> Result { - let raw = if neg { format!("-{}", lit.base10_digits()) } else { lit.base10_digits().to_string() }; - raw.parse().map_err(|_| Error::new("failed to parse literal").with_span(lit.span())) - } - - match lit.suffix() { - "i8" => Ok(Value::I8(parse(lit, neg)?)), - "u8" => Ok(Value::U8(parse(lit, neg)?)), - "i16" => Ok(Value::I16(parse(lit, neg)?)), - "u16" => Ok(Value::U16(parse(lit, neg)?)), - "i32" => Ok(Value::I32(parse(lit, neg)?)), - "u32" => Ok(Value::U32(parse(lit, neg)?)), - "i64" => Ok(Value::I64(parse(lit, neg)?)), - "u64" => Ok(Value::U64(parse(lit, neg)?)), - suffix => todo!("suffix {:?}", suffix), - } - } - - fn read_ty(&mut self, namespace: &str, ty: &syn::Type) -> Result { - match ty { - syn::Type::Path(ty) => self.read_type_path(namespace, ty), - syn::Type::Ptr(ptr) => self.read_type_ptr(namespace, ptr), - syn::Type::Array(array) => self.read_type_array(namespace, array), - _ => unimplemented!(), - } - } - - fn read_type_array(&mut self, namespace: &str, array: &syn::TypeArray) -> Result { - let ty = self.read_ty(namespace, &array.elem)?; - - if let syn::Expr::Lit(lit) = &array.len { - if let syn::Lit::Int(lit) = &lit.lit { - return Ok(ty.into_array(lit.base10_parse()?)); - } - } - - todo!() - } - - fn read_type_ptr(&mut self, namespace: &str, ptr: &syn::TypePtr) -> Result { - let ty = self.read_ty(namespace, &ptr.elem)?; - if ptr.mutability.is_some() { - Ok(ty.into_mut_ptr()) - } else { - Ok(ty.into_const_ptr()) - } - } - - fn read_type_path(&mut self, namespace: &str, ty: &syn::TypePath) -> Result { - if ty.qself.is_some() { - unimplemented!(); - } - - self.read_path(namespace, &ty.path) - } - - fn read_path(&mut self, current: &str, path: &syn::Path) -> Result { - if let Some(segment) = path.segments.first() { - if path.segments.len() == 1 { - let name = segment.ident.to_string(); - - return match name.as_str() { - "void" => Ok(Type::Void), - "bool" => Ok(Type::Bool), - "char" => Ok(Type::Char), - "i8" => Ok(Type::I8), - "u8" => Ok(Type::U8), - "i16" => Ok(Type::I16), - "u16" => Ok(Type::U16), - "i32" => Ok(Type::I32), - "u32" => Ok(Type::U32), - "i64" => Ok(Type::I64), - "u64" => Ok(Type::U64), - "f32" => Ok(Type::F32), - "f64" => Ok(Type::F64), - "isize" => Ok(Type::ISize), - "usize" => Ok(Type::USize), - "HSTRING" => Ok(Type::String), - "GUID" => Ok(Type::GUID), - "IUnknown" => Ok(Type::IUnknown), - "IInspectable" => Ok(Type::IInspectable), - "HRESULT" => Ok(Type::HRESULT), - "PSTR" => Ok(Type::PSTR), - "PWSTR" => Ok(Type::PWSTR), - "PCSTR" => Ok(Type::PCSTR), - "PCWSTR" => Ok(Type::PCWSTR), - "BSTR" => Ok(Type::BSTR), - _ => Ok(Type::TypeRef(TypeRef { namespace: current.to_string(), name, ..Default::default() })), - }; - } - } - - let mut current: Vec = current.split('.').map(|segment| segment.to_string()).collect(); - let mut builder = vec![]; - - for segment in &path.segments { - let segment = segment.ident.to_string(); - if segment == "super" { - current.pop().ok_or_else(|| syn::Error::new(path.span(), "no parent module"))?; - } else { - builder.push(segment); - } - } - - current.extend_from_slice(&builder); - - let (name, namespace) = current.split_last().ok_or_else(|| syn::Error::new(path.span(), "no type name"))?; - let namespace = namespace.join("."); - - if self.contains_type(&namespace, name) { - Ok(Type::TypeRef(TypeRef { namespace, name: name.to_string(), ..Default::default() })) - } else { - let (name, namespace) = builder.split_last().ok_or_else(|| syn::Error::new(path.span(), "no type name"))?; - let namespace = namespace.join("."); - - // TODO: should we include winmd references to validate - that will also help with AssemblyRef info needed for winmd - //if self.contains_type(&namespace, name) { - Ok(Type::TypeRef(TypeRef { namespace, name: name.to_string(), ..Default::default() })) - // } else { - // Err(Error::new("type not found").with_span(path.span())) - // } - } - } -} diff --git a/crates/libs/metadata/src/writer/idl/write.rs b/crates/libs/metadata/src/writer/idl/write.rs deleted file mode 100644 index 73849d004e..0000000000 --- a/crates/libs/metadata/src/writer/idl/write.rs +++ /dev/null @@ -1,338 +0,0 @@ -use super::*; -use tokens::*; - -pub fn write_idl(module: &Module, path: &str) -> Result<()> { - write_to_file(path, format_idl(&module_to_idl("", module).to_string()).map_err(|error| error.with_path(path))?) -} - -fn module_to_idl(name: &str, module: &Module) -> TokenStream { - let modules = module.modules.iter().map(|(name, module)| module_to_idl(name, module)); - - if name.is_empty() { - quote! { #(#modules)* } - } else { - let name = to_ident(name); - let types = module.types.iter().flat_map(|(name, ty)| ty.iter().map(move |ty| (name, ty))).map(|(name, ty)| type_def_to_idl(module, name, ty)); - - quote! { - mod #name { - #(#modules)* - #(#types)* - } - } - } -} - -fn type_def_to_idl(module: &Module, name: &str, ty: &TypeDef) -> TokenStream { - let attributes = attributes_to_idl(module, &ty.attributes); - - let ty = if let Some(extends) = &ty.extends { - if extends.namespace == "System" { - if extends.name == "Enum" { - enum_to_idl(module, name, ty) - } else if extends.name == "ValueType" { - struct_to_idl(module, name, ty) - } else if extends.name == "MulticastDelegate" { - delegate_to_idl(module, name, ty) - } else { - class_to_idl(module, name, ty) - } - } else { - class_to_idl(module, name, ty) - } - } else { - interface_to_idl(module, name, ty) - }; - - quote! { - #attributes - #ty - } -} - -fn enum_to_idl(module: &Module, name: &str, ty: &TypeDef) -> TokenStream { - let name = to_ident(name); - - let constants = ty.fields.iter().filter_map(|field| { - let Some(value) = &field.value else { - return None; - }; - - let name = to_ident(&field.name); - let value = value_to_idl(module, value); - - Some(quote! { - #name = #value - }) - }); - - quote! { - enum #name { - #(#constants),* - } - } -} - -fn value_to_idl(module: &Module, value: &Value) -> TokenStream { - match value { - Value::Bool(value) => value.to_string().into(), - Value::U8(value) => format!("{value}u8").into(), - Value::I8(value) => format!("{value}i8").into(), - Value::U16(value) => format!("{value}u16").into(), - Value::I16(value) => format!("{value}i16").into(), - Value::U32(value) => format!("{value}u32").into(), - Value::I32(value) => format!("{value}i32").into(), - Value::U64(value) => format!("{value}u64").into(), - Value::I64(value) => format!("{value}i64").into(), - Value::F32(value) => format!("{value}f32").into(), - Value::F64(value) => format!("{value}f64").into(), - Value::String(value) => format!("\"{value}\"").into(), - Value::TypeName(type_name) => { - let type_name = reader::TypeName::parse(type_name); - let namespace = namespace_to_idl(&module.namespace, type_name.namespace); - let name = to_ident(type_name.name); - quote! { #namespace#name } - } - Value::Enum(_, value) => { - // TODO: replace the integer with the constant from the enum's definition - value_to_idl(module, value) - } - } -} - -fn struct_to_idl(module: &Module, name: &str, ty: &TypeDef) -> TokenStream { - let name = to_ident(name); - - let fields = ty.fields.iter().map(|field| { - let name = to_ident(&field.name); - let ty = type_to_idl(module, &field.ty); - quote! { - #name: #ty - } - }); - - quote! { - struct #name { - #(#fields),* - } - } -} - -fn attributes_to_idl(module: &Module, attributes: &[Attribute]) -> TokenStream { - let attributes = attributes.iter().map(|attribute| { - let namespace = namespace_to_idl(&module.namespace, &attribute.ty.namespace); - let name = to_ident(&attribute.ty.name); - - if attribute.args.is_empty() { - quote! { - #[#namespace#name] - } - } else { - let args = attribute.args.iter().map(|(name, value)| { - if name.is_empty() { - value_to_idl(module, value) - } else { - let name = to_ident(name); - let value = value_to_idl(module, value); - quote! { - #name: #value - } - } - }); - - quote! { - #[#namespace#name(#(#args),*)] - } - } - }); - - quote! { - #(#attributes)* - } -} - -fn delegate_to_idl(_module: &Module, name: &str, _ty: &TypeDef) -> TokenStream { - let name = to_ident(name); - - quote! { - struct #name(); - } -} - -fn class_to_idl(_module: &Module, name: &str, _ty: &TypeDef) -> TokenStream { - let name = to_ident(name); - - quote! { - struct #name { - } - } -} - -fn interface_to_idl(module: &Module, name: &str, ty: &TypeDef) -> TokenStream { - let name = to_ident(name); - - let methods = ty.methods.iter().map(|method| { - let attributes = attributes_to_idl(module, &method.attributes); - let name = to_ident(&method.name); - let params = method.params.iter().map(|param| { - let name = to_ident(¶m.name); - let ty = type_to_idl(module, ¶m.ty); - quote! { #name: #ty } - }); - let return_type = if method.return_type.ty != Type::Void { - let ty = type_to_idl(module, &method.return_type.ty); - quote! { -> #ty } - } else { - quote! {} - }; - quote! { - #attributes - fn #name(#(#params),*) #return_type; - } - }); - - quote! { - interface #name { - #(#methods)* - } - } -} - -fn type_to_idl(module: &Module, ty: &Type) -> TokenStream { - match ty { - Type::Void => quote! { ::core::ffi::c_void }, - Type::Bool => quote! { bool }, - Type::Char => quote! { u16 }, - Type::I8 => quote! { i8 }, - Type::U8 => quote! { u8 }, - Type::I16 => quote! { i16 }, - Type::U16 => quote! { u16 }, - Type::I32 => quote! { i32 }, - Type::U32 => quote! { u32 }, - Type::I64 => quote! { i64 }, - Type::U64 => quote! { u64 }, - Type::F32 => quote! { f32 }, - Type::F64 => quote! { f64 }, - Type::ISize => quote! { isize }, - Type::USize => quote! { usize }, - Type::String => { - quote! { HSTRING } - } - Type::BSTR => { - quote! { BSTR } - } - Type::IInspectable => { - quote! { IInspectable } - } - Type::GUID => { - quote! { GUID } - } - Type::IUnknown => { - quote! { IUnknown } - } - Type::HRESULT => { - quote! { HRESULT } - } - Type::PSTR => { - quote! { PSTR } - } - Type::PWSTR => { - quote! { PWSTR } - } - Type::PCSTR => { - quote! { PCSTR } - } - Type::PCWSTR => { - quote! { PCWSTR } - } - Type::Win32Array((ty, len)) => { - let ty = type_to_idl(module, ty); - let len = Literal::usize_unsuffixed(*len); - quote! { [#ty; #len] } - } - Type::GenericParam(generic) => { - let generic = to_ident(generic); - quote! { #generic } - } - Type::TypeRef(ty) => { - let namespace = namespace_to_idl(&module.namespace, &ty.namespace); - let name = to_ident(&ty.name); - if ty.generics.is_empty() { - quote! { #namespace#name } - } else { - let generics = ty.generics.iter().map(|ty| type_to_idl(module, ty)); - quote! { #namespace#name<#(#generics,)*> } - } - } - Type::MutPtr((ty, pointers)) => { - let pointers = mut_ptrs(*pointers); - let ty = type_to_idl(module, ty); - quote! { #pointers #ty } - } - Type::ConstPtr((ty, pointers)) => { - let pointers = const_ptrs(*pointers); - let ty = type_to_idl(module, ty); - quote! { #pointers #ty } - } - Type::WinrtArray(ty) => type_to_idl(module, ty), - Type::WinrtArrayRef(ty) => type_to_idl(module, ty), - Type::ConstRef(ty) => type_to_idl(module, ty), - _ => todo!(), - } -} - -// TODO: maybe move these functions into `tokens` crate as they duplicated in bindgen - -fn namespace_to_idl(relative: &str, namespace: &str) -> TokenStream { - // TODO: handle nested structs? - if namespace.is_empty() || relative == namespace { - quote! {} - } else { - let mut relative = relative.split('.').peekable(); - let mut namespace = namespace.split('.').peekable(); - let mut related = false; - - while relative.peek() == namespace.peek() { - related = true; - - if relative.next().is_none() { - break; - } - - namespace.next(); - } - - let mut tokens = TokenStream::new(); - - if related { - for _ in 0..relative.count() { - tokens.push_str("super::"); - } - } - - for namespace in namespace { - tokens.push_str(namespace); - tokens.push_str("::"); - } - - tokens - } -} - -pub fn to_ident(name: &str) -> TokenStream { - // keywords list based on https://doc.rust-lang.org/reference/keywords.html - match name { - "abstract" | "as" | "become" | "box" | "break" | "const" | "continue" | "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "override" | "priv" | "pub" | "ref" | "return" | "static" | "struct" | "super" | "trait" | "true" | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while" | "yield" | "try" | "async" | "await" | "dyn" => format!("r#{name}").into(), - "Self" | "self" => format!("{name}_").into(), - "_" => "unused".into(), - _ => reader::trim_tick(name).into(), - } -} - -fn mut_ptrs(pointers: usize) -> TokenStream { - "*mut ".repeat(pointers).into() -} - -fn const_ptrs(pointers: usize) -> TokenStream { - "*const ".repeat(pointers).into() -} diff --git a/crates/libs/metadata/src/writer/mod.rs b/crates/libs/metadata/src/writer/mod.rs deleted file mode 100644 index ad2deb7192..0000000000 --- a/crates/libs/metadata/src/writer/mod.rs +++ /dev/null @@ -1,263 +0,0 @@ -mod extend; -mod idl; -mod validate; -mod winmd; - -// TODO: this "writer" is really both a reader and a writer that is more generalized than "reader". The "reader" should be the raw or low-level reader -// that is secondary while this should be the main metadata API. - -use super::*; -pub use idl::format_idl; - -impl Module { - pub(crate) fn new(namespace: &str) -> Self { - Self { namespace: namespace.to_string(), ..Default::default() } - } - - pub(crate) fn insert(&mut self, namespace: &str, pos: usize) -> &mut Self { - if let Some(next) = namespace[pos..].find('.') { - let next = pos + next; - self.modules.entry(namespace[pos..next].to_string()).or_insert_with(|| Self::new(&namespace[..next])).insert(namespace, next + 1) - } else { - self.modules.entry(namespace[pos..].to_string()).or_insert_with(|| Self::new(namespace)) - } - } - - pub fn read(input: &[String], filter: &Filter) -> Result { - let mut module = Module::default(); - winmd::read_winmd(&mut module, input, filter)?; - module.read_idl(input, filter)?; - Ok(module) - } - - pub fn extend(&mut self, other: Module) { - extend::extend(self, other) - } - - pub fn validate(&self) -> Result<()> { - validate::validate(self) - } - - pub fn write(&self, path: &str) -> Result<()> { - match extension(path) { - (_, "idl") => idl::write_idl(self, path), - (_, "winmd") => winmd::write_winmd(self, path), - _ => Err(Error::new("output extension must be either .winmd or .idl")), - } - } - - pub fn contains_type(&self, namespace: &str, name: &str) -> bool { - if let Some(next) = namespace.find('.') { - if let Some(module) = self.modules.get(&namespace[..next]) { - module.contains_type(&namespace[next + 1..], name) - } else { - false - } - } else if let Some(module) = self.modules.get(namespace) { - module.types.contains_key(name) - } else { - false - } - } - - // TODO: add a `write_rust` method and get rid of the `bindgen` crate? -} - -#[derive(Default, Debug)] -pub struct Module { - pub namespace: String, // TODO: call this name for clarity - pub modules: BTreeMap, - - // Note that a `Vec` is used since Win32 structs may have per-architecture definitions - pub types: BTreeMap>, -} - -#[derive(Default, Debug)] -pub struct TypeDef { - pub flags: TypeAttributes, - pub attributes: Vec, - pub generics: Vec, - pub extends: Option, - pub fields: Vec, - pub methods: Vec, - pub interfaces: Vec, -} - -#[derive(Debug, Clone, Default, PartialEq)] -pub struct TypeRef { - pub namespace: String, - pub name: String, - pub generics: Vec, - // TODO: store an `assembly` for the name of the winmd/idl file where the type originates? - // pub assembly: String, -} - -#[derive(Debug, Clone)] -pub(crate) enum TypeRefIndex { - None, - TypeDef(usize), - TypeRef(usize), -} - -impl Default for TypeRefIndex { - fn default() -> Self { - Self::None - } -} - -// TODO: why not use 'syn' types directly? - -#[derive(Debug, Clone, PartialEq)] -pub enum Type { - Void, - Bool, - Char, - I8, - U8, - I16, - U16, - I32, - U32, - I64, - U64, - F32, - F64, - ISize, - USize, - String, - GUID, - IUnknown, - IInspectable, - HRESULT, - PSTR, - PWSTR, - PCSTR, - PCWSTR, - BSTR, - TypeName, - GenericParam(String), // TODO: need this in the writer? - TypeRef(TypeRef), - MutPtr((Box, usize)), - ConstPtr((Box, usize)), - Win32Array((Box, usize)), - WinrtArray(Box), - WinrtArrayRef(Box), - ConstRef(Box), -} - -impl Default for Type { - fn default() -> Self { - Self::Void - } -} - -impl Type { - fn into_mut_ptr(self) -> Self { - match self { - Self::MutPtr((ty, count)) => Self::MutPtr((ty, count + 1)), - Self::ConstPtr((ty, count)) => Self::MutPtr((ty, count + 1)), - _ => Self::MutPtr((Box::new(self), 1)), - } - } - - fn into_const_ptr(self) -> Self { - match self { - Self::MutPtr((ty, count)) => Self::ConstPtr((ty, count + 1)), - Self::ConstPtr((ty, count)) => Self::ConstPtr((ty, count + 1)), - _ => Self::ConstPtr((Box::new(self), 1)), - } - } - - fn into_array(self, len: usize) -> Self { - Self::Win32Array((Box::new(self), len)) - } -} - -#[derive(Debug)] -pub struct Attribute { - ty: TypeRef, - args: Vec<(String, Value)>, -} - -#[derive(Default, Debug)] -pub struct Field { - flags: FieldAttributes, - name: String, - ty: Type, - value: Option, -} - -#[derive(Default, Debug)] -pub struct Param { - flags: ParamAttributes, - name: String, - ty: Type, -} - -#[derive(Default, Debug)] -pub struct Method { - flags: MethodAttributes, - call_flags: MethodCallAttributes, - name: String, - params: Vec, - return_type: Param, - attributes: Vec, - //impl_flags: MethodImplAttributes, -} - -#[derive(Clone, Debug)] -pub enum Value { - Bool(bool), - U8(u8), - I8(i8), - U16(u16), - I16(i16), - U32(u32), - I32(i32), - U64(u64), - I64(i64), - F32(f32), - F64(f64), - String(String), - TypeName(String), - Enum(String, Box), -} - -impl Value { - // TODO: should return u8 - fn to_code(&self) -> u16 { - match self { - Self::Bool(_) => ELEMENT_TYPE_BOOLEAN as _, - Self::I8(_) => ELEMENT_TYPE_I1 as _, - Self::U8(_) => ELEMENT_TYPE_U1 as _, - Self::I16(_) => ELEMENT_TYPE_I2 as _, - Self::U16(_) => ELEMENT_TYPE_U2 as _, - Self::I32(_) => ELEMENT_TYPE_I4 as _, - Self::U32(_) => ELEMENT_TYPE_U4 as _, - Self::I64(_) => ELEMENT_TYPE_I8 as _, - Self::U64(_) => ELEMENT_TYPE_U8 as _, - Self::F32(_) => ELEMENT_TYPE_R4 as _, - Self::F64(_) => ELEMENT_TYPE_R8 as _, - Self::String(_) => ELEMENT_TYPE_STRING as _, - _ => todo!(), - } - } - - fn neg(&self) -> Self { - use std::ops::Neg; - match self { - Self::Bool(value) => Self::Bool(!value), - Self::U8(value) => Self::U8(*value), - Self::I8(value) => Self::I8(value.neg()), - Self::U16(value) => Self::U16(*value), - Self::I16(value) => Self::I16(value.neg()), - Self::U32(value) => Self::U32(*value), - Self::I32(value) => Self::I32(value.neg()), - Self::U64(value) => Self::U64(*value), - Self::I64(value) => Self::I64(value.neg()), - Self::F32(value) => Self::F32(value.neg()), - Self::F64(value) => Self::F64(value.neg()), - _ => todo!(), - } - } -} diff --git a/crates/libs/metadata/src/writer/validate.rs b/crates/libs/metadata/src/writer/validate.rs deleted file mode 100644 index 0381e1ba2f..0000000000 --- a/crates/libs/metadata/src/writer/validate.rs +++ /dev/null @@ -1,9 +0,0 @@ -use super::*; - -pub fn validate(_module: &Module) -> Result<()> { - // TODO: - // *. if a TypeDef is WinRT then there shall only be one for a given name - MIDLRT may be produce more but that should be removed before `validate` is run. - // *. if a TypeDef is not WinRT then there can be more - check that they have non-overlapping supported architectures. - // *. - Ok(()) -} diff --git a/crates/libs/metadata/src/writer/winmd/mod.rs b/crates/libs/metadata/src/writer/winmd/mod.rs deleted file mode 100644 index 7214a3b4dc..0000000000 --- a/crates/libs/metadata/src/writer/winmd/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod read; -mod write; - -use super::*; -pub use read::*; -pub use write::*; diff --git a/crates/libs/metadata/src/writer/winmd/read.rs b/crates/libs/metadata/src/writer/winmd/read.rs deleted file mode 100644 index ffc7787952..0000000000 --- a/crates/libs/metadata/src/writer/winmd/read.rs +++ /dev/null @@ -1,156 +0,0 @@ -use super::*; - -pub fn read_winmd(module: &mut Module, paths: &[String], filter: &Filter) -> Result<()> { - let mut files = vec![]; - - for path in paths { - if extension(path).1 == "winmd" { - files.push(reader::File::new(path)?); - } - } - - let reader = reader::Reader::new(&files); - - for ns in reader.namespaces() { - if filter.includes_namespace(ns) { - for ty in reader.namespace_types(ns, filter) { - module.insert(reader.type_def_namespace(ty), 0).types.entry(crate::reader::trim_tick(reader.type_def_name(ty)).to_string()).or_default().push(read_type_def(&reader, ty)?); - } - } - } - - Ok(()) -} - -fn read_type_def(reader: &reader::Reader, ty: reader::TypeDef) -> Result { - let mut result = TypeDef { flags: reader.type_def_flags(ty), ..Default::default() }; - result.attributes = read_attributes(reader, reader.type_def_attributes(ty))?; - result.extends = reader.type_def_extends(ty).map(|extends| TypeRef { namespace: extends.namespace.to_string(), name: extends.name.to_string(), ..Default::default() }); - - if result.flags.contains(TypeAttributes::Interface) || !result.flags.contains(TypeAttributes::WindowsRuntime) { - for method in reader.type_def_methods(ty) { - let flags = reader.method_def_flags(method); - let sig = reader.method_def_signature(method, &[]); - let name = reader.method_def_name(method).to_string(); - let attributes = read_attributes(reader, reader.method_def_attributes(method))?; - let mut params = vec![]; - - for param in sig.params { - let flags = reader.param_flags(param.def); - let name = reader.param_name(param.def).to_string(); - let ty = read_type(reader, ¶m.ty)?; - params.push(Param { flags, name, ty }); - } - - let return_type = Param { ty: read_type(reader, &sig.return_type)?, ..Default::default() }; - result.methods.push(Method { flags, name, params, return_type, attributes, ..Default::default() }); - } - } - - for field in reader.type_def_fields(ty) { - let flags = reader.field_flags(field); - let name = reader.field_name(field).to_string(); - let ty = read_type(reader, &reader.field_type(field, Some(ty)))?; - - let value = if flags.contains(FieldAttributes::Literal) { - let constant = reader.field_constant(field).unwrap(); - read_value(reader, &reader.constant_value(constant)).ok() - } else { - None - }; - - result.fields.push(Field { flags, name, ty, value }); - } - - Ok(result) -} - -fn read_attributes(reader: &reader::Reader, attributes: impl Iterator) -> Result> { - let mut result = vec![]; - - for attribute in attributes { - let ty = reader.attribute_type_name(attribute); - let ty = TypeRef { namespace: ty.namespace.to_string(), name: ty.name.to_string(), generics: vec![] }; - let mut args = vec![]; - - for (name, value) in reader.attribute_args(attribute) { - args.push((name, read_value(reader, &value)?)); - } - - result.push(Attribute { ty, args }); - } - - Ok(result) -} - -fn read_value(reader: &reader::Reader, value: &reader::Value) -> Result { - match value { - reader::Value::Bool(value) => Ok(Value::Bool(*value)), - reader::Value::U8(value) => Ok(Value::U8(*value)), - reader::Value::I8(value) => Ok(Value::I8(*value)), - reader::Value::U16(value) => Ok(Value::U16(*value)), - reader::Value::I16(value) => Ok(Value::I16(*value)), - reader::Value::U32(value) => Ok(Value::U32(*value)), - reader::Value::I32(value) => Ok(Value::I32(*value)), - reader::Value::U64(value) => Ok(Value::U64(*value)), - reader::Value::I64(value) => Ok(Value::I64(*value)), - reader::Value::F32(value) => Ok(Value::F32(*value)), - reader::Value::F64(value) => Ok(Value::F64(*value)), - reader::Value::String(value) => Ok(Value::String(value.clone())), - reader::Value::TypeDef(def) => Ok(Value::TypeName(format!("{}", reader.type_def_type_name(*def)))), - reader::Value::TypeRef(code) => Ok(Value::TypeName(format!("{}", reader.type_def_or_ref(*code)))), - reader::Value::EnumDef(def, value) => Ok(Value::Enum(format!("{}", reader.type_def_type_name(*def)), Box::new(read_value(reader, value)?))), - reader::Value::EnumRef(code, value) => Ok(Value::Enum(format!("{}", reader.type_def_or_ref(*code)), Box::new(read_value(reader, value)?))), - } -} - -fn read_type(reader: &reader::Reader, ty: &reader::Type) -> Result { - Ok(match ty { - reader::Type::Void => Type::Void, - reader::Type::Bool => Type::Bool, - reader::Type::Char => Type::Char, - reader::Type::I8 => Type::I8, - reader::Type::U8 => Type::U8, - reader::Type::I16 => Type::I16, - reader::Type::U16 => Type::U16, - reader::Type::I32 => Type::I32, - reader::Type::U32 => Type::U32, - reader::Type::I64 => Type::I64, - reader::Type::U64 => Type::U64, - reader::Type::F32 => Type::F32, - reader::Type::F64 => Type::F64, - reader::Type::ISize => Type::ISize, - reader::Type::USize => Type::USize, - reader::Type::String => Type::String, - reader::Type::GUID => Type::GUID, - reader::Type::IUnknown => Type::IUnknown, - reader::Type::IInspectable => Type::IInspectable, - reader::Type::HRESULT => Type::HRESULT, - reader::Type::PSTR => Type::PSTR, - reader::Type::PWSTR => Type::PWSTR, - reader::Type::PCSTR => Type::PCSTR, - reader::Type::PCWSTR => Type::PCWSTR, - reader::Type::BSTR => Type::BSTR, - reader::Type::TypeName => Type::TypeName, - reader::Type::GenericParam(param) => Type::GenericParam(reader.generic_param_name(*param).to_string()), - reader::Type::TypeDef((ty, reader_generics)) => { - let mut generics = vec![]; - - for ty in reader_generics { - generics.push(read_type(reader, ty)?); - } - - Type::TypeRef(TypeRef { namespace: reader.type_def_namespace(*ty).to_string(), name: reader.type_def_name(*ty).to_string(), generics }) - } - reader::Type::TypeRef(code) => { - let type_name = reader.type_def_or_ref(*code); - Type::TypeRef(TypeRef { namespace: type_name.namespace.to_string(), name: type_name.name.to_string(), generics: vec![] }) - } - reader::Type::MutPtr((ty, pointers)) => Type::MutPtr((Box::new(read_type(reader, ty)?), *pointers)), - reader::Type::ConstPtr((ty, pointers)) => Type::ConstPtr((Box::new(read_type(reader, ty)?), *pointers)), - reader::Type::Win32Array((ty, pointers)) => Type::Win32Array((Box::new(read_type(reader, ty)?), *pointers)), - reader::Type::WinrtArray(ty) => Type::WinrtArray(Box::new(read_type(reader, ty)?)), - reader::Type::WinrtArrayRef(ty) => Type::WinrtArrayRef(Box::new(read_type(reader, ty)?)), - reader::Type::ConstRef(ty) => Type::ConstRef(Box::new(read_type(reader, ty)?)), - }) -} diff --git a/crates/libs/metadata/src/writer/winmd/write/mod.rs b/crates/libs/metadata/src/writer/winmd/write/mod.rs deleted file mode 100644 index a925c5abd8..0000000000 --- a/crates/libs/metadata/src/writer/winmd/write/mod.rs +++ /dev/null @@ -1,361 +0,0 @@ -mod blobs; -mod codes; -mod file; -mod strings; -mod tables; -mod traits; - -use super::*; -use blobs::Blobs; -use codes::*; -use strings::Strings; -use traits::*; - -struct Gen<'a> { - blobs: Blobs, - strings: Strings<'a>, - tables: tables::Tables, - module: &'a Module, - module_scope: u32, - scopes: HashMap<&'a str, u32>, - references: HashMap<(&'a str, &'a str), u32>, -} - -pub fn write_winmd(module: &Module, path: &str) -> Result<()> { - let mut gen = Gen::new(module); - - gen.tables.TypeDef.push(tables::TypeDef { TypeName: gen.strings.insert(""), ..Default::default() }); - - // The Assembly table needs the module file name without it's extension. - let file_name = std::path::Path::new(path).with_extension("").file_name().map_or(path.to_string(), |name| name.to_string_lossy().to_string()); - - gen.tables.Assembly.push(tables::Assembly { - Name: gen.strings.insert(&file_name), - HashAlgId: 0x00008004, - MajorVersion: 0xFF, - MinorVersion: 0xFF, - BuildNumber: 0xFF, - RevisionNumber: 0xFF, - Flags: AssemblyFlags::WindowsRuntime.0, - ..Default::default() - }); - - gen.module_scope = ResolutionScope::Module(gen.tables.Module.push2(tables::Module { Name: gen.strings.insert("name.winmd"), Mvid: 1, ..Default::default() })).encode(); - - // Some winmd parsers will fail to read without an `mscorlib` reference. The `insert_module_types` function will typically include it - // automatically but a minimal `Module` tree may not add this dependency. - gen.insert_scope("System"); - - gen.insert_module_types(module); - - let file = file::write(gen.tables.into_stream()?, gen.strings.into_stream(), gen.blobs.into_stream())?; - write_to_file(path, file) -} - -impl<'a> Gen<'a> { - fn new(module: &'a Module) -> Self { - Self { - blobs: Default::default(), - strings: Default::default(), - tables: Default::default(), - module, - module_scope: 0, - scopes: Default::default(), - references: Default::default(), - } - } - - fn insert_module_types(&mut self, module: &'a Module) { - for (name, def) in &module.types { - self.insert_type_def(&module.namespace, name, def); - } - module.modules.values().for_each(|module| self.insert_module_types(module)); - } - - fn insert_type_def(&mut self, namespace: &'a str, name: &'a str, def: &'a [TypeDef]) { - for def in def { - let extends = if let Some(extends) = &def.extends { self.insert_type_ref(&extends.namespace, &extends.name) } else { 0 }; - self.tables.TypeDef.push(tables::TypeDef { - Flags: def.flags.0, - TypeName: self.strings.insert(name), - TypeNamespace: self.strings.insert(namespace), - Extends: extends, - FieldList: self.tables.Field.len() as _, - MethodList: self.tables.MethodDef.len() as _, - }); - for field in &def.fields { - let blob = self.insert_field_sig(&field.ty); - let parent = self.tables.Field.push2(tables::Field { Flags: field.flags.0, Name: self.strings.insert(&field.name), Signature: blob }); - if let Some(value) = &field.value { - let blob = self.insert_value_blob(value); - self.tables.Constant.push(tables::Constant { Type: value.to_code(), Parent: HasConstant::Field(parent).encode(), Value: blob }); - } - } - for method in &def.methods { - let blob = self.insert_method_sig(method); - self.tables.MethodDef.push(tables::MethodDef { RVA: 0, ImplFlags: 0, Flags: method.flags.0, Name: self.strings.insert(&method.name), Signature: blob, ParamList: self.tables.Param.len() as _ }); - for (sequence, param) in method.params.iter().enumerate() { - self.tables.Param.push(tables::Param { Flags: param.flags.0, Sequence: (sequence + 1) as _, Name: self.strings.insert(¶m.name) }); - } - } - } - } - - fn insert_value_blob(&mut self, value: &Value) -> u32 { - // TODO: can either cache in Gen, like we do for scopes and references, or regenerate each time. - // Profile once we can stress test this with field/method signatures. - - let blob = match value { - Value::I8(value) => value.to_le_bytes().to_vec(), - Value::U8(value) => value.to_le_bytes().to_vec(), - Value::I16(value) => value.to_le_bytes().to_vec(), - Value::U16(value) => value.to_le_bytes().to_vec(), - Value::I32(value) => value.to_le_bytes().to_vec(), - Value::U32(value) => value.to_le_bytes().to_vec(), - Value::I64(value) => value.to_le_bytes().to_vec(), - Value::U64(value) => value.to_le_bytes().to_vec(), - Value::F32(value) => value.to_le_bytes().to_vec(), - Value::F64(value) => value.to_le_bytes().to_vec(), - Value::String(value) => { - let mut blob = vec![]; - usize_blob(value.len(), &mut blob); - blob.extend_from_slice(value.as_bytes()); - blob - } - _ => todo!("{:?}", value), - }; - - self.blobs.insert(&blob) - } - - fn insert_method_sig(&mut self, method: &'a Method) -> u32 { - // TODO: can either cache in Gen, like we do for scopes and references, or regenerate each time. - // Profile once we can stress test this with field/method signatures. - - let mut blob = vec![method.call_flags.0]; - usize_blob(method.params.len(), &mut blob); - self.type_blob(&method.return_type.ty, &mut blob); - for param in &method.params { - self.type_blob(¶m.ty, &mut blob); - } - - self.blobs.insert(&blob) - } - - fn insert_field_sig(&mut self, ty: &'a Type) -> u32 { - // TODO: can either cache in Gen, like we do for scopes and references, or regenerate each time. - // Profile once we can stress test this with field/method signatures. - - let mut blob = vec![0x6]; // FIELD - self.type_blob(ty, &mut blob); - - self.blobs.insert(&blob) - } - - fn insert_scope(&mut self, namespace: &'a str) -> u32 { - if let Some(scope) = self.scopes.get(namespace) { - *scope - } else if namespace == "System" { - let scope = ResolutionScope::AssemblyRef(self.tables.AssemblyRef.push2(tables::AssemblyRef { - Name: self.strings.insert("mscorlib"), - MajorVersion: 4, - PublicKeyOrToken: self.blobs.insert(&[0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89]), - ..Default::default() - })) - .encode(); - self.scopes.insert(namespace, scope); - scope - } else { - // TODO: may need to capture the original assembly info for external references. - let scope = ResolutionScope::AssemblyRef(self.tables.AssemblyRef.push2(tables::AssemblyRef { - Name: self.strings.insert(namespace), - MajorVersion: 0xFF, - MinorVersion: 0xFF, - BuildNumber: 0xFF, - RevisionNumber: 0xFF, - Flags: AssemblyFlags::WindowsRuntime.0, - ..Default::default() - })) - .encode(); - self.scopes.insert(namespace, scope); - scope - } - } - - fn insert_type_ref(&mut self, namespace: &'a str, name: &'a str) -> u32 { - if let Some(reference) = self.references.get(&(namespace, name)) { - *reference - } else { - let scope = if self.module.contains_type(namespace, name) { self.module_scope } else { self.insert_scope(namespace) }; - - let reference = TypeDefOrRef::TypeRef(self.tables.TypeRef.push2(tables::TypeRef { TypeName: self.strings.insert(name), TypeNamespace: self.strings.insert(namespace), ResolutionScope: scope })).encode(); - self.references.insert((namespace, name), reference); - reference - } - } - - fn type_blob(&mut self, ty: &'a Type, blob: &mut Vec) { - match ty { - Type::Void => blob.push(ELEMENT_TYPE_VOID as _), - Type::Bool => blob.push(ELEMENT_TYPE_BOOLEAN as _), - Type::Char => blob.push(ELEMENT_TYPE_CHAR as _), - Type::I8 => blob.push(ELEMENT_TYPE_I1 as _), - Type::U8 => blob.push(ELEMENT_TYPE_U1 as _), - Type::I16 => blob.push(ELEMENT_TYPE_I2 as _), - Type::U16 => blob.push(ELEMENT_TYPE_U2 as _), - Type::I32 => blob.push(ELEMENT_TYPE_I4 as _), - Type::U32 => blob.push(ELEMENT_TYPE_U4 as _), - Type::I64 => blob.push(ELEMENT_TYPE_I8 as _), - Type::U64 => blob.push(ELEMENT_TYPE_U8 as _), - Type::F32 => blob.push(ELEMENT_TYPE_R4 as _), - Type::F64 => blob.push(ELEMENT_TYPE_R8 as _), - Type::ISize => blob.push(ELEMENT_TYPE_I as _), - Type::USize => blob.push(ELEMENT_TYPE_U as _), - Type::String => blob.push(ELEMENT_TYPE_STRING as _), - Type::IInspectable => blob.push(ELEMENT_TYPE_OBJECT as _), - Type::GUID => { - let code = self.insert_type_ref("System", "Guid"); - blob.push(ELEMENT_TYPE_VALUETYPE as _); - usize_blob(code as _, blob); - } - Type::HRESULT => { - let code = self.insert_type_ref("Windows.Foundation", "HResult"); - blob.push(ELEMENT_TYPE_VALUETYPE as _); - usize_blob(code as _, blob); - } - Type::TypeRef(ty) => { - let code = self.insert_type_ref(&ty.namespace, &ty.name); - blob.push(ELEMENT_TYPE_VALUETYPE as _); - usize_blob(code as _, blob); - } - Type::BSTR => { - let code = self.insert_type_ref("Windows.Win32.Foundation", "BSTR"); - blob.push(ELEMENT_TYPE_VALUETYPE as _); - usize_blob(code as _, blob); - } - Type::IUnknown => { - let code = self.insert_type_ref("Windows.Win32.Foundation", "IUnknown"); - blob.push(ELEMENT_TYPE_VALUETYPE as _); - usize_blob(code as _, blob); - } - Type::PCWSTR | Type::PWSTR => { - let code = self.insert_type_ref("Windows.Win32.Foundation", "PWSTR"); - blob.push(ELEMENT_TYPE_VALUETYPE as _); - usize_blob(code as _, blob); - } - Type::PCSTR | Type::PSTR => { - let code = self.insert_type_ref("Windows.Win32.Foundation", "PSTR"); - blob.push(ELEMENT_TYPE_VALUETYPE as _); - usize_blob(code as _, blob); - } - Type::ConstRef(ty) => { - usize_blob(ELEMENT_TYPE_CMOD_OPT as _, blob); - usize_blob(self.insert_type_ref("System.Runtime.CompilerServices", "IsConst") as _, blob); - usize_blob(ELEMENT_TYPE_BYREF as _, blob); - self.type_blob(ty, blob); - } - Type::WinrtArrayRef(ty) => { - usize_blob(ELEMENT_TYPE_BYREF as _, blob); - usize_blob(ELEMENT_TYPE_SZARRAY as _, blob); - self.type_blob(ty, blob); - } - Type::WinrtArray(ty) => { - usize_blob(ELEMENT_TYPE_SZARRAY as _, blob); - self.type_blob(ty, blob); - } - Type::Win32Array((ty, bounds)) => { - usize_blob(ELEMENT_TYPE_ARRAY as _, blob); - self.type_blob(ty, blob); - usize_blob(1, blob); // rank - usize_blob(1, blob); // count - usize_blob(*bounds as _, blob); - } - Type::TypeName => { - let code = self.insert_type_ref("System", "Type"); - blob.push(ELEMENT_TYPE_CLASS as _); - usize_blob(code as _, blob); - } - Type::MutPtr((ty, pointers)) | Type::ConstPtr((ty, pointers)) => { - for _ in 0..*pointers { - usize_blob(ELEMENT_TYPE_PTR as _, blob); - } - self.type_blob(ty, blob); - } - _ => todo!("{:?}", ty), - } - } -} - -fn round(size: usize, round: usize) -> usize { - let round = round - 1; - (size + round) & !round -} - -fn usize_blob(value: usize, blob: &mut Vec) { - // See II.23.2 in ECMA-335 - assert!(value < 0x20000000); - - if value < 0x80 { - blob.push(value as _); - } else if value < 0x4000 { - blob.push((0x80 | (value & 0x3F00) >> 8) as _); - blob.push((value & 0xFF) as _); - } else { - blob.push((0xC0 | (value & 0x1F000000) >> 24) as _); - blob.push(((value & 0xFF0000) >> 16) as _); - blob.push(((value & 0xFF00) >> 8) as _); - blob.push((value & 0xFF) as _); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_usize_blob() { - let mut blob = vec![]; - usize_blob(0, &mut blob); - usize_blob(1, &mut blob); - usize_blob(2, &mut blob); - - usize_blob(0x80 - 2, &mut blob); - usize_blob(0x80 - 1, &mut blob); - usize_blob(0x80 - 0, &mut blob); - usize_blob(0x80 + 1, &mut blob); - usize_blob(0x80 + 2, &mut blob); - - usize_blob(0x4000 - 2, &mut blob); - usize_blob(0x4000 - 1, &mut blob); - usize_blob(0x4000 - 0, &mut blob); - usize_blob(0x4000 + 1, &mut blob); - usize_blob(0x4000 + 2, &mut blob); - - usize_blob(0x20000000 - 3, &mut blob); - usize_blob(0x20000000 - 2, &mut blob); - usize_blob(0x20000000 - 1, &mut blob); - - let mut blob = reader::Blob::new(0, &blob); - assert_eq!(blob.read_usize(), 0); - assert_eq!(blob.read_usize(), 1); - assert_eq!(blob.read_usize(), 2); - - assert_eq!(blob.read_usize(), 0x80 - 2); - assert_eq!(blob.read_usize(), 0x80 - 1); - assert_eq!(blob.read_usize(), 0x80 - 0); - assert_eq!(blob.read_usize(), 0x80 + 1); - assert_eq!(blob.read_usize(), 0x80 + 2); - - assert_eq!(blob.read_usize(), 0x4000 - 2); - assert_eq!(blob.read_usize(), 0x4000 - 1); - assert_eq!(blob.read_usize(), 0x4000 - 0); - assert_eq!(blob.read_usize(), 0x4000 + 1); - assert_eq!(blob.read_usize(), 0x4000 + 2); - - assert_eq!(blob.read_usize(), 0x20000000 - 3); - assert_eq!(blob.read_usize(), 0x20000000 - 2); - assert_eq!(blob.read_usize(), 0x20000000 - 1); - - assert_eq!(blob.slice.len(), 0); - } -} diff --git a/crates/libs/sys/Cargo.toml b/crates/libs/sys/Cargo.toml index e0176538cb..f5cac17443 100644 --- a/crates/libs/sys/Cargo.toml +++ b/crates/libs/sys/Cargo.toml @@ -22,6 +22,7 @@ path = "../targets" [features] default = [] +# generated features Wdk = [] Wdk_Foundation = ["Wdk"] Wdk_Graphics = ["Wdk"] diff --git a/crates/libs/tokens/Cargo.toml b/crates/libs/tokens/Cargo.toml deleted file mode 100644 index 9c6da256ec..0000000000 --- a/crates/libs/tokens/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "windows-tokens" -version = "0.48.0" -authors = ["Microsoft"] -edition = "2018" -license = "MIT OR Apache-2.0" -description = "Code gen support for the windows crate" -repository = "https://github.com/microsoft/windows-rs" - -[package.metadata.docs.rs] -default-target = "x86_64-pc-windows-msvc" -targets = [] diff --git a/crates/libs/tokens/license-apache-2.0 b/crates/libs/tokens/license-apache-2.0 deleted file mode 100644 index b5ed4ecec2..0000000000 --- a/crates/libs/tokens/license-apache-2.0 +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright (c) Microsoft Corporation. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/crates/libs/tokens/license-mit b/crates/libs/tokens/license-mit deleted file mode 100644 index 9e841e7a26..0000000000 --- a/crates/libs/tokens/license-mit +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/crates/libs/windows/Cargo.toml b/crates/libs/windows/Cargo.toml index 36538e05e0..45a3401f87 100644 --- a/crates/libs/windows/Cargo.toml +++ b/crates/libs/windows/Cargo.toml @@ -27,6 +27,7 @@ windows-interface = { path = "../interface", version = "0.48.0", optional = tru default = [] deprecated = [] implement = ["windows-implement", "windows-interface", "windows-core/implement"] +# generated features AI = [] AI_MachineLearning = ["AI"] ApplicationModel = [] diff --git a/crates/tests/component/Cargo.toml b/crates/tests/component/Cargo.toml index 8efbc59e8d..85920f1c9b 100644 --- a/crates/tests/component/Cargo.toml +++ b/crates/tests/component/Cargo.toml @@ -17,8 +17,3 @@ features = [ "Win32_Foundation", "Win32_System_WinRT", ] - -[build-dependencies] -bindgen = { package = "windows-bindgen", path = "../../libs/bindgen" } -metadata = { package = "windows-metadata", path = "../../libs/metadata" } - diff --git a/crates/tests/component/build.rs b/crates/tests/component/build.rs index 7878d457fd..d7ef8d96f2 100644 --- a/crates/tests/component/build.rs +++ b/crates/tests/component/build.rs @@ -1,12 +1,8 @@ -use std::fs::*; -use std::process::*; - -fn main() -> std::io::Result<()> { +fn main() { println!("cargo:rerun-if-changed=src/component.idl"); let metadata_dir = format!("{}\\System32\\WinMetadata", env!("windir")); - std::fs::create_dir_all(".windows/winmd")?; - let mut command = Command::new("midlrt.exe"); + let mut command = std::process::Command::new("midlrt.exe"); command .arg("/winrt") .arg("/nomidl") @@ -17,18 +13,36 @@ fn main() -> std::io::Result<()> { .arg("/reference") .arg(format!("{metadata_dir}\\Windows.Foundation.winmd")) .arg("/winmd") - .arg(".windows/winmd/component.winmd") + .arg("component.winmd") .arg("src/component.idl"); - if !command.status()?.success() { - panic!(); + if !command.status().unwrap().success() { + panic!("Failed to run midlrt"); } - let files = metadata::reader::File::with_default(&[".windows/winmd/component.winmd"])?; - write( + // TODO: this looks more complicated but soon the midlrt step above should disappear and then overall it should be simpler... + + let mut command = std::process::Command::new("cargo.exe"); + + command.args([ + "run", + "-p", + "riddle", + "--target-dir", + "target", // TODO: workaround for https://github.com/rust-lang/cargo/issues/6412 + "--", + "-in", + "component.winmd", + &metadata_dir, + "-out", "src/bindings.rs", - bindgen::component("test_component", &files), - )?; + "-filter", + "test_component", + "-config", + "IMPLEMENT", + ]); - Ok(()) + if !command.status().unwrap().success() { + panic!("Failed to run riddle"); + } } diff --git a/crates/tests/component/src/bindings.rs b/crates/tests/component/src/bindings.rs index 283d16e6ff..759223f885 100644 --- a/crates/tests/component/src/bindings.rs +++ b/crates/tests/component/src/bindings.rs @@ -5,6 +5,99 @@ dead_code, clippy::all )] +pub mod Nested { + #![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all + )] + #[repr(transparent)] + pub struct IThing(::windows_core::IUnknown); + impl IThing { + pub fn Method(&self) -> ::windows_core::Result<()> { + let this = self; + unsafe { + (::windows_core::Interface::vtable(this).Method)(::windows_core::Interface::as_raw( + this, + )) + .ok() + } + } + } + ::windows_core::imp::interface_hierarchy!( + IThing, + ::windows_core::IUnknown, + ::windows_core::IInspectable + ); + impl ::core::cmp::PartialEq for IThing { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + impl ::core::cmp::Eq for IThing {} + impl ::core::fmt::Debug for IThing { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple("IThing").field(&self.0).finish() + } + } + impl ::windows_core::RuntimeType for IThing { + const SIGNATURE: ::windows_core::imp::ConstBuffer = + ::windows_core::imp::ConstBuffer::from_slice(b"{5448be22-9873-5ae6-9106-f6e8455d2fdd}"); + } + unsafe impl ::windows_core::Interface for IThing { + type Vtable = IThing_Vtbl; + } + impl ::core::clone::Clone for IThing { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + unsafe impl ::windows_core::ComInterface for IThing { + const IID: ::windows_core::GUID = + ::windows_core::GUID::from_u128(0x5448be22_9873_5ae6_9106_f6e8455d2fdd); + } + #[repr(C)] + #[doc(hidden)] + pub struct IThing_Vtbl { + pub base__: ::windows_core::IInspectable_Vtbl, + pub Method: + unsafe extern "system" fn(this: *mut ::core::ffi::c_void) -> ::windows_core::HRESULT, + } + pub trait IThing_Impl: Sized { + fn Method(&self) -> ::windows_core::Result<()>; + } + impl ::windows_core::RuntimeName for IThing { + const NAME: &'static str = "test_component.Nested.IThing"; + } + impl IThing_Vtbl { + pub const fn new< + Identity: ::windows_core::IUnknownImpl, + Impl: IThing_Impl, + const OFFSET: isize, + >() -> IThing_Vtbl { + unsafe extern "system" fn Method< + Identity: ::windows_core::IUnknownImpl, + Impl: IThing_Impl, + const OFFSET: isize, + >( + this: *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT { + let this = (this as *const *const ()).offset(OFFSET) as *const Identity; + let this = (*this).get_impl(); + this.Method().into() + } + Self { + base__: ::windows_core::IInspectable_Vtbl::new::(), + Method: Method::, + } + } + pub fn matches(iid: &::windows_core::GUID) -> bool { + iid == &::IID + } + } +} #[doc(hidden)] #[repr(transparent)] pub struct IClass(::windows_core::IUnknown); diff --git a/crates/tests/component/src/component.idl b/crates/tests/component/src/component.idl index 45372f69d1..b2635d1bc4 100644 --- a/crates/tests/component/src/component.idl +++ b/crates/tests/component/src/component.idl @@ -1,5 +1,13 @@ namespace test_component { + namespace Nested + { + interface IThing + { + void Method(); + }; + } + delegate Int32 Callback(Int32 a); runtimeclass Class diff --git a/crates/tests/component_client/Cargo.toml b/crates/tests/component_client/Cargo.toml index fc43c1ac91..0e77065668 100644 --- a/crates/tests/component_client/Cargo.toml +++ b/crates/tests/component_client/Cargo.toml @@ -15,9 +15,7 @@ features = [ "Win32_Foundation", ] -[build-dependencies] -bindgen = { package = "windows-bindgen", path = "../../libs/bindgen" } -metadata = { package = "windows-metadata", path = "../../libs/metadata" } - +# TODO: this causes a warning about lack of linkage target. The point is to ensure that this binary dependency is built first but +# I'm not sure how to do that without this spurious warning. [build-dependencies.test_component] path = "../component" diff --git a/crates/tests/component_client/build.rs b/crates/tests/component_client/build.rs index 0ea4395191..a30af087ab 100644 --- a/crates/tests/component_client/build.rs +++ b/crates/tests/component_client/build.rs @@ -1,16 +1,23 @@ -use std::fs::*; +fn main() { + let mut command = std::process::Command::new("cargo.exe"); -fn main() -> std::io::Result<()> { - create_dir_all(".windows/winmd")?; - copy( - "../component/.windows/winmd/component.winmd", - ".windows/winmd/component.winmd", - )?; - - let files = metadata::reader::File::with_default(&[".windows/winmd/component.winmd"])?; - write( + command.args([ + "run", + "-p", + "riddle", + "--target-dir", + "target", + "--", + "-in", + "../component/component.winmd", + &format!("{}\\System32\\WinMetadata", env!("windir")), + "-out", "src/bindings.rs", - bindgen::component("test_component", &files), - )?; - Ok(()) + "-filter", + "test_component", + ]); + + if !command.status().unwrap().success() { + panic!("Failed to run riddle"); + } } diff --git a/crates/tests/component_client/src/bindings.rs b/crates/tests/component_client/src/bindings.rs index 283d16e6ff..4d7cadb666 100644 --- a/crates/tests/component_client/src/bindings.rs +++ b/crates/tests/component_client/src/bindings.rs @@ -5,6 +5,67 @@ dead_code, clippy::all )] +pub mod Nested { + #![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all + )] + #[repr(transparent)] + pub struct IThing(::windows_core::IUnknown); + impl IThing { + pub fn Method(&self) -> ::windows_core::Result<()> { + let this = self; + unsafe { + (::windows_core::Interface::vtable(this).Method)(::windows_core::Interface::as_raw( + this, + )) + .ok() + } + } + } + ::windows_core::imp::interface_hierarchy!( + IThing, + ::windows_core::IUnknown, + ::windows_core::IInspectable + ); + impl ::core::cmp::PartialEq for IThing { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + impl ::core::cmp::Eq for IThing {} + impl ::core::fmt::Debug for IThing { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple("IThing").field(&self.0).finish() + } + } + impl ::windows_core::RuntimeType for IThing { + const SIGNATURE: ::windows_core::imp::ConstBuffer = + ::windows_core::imp::ConstBuffer::from_slice(b"{5448be22-9873-5ae6-9106-f6e8455d2fdd}"); + } + unsafe impl ::windows_core::Interface for IThing { + type Vtable = IThing_Vtbl; + } + impl ::core::clone::Clone for IThing { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + unsafe impl ::windows_core::ComInterface for IThing { + const IID: ::windows_core::GUID = + ::windows_core::GUID::from_u128(0x5448be22_9873_5ae6_9106_f6e8455d2fdd); + } + #[repr(C)] + #[doc(hidden)] + pub struct IThing_Vtbl { + pub base__: ::windows_core::IInspectable_Vtbl, + pub Method: + unsafe extern "system" fn(this: *mut ::core::ffi::c_void) -> ::windows_core::HRESULT, + } +} #[doc(hidden)] #[repr(transparent)] pub struct IClass(::windows_core::IUnknown); @@ -410,195 +471,3 @@ pub struct Callback_Vtbl { result__: *mut i32, ) -> ::windows_core::HRESULT, } -pub trait IClass_Impl: Sized { - fn Property(&self) -> ::windows_core::Result; - fn SetProperty(&self, value: i32) -> ::windows_core::Result<()>; - fn Flags(&self) -> ::windows_core::Result; - fn Int32Array( - &self, - a: &[i32], - b: &mut [i32], - c: &mut ::windows_core::Array, - ) -> ::windows_core::Result<::windows_core::Array>; - fn StringArray( - &self, - a: &[::windows_core::HSTRING], - b: &mut [::windows_core::HSTRING], - c: &mut ::windows_core::Array<::windows_core::HSTRING>, - ) -> ::windows_core::Result<::windows_core::Array<::windows_core::HSTRING>>; - fn Input( - &self, - a: ::core::option::Option<&::windows_core::IInspectable>, - b: ::core::option::Option<&Class>, - c: ::core::option::Option<&::windows::Foundation::IStringable>, - d: ::core::option::Option<&Callback>, - ) -> ::windows_core::Result<()>; -} -impl ::windows_core::RuntimeName for IClass { - const NAME: &'static str = "test_component.IClass"; -} -impl IClass_Vtbl { - pub const fn new< - Identity: ::windows_core::IUnknownImpl, - Impl: IClass_Impl, - const OFFSET: isize, - >() -> IClass_Vtbl { - unsafe extern "system" fn Property< - Identity: ::windows_core::IUnknownImpl, - Impl: IClass_Impl, - const OFFSET: isize, - >( - this: *mut ::core::ffi::c_void, - result__: *mut i32, - ) -> ::windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); - match this.Property() { - ::core::result::Result::Ok(ok__) => { - ::core::ptr::write(result__, ::core::mem::transmute_copy(&ok__)); - ::windows_core::HRESULT(0) - } - ::core::result::Result::Err(err) => err.into(), - } - } - unsafe extern "system" fn SetProperty< - Identity: ::windows_core::IUnknownImpl, - Impl: IClass_Impl, - const OFFSET: isize, - >( - this: *mut ::core::ffi::c_void, - value: i32, - ) -> ::windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); - this.SetProperty(value).into() - } - unsafe extern "system" fn Flags< - Identity: ::windows_core::IUnknownImpl, - Impl: IClass_Impl, - const OFFSET: isize, - >( - this: *mut ::core::ffi::c_void, - result__: *mut Flags, - ) -> ::windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); - match this.Flags() { - ::core::result::Result::Ok(ok__) => { - ::core::ptr::write(result__, ::core::mem::transmute_copy(&ok__)); - ::windows_core::HRESULT(0) - } - ::core::result::Result::Err(err) => err.into(), - } - } - unsafe extern "system" fn Int32Array< - Identity: ::windows_core::IUnknownImpl, - Impl: IClass_Impl, - const OFFSET: isize, - >( - this: *mut ::core::ffi::c_void, - a_array_size: u32, - a: *const i32, - b_array_size: u32, - b: *mut i32, - c_array_size: *mut u32, - c: *mut *mut i32, - result_size__: *mut u32, - result__: *mut *mut i32, - ) -> ::windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); - match this.Int32Array( - ::core::slice::from_raw_parts(::core::mem::transmute_copy(&a), a_array_size as _), - ::core::slice::from_raw_parts_mut( - ::core::mem::transmute_copy(&b), - b_array_size as _, - ), - ::windows_core::ArrayProxy::from_raw_parts( - ::core::mem::transmute_copy(&c), - c_array_size, - ) - .as_array(), - ) { - ::core::result::Result::Ok(ok__) => { - let (ok_data__, ok_data_len__) = ok__.into_abi(); - ::core::ptr::write(result__, ok_data__); - ::core::ptr::write(result_size__, ok_data_len__); - ::windows_core::HRESULT(0) - } - ::core::result::Result::Err(err) => err.into(), - } - } - unsafe extern "system" fn StringArray< - Identity: ::windows_core::IUnknownImpl, - Impl: IClass_Impl, - const OFFSET: isize, - >( - this: *mut ::core::ffi::c_void, - a_array_size: u32, - a: *const ::std::mem::MaybeUninit<::windows_core::HSTRING>, - b_array_size: u32, - b: *mut ::std::mem::MaybeUninit<::windows_core::HSTRING>, - c_array_size: *mut u32, - c: *mut *mut ::std::mem::MaybeUninit<::windows_core::HSTRING>, - result_size__: *mut u32, - result__: *mut *mut ::std::mem::MaybeUninit<::windows_core::HSTRING>, - ) -> ::windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); - match this.StringArray( - ::core::slice::from_raw_parts(::core::mem::transmute_copy(&a), a_array_size as _), - ::core::slice::from_raw_parts_mut( - ::core::mem::transmute_copy(&b), - b_array_size as _, - ), - ::windows_core::ArrayProxy::from_raw_parts( - ::core::mem::transmute_copy(&c), - c_array_size, - ) - .as_array(), - ) { - ::core::result::Result::Ok(ok__) => { - let (ok_data__, ok_data_len__) = ok__.into_abi(); - ::core::ptr::write(result__, ok_data__); - ::core::ptr::write(result_size__, ok_data_len__); - ::windows_core::HRESULT(0) - } - ::core::result::Result::Err(err) => err.into(), - } - } - unsafe extern "system" fn Input< - Identity: ::windows_core::IUnknownImpl, - Impl: IClass_Impl, - const OFFSET: isize, - >( - this: *mut ::core::ffi::c_void, - a: *mut ::core::ffi::c_void, - b: *mut ::core::ffi::c_void, - c: *mut ::core::ffi::c_void, - d: *mut ::core::ffi::c_void, - ) -> ::windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); - this.Input( - ::windows_core::from_raw_borrowed(&a), - ::windows_core::from_raw_borrowed(&b), - ::windows_core::from_raw_borrowed(&c), - ::windows_core::from_raw_borrowed(&d), - ) - .into() - } - Self { - base__: ::windows_core::IInspectable_Vtbl::new::(), - Property: Property::, - SetProperty: SetProperty::, - Flags: Flags::, - Int32Array: Int32Array::, - StringArray: StringArray::, - Input: Input::, - } - } - pub fn matches(iid: &::windows_core::GUID) -> bool { - iid == &::IID - } -} diff --git a/crates/tests/component_client/src/lib.rs b/crates/tests/component_client/src/lib.rs index a22a3e0d1f..19a7a1dffb 100644 --- a/crates/tests/component_client/src/lib.rs +++ b/crates/tests/component_client/src/lib.rs @@ -39,7 +39,7 @@ fn test() -> Result<()> { assert_eq!(a, d[..]); let c: IStringable = Stringable.into(); - let d = Callback::new(|input| Ok(input)); + let d = Callback::new(Ok); class.Input(&class, &class, &c, &d)?; assert!(class.Input(None, None, None, None).is_err()); diff --git a/crates/tests/metadata/Cargo.toml b/crates/tests/metadata/Cargo.toml index 098a379121..00fd31a50d 100644 --- a/crates/tests/metadata/Cargo.toml +++ b/crates/tests/metadata/Cargo.toml @@ -21,4 +21,7 @@ features = [ "Win32_Graphics_Gdi", "Win32_System_SystemServices", "Win32_Foundation", -] \ No newline at end of file +] + +[dependencies.tool_lib] +path = "../../tools/lib" diff --git a/crates/tests/metadata/tests/attribute_enum.rs b/crates/tests/metadata/tests/attribute_enum.rs index 727ac4f882..c9f782fceb 100644 --- a/crates/tests/metadata/tests/attribute_enum.rs +++ b/crates/tests/metadata/tests/attribute_enum.rs @@ -1,8 +1,8 @@ -use metadata::reader::{Attribute, File, Reader, TypeName, Value}; +use metadata::{Attribute, Reader, TypeName, Value}; #[test] fn attribute_enum() { - let files = File::with_default(&[]).unwrap(); + let files = tool_lib::default_metadata(); let reader = &Reader::new(&files); let method = reader diff --git a/crates/tests/metadata/tests/fn_call_size.rs b/crates/tests/metadata/tests/fn_call_size.rs index 349e1ccd04..01b60b246e 100644 --- a/crates/tests/metadata/tests/fn_call_size.rs +++ b/crates/tests/metadata/tests/fn_call_size.rs @@ -1,11 +1,11 @@ -use metadata::reader::*; +use metadata::*; #[test] fn size() { // Note: you can double check these export names from a Visual Studio x86 command prompt as follows: // dumpbin /exports kernel32.lib | findstr /i RtmConvertIpv6AddressAndLengthToNetAddress - let files = File::with_default(&[]).unwrap(); + let files = tool_lib::default_metadata(); let reader = &Reader::new(&files); assert_eq!( diff --git a/crates/tests/riddle/Cargo.toml b/crates/tests/riddle/Cargo.toml index f2bf9ccca6..1ed54384bf 100644 --- a/crates/tests/riddle/Cargo.toml +++ b/crates/tests/riddle/Cargo.toml @@ -6,3 +6,6 @@ edition = "2018" [dependencies.windows-metadata] path = "../../libs/metadata" + +[dependencies.tool_lib] +path = "../../tools/lib" diff --git a/crates/tests/riddle/src/lib.rs b/crates/tests/riddle/src/lib.rs index faddf58649..479abaafe7 100644 --- a/crates/tests/riddle/src/lib.rs +++ b/crates/tests/riddle/src/lib.rs @@ -1,6 +1,7 @@ use std::process::Command; -pub fn run_riddle(idl: &str) -> String { +pub fn run_riddle(idl: &str) -> Vec { + let before = std::fs::read_to_string(idl).expect("Failed to read input"); let mut command = Command::new("cargo.exe"); command @@ -18,18 +19,21 @@ pub fn run_riddle(idl: &str) -> String { .into_owned(); let mut command = Command::new("riddle.exe"); - command.arg("-in").arg(idl).arg("-out").arg(&winmd); + command.args(["-in", idl, "-out", &winmd, "-filter", "Test"]); - if !command.status().unwrap().success() { - panic!("Failed to run riddle"); - } + assert!(command.status().unwrap().success()); let mut command = Command::new("riddle.exe"); - command.arg("-in").arg(&winmd).arg("-out").arg(idl); - - if !command.status().unwrap().success() { - panic!("Failed to run riddle"); - } - - winmd + command.args(["-in", &winmd, "-out", idl, "-filter", "Test"]); + assert!(command.status().unwrap().success()); + + let after = std::fs::read_to_string(idl).expect("Failed to read output"); + assert_eq!(before, after); + + let mut files = tool_lib::default_metadata(); + files.push( + windows_metadata::File::new(std::fs::read(winmd).expect("failed to read winmd")) + .expect("failed to parse winmd"), + ); + files } diff --git a/crates/tests/riddle/tests/struct.idl b/crates/tests/riddle/tests/struct.idl index 3b7819f91d..fd87fd434d 100644 --- a/crates/tests/riddle/tests/struct.idl +++ b/crates/tests/riddle/tests/struct.idl @@ -1,8 +1,17 @@ mod Test { - struct Name { - a: i32, - b: f32, - c: u64, - d: f64, + struct Primitives { + field_bool: bool, + field_i8: i8, + field_u8: u8, + field_i16: i16, + field_u16: u16, + field_i32: i32, + field_u32: u32, + field_i64: i64, + field_u64: u64, + field_f32: f32, + field_f64: f64, + field_isize: isize, + field_usize: usize, } } \ No newline at end of file diff --git a/crates/tests/riddle/tests/struct.rs b/crates/tests/riddle/tests/struct.rs index d4fff6817b..74e10817f1 100644 --- a/crates/tests/riddle/tests/struct.rs +++ b/crates/tests/riddle/tests/struct.rs @@ -1,29 +1,46 @@ use test_riddle::run_riddle; -use windows_metadata::reader::*; +use windows_metadata::*; #[test] -fn riddle_struct() { - let output = run_riddle("tests/struct.idl"); - let files = File::with_default(&[&output]).expect("Failed to open winmd files"); +fn primitives() { + let files = run_riddle("tests/struct.idl"); let reader = &Reader::new(&files); let def = reader - .get(TypeName::new("Test", "Name")) + .get(TypeName::new("Test", "Primitives")) .next() .expect("Type missing"); assert_eq!(reader.type_def_kind(def), TypeKind::Struct); let fields: Vec = reader.type_def_fields(def).collect(); - assert_eq!(fields.len(), 4); + assert_eq!(fields.len(), 13); - assert_eq!(reader.field_name(fields[0]), "a"); - assert_eq!(reader.field_name(fields[1]), "b"); - assert_eq!(reader.field_name(fields[2]), "c"); - assert_eq!(reader.field_name(fields[3]), "d"); + assert_eq!(reader.field_name(fields[0]), "field_bool"); + assert_eq!(reader.field_name(fields[1]), "field_i8"); + assert_eq!(reader.field_name(fields[2]), "field_u8"); + assert_eq!(reader.field_name(fields[3]), "field_i16"); + assert_eq!(reader.field_name(fields[4]), "field_u16"); + assert_eq!(reader.field_name(fields[5]), "field_i32"); + assert_eq!(reader.field_name(fields[6]), "field_u32"); + assert_eq!(reader.field_name(fields[7]), "field_i64"); + assert_eq!(reader.field_name(fields[8]), "field_u64"); + assert_eq!(reader.field_name(fields[9]), "field_f32"); + assert_eq!(reader.field_name(fields[10]), "field_f64"); + assert_eq!(reader.field_name(fields[11]), "field_isize"); + assert_eq!(reader.field_name(fields[12]), "field_usize"); - assert!(matches!(reader.field_type(fields[0], None), Type::I32)); - assert!(matches!(reader.field_type(fields[1], None), Type::F32)); - assert!(matches!(reader.field_type(fields[2], None), Type::U64)); - assert!(matches!(reader.field_type(fields[3], None), Type::F64)); + assert!(matches!(reader.field_type(fields[0], None), Type::Bool)); + assert!(matches!(reader.field_type(fields[1], None), Type::I8)); + assert!(matches!(reader.field_type(fields[2], None), Type::U8)); + assert!(matches!(reader.field_type(fields[3], None), Type::I16)); + assert!(matches!(reader.field_type(fields[4], None), Type::U16)); + assert!(matches!(reader.field_type(fields[5], None), Type::I32)); + assert!(matches!(reader.field_type(fields[6], None), Type::U32)); + assert!(matches!(reader.field_type(fields[7], None), Type::I64)); + assert!(matches!(reader.field_type(fields[8], None), Type::U64)); + assert!(matches!(reader.field_type(fields[9], None), Type::F32)); + assert!(matches!(reader.field_type(fields[10], None), Type::F64)); + assert!(matches!(reader.field_type(fields[11], None), Type::ISize)); + assert!(matches!(reader.field_type(fields[12], None), Type::USize)); } diff --git a/crates/tests/standalone/Cargo.toml b/crates/tests/standalone/Cargo.toml index bc7a811fab..96cdc05b7d 100644 --- a/crates/tests/standalone/Cargo.toml +++ b/crates/tests/standalone/Cargo.toml @@ -9,6 +9,3 @@ path = "../../libs/core" [dependencies.windows-targets] path = "../../libs/targets" - -[build-dependencies.windows-bindgen] -path = "../../libs/bindgen" diff --git a/crates/tests/standalone/build.rs b/crates/tests/standalone/build.rs index 762f337b9b..f1573de7d4 100644 --- a/crates/tests/standalone/build.rs +++ b/crates/tests/standalone/build.rs @@ -138,17 +138,40 @@ fn main() { ); } -fn write_sys(filename: &str, apis: &[&str]) { - let bindings = windows_bindgen::standalone_sys(apis); - std::fs::write(filename, bindings).unwrap(); +fn write_sys(output: &str, filter: &[&str]) { + riddle(output, filter, &["FLATTEN", "SYS"]); } -fn write_win(filename: &str, apis: &[&str]) { - let bindings = windows_bindgen::standalone_win(apis); - std::fs::write(filename, bindings).unwrap(); +fn write_win(output: &str, filter: &[&str]) { + riddle(output, filter, &["FLATTEN"]); } -fn write_std(filename: &str, apis: &[&str]) { - let bindings = windows_bindgen::standalone_std(apis); - std::fs::write(filename, bindings).unwrap(); +fn write_std(output: &str, filter: &[&str]) { + riddle(output, filter, &["FLATTEN", "STD"]); +} + +fn riddle(output: &str, filter: &[&str], config: &[&str]) { + let mut command = std::process::Command::new("cargo.exe"); + + command.args([ + "run", + "-p", + "riddle", + "--target-dir", + "target", // TODO: workaround for https://github.com/rust-lang/cargo/issues/6412 + "--", + "-in", + "../../libs/metadata/default", + "-out", + output, + "-filter", + ]); + + command.args(filter); + command.arg("-config"); + command.args(config); + + if !command.status().unwrap().success() { + panic!("Failed to run riddle"); + } } diff --git a/crates/tests/standalone/src/b_arch.rs b/crates/tests/standalone/src/b_arch.rs index 8e1f3c798b..3c2002e4f5 100644 --- a/crates/tests/standalone/src/b_arch.rs +++ b/crates/tests/standalone/src/b_arch.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_arch_dependencies.rs b/crates/tests/standalone/src/b_arch_dependencies.rs index bf8e36436f..de376e848d 100644 --- a/crates/tests/standalone/src/b_arch_dependencies.rs +++ b/crates/tests/standalone/src/b_arch_dependencies.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_bstr.rs b/crates/tests/standalone/src/b_bstr.rs index 2f4917a4b8..3d9b513d80 100644 --- a/crates/tests/standalone/src/b_bstr.rs +++ b/crates/tests/standalone/src/b_bstr.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_calendar.rs b/crates/tests/standalone/src/b_calendar.rs index 266994dbec..7aaf035875 100644 --- a/crates/tests/standalone/src/b_calendar.rs +++ b/crates/tests/standalone/src/b_calendar.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_constant_types.rs b/crates/tests/standalone/src/b_constant_types.rs index bf1174f86e..98432098f4 100644 --- a/crates/tests/standalone/src/b_constant_types.rs +++ b/crates/tests/standalone/src/b_constant_types.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_depends.rs b/crates/tests/standalone/src/b_depends.rs index e17d4e6268..ad7c58837c 100644 --- a/crates/tests/standalone/src/b_depends.rs +++ b/crates/tests/standalone/src/b_depends.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_enumeration.rs b/crates/tests/standalone/src/b_enumeration.rs index e10bfce178..091d02b9a0 100644 --- a/crates/tests/standalone/src/b_enumeration.rs +++ b/crates/tests/standalone/src/b_enumeration.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_enumerator.rs b/crates/tests/standalone/src/b_enumerator.rs index 7b1b153601..fa3af82355 100644 --- a/crates/tests/standalone/src/b_enumerator.rs +++ b/crates/tests/standalone/src/b_enumerator.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_guid.rs b/crates/tests/standalone/src/b_guid.rs index d8823c3734..128f33c3a7 100644 --- a/crates/tests/standalone/src/b_guid.rs +++ b/crates/tests/standalone/src/b_guid.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_hresult.rs b/crates/tests/standalone/src/b_hresult.rs index 07a7922114..77ba7e9a2f 100644 --- a/crates/tests/standalone/src/b_hresult.rs +++ b/crates/tests/standalone/src/b_hresult.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_hstring.rs b/crates/tests/standalone/src/b_hstring.rs index de329b52f9..0bd3a7b3db 100644 --- a/crates/tests/standalone/src/b_hstring.rs +++ b/crates/tests/standalone/src/b_hstring.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_inspectable.rs b/crates/tests/standalone/src/b_inspectable.rs index a0d9b5e276..e1f57eae42 100644 --- a/crates/tests/standalone/src/b_inspectable.rs +++ b/crates/tests/standalone/src/b_inspectable.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_nested.rs b/crates/tests/standalone/src/b_nested.rs index 48f3a412db..5d63b75b27 100644 --- a/crates/tests/standalone/src/b_nested.rs +++ b/crates/tests/standalone/src/b_nested.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_none.rs b/crates/tests/standalone/src/b_none.rs index 47a3d1163c..51585c1a24 100644 --- a/crates/tests/standalone/src/b_none.rs +++ b/crates/tests/standalone/src/b_none.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_overloads.rs b/crates/tests/standalone/src/b_overloads.rs index 49ea5dc3f1..daec9b3e91 100644 --- a/crates/tests/standalone/src/b_overloads.rs +++ b/crates/tests/standalone/src/b_overloads.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_pcstr.rs b/crates/tests/standalone/src/b_pcstr.rs index 0cde0a7ecf..2a8a265c35 100644 --- a/crates/tests/standalone/src/b_pcstr.rs +++ b/crates/tests/standalone/src/b_pcstr.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_pcwstr.rs b/crates/tests/standalone/src/b_pcwstr.rs index cbf383f60f..dafb131d1a 100644 --- a/crates/tests/standalone/src/b_pcwstr.rs +++ b/crates/tests/standalone/src/b_pcwstr.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_pstr.rs b/crates/tests/standalone/src/b_pstr.rs index 76c7c04990..2d0d9e7244 100644 --- a/crates/tests/standalone/src/b_pstr.rs +++ b/crates/tests/standalone/src/b_pstr.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_pwstr.rs b/crates/tests/standalone/src/b_pwstr.rs index b9e9fd1dae..6f66d278dd 100644 --- a/crates/tests/standalone/src/b_pwstr.rs +++ b/crates/tests/standalone/src/b_pwstr.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_std.rs b/crates/tests/standalone/src/b_std.rs index d65feabcde..0fa98564fe 100644 --- a/crates/tests/standalone/src/b_std.rs +++ b/crates/tests/standalone/src/b_std.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_stringable.rs b/crates/tests/standalone/src/b_stringable.rs index d8f1ee1337..a575866377 100644 --- a/crates/tests/standalone/src/b_stringable.rs +++ b/crates/tests/standalone/src/b_stringable.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_test.rs b/crates/tests/standalone/src/b_test.rs index 8026d01d9f..41d48c0bb5 100644 --- a/crates/tests/standalone/src/b_test.rs +++ b/crates/tests/standalone/src/b_test.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_unknown.rs b/crates/tests/standalone/src/b_unknown.rs index 280590949d..330b76c96c 100644 --- a/crates/tests/standalone/src/b_unknown.rs +++ b/crates/tests/standalone/src/b_unknown.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_uri.rs b/crates/tests/standalone/src/b_uri.rs index 9b5edce0a7..93e51452aa 100644 --- a/crates/tests/standalone/src/b_uri.rs +++ b/crates/tests/standalone/src/b_uri.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tests/standalone/src/b_win_enumerator.rs b/crates/tests/standalone/src/b_win_enumerator.rs index 0d857e89b9..c9791deb35 100644 --- a/crates/tests/standalone/src/b_win_enumerator.rs +++ b/crates/tests/standalone/src/b_win_enumerator.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `riddle` 0.0.1 #![allow( non_snake_case, diff --git a/crates/tools/core/Cargo.toml b/crates/tools/core/Cargo.toml index bd12e139c7..e821efd76a 100644 --- a/crates/tools/core/Cargo.toml +++ b/crates/tools/core/Cargo.toml @@ -3,6 +3,3 @@ name = "tool_core" version = "0.0.0" edition = "2018" publish = false - -[dependencies.windows-bindgen] -path = "../../libs/bindgen" diff --git a/crates/tools/core/bindings.txt b/crates/tools/core/bindings.txt new file mode 100644 index 0000000000..091991b92a --- /dev/null +++ b/crates/tools/core/bindings.txt @@ -0,0 +1,28 @@ +-in crates/libs/metadata/default +-out crates/libs/core/src/imp/bindings.rs +-config FLATTEN SYS + +-filter + Windows.Win32.Foundation.CloseHandle + Windows.Win32.Foundation.ERROR_NO_UNICODE_TRANSLATION + Windows.Win32.Foundation.GetLastError + Windows.Win32.Foundation.SysAllocStringLen + Windows.Win32.Foundation.SysFreeString + Windows.Win32.Foundation.SysStringLen + Windows.Win32.System.Com.CoTaskMemAlloc + Windows.Win32.System.Com.CoTaskMemFree + Windows.Win32.System.Diagnostics.Debug.EncodePointer + Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ALLOCATE_BUFFER + Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_SYSTEM + Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS + Windows.Win32.System.Diagnostics.Debug.FormatMessageW + Windows.Win32.System.LibraryLoader.FreeLibrary + Windows.Win32.System.LibraryLoader.GetProcAddress + Windows.Win32.System.LibraryLoader.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS + Windows.Win32.System.LibraryLoader.LoadLibraryExA + Windows.Win32.System.Memory.GetProcessHeap + Windows.Win32.System.Memory.HeapAlloc + Windows.Win32.System.Memory.HeapFree + Windows.Win32.System.Threading.CreateEventW + Windows.Win32.System.Threading.SetEvent + Windows.Win32.System.Threading.WaitForSingleObject \ No newline at end of file diff --git a/crates/tools/core/com_bindings.txt b/crates/tools/core/com_bindings.txt new file mode 100644 index 0000000000..2532a19734 --- /dev/null +++ b/crates/tools/core/com_bindings.txt @@ -0,0 +1,26 @@ +-in crates/libs/metadata/default +-out crates/libs/core/src/imp/com_bindings.rs +-config FLATTEN + +-filter + Windows.Foundation.IReference + Windows.Foundation.IStringable + Windows.Foundation.PropertyValue + Windows.Win32.Foundation.CLASS_E_CLASSNOTAVAILABLE + Windows.Win32.Foundation.CO_E_NOTINITIALIZED + Windows.Win32.Foundation.E_BOUNDS + Windows.Win32.Foundation.E_NOINTERFACE + Windows.Win32.Foundation.E_OUTOFMEMORY + Windows.Win32.Foundation.JSCRIPT_E_CANTEXECUTE + Windows.Win32.Foundation.RPC_E_DISCONNECTED + Windows.Win32.System.Com.CoCreateGuid + Windows.Win32.System.Com.GetErrorInfo + Windows.Win32.System.Com.IAgileObject + Windows.Win32.System.Com.IErrorInfo + Windows.Win32.System.Com.SetErrorInfo + Windows.Win32.System.WinRT.AGILEREFERENCE_DEFAULT + Windows.Win32.System.WinRT.IAgileReference + Windows.Win32.System.WinRT.ILanguageExceptionErrorInfo2 + Windows.Win32.System.WinRT.IRestrictedErrorInfo + Windows.Win32.System.WinRT.IWeakReferenceSource + Windows.Win32.System.WinRT.RoGetAgileReference \ No newline at end of file diff --git a/crates/tools/core/src/main.rs b/crates/tools/core/src/main.rs index 9db4560a25..24a2041731 100644 --- a/crates/tools/core/src/main.rs +++ b/crates/tools/core/src/main.rs @@ -1,57 +1,27 @@ fn main() { - let bindings = [ - "Windows.Win32.Foundation.CloseHandle", - "Windows.Win32.Foundation.ERROR_NO_UNICODE_TRANSLATION", - "Windows.Win32.Foundation.GetLastError", - "Windows.Win32.Foundation.SysAllocStringLen", - "Windows.Win32.Foundation.SysFreeString", - "Windows.Win32.Foundation.SysStringLen", - "Windows.Win32.System.Com.CoTaskMemAlloc", - "Windows.Win32.System.Com.CoTaskMemFree", - "Windows.Win32.System.Diagnostics.Debug.EncodePointer", - "Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ALLOCATE_BUFFER", - "Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_SYSTEM", - "Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS", - "Windows.Win32.System.Diagnostics.Debug.FormatMessageW", - "Windows.Win32.System.LibraryLoader.FreeLibrary", - "Windows.Win32.System.LibraryLoader.GetProcAddress", - "Windows.Win32.System.LibraryLoader.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS", - "Windows.Win32.System.LibraryLoader.LoadLibraryExA", - "Windows.Win32.System.Memory.GetProcessHeap", - "Windows.Win32.System.Memory.HeapAlloc", - "Windows.Win32.System.Memory.HeapFree", - "Windows.Win32.System.Threading.CreateEventW", - "Windows.Win32.System.Threading.SetEvent", - "Windows.Win32.System.Threading.WaitForSingleObject", - ]; + let mut command = std::process::Command::new("cargo.exe"); - let bindings = windows_bindgen::standalone_sys(&bindings); - std::fs::write("crates/libs/core/src/imp/bindings.rs", bindings).unwrap(); + command.args([ + "run", + "-p", + "riddle", + "--", + "-etc", + "crates/tools/core/bindings.txt", + ]); - let bindings = [ - "Windows.Foundation.IReference", - "Windows.Foundation.IStringable", - "Windows.Foundation.PropertyValue", - "Windows.Win32.Foundation.CLASS_E_CLASSNOTAVAILABLE", - "Windows.Win32.Foundation.CO_E_NOTINITIALIZED", - "Windows.Win32.Foundation.E_BOUNDS", - "Windows.Win32.Foundation.E_NOINTERFACE", - "Windows.Win32.Foundation.E_OUTOFMEMORY", - "Windows.Win32.Foundation.JSCRIPT_E_CANTEXECUTE", - "Windows.Win32.Foundation.RPC_E_DISCONNECTED", - "Windows.Win32.System.Com.CoCreateGuid", - "Windows.Win32.System.Com.GetErrorInfo", - "Windows.Win32.System.Com.IAgileObject", - "Windows.Win32.System.Com.IErrorInfo", - "Windows.Win32.System.Com.SetErrorInfo", - "Windows.Win32.System.WinRT.AGILEREFERENCE_DEFAULT", - "Windows.Win32.System.WinRT.IAgileReference", - "Windows.Win32.System.WinRT.ILanguageExceptionErrorInfo2", - "Windows.Win32.System.WinRT.IRestrictedErrorInfo", - "Windows.Win32.System.WinRT.IWeakReferenceSource", - "Windows.Win32.System.WinRT.RoGetAgileReference", - ]; + assert!(command.status().unwrap().success()); - let bindings = windows_bindgen::standalone_win(&bindings); - std::fs::write("crates/libs/core/src/imp/com_bindings.rs", bindings).unwrap(); + let mut command = std::process::Command::new("cargo.exe"); + + command.args([ + "run", + "-p", + "riddle", + "--", + "-etc", + "crates/tools/core/com_bindings.txt", + ]); + + assert!(command.status().unwrap().success()); } diff --git a/crates/tools/gnu/src/main.rs b/crates/tools/gnu/src/main.rs index 7e4341900a..231aa07d7e 100644 --- a/crates/tools/gnu/src/main.rs +++ b/crates/tools/gnu/src/main.rs @@ -3,7 +3,12 @@ use std::io::prelude::*; use std::process::{Command, Stdio}; fn main() { - const ALL_PLATFORMS: [&str; 4] = ["x86_64_gnu", "i686_gnu", "x86_64_gnullvm", "aarch64_gnullvm"]; + const ALL_PLATFORMS: [&str; 4] = [ + "x86_64_gnu", + "i686_gnu", + "x86_64_gnullvm", + "aarch64_gnullvm", + ]; let mut platforms = BTreeSet::new(); for platform in std::env::args().skip(1) { if ALL_PLATFORMS.contains(&&*platform) { @@ -21,9 +26,18 @@ fn main() { }; for platform in platforms { - let tools = if platform.ends_with("_gnu") { &["dlltool", "ar", "objcopy"][..] } else { &["llvm-dlltool", "llvm-ar"][..] }; + let tools = if platform.ends_with("_gnu") { + &["dlltool", "ar", "objcopy"][..] + } else { + &["llvm-dlltool", "llvm-ar"][..] + }; for tool in tools { - if Command::new(tool).stdout(Stdio::null()).stderr(Stdio::null()).spawn().is_err() { + if Command::new(tool) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .is_err() + { eprintln!("Could not find {tool}. Is it in your $PATH?"); std::process::exit(1); } @@ -52,7 +66,13 @@ fn build_platform(platform: &str, dlltool: &str, ar: &str) { } } -fn build_library(output: &std::path::Path, dlltool: &str, library: &str, functions: &BTreeMap, platform: &str) { +fn build_library( + output: &std::path::Path, + dlltool: &str, + library: &str, + functions: &BTreeMap, + platform: &str, +) { println!("{library}"); // Note that we don't use set_extension as it confuses PathBuf when the library name includes a period. @@ -73,8 +93,12 @@ EXPORTS for (function, calling_convention) in functions { match calling_convention { - lib::CallingConvention::Stdcall(params) if platform.eq("i686_gnu") => def.write_all(format!("{function}@{params} @ 0\n").as_bytes()).unwrap(), - _ => def.write_all(format!("{function} @ 0\n").as_bytes()).unwrap(), + lib::CallingConvention::Stdcall(params) if platform.eq("i686_gnu") => def + .write_all(format!("{function}@{params} @ 0\n").as_bytes()) + .unwrap(), + _ => def + .write_all(format!("{function} @ 0\n").as_bytes()) + .unwrap(), } } @@ -140,15 +164,21 @@ EXPORTS std::fs::remove_file(output.join(format!("{library}.def"))).unwrap(); } -fn build_mri(output: &std::path::Path, ar: &str, libraries: &BTreeMap>) { +fn build_mri( + output: &std::path::Path, + ar: &str, + libraries: &BTreeMap>, +) { let mri_path = output.join("unified.mri"); let mut mri = std::fs::File::create(&mri_path).unwrap(); println!("Generating {}", mri_path.to_string_lossy()); - mri.write_all(format!("CREATE libwindows.{}.a\n", std::env!("CARGO_PKG_VERSION")).as_bytes()).unwrap(); + mri.write_all(format!("CREATE libwindows.{}.a\n", std::env!("CARGO_PKG_VERSION")).as_bytes()) + .unwrap(); for library in libraries.keys() { - mri.write_all(format!("ADDLIB lib{library}.a\n").as_bytes()).unwrap(); + mri.write_all(format!("ADDLIB lib{library}.a\n").as_bytes()) + .unwrap(); } mri.write_all(b"SAVE\nEND\n").unwrap(); diff --git a/crates/tools/lib/src/lib.rs b/crates/tools/lib/src/lib.rs index 266130b863..e766139777 100644 --- a/crates/tools/lib/src/lib.rs +++ b/crates/tools/lib/src/lib.rs @@ -1,35 +1,58 @@ -use std::collections::*; - -/// Returns the libraries and their function and stack sizes used by the gnu and msvc tools to build the umbrella libs. -pub fn libraries() -> BTreeMap> { - use metadata::reader::File; - let mut libraries = BTreeMap::>::new(); - - let files = File::with_default(&[]).unwrap(); - combine(&files, &mut libraries); - - libraries -} +use std::collections::BTreeMap; pub enum CallingConvention { Stdcall(usize), Cdecl, } -fn combine(files: &[metadata::reader::File], libraries: &mut BTreeMap>) { - let reader = &metadata::reader::Reader::new(files); - for method in reader.namespaces().flat_map(|namespace| reader.namespace_functions(namespace)) { +pub fn default_metadata() -> Vec { + vec![ + metadata::File::new( + std::include_bytes!("../../../libs/metadata/default/Windows.winmd").to_vec(), + ) + .expect("invalid winmd"), + metadata::File::new( + std::include_bytes!("../../../libs/metadata/default/Windows.Win32.winmd").to_vec(), + ) + .expect("invalid winmd"), + metadata::File::new( + std::include_bytes!("../../../libs/metadata/default/Windows.Wdk.winmd").to_vec(), + ) + .expect("invalid winmd"), + ] +} + +/// Returns the libraries and their function and stack sizes used by the gnu and msvc tools to build the umbrella libs. +pub fn libraries() -> BTreeMap> { + let files = default_metadata(); + let reader = &metadata::Reader::new(&files); + let mut libraries = BTreeMap::>::new(); + + for method in reader + .namespaces() + .flat_map(|namespace| reader.namespace_functions(namespace)) + { let library = reader.method_def_module_name(method); - let impl_map = reader.method_def_impl_map(method).expect("ImplMap not found"); + let impl_map = reader + .method_def_impl_map(method) + .expect("ImplMap not found"); let flags = reader.impl_map_flags(impl_map); let name = reader.impl_map_import_name(impl_map).to_string(); if flags.contains(metadata::PInvokeAttributes::CallConvPlatformapi) { let params = reader.method_def_size(method); - libraries.entry(library).or_default().insert(name, CallingConvention::Stdcall(params)); + libraries + .entry(library) + .or_default() + .insert(name, CallingConvention::Stdcall(params)); } else if flags.contains(metadata::PInvokeAttributes::CallConvCdecl) { - libraries.entry(library).or_default().insert(name, CallingConvention::Cdecl); + libraries + .entry(library) + .or_default() + .insert(name, CallingConvention::Cdecl); } else { unimplemented!(); } } + + libraries } diff --git a/crates/tools/metadata/Cargo.toml b/crates/tools/metadata/Cargo.toml index ad500b243d..537684c254 100644 --- a/crates/tools/metadata/Cargo.toml +++ b/crates/tools/metadata/Cargo.toml @@ -3,6 +3,3 @@ name = "tool_metadata" version = "0.0.0" edition = "2018" publish = false - -[dependencies.windows-bindgen] -path = "../../libs/bindgen" diff --git a/crates/tools/metadata/bindings.txt b/crates/tools/metadata/bindings.txt new file mode 100644 index 0000000000..0cfbb08295 --- /dev/null +++ b/crates/tools/metadata/bindings.txt @@ -0,0 +1,52 @@ +-in crates/libs/metadata/default +-out crates/libs/metadata/src/imp/bindings.rs +-config FLATTEN SYS + +-filter + Windows.Win32.System.Diagnostics.Debug.IMAGE_COR20_HEADER + Windows.Win32.System.Diagnostics.Debug.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR + Windows.Win32.System.Diagnostics.Debug.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + Windows.Win32.System.Diagnostics.Debug.IMAGE_DLLCHARACTERISTICS_NO_SEH + Windows.Win32.System.Diagnostics.Debug.IMAGE_DLLCHARACTERISTICS_NX_COMPAT + Windows.Win32.System.Diagnostics.Debug.IMAGE_FILE_32BIT_MACHINE + Windows.Win32.System.Diagnostics.Debug.IMAGE_FILE_DLL + Windows.Win32.System.Diagnostics.Debug.IMAGE_FILE_EXECUTABLE_IMAGE + Windows.Win32.System.Diagnostics.Debug.IMAGE_FILE_HEADER + Windows.Win32.System.Diagnostics.Debug.IMAGE_NT_OPTIONAL_HDR32_MAGIC + Windows.Win32.System.Diagnostics.Debug.IMAGE_NT_OPTIONAL_HDR64_MAGIC + Windows.Win32.System.Diagnostics.Debug.IMAGE_OPTIONAL_HEADER32 + Windows.Win32.System.Diagnostics.Debug.IMAGE_OPTIONAL_HEADER64 + Windows.Win32.System.Diagnostics.Debug.IMAGE_SECTION_HEADER + Windows.Win32.System.Diagnostics.Debug.IMAGE_SUBSYSTEM_WINDOWS_CUI + Windows.Win32.System.SystemInformation.IMAGE_FILE_MACHINE_I386 + Windows.Win32.System.SystemServices.IMAGE_DOS_HEADER + Windows.Win32.System.SystemServices.IMAGE_DOS_SIGNATURE + Windows.Win32.System.SystemServices.IMAGE_NT_SIGNATURE + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_ARRAY + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_BOOLEAN + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_BYREF + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_CHAR + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_CLASS + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_CMOD_OPT + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_CMOD_REQD + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_GENERICINST + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I1 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I2 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I4 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I8 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_OBJECT + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_PTR + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_R4 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_R8 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_STRING + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_SZARRAY + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U1 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U2 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U4 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U8 + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_VALUETYPE + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_VALUETYPE + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_VAR + Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_VOID \ No newline at end of file diff --git a/crates/tools/metadata/src/main.rs b/crates/tools/metadata/src/main.rs index 47b481f92d..848e12be65 100644 --- a/crates/tools/metadata/src/main.rs +++ b/crates/tools/metadata/src/main.rs @@ -1,54 +1,14 @@ fn main() { - let bindings = [ - "Windows.Win32.System.Diagnostics.Debug.IMAGE_COR20_HEADER", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_DLLCHARACTERISTICS_NO_SEH", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_DLLCHARACTERISTICS_NX_COMPAT", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_FILE_32BIT_MACHINE", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_FILE_DLL", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_FILE_EXECUTABLE_IMAGE", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_FILE_HEADER", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_NT_OPTIONAL_HDR32_MAGIC", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_NT_OPTIONAL_HDR64_MAGIC", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_OPTIONAL_HEADER32", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_OPTIONAL_HEADER64", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_SECTION_HEADER", - "Windows.Win32.System.Diagnostics.Debug.IMAGE_SUBSYSTEM_WINDOWS_CUI", - "Windows.Win32.System.SystemInformation.IMAGE_FILE_MACHINE_I386", - "Windows.Win32.System.SystemServices.IMAGE_DOS_HEADER", - "Windows.Win32.System.SystemServices.IMAGE_DOS_SIGNATURE", - "Windows.Win32.System.SystemServices.IMAGE_NT_SIGNATURE", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_ARRAY", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_BOOLEAN", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_BYREF", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_CHAR", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_CLASS", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_CMOD_OPT", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_CMOD_REQD", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_GENERICINST", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I1", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I2", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I4", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_I8", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_OBJECT", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_PTR", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_R4", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_R8", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_STRING", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_SZARRAY", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U1", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U2", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U4", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_U8", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_VALUETYPE", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_VALUETYPE", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_VAR", - "Windows.Win32.System.WinRT.Metadata.ELEMENT_TYPE_VOID", - ]; + let mut command = std::process::Command::new("cargo.exe"); - let bindings = windows_bindgen::standalone_sys(&bindings); - std::fs::write("crates/libs/metadata/src/bindings.rs", bindings).unwrap(); + command.args([ + "run", + "-p", + "riddle", + "--", + "-etc", + "crates/tools/metadata/bindings.txt", + ]); + + assert!(command.status().unwrap().success()); } diff --git a/crates/tools/msvc/src/main.rs b/crates/tools/msvc/src/main.rs index e544010d59..f8189866cc 100644 --- a/crates/tools/msvc/src/main.rs +++ b/crates/tools/msvc/src/main.rs @@ -54,7 +54,11 @@ changes can sneak in undetected. } // Clear out timestamps in the resulting library. - let mut archive = std::fs::File::options().read(true).write(true).open(output.join("windows.lib")).unwrap(); + let mut archive = std::fs::File::options() + .read(true) + .write(true) + .open(output.join("windows.lib")) + .unwrap(); let len = archive.metadata().unwrap().len(); let mut header = [0u8; 8]; archive.read_exact(&mut header).unwrap(); @@ -64,7 +68,16 @@ changes can sneak in undetected. assert_eq!(archive.write(b"-1 ").unwrap(), 12); // replace archive timestamp let mut buf = [0u8; 32]; archive.read_exact(&mut buf).unwrap(); // remainder of the archive member header - let mut size = i64::from_str(std::str::from_utf8(buf[20..][..10].split(u8::is_ascii_whitespace).next().unwrap()).unwrap()).unwrap(); + let mut size = i64::from_str( + std::str::from_utf8( + buf[20..][..10] + .split(u8::is_ascii_whitespace) + .next() + .unwrap(), + ) + .unwrap(), + ) + .unwrap(); // The first two members are indexes, skip them. if num > 3 { archive.read_exact(&mut buf[..4]).unwrap(); // member header @@ -82,10 +95,21 @@ changes can sneak in undetected. std::fs::remove_dir_all(format!("crates/targets/{platform}/lib")).unwrap(); std::fs::create_dir_all(format!("crates/targets/{platform}/lib")).unwrap(); - std::fs::rename(output.join("windows.lib"), format!("crates/targets/{platform}/lib/windows.{}.lib", std::env!("CARGO_PKG_VERSION"))).unwrap(); + std::fs::rename( + output.join("windows.lib"), + format!( + "crates/targets/{platform}/lib/windows.{}.lib", + std::env!("CARGO_PKG_VERSION") + ), + ) + .unwrap(); } -fn build_library(output: &std::path::Path, library: &str, functions: &BTreeMap) { +fn build_library( + output: &std::path::Path, + library: &str, + functions: &BTreeMap, +) { // Note that we don't use set_extension as it confuses PathBuf when the library name includes a period. let mut path = std::path::PathBuf::from(output); diff --git a/crates/tools/riddle/Cargo.toml b/crates/tools/riddle/Cargo.toml index 7e83b92522..6e96bbe97e 100644 --- a/crates/tools/riddle/Cargo.toml +++ b/crates/tools/riddle/Cargo.toml @@ -9,3 +9,13 @@ repository = "https://github.com/microsoft/windows-rs" [dependencies] metadata = { package = "windows-metadata", path = "../../libs/metadata", version = "0.49.0" } +rayon = "1.7" + +[dependencies.syn] +version = "2.0" +features = ["full", "extra-traits"] + +[dependencies.proc-macro2] +version = "1.0" +features = ["span-locations"] + diff --git a/crates/tools/riddle/src/args.rs b/crates/tools/riddle/src/args.rs new file mode 100644 index 0000000000..b85b47641d --- /dev/null +++ b/crates/tools/riddle/src/args.rs @@ -0,0 +1,39 @@ +use crate::Result; + +pub fn from_process() -> Result> { + let mut result = vec![]; + from_iter(&mut result, std::env::args().skip(1))?; + Ok(result) +} + +fn from_string(result: &mut Vec, value: &str) -> Result<()> { + let value = if let Some((value, _)) = value.split_once('#') { + value + } else { + value + }; + + from_iter(result, value.split_whitespace().map(|arg| arg.to_string()))?; + Ok(()) +} + +fn from_iter>(result: &mut Vec, args: T) -> Result<()> { + let mut expand = false; + + for arg in args { + if arg.starts_with('-') { + expand = false; + } + if expand { + for args in crate::read_file_lines(&arg)? { + from_string(result, &args)?; + } + } else if arg == "-etc" { + expand = true; + } else { + result.push(arg); + } + } + + Ok(()) +} diff --git a/crates/tools/riddle/src/error.rs b/crates/tools/riddle/src/error.rs new file mode 100644 index 0000000000..94205c3033 --- /dev/null +++ b/crates/tools/riddle/src/error.rs @@ -0,0 +1,65 @@ +pub type Result = std::result::Result; + +#[derive(Default, Debug)] +pub struct Error { + message: String, + path: String, + span: Option<(usize, usize)>, +} + +impl std::error::Error for Error {} + +impl From for std::io::Error { + fn from(error: Error) -> Self { + std::io::Error::new(std::io::ErrorKind::Other, error.message.as_str()) + } +} + +impl From for Error { + fn from(error: syn::Error) -> Self { + let start = error.span().start(); + Self { + message: error.to_string(), + span: Some((start.line, start.column)), + ..Self::default() + } + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(fmt, "error: {}", self.message)?; + if !self.path.is_empty() { + if let Some((line, column)) = self.span { + writeln!(fmt, " --> {}:{line}:{column}", self.path)?; + } else { + writeln!(fmt, " --> {}", self.path)?; + } + } + Ok(()) + } +} + +impl Error { + pub fn new(message: &str) -> Self { + Self { + message: message.to_string(), + ..Self::default() + } + } + + pub fn with_path(self, path: &str) -> Self { + Self { + path: path.to_string(), + ..self + } + } + + pub fn with_span(self, span: proc_macro2::Span) -> Self { + let start = span.start(); + Self { + span: Some((start.line, start.column)), + ..self + } + } +} diff --git a/crates/tools/riddle/src/idl/mod.rs b/crates/tools/riddle/src/idl/mod.rs new file mode 100644 index 0000000000..a7d62e2a91 --- /dev/null +++ b/crates/tools/riddle/src/idl/mod.rs @@ -0,0 +1,268 @@ +mod to_idl; +mod to_winmd; +mod writer; +use crate::Result; +use syn::spanned::Spanned; +pub use to_idl::from_reader; + +impl File { + pub fn parse_str(input: &str) -> Result { + Ok(syn::parse_str::(input)?) + } + + // Note: this isn't called automatically by `parse_str` to avoid canonicalizing when we're merely formatting IDL. + pub fn canonicalize(&mut self) -> Result<()> { + // TODO maybe we rewrite the `File` here to resolve any `super` references and use declarations so that + // subsequently the idl-to-winmd conversion can just assume everything's fully qualified? + // * super can't refer to something outside of the IDL file + // * use declarations are only used for unqualified names that aren't defined in the IDL file + // * use declarations don't support globs and must name all externally defined types + // This way we can quickly kick out common invalid IDL files before we lost file/span context info + + Ok(()) + } + + pub fn fmt(&self) -> String { + writer::Writer::new(self).into_string() + } + + pub fn to_winmd(&self) -> Result> { + to_winmd::idl_to_winmd(self) + } +} + +// TODO: always set the winrt bit on the assembly but only set the winrt bit on the TypeDef if its a WinRT type. +// Also, use a file-level attribute in the IDL file to indicate whether it contains WinRT or Win32 types +// e.g. #![win32|winrt] - with default being winrt - that way Win32 and WinRT types could conceivably share a +// namespace but live in separate IDL files to simplify the IDL syntax. + +// The value of the IDL-specific memory representation is that it allows for constructs that are not modeled in the abstract Module +// tree such as the use declarations and if we get rid of it we'd always "format" IDL by stripping out any of that into a single +// canonical form which would not be very friendly to developers. +pub struct File { + pub references: Vec, + pub modules: Vec, +} + +// TODO: need to change these to unpack the syn types and store strings we can reference for efficiency along with spans since the syn +// is made for value semantics. + +#[derive(Clone)] +pub struct Module { + pub attributes: Vec, // winrt/win32 + pub name: String, + pub members: Vec, +} + +#[derive(Clone)] +pub enum ModuleMember { + Module(Module), + Interface(Interface), + Struct(Struct), + Enum(Enum), + Class(Class), + // Function and Delegate +} + +impl ModuleMember { + pub fn name(&self) -> &str { + match self { + Self::Module(module) => &module.name, + Self::Interface(member) => &member.name, + Self::Struct(member) => &member.name, + Self::Enum(member) => &member.name, + Self::Class(member) => &member.name, + } + } +} + +#[derive(Clone)] +pub struct Enum { + pub name: String, + pub item: syn::ItemEnum, +} + +#[derive(Clone)] +pub struct Struct { + pub name: String, + pub attributes: Vec, + pub span: proc_macro2::Span, + pub fields: Vec, +} + +#[derive(Clone)] +pub struct Field { + pub name: String, + pub attributes: Vec, + pub span: proc_macro2::Span, + pub ty: syn::Type, +} + +#[derive(Clone)] +pub struct Class { + pub name: String, + pub attributes: Vec, + pub extends: Vec, +} + +#[derive(Clone)] +pub struct Interface { + pub name: String, + pub attributes: Vec, + pub methods: Vec, +} + +syn::custom_keyword!(interface); +syn::custom_keyword!(class); + +impl syn::parse::Parse for File { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut references = vec![]; + let mut modules = vec![]; + while !input.is_empty() { + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![mod]) { + modules.push(input.parse()?); + } else if lookahead.peek(syn::Token![use]) { + references.push(input.parse()?); + } else { + return Err(lookahead.error()); + } + } + Ok(Self { + references, + modules, + }) + } +} + +impl syn::parse::Parse for Module { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let name = input.parse::()?.to_string(); + let content; + syn::braced!(content in input); + let mut members = vec![]; + while !content.is_empty() { + members.push(content.parse::()?); + } + Ok(Self { + attributes: vec![], + name, + members, + }) + } +} + +impl syn::parse::Parse for ModuleMember { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attributes: Vec = input.call(syn::Attribute::parse_outer)?; + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![mod]) { + if let Some(attribute) = attributes.first() { + return Err(syn::Error::new( + attribute.span(), + "module attributes not supported", + )); + } + Ok(ModuleMember::Module(input.parse()?)) + } else if lookahead.peek(interface) { + Ok(ModuleMember::Interface(Interface::parse( + attributes, input, + )?)) + } else if lookahead.peek(syn::Token![struct]) { + Ok(ModuleMember::Struct(Struct::parse(attributes, input)?)) + } else if lookahead.peek(syn::Token![enum]) { + Ok(ModuleMember::Enum(Enum::parse(attributes, input)?)) + } else if lookahead.peek(class) { + Ok(ModuleMember::Class(Class::parse(attributes, input)?)) + } else { + Err(lookahead.error()) + } + } +} + +impl Class { + fn parse(attributes: Vec, input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let name = input.parse::()?.to_string(); + let mut extends = Vec::new(); + + if input.peek(syn::Token![:]) { + input.parse::()?; + while input.peek(syn::Ident) { + extends.push(input.parse::()?); + _ = input.parse::(); + } + } + + input.parse::()?; + Ok(Self { + attributes, + name, + extends, + }) + } +} + +impl Interface { + fn parse(attributes: Vec, input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let name = input.parse::()?.to_string(); + let content; + syn::braced!(content in input); + let mut methods = vec![]; + while !content.is_empty() { + methods.push(content.parse::()?); + } + Ok(Self { + attributes, + name, + methods, + }) + } +} + +impl Struct { + fn parse(attributes: Vec, input: syn::parse::ParseStream) -> syn::Result { + // TODO: need to validate that the struct is valid according to the constraints of the winmd type system. + // Same for the other types. That way we can spit out errors quickly for things like unnamed fields. + let span = input.span(); + let item: syn::ItemStruct = input.parse()?; + let name = item.ident.to_string(); + let mut fields = vec![]; + + let syn::Fields::Named(named) = item.fields else { + return Err(syn::Error::new( + item.span(), + "unnamed fields not supported", + )); + }; + + for field in named.named { + fields.push(Field { + span: field.span(), + attributes: field.attrs, + // Simply unwrapping since we already know that it is a named field. + name: field.ident.unwrap().to_string(), + ty: field.ty, + }); + } + + Ok(Self { + name, + attributes, + span, + fields, + }) + } +} + +impl Enum { + fn parse(attributes: Vec, input: syn::parse::ParseStream) -> syn::Result { + let mut item: syn::ItemEnum = input.parse()?; + item.attrs = attributes; + let name = item.ident.to_string(); + Ok(Self { name, item }) + } +} diff --git a/crates/tools/riddle/src/idl/to_idl.rs b/crates/tools/riddle/src/idl/to_idl.rs new file mode 100644 index 0000000000..537c95ec7e --- /dev/null +++ b/crates/tools/riddle/src/idl/to_idl.rs @@ -0,0 +1,221 @@ +use crate::tokens::{quote, to_ident, TokenStream}; +use crate::{idl, Error, Result, Tree}; + +pub fn from_reader( + reader: &metadata::Reader, + filter: &metadata::Filter, + config: std::collections::BTreeMap<&str, &str>, + output: &str, +) -> Result<()> { + let writer = Writer::new(reader, filter); + + // TODO: do we need any configuration values for IDL generation? + // Maybe per-namespace IDL files for namespace-splitting - be sure to use + // the same key as for winmd generation. + + if let Some((key, _)) = config.first_key_value() { + return Err(Error::new(&format!("invalid configuration value: `{key}`"))); + } + + let tree = Tree::new(writer.reader, writer.filter); + let tokens = writer.tree(&tree); + let file = idl::File::parse_str(&tokens.into_string())?; + crate::write_to_file(output, file.fmt()) +} + +struct Writer<'a> { + reader: &'a metadata::Reader<'a>, + filter: &'a metadata::Filter<'a>, + namespace: &'a str, +} + +impl<'a> Writer<'a> { + fn new(reader: &'a metadata::Reader, filter: &'a metadata::Filter) -> Self { + Self { + reader, + filter, + namespace: "", + } + } + + fn with_namespace(&self, namespace: &'a str) -> Self { + Self { + reader: self.reader, + filter: self.filter, + namespace, + } + } + + fn tree(&self, tree: &'a Tree) -> TokenStream { + let modules = tree + .nested + .values() + .map(|tree| self.with_namespace(tree.namespace).tree(tree)); + + if tree.namespace.is_empty() { + quote! { #(#modules)* } + } else { + let name = to_ident( + tree.namespace + .rsplit_once('.') + .map_or(tree.namespace, |(_, name)| name), + ); + let types = self + .reader + .namespace_types(tree.namespace, self.filter) + .map(|def| self.type_def(def)); + + quote! { + mod #name { + #(#modules)* + #(#types)* + } + } + } + } + + fn type_def(&self, def: metadata::TypeDef) -> TokenStream { + if let Some(extends) = self.reader.type_def_extends(def) { + if extends.namespace == "System" { + if extends.name == "Enum" { + self.enum_def(def) + } else if extends.name == "ValueType" { + self.struct_def(def) + } else if extends.name == "MulticastDelegate" { + self.delegate_def(def) + } else { + self.class_def(def) + } + } else { + self.class_def(def) + } + } else { + self.interface_def(def) + } + } + + fn enum_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + + quote! { + struct #name { + + } + } + } + + fn struct_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + + let fields = self.reader.type_def_fields(def).map(|field| { + let name = to_ident(self.reader.field_name(field)); + let ty = self.ty(&self.reader.field_type(field, Some(def))); + quote! { + #name: #ty + } + }); + + quote! { + struct #name { + #(#fields),* + } + } + } + + fn delegate_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + + quote! { + struct #name { + + } + } + } + + fn class_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + + quote! { + struct #name { + + } + } + } + + fn interface_def(&self, def: metadata::TypeDef) -> TokenStream { + let name = to_ident(self.reader.type_def_name(def)); + + quote! { + struct #name { + + } + } + } + + fn ty(&self, ty: &metadata::Type) -> TokenStream { + match ty { + metadata::Type::Void => quote! { ::core::ffi::c_void }, + metadata::Type::Bool => quote! { bool }, + metadata::Type::Char => quote! { u16 }, + metadata::Type::I8 => quote! { i8 }, + metadata::Type::U8 => quote! { u8 }, + metadata::Type::I16 => quote! { i16 }, + metadata::Type::U16 => quote! { u16 }, + metadata::Type::I32 => quote! { i32 }, + metadata::Type::U32 => quote! { u32 }, + metadata::Type::I64 => quote! { i64 }, + metadata::Type::U64 => quote! { u64 }, + metadata::Type::F32 => quote! { f32 }, + metadata::Type::F64 => quote! { f64 }, + metadata::Type::ISize => quote! { isize }, + metadata::Type::USize => quote! { usize }, + metadata::Type::TypeDef(def, generics) => { + let namespace = self.namespace(self.reader.type_def_namespace(*def)); + let name = to_ident(self.reader.type_def_name(*def)); + if generics.is_empty() { + quote! { #namespace#name } + } else { + let generics = generics.iter().map(|ty| self.ty(ty)); + quote! { #namespace#name<#(#generics,)*> } + } + } + _ => todo!("{:?}", ty), + } + } + + fn namespace(&self, namespace: &str) -> TokenStream { + // TODO: handle nested structs? + if namespace.is_empty() || self.namespace == namespace { + quote! {} + } else { + let mut relative = self.namespace.split('.').peekable(); + let mut namespace = namespace.split('.').peekable(); + let mut related = false; + + while relative.peek() == namespace.peek() { + related = true; + + if relative.next().is_none() { + break; + } + + namespace.next(); + } + + let mut tokens = TokenStream::new(); + + if related { + for _ in 0..relative.count() { + tokens.push_str("super::"); + } + } + + for namespace in namespace { + tokens.push_str(namespace); + tokens.push_str("::"); + } + + tokens + } + } +} diff --git a/crates/tools/riddle/src/idl/to_winmd.rs b/crates/tools/riddle/src/idl/to_winmd.rs new file mode 100644 index 0000000000..d3278f4046 --- /dev/null +++ b/crates/tools/riddle/src/idl/to_winmd.rs @@ -0,0 +1,399 @@ +use crate::{idl, winmd, Result}; + +// TODO: store span in winmd so that errors resolving type references can be traced back to file/line/column +use std::collections::HashMap; +//use syn::spanned::Spanned; + +// TODO: this creates a temporary in-memory winmd used to treat the IDL content uniformly as metadata. +// The winmd_to_winmd does the harder job of validating and producing canonical winmd for public consumption. + +pub fn idl_to_winmd(file: &idl::File) -> Result> { + // Local-to-qualified type names found in use declaration - e.g. "IStringable" -> "Windows.Foundation.IStringable" + // This is just a convenience for the developer to shorten common references like this but would not support globs or renames. + // Note that none of these are verified to be real until much later when the winmd is validated since we don't + // know what other metadata may be combined + let mut _use_map = HashMap::::new(); + + // TODO: read file and populate use_map + + // Types are collected here in two passes - this allows us to figure out whether a local name points to a relative type + // or a type from a use declaration...? + let mut collector = HashMap::>::new(); + + file.modules + .iter() + .for_each(|module| collect_module(&mut collector, module, &module.name)); + + // TODO: collect type names into hashmap (phase 1) and just drop clones of the IDL members into the collector + + // TODO: Can we just walk the collector at this point and populate the winmd writer and thus need the read-phase? + // this second walking of the collector is basically the "define" phase + + let mut writer = winmd::Writer::new("temp.winmd"); + + collector.iter().for_each(|(namespace, members)| { + members + .iter() + .for_each(|(name, member)| write_member(&mut writer, namespace, name, member)) + }); + + Ok(writer.into_stream()) +} + +fn collect_module<'a>( + collector: &mut HashMap>, + module: &'a idl::Module, + namespace: &str, +) { + module + .members + .iter() + .for_each(|member| collect_member(collector, member, namespace)); +} + +fn collect_member<'a>( + collector: &mut HashMap>, + member: &'a idl::ModuleMember, + namespace: &str, +) { + match member { + idl::ModuleMember::Module(module) => { + collect_module(collector, module, &format!("{namespace}.{}", module.name)) + } + _ => { + _ = collector + .entry(namespace.to_string()) + .or_default() + .entry(member.name()) + .or_insert(member.clone()) + } + } +} + +fn write_member( + writer: &mut winmd::Writer, + namespace: &str, + name: &str, + member: &idl::ModuleMember, +) { + match member { + idl::ModuleMember::Interface(member) => write_interface(writer, namespace, name, member), + idl::ModuleMember::Struct(member) => write_struct(writer, namespace, name, member), + idl::ModuleMember::Enum(member) => write_enum(writer, namespace, name, member), + idl::ModuleMember::Class(member) => write_class(writer, namespace, name, member), + idl::ModuleMember::Module(_) => {} // modules have already been flattened but rustc doesn't know this + } +} + +fn write_interface( + writer: &mut winmd::Writer, + namespace: &str, + name: &str, + _member: &idl::Interface, +) { + let flags = metadata::TypeAttributes::Public + | metadata::TypeAttributes::Interface + | metadata::TypeAttributes::WindowsRuntime + | metadata::TypeAttributes::Abstract; + + writer.tables.TypeDef.push(winmd::TypeDef { + Extends: 0, + FieldList: 0, + Flags: flags.0, + MethodList: writer.tables.MethodDef.len() as _, + TypeName: writer.strings.insert(name), + TypeNamespace: writer.strings.insert(namespace), + }); +} + +fn write_struct(writer: &mut winmd::Writer, namespace: &str, name: &str, member: &idl::Struct) { + let flags = metadata::TypeAttributes::Public + | metadata::TypeAttributes::WindowsRuntime + | metadata::TypeAttributes::Sealed + | metadata::TypeAttributes::Import + | metadata::TypeAttributes::SequentialLayout; + + let extends = writer.insert_type_ref("System", "ValueType"); + + writer.tables.TypeDef.push(winmd::TypeDef { + Extends: extends, + FieldList: writer.tables.Field.len() as _, + Flags: flags.0, + MethodList: 0, + TypeName: writer.strings.insert(name), + TypeNamespace: writer.strings.insert(namespace), + }); + + for field in &member.fields { + let ty = syn_type(namespace, &field.ty); + let signature = writer.insert_field_sig(&ty); + + writer.tables.Field.push(winmd::Field { + Flags: 0, + Name: writer.strings.insert(&field.name), + Signature: signature, + }); + } +} + +fn write_enum(_writer: &mut winmd::Writer, _namespace: &str, _name: &str, _member: &idl::Enum) {} + +fn write_class(_writer: &mut winmd::Writer, _namespace: &str, _name: &str, _member: &idl::Class) {} + +// fn idl_interface(writer: &mut winmd::Writer, _file: &idl::File, ty: &idl::Interface, namespace: &str, phase: ReadPhase) -> Result<()> { +// let ident = ty.ident.to_string(); + +// match phase { +// ReadPhase::Index => { +// self.insert(namespace, 0).types.entry(ident).or_default(); +// } +// ReadPhase::Define => { +// let flags = TypeAttributes::Public | TypeAttributes::Interface | TypeAttributes::WindowsRuntime | TypeAttributes::Abstract; +// let mut def = TypeDef { flags, extends: None, ..Default::default() }; + +// for method in &ty.methods { +// let name = method.sig.ident.to_string(); +// let mut params = vec![]; + +// for input in &method.sig.inputs { +// let syn::FnArg::Typed(pat_type) = input else { +// todo!(); +// }; + +// let syn::Pat::Ident(ref pat_ident) = *pat_type.pat else { +// todo!(); +// }; + +// let name = pat_ident.ident.to_string(); +// let ty = self.read_ty(namespace, &pat_type.ty)?; +// params.push(Param { name, ty, ..Default::default() }); +// } + +// let ty = if let syn::ReturnType::Type(_, ty) = &method.sig.output { self.read_ty(namespace, ty)? } else { Type::Void }; +// let return_type = Param { ty, ..Default::default() }; +// let flags = MethodAttributes::Public; + +// def.methods.push(Method { flags, name, params, return_type, ..Default::default() }); +// } + +// self.insert(namespace, 0).types.entry(ident).or_default().push(def); +// } +// } + +// Ok(()) +// } + +// fn idl_struct(writer: &mut winmd::Writer, _file: &idl::File, ty: &idl::Struct, namespace: &str, phase: ReadPhase) -> Result<()> { +// let ident = ty.item.ident.to_string(); + +// match phase { +// ReadPhase::Index => { +// self.insert(namespace, 0).types.entry(ident).or_default(); +// } +// ReadPhase::Define => { +// let flags = TypeAttributes::Public | TypeAttributes::WindowsRuntime | TypeAttributes::Sealed | TypeAttributes::Import | TypeAttributes::SequentialLayout; +// let mut def = TypeDef { flags, extends: Some(TypeRef { namespace: "System".to_string(), name: "ValueType".to_string(), ..Default::default() }), ..Default::default() }; + +// let syn::Fields::Named(fields) = &ty.item.fields else { +// unimplemented!(); +// }; + +// for field in &fields.named { +// let Some(ref ident) = field.ident else { +// unimplemented!(); +// }; + +// let flags = FieldAttributes::Public; +// let name = ident.to_string(); +// let ty = self.read_ty(namespace, &field.ty)?; +// def.fields.push(Field { flags, name, ty, ..Default::default() }); +// } + +// self.insert(namespace, 0).types.entry(ident).or_default().push(def); +// } +// } + +// Ok(()) +// } + +// fn idl_enum(writer: &mut winmd::Writer, _file: &idl::File, ty: &idl::Enum, namespace: &str, phase: ReadPhase) -> Result<()> { +// let ident = ty.item.ident.to_string(); + +// match phase { +// ReadPhase::Index => { +// self.insert(namespace, 0).types.entry(ident).or_default(); +// } +// ReadPhase::Define => { +// let mut def = TypeDef { extends: Some(TypeRef { namespace: "System".to_string(), name: "Enum".to_string(), ..Default::default() }), ..Default::default() }; +// let enum_type = Type::TypeRef(TypeRef { namespace: namespace.to_string(), name: ident.clone(), ..Default::default() }); + +// for variant in &ty.item.variants { +// if let Some((_, expr)) = &variant.discriminant { +// let flags = FieldAttributes::Public; +// let name = variant.ident.to_string(); +// let value = self.read_expr(expr, false)?; + +// def.fields.push(Field { flags, name, ty: enum_type.clone(), value: Some(value) }); +// } +// } + +// self.insert(namespace, 0).types.entry(ident).or_default().push(def); +// } +// } + +// Ok(()) +// } + +// fn idl_class(writer: &mut winmd::Writer, _file: &idl::File, ty: &idl::Class, namespace: &str, _phase: ReadPhase) -> Result<()> { +// let ident = ty.ident.to_string(); +// self.insert(namespace, 0).types.entry(ident).or_default(); +// Ok(()) +// } + +// fn syn_expr(writer: &mut winmd::Writer, expr: &syn::Expr, neg: bool) -> Result { +// match expr { +// syn::Expr::Lit(lit) => self.read_expr_lit(lit, neg), +// syn::Expr::Unary(unary) => self.read_expr_unary(unary), +// _ => todo!("{:?}", expr), +// } +// } + +// fn syn_expr_unary(writer: &mut winmd::Writer, unary: &syn::ExprUnary) -> Result { +// self.read_expr(&unary.expr, true) +// } + +// fn syn_expr_lit(writer: &mut winmd::Writer, expr: &syn::ExprLit, neg: bool) -> Result { +// self.read_lit(&expr.lit, neg) +// } + +// fn syn_lit(writer: &mut winmd::Writer, lit: &syn::Lit, neg: bool) -> Result { +// match lit { +// syn::Lit::Int(lit) => self.read_lit_int(lit, neg), +// syn::Lit::Str(lit) => self.read_lit_str(lit), +// _ => todo!("{:?}", lit), +// } +// } + +// fn syn_lit_str(writer: &mut winmd::Writer, lit: &syn::LitStr) -> Result { +// Ok(Value::String(lit.value())) +// } + +// fn syn_lit_int(writer: &mut winmd::Writer, lit: &syn::LitInt, neg: bool) -> Result { +// fn parse(lit: &syn::LitInt, neg: bool) -> Result { +// let raw = if neg { format!("-{}", lit.base10_digits()) } else { lit.base10_digits().to_string() }; +// raw.parse().map_err(|_| Error::new("failed to parse literal").with_span(lit.span())) +// } + +// match lit.suffix() { +// "i8" => Ok(Value::I8(parse(lit, neg)?)), +// "u8" => Ok(Value::U8(parse(lit, neg)?)), +// "i16" => Ok(Value::I16(parse(lit, neg)?)), +// "u16" => Ok(Value::U16(parse(lit, neg)?)), +// "i32" => Ok(Value::I32(parse(lit, neg)?)), +// "u32" => Ok(Value::U32(parse(lit, neg)?)), +// "i64" => Ok(Value::I64(parse(lit, neg)?)), +// "u64" => Ok(Value::U64(parse(lit, neg)?)), +// suffix => todo!("suffix {:?}", suffix), +// } +// } + +fn syn_type(namespace: &str, ty: &syn::Type) -> winmd::Type { + match ty { + syn::Type::Path(ty) => syn_type_path(namespace, ty), + syn::Type::Ptr(ptr) => syn_type_ptr(namespace, ptr), + syn::Type::Array(array) => syn_type_array(namespace, array), + _ => unimplemented!(), + } +} + +fn syn_type_array(namespace: &str, array: &syn::TypeArray) -> winmd::Type { + let ty = syn_type(namespace, &array.elem); + + if let syn::Expr::Lit(lit) = &array.len { + if let syn::Lit::Int(lit) = &lit.lit { + if let Ok(len) = lit.base10_parse() { + return ty.into_array(len); + } + } + } + + unimplemented!() +} + +fn syn_type_ptr(namespace: &str, ptr: &syn::TypePtr) -> winmd::Type { + let ty = syn_type(namespace, &ptr.elem); + if ptr.mutability.is_some() { + ty.into_mut_ptr() + } else { + ty.into_const_ptr() + } +} + +fn syn_type_path(namespace: &str, ty: &syn::TypePath) -> winmd::Type { + if ty.qself.is_none() { + return syn_path(namespace, &ty.path); + } + + unimplemented!() +} + +fn syn_path(namespace: &str, path: &syn::Path) -> winmd::Type { + if let Some(segment) = path.segments.first() { + if path.segments.len() == 1 { + let name = segment.ident.to_string(); + + return match name.as_str() { + "void" => winmd::Type::Void, + "bool" => winmd::Type::Bool, + "char" => winmd::Type::Char, + "i8" => winmd::Type::I8, + "u8" => winmd::Type::U8, + "i16" => winmd::Type::I16, + "u16" => winmd::Type::U16, + "i32" => winmd::Type::I32, + "u32" => winmd::Type::U32, + "i64" => winmd::Type::I64, + "u64" => winmd::Type::U64, + "f32" => winmd::Type::F32, + "f64" => winmd::Type::F64, + "isize" => winmd::Type::ISize, + "usize" => winmd::Type::USize, + "HSTRING" => winmd::Type::String, + "GUID" => winmd::Type::GUID, + "IUnknown" => winmd::Type::IUnknown, + "IInspectable" => winmd::Type::IInspectable, + "HRESULT" => winmd::Type::HRESULT, + "PSTR" => winmd::Type::PSTR, + "PWSTR" => winmd::Type::PWSTR, + "PCSTR" => winmd::Type::PCSTR, + "PCWSTR" => winmd::Type::PCWSTR, + "BSTR" => winmd::Type::BSTR, + _ => winmd::Type::TypeRef(winmd::TypeName { + namespace: namespace.to_string(), + name, + generics: vec![], + }), + }; + } + } + + // TODO: Here we assume that paths are absoluate since there's now way to disambiguate between nested and absoluate paths + // The canonicalize function preprocesses the IDL to make this work + + let mut builder = vec![]; + + for segment in &path.segments { + let segment = segment.ident.to_string(); + builder.push(segment); + } + + // Unwrapping as there are more one segments + let (name, namespace) = builder.split_last().unwrap(); + let namespace = namespace.join("."); + + winmd::Type::TypeRef(winmd::TypeName { + namespace, + name: name.to_string(), + generics: vec![], + }) +} diff --git a/crates/libs/metadata/src/writer/idl/format.rs b/crates/tools/riddle/src/idl/writer.rs similarity index 81% rename from crates/libs/metadata/src/writer/idl/format.rs rename to crates/tools/riddle/src/idl/writer.rs index 7f84a8de7a..6f0adc0191 100644 --- a/crates/libs/metadata/src/writer/idl/format.rs +++ b/crates/tools/riddle/src/idl/writer.rs @@ -1,23 +1,21 @@ -use super::*; - -pub fn format_idl(source: &str) -> Result { - let mut printer = Printer::new(); - printer.idl_file(&IdlFile::parse_str(source)?); - Ok(printer.out) - // TODO: workaround until we have complete formatting support - //Ok(source.to_string()) -} +use crate::idl; #[derive(Default)] -struct Printer { +pub struct Writer { out: String, indent: usize, newline: bool, } -impl Printer { - fn new() -> Self { - Self::default() +impl Writer { + pub fn new(file: &idl::File) -> Self { + let mut writer = Self::default(); + writer.idl_file(file); + writer + } + + pub fn into_string(self) -> String { + self.out } fn word(&mut self, value: &str) { @@ -36,7 +34,7 @@ impl Printer { self.newline = true; } - fn idl_file(&mut self, file: &IdlFile) { + fn idl_file(&mut self, file: &idl::File) { for reference in &file.references { self.item_use(reference); } @@ -46,9 +44,9 @@ impl Printer { } } - fn idl_module(&mut self, module: &IdlModule) { + fn idl_module(&mut self, module: &idl::Module) { self.word("mod "); - self.ident(&module.ident); + self.word(&module.name); self.word(" {"); self.newline(); self.indent += 1; @@ -63,20 +61,20 @@ impl Printer { self.word("}"); } - fn idl_module_member(&mut self, member: &IdlModuleMember) { + fn idl_module_member(&mut self, member: &idl::ModuleMember) { match member { - IdlModuleMember::Module(member) => self.idl_module(member), - IdlModuleMember::Interface(member) => self.idl_interface(member), - IdlModuleMember::Struct(member) => self.idl_struct(member), - IdlModuleMember::Enum(member) => self.idl_enum(member), - IdlModuleMember::Class(member) => self.idl_class(member), + idl::ModuleMember::Module(member) => self.idl_module(member), + idl::ModuleMember::Interface(member) => self.idl_interface(member), + idl::ModuleMember::Struct(member) => self.idl_struct(member), + idl::ModuleMember::Enum(member) => self.idl_enum(member), + idl::ModuleMember::Class(member) => self.idl_class(member), } } - fn idl_interface(&mut self, member: &IdlInterface) { + fn idl_interface(&mut self, member: &idl::Interface) { self.attrs(&member.attributes); self.word("interface "); - self.ident(&member.ident); + self.word(&member.name); self.word(" {"); self.newline(); self.indent += 1; @@ -120,21 +118,21 @@ impl Printer { self.word(")"); } - fn idl_struct(&mut self, member: &IdlStruct) { - self.attrs(&member.item.attrs); + fn idl_struct(&mut self, member: &idl::Struct) { + self.attrs(&member.attributes); self.word("struct "); - self.ident(&member.item.ident); + self.word(&member.name); self.word(" {"); self.newline(); self.indent += 1; - if let syn::Fields::Named(fields) = &member.item.fields { - for field in &fields.named { - self.field(field); - self.word(","); - self.newline(); - } + for field in &member.fields { + self.word(&field.name); + self.word(": "); + self.ty(&field.ty); + self.word(","); + self.newline(); } self.indent -= 1; @@ -142,7 +140,7 @@ impl Printer { self.word("}"); } - fn idl_enum(&mut self, member: &IdlEnum) { + fn idl_enum(&mut self, member: &idl::Enum) { self.attrs(&member.item.attrs); self.word("enum "); @@ -166,7 +164,7 @@ impl Printer { self.word("}"); } - fn idl_class(&mut self, _member: &IdlClass) {} + fn idl_class(&mut self, _member: &idl::Class) {} fn trait_item_fn(&mut self, method: &syn::TraitItemFn) { self.attrs(&method.attrs); @@ -219,14 +217,6 @@ impl Printer { self.ident(&pat_ident.ident); } - fn field(&mut self, field: &syn::Field) { - if let Some(ref ident) = field.ident { - self.ident(ident); - self.word(": "); - self.ty(&field.ty); - } - } - fn ty(&mut self, ty: &syn::Type) { match ty { syn::Type::Path(ty) => self.type_path(ty), diff --git a/crates/tools/riddle/src/main.rs b/crates/tools/riddle/src/main.rs index 76623ad2c5..f4be1d26a0 100644 --- a/crates/tools/riddle/src/main.rs +++ b/crates/tools/riddle/src/main.rs @@ -1,10 +1,23 @@ -use metadata::*; +#![allow(dead_code)] + +mod args; +mod error; +mod idl; +mod rust; +mod tokens; +mod tree; +mod winmd; + +use error::{Error, Result}; +use tree::Tree; enum ArgKind { None, Input, Output, Filter, + Config, + Etc, } fn main() { @@ -16,7 +29,7 @@ fn main() { fn run() -> Result<()> { let time = std::time::Instant::now(); - let args: Vec<_> = std::env::args().skip(1).collect(); + let args = args::from_process()?; if args.is_empty() { println!( @@ -27,6 +40,8 @@ Options: -out Path to .winmd or .idl file to generate -filter Namespaces to include or !exclude in output -format Format .idl files only + -config Override a configuration value + -etc File containing command line options "# ); return Ok(()); @@ -37,6 +52,7 @@ Options: let mut input = Vec::<&str>::new(); let mut include = Vec::<&str>::new(); let mut exclude = Vec::<&str>::new(); + let mut config = std::collections::BTreeMap::<&str, &str>::new(); let mut format = false; for arg in &args { @@ -48,7 +64,9 @@ Options: ArgKind::None => match arg.as_str() { "-in" => kind = ArgKind::Input, "-out" => kind = ArgKind::Output, + "-etc" => kind = ArgKind::Etc, "-filter" => kind = ArgKind::Filter, + "-config" => kind = ArgKind::Config, "-format" => format = true, _ => return Err(Error::new(&format!("invalid option: `{arg}`"))), }, @@ -59,6 +77,10 @@ Options: return Err(Error::new("too many outputs")); } } + ArgKind::Etc => { + // TODO: Move arg reader to a separate type so we can recursively call it here + // with the contents of the file. + } ArgKind::Input => input.push(arg.as_str()), ArgKind::Filter => { if let Some(rest) = arg.strip_prefix('!') { @@ -67,53 +89,83 @@ Options: include.push(arg.as_str()); } } + ArgKind::Config => { + if let Some((key, value)) = arg.split_once('=') { + config.insert(key, value); + } else { + config.insert(arg, ""); + } + } } } if format { if output.is_some() || !include.is_empty() || !exclude.is_empty() { - return Err(Error::new("-format cannot be combined with -output, -include, or -exclude")); + return Err(Error::new( + "-format cannot be combined with -output, -include, or -exclude", + )); } - let input = filter_input(input, &["idl"])?; + let input = filter_input(&input, &["idl"])?; if input.is_empty() { return Err(Error::new("no .idl inputs")); } for path in &input { - let source = read_to_string(path)?; - write_to_file(path, writer::format_idl(&source).map_err(|error| error.with_path(path))?)?; + read_file_text(path) + .and_then(|source| idl::File::parse_str(&source)) + .and_then(|file| write_to_file(path, file.fmt())) + .map_err(|_| Error::new("failed to format .idl file").with_path(path))?; } return Ok(()); } - let input = filter_input(input, &["winmd", "idl"])?; - - if input.is_empty() { - return Err(Error::new("no inputs")); - } - let Some(output) = output else { return Err(Error::new("no output")); }; - let filter = reader::Filter::new(&include, &exclude); - let module = writer::Module::read(&input, &filter)?; + // This isn't strictly necessary but avoids a common newbie pitfall where all metadata + // would be generated when building a component for a specific API. + if include.is_empty() { + return Err(Error::new("at least one filter must be specified")); + } + + let output = canonicalize(output)?; - module.validate()?; - module.write(output)?; + let input = read_input(&input)?; + let reader = metadata::Reader::new(&input); + let filter = metadata::Filter::new(&include, &exclude); + + match extension(&output) { + "idl" => idl::from_reader(&reader, &filter, config, &output)?, + "winmd" => winmd::from_reader(&reader, &filter, config, &output)?, + "rs" => rust::from_reader(&reader, &filter, config, &output)?, + _ => return Err(Error::new("output extension must be one of winmd/idl/rs")), + } + + let elapsed = time.elapsed().as_secs_f32(); + + if elapsed > 0.1 { + println!( + " Finished writing `{}` in {:.2}s", + output, + time.elapsed().as_secs_f32() + ); + } else { + println!(" Finished writing `{}`", output,); + } - println!(" Finished writing `{}` in {:.2}s", canonicalize(output)?, time.elapsed().as_secs_f32()); Ok(()) } -fn filter_input(input: Vec<&str>, filter: &[&str]) -> Result> { - fn try_push(path: &str, filter: &[&str], results: &mut Vec) -> Result<()> { +fn filter_input(input: &[&str], extensions: &[&str]) -> Result> { + fn try_push(path: &str, extensions: &[&str], results: &mut Vec) -> Result<()> { + // First canonicalize input so that the extension check below will match the case of the path. let path = canonicalize(path)?; - if filter.contains(&extension(&path).1) { + if extensions.contains(&extension(&path)) { results.push(path); } @@ -122,20 +174,135 @@ fn filter_input(input: Vec<&str>, filter: &[&str]) -> Result> { let mut results = vec![]; - for input in &input { + for input in input { let path = std::path::Path::new(input); + if !path.exists() { + return Err(Error::new("failed to read input").with_path(input)); + } + if path.is_dir() { - for entry in path.read_dir().map_err(|_| Error::new("failed to read directory").with_path(input))?.flatten() { + for entry in path + .read_dir() + .map_err(|_| Error::new("failed to read directory").with_path(input))? + .flatten() + { let path = entry.path(); if path.is_file() { - try_push(&path.to_string_lossy(), filter, &mut results)?; + try_push(&path.to_string_lossy(), extensions, &mut results)?; } } } else { - try_push(&path.to_string_lossy(), filter, &mut results)?; + try_push(&path.to_string_lossy(), extensions, &mut results)?; } } Ok(results) } + +fn read_input(input: &[&str]) -> Result> { + let input = filter_input(input, &["winmd", "idl"])?; + + if input.is_empty() { + return Err(Error::new("no inputs")); + } + + let mut results = vec![]; + + for input in &input { + let file = if extension(input) == "winmd" { + read_winmd_file(input)? + } else { + read_idl_file(input)? + }; + + results.push(file); + } + + Ok(results) +} + +fn read_file_text(path: &str) -> Result { + std::fs::read_to_string(path).map_err(|_| Error::new("failed to read text file")) +} + +fn read_file_bytes(path: &str) -> Result> { + std::fs::read(path).map_err(|_| Error::new("failed to read binary file")) +} + +fn read_file_lines(path: &str) -> Result> { + use std::io::BufRead; + fn error(path: &str) -> Error { + Error::new("failed to read lines").with_path(path) + } + let file = std::io::BufReader::new(std::fs::File::open(path).map_err(|_| error(path))?); + let mut lines = vec![]; + for line in file.lines() { + lines.push(line.map_err(|_| error(path))?); + } + Ok(lines) +} + +fn read_idl_file(path: &str) -> Result { + read_file_text(path) + .and_then(|source| idl::File::parse_str(&source)) + .and_then(|file| file.to_winmd()) + .map(|bytes| { + // Write bytes to file if you need to debug the intermediate .winmd file like so: + _ = write_to_file("temp.winmd", &bytes); + + // Unwrapping here is fine since `idl_to_winmd` should have produced a valid winmd + metadata::File::new(bytes).unwrap() + }) + .map_err(|err| err.with_path(path)) +} + +fn read_winmd_file(path: &str) -> Result { + read_file_bytes(path).and_then(|bytes| { + metadata::File::new(bytes) + .ok_or_else(|| Error::new("failed to read .winmd format").with_path(path)) + }) +} + +fn write_to_file>(path: &str, contents: C) -> Result<()> { + if let Some(parent) = std::path::Path::new(path).parent() { + std::fs::create_dir_all(parent) + .map_err(|_| Error::new("failed to create directory").with_path(path))?; + } + + std::fs::write(path, contents).map_err(|_| Error::new("failed to write file").with_path(path)) +} + +fn canonicalize(path: &str) -> Result { + if !std::path::Path::new(path).exists() { + write_to_file(path, "")?; + } + + let path = std::fs::canonicalize(path) + .map_err(|_| Error::new("failed to find path").with_path(path))?; + + let path = path + .to_string_lossy() + .trim_start_matches(r#"\\?\"#) + .to_string(); + + match path.rsplit_once('.') { + Some((file, extension)) => Ok(format!("{file}.{}", extension.to_lowercase())), + _ => Ok(path), + } +} + +fn extension(path: &str) -> &str { + path.rsplit_once('.').map_or("", |(_, extension)| extension) +} + +fn directory(path: &str) -> &str { + path.rsplit_once(&['/', '\\']) + .map_or("", |(directory, _)| directory) +} + +fn explode(path: &str) -> (&str, &str, &str) { + let (directory, file_name) = path.rsplit_once(&['/', '\\']).unwrap_or(("", path)); + let (file_name, extension) = file_name.rsplit_once('.').unwrap_or((file_name, "")); + (directory, file_name, extension) +} diff --git a/crates/libs/bindgen/src/classes.rs b/crates/tools/riddle/src/rust/classes.rs similarity index 95% rename from crates/libs/bindgen/src/classes.rs rename to crates/tools/riddle/src/rust/classes.rs index 8d95bbc4e1..1c089f9d1e 100644 --- a/crates/libs/bindgen/src/classes.rs +++ b/crates/tools/riddle/src/rust/classes.rs @@ -21,9 +21,7 @@ fn gen_class(gen: &Gen, def: TypeDef) -> TokenStream { } let name = to_ident(gen.reader.type_def_name(def)); - let interfaces = gen - .reader - .type_interfaces(&Type::TypeDef((def, Vec::new()))); + let interfaces = gen.reader.type_interfaces(&Type::TypeDef(def, Vec::new())); let mut methods = quote! {}; let mut method_names = MethodNames::new(); @@ -32,7 +30,7 @@ fn gen_class(gen: &Gen, def: TypeDef) -> TokenStream { let features = gen.cfg_features(&cfg); for interface in &interfaces { - if let Type::TypeDef((def, generics)) = &interface.ty { + if let Type::TypeDef(def, generics) = &interface.ty { let mut virtual_names = MethodNames::new(); for method in gen.reader.type_def_methods(*def) { @@ -51,12 +49,12 @@ fn gen_class(gen: &Gen, def: TypeDef) -> TokenStream { let factories = interfaces.iter().filter_map(|interface| match interface.kind { InterfaceKind::Static => { - if let Type::TypeDef((def, generics)) = &interface.ty { + if let Type::TypeDef(def, generics) = &interface.ty { if gen.reader.type_def_methods(*def).next().is_some() { let interface_type = gen.type_name(&interface.ty); let features = gen.cfg_features(&gen.reader.type_def_cfg(*def, generics)); - let hidden = if gen.doc { + let hidden = if gen.package { quote! { #[doc(hidden)] } } else { quote! {} diff --git a/crates/libs/bindgen/src/com_methods.rs b/crates/tools/riddle/src/rust/com_methods.rs similarity index 100% rename from crates/libs/bindgen/src/com_methods.rs rename to crates/tools/riddle/src/rust/com_methods.rs diff --git a/crates/libs/bindgen/src/constants.rs b/crates/tools/riddle/src/rust/constants.rs similarity index 98% rename from crates/libs/bindgen/src/constants.rs rename to crates/tools/riddle/src/rust/constants.rs index 1a08db0aeb..1e4333a104 100644 --- a/crates/libs/bindgen/src/constants.rs +++ b/crates/tools/riddle/src/rust/constants.rs @@ -90,7 +90,7 @@ fn initializer(gen: &Gen, def: Field) -> Option { let mut input = value.as_str(); - let Type::TypeDef((def, _)) = gen.reader.field_type(def, None) else { + let Type::TypeDef(def, _) = gen.reader.field_type(def, None) else { unimplemented!(); }; @@ -114,7 +114,7 @@ fn field_initializer<'a>(gen: &Gen, field: Field, input: &'a str) -> (TokenStrea let value = gen.guid(&GUID::from_string_args(&literals)); (quote! { #name: #value, }, rest) } - Type::Win32Array((_, len)) => { + Type::Win32Array(_, len) => { let (literals, rest) = read_literal_array(input, len); let literals = literals.iter().map(|literal| TokenStream::from(*literal)); (quote! { #name: [#(#literals,)*], }, rest) diff --git a/crates/libs/bindgen/src/delegates.rs b/crates/tools/riddle/src/rust/delegates.rs similarity index 100% rename from crates/libs/bindgen/src/delegates.rs rename to crates/tools/riddle/src/rust/delegates.rs diff --git a/crates/libs/bindgen/src/enums.rs b/crates/tools/riddle/src/rust/enums.rs similarity index 99% rename from crates/libs/bindgen/src/enums.rs rename to crates/tools/riddle/src/rust/enums.rs index d241761968..5d744bbf06 100644 --- a/crates/libs/bindgen/src/enums.rs +++ b/crates/tools/riddle/src/rust/enums.rs @@ -68,7 +68,7 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { #(#fields)* } }); - } else if !gen.standalone { + } else if gen.package { if gen.sys { let fields = fields.iter().map(|(field_name, value)| { quote! { diff --git a/crates/libs/bindgen/src/extensions/impl/Foundation/Collections/Iterable.rs b/crates/tools/riddle/src/rust/extensions/impl/Foundation/Collections/Iterable.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/impl/Foundation/Collections/Iterable.rs rename to crates/tools/riddle/src/rust/extensions/impl/Foundation/Collections/Iterable.rs diff --git a/crates/libs/bindgen/src/extensions/impl/Foundation/Collections/MapView.rs b/crates/tools/riddle/src/rust/extensions/impl/Foundation/Collections/MapView.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/impl/Foundation/Collections/MapView.rs rename to crates/tools/riddle/src/rust/extensions/impl/Foundation/Collections/MapView.rs diff --git a/crates/libs/bindgen/src/extensions/impl/Foundation/Collections/VectorView.rs b/crates/tools/riddle/src/rust/extensions/impl/Foundation/Collections/VectorView.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/impl/Foundation/Collections/VectorView.rs rename to crates/tools/riddle/src/rust/extensions/impl/Foundation/Collections/VectorView.rs diff --git a/crates/libs/bindgen/src/extensions/mod.rs b/crates/tools/riddle/src/rust/extensions/mod.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod.rs rename to crates/tools/riddle/src/rust/extensions/mod.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Matrix3x2.rs b/crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Matrix3x2.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Matrix3x2.rs rename to crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Matrix3x2.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Matrix4x4.rs b/crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Matrix4x4.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Matrix4x4.rs rename to crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Matrix4x4.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Vector2.rs b/crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Vector2.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Vector2.rs rename to crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Vector2.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Vector3.rs b/crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Vector3.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Vector3.rs rename to crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Vector3.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Vector4.rs b/crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Vector4.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Foundation/Numerics/Vector4.rs rename to crates/tools/riddle/src/rust/extensions/mod/Foundation/Numerics/Vector4.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Foundation/TimeSpan.rs b/crates/tools/riddle/src/rust/extensions/mod/Foundation/TimeSpan.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Foundation/TimeSpan.rs rename to crates/tools/riddle/src/rust/extensions/mod/Foundation/TimeSpan.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Foundation/BOOL.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/BOOL.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Foundation/BOOL.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/BOOL.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Foundation/BOOLEAN.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/BOOLEAN.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Foundation/BOOLEAN.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/BOOLEAN.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Foundation/NTSTATUS.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/NTSTATUS.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Foundation/NTSTATUS.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/NTSTATUS.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Foundation/VARIANT_BOOL.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/VARIANT_BOOL.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Foundation/VARIANT_BOOL.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/VARIANT_BOOL.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Foundation/WIN32_ERROR.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/WIN32_ERROR.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Foundation/WIN32_ERROR.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Foundation/WIN32_ERROR.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/IN6_ADDR.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/IN6_ADDR.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/IN6_ADDR.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/IN6_ADDR.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/IN_ADDR.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/IN_ADDR.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/IN_ADDR.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/IN_ADDR.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN6.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN6.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN6.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/SOCKADDR_IN6.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/SOCKADDR_INET.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/SOCKADDR_INET.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/Networking/WinSock/SOCKADDR_INET.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/Networking/WinSock/SOCKADDR_INET.rs diff --git a/crates/libs/bindgen/src/extensions/mod/Win32/UI/WindowsAndMessaging/WindowLong.rs b/crates/tools/riddle/src/rust/extensions/mod/Win32/UI/WindowsAndMessaging/WindowLong.rs similarity index 100% rename from crates/libs/bindgen/src/extensions/mod/Win32/UI/WindowsAndMessaging/WindowLong.rs rename to crates/tools/riddle/src/rust/extensions/mod/Win32/UI/WindowsAndMessaging/WindowLong.rs diff --git a/crates/libs/bindgen/src/functions.rs b/crates/tools/riddle/src/rust/functions.rs similarity index 99% rename from crates/libs/bindgen/src/functions.rs rename to crates/tools/riddle/src/rust/functions.rs index a5eb9181d9..4a31186516 100644 --- a/crates/libs/bindgen/src/functions.rs +++ b/crates/tools/riddle/src/rust/functions.rs @@ -272,7 +272,7 @@ fn handle_last_error(gen: &Gen, def: MethodDef, signature: &Signature) -> bool { .impl_map_flags(map) .contains(PInvokeAttributes::SupportsLastError) { - if let Type::TypeDef((return_type, _)) = &signature.return_type { + if let Type::TypeDef(return_type, _) = &signature.return_type { if gen.reader.type_def_is_handle(*return_type) { if gen .reader diff --git a/crates/libs/bindgen/src/gen.rs b/crates/tools/riddle/src/rust/gen.rs similarity index 96% rename from crates/libs/bindgen/src/gen.rs rename to crates/tools/riddle/src/rust/gen.rs index 6674857a10..4648969dba 100644 --- a/crates/libs/bindgen/src/gen.rs +++ b/crates/tools/riddle/src/rust/gen.rs @@ -1,25 +1,37 @@ use super::*; +// TODO: rename to Writer for consistency +#[derive(Clone)] pub struct Gen<'a> { pub reader: &'a Reader<'a>, + pub filter: &'a metadata::Filter<'a>, + pub output: &'a str, pub namespace: &'a str, - pub sys: bool, - pub doc: bool, - pub component: bool, - pub standalone: bool, - pub std: bool, + pub implement: bool, // TODO: ideally we can use this to generate implementation traits on the fly and + // and have a single interface definition macro for consumption that expands to include + // impl traits when the `implement` cfg flag is set and then this gen option would be + // unecessary. + // + // Maybe this macro is the embedable version of the IDL format?! like a more intelligient + // version of the existing interface macro... + pub std: bool, // tweaks for internal std library support + pub sys: bool, // gen sys-style bindings + pub flatten: bool, // strips out namespaces - implies !package + pub package: bool, // default is single file with no cfg - implies !flatten } impl<'a> Gen<'a> { - pub fn new(reader: &'a Reader) -> Self { + pub fn new(reader: &'a Reader, filter: &'a metadata::Filter, output: &'a str) -> Self { Self { reader, + filter, + output, namespace: "", - sys: false, - doc: false, - component: false, - standalone: false, + implement: false, std: false, + sys: false, + flatten: false, + package: false, } } @@ -136,19 +148,19 @@ impl<'a> Gen<'a> { let crate_name = self.crate_name(); quote! { #crate_name PCWSTR } } - Type::Win32Array((ty, len)) => { + Type::Win32Array(ty, len) => { let name = self.type_default_name(ty); let len = Literal::usize_unsuffixed(*len); quote! { [#name; #len] } } Type::GenericParam(generic) => self.reader.generic_param_name(*generic).into(), - Type::TypeDef((def, generics)) => self.type_def_name(*def, generics), - Type::MutPtr((ty, pointers)) => { + Type::TypeDef(def, generics) => self.type_def_name(*def, generics), + Type::MutPtr(ty, pointers) => { let pointers = mut_ptrs(*pointers); let ty = self.type_default_name(ty); quote! { #pointers #ty } } - Type::ConstPtr((ty, pointers)) => { + Type::ConstPtr(ty, pointers) => { let pointers = const_ptrs(*pointers); let ty = self.type_default_name(ty); quote! { #pointers #ty } @@ -161,7 +173,7 @@ impl<'a> Gen<'a> { } pub fn type_vtbl_name(&self, ty: &Type) -> TokenStream { match ty { - Type::TypeDef((def, generics)) => self.type_def_vtbl_name(*def, generics), + Type::TypeDef(def, generics) => self.type_def_vtbl_name(*def, generics), _ => unimplemented!(), } } @@ -180,7 +192,7 @@ impl<'a> Gen<'a> { Type::BSTR => { quote! { ::std::mem::MaybeUninit<::windows_core::BSTR> } } - Type::Win32Array((kind, len)) => { + Type::Win32Array(kind, len) => { let name = self.type_abi_name(kind); let len = Literal::usize_unsuffixed(*len); quote! { [#name; #len] } @@ -189,7 +201,7 @@ impl<'a> Gen<'a> { let name = to_ident(self.reader.generic_param_name(*generic)); quote! { ::windows_core::AbiType<#name> } } - Type::TypeDef((def, _)) => match self.reader.type_def_kind(*def) { + Type::TypeDef(def, _) => match self.reader.type_def_kind(*def) { TypeKind::Enum => self.type_def_name(*def, &[]), TypeKind::Struct => { let tokens = self.type_def_name(*def, &[]); @@ -212,7 +224,7 @@ impl<'a> Gen<'a> { } _ => quote! { *mut ::core::ffi::c_void }, }, - Type::MutPtr((kind, pointers)) => { + Type::MutPtr(kind, pointers) => { let pointers_tokens = gen_mut_ptrs(*pointers); let kind = if *pointers > 1 { self.type_name(kind) @@ -221,7 +233,7 @@ impl<'a> Gen<'a> { }; quote! { #pointers_tokens #kind } } - Type::ConstPtr((kind, pointers)) => { + Type::ConstPtr(kind, pointers) => { let pointers_tokens = gen_const_ptrs(*pointers); let kind = if *pointers > 1 { self.type_name(kind) @@ -336,7 +348,7 @@ impl<'a> Gen<'a> { /// Generates doc comments for types, free functions, and constants. pub(crate) fn cfg_doc(&self, cfg: &Cfg) -> TokenStream { - if !self.doc { + if !self.package { quote! {} } else { let mut tokens = format!(r#"`\"{}\"`"#, to_feature(self.namespace)); @@ -357,7 +369,7 @@ impl<'a> Gen<'a> { /// Generates doc comments for member functions (methods) and avoids redundantly declaring the /// enclosing module feature required by the method's type. pub(crate) fn cfg_method_doc(&self, cfg: &Cfg) -> TokenStream { - if !self.doc { + if !self.package { quote! {} } else { let features = self.cfg_features_imp(cfg, self.namespace); @@ -405,7 +417,7 @@ impl<'a> Gen<'a> { fn cfg_features_imp(&self, cfg: &'a Cfg, namespace: &'a str) -> Vec<&'a str> { let mut compact = Vec::<&'static str>::new(); - if !self.standalone && !self.component { + if self.package { for feature in cfg.types.keys() { if !feature.is_empty() && !starts_with(namespace, feature) { for pos in 0..compact.len() { @@ -445,7 +457,7 @@ impl<'a> Gen<'a> { // pub(crate) fn namespace(&self, namespace: &str) -> TokenStream { - if self.standalone || namespace == self.namespace { + if self.flatten || namespace == self.namespace { quote! {} } else { let is_external = @@ -482,7 +494,7 @@ impl<'a> Gen<'a> { } pub fn crate_name(&self) -> TokenStream { if self.sys { - if self.standalone { + if self.flatten { TokenStream::new() } else { "::windows_sys::core::".into() @@ -611,7 +623,7 @@ impl<'a> Gen<'a> { if kind == AsyncKind::None { for interface in self.reader.type_def_interfaces(def, generics) { - if let Type::TypeDef((interface_def, interface_generics)) = &interface.ty { + if let Type::TypeDef(interface_def, interface_generics) = &interface.ty { kind = self.reader.type_def_async_kind(*interface_def); if kind != AsyncKind::None { async_generics = interface_generics.to_vec(); @@ -868,7 +880,7 @@ impl<'a> Gen<'a> { Some(Type::IInspectable) => { methods.combine("e! { pub base__: ::windows_core::IInspectable_Vtbl, }) } - Some(Type::TypeDef((def, _))) => { + Some(Type::TypeDef(def, _)) => { let vtbl = self.type_def_vtbl_name(*def, &[]); methods.combine("e! { pub base__: #vtbl, }); } @@ -1309,21 +1321,6 @@ impl<'a> Gen<'a> { } } -pub fn to_ident(name: &str) -> TokenStream { - // keywords list based on https://doc.rust-lang.org/reference/keywords.html - match name { - "abstract" | "as" | "become" | "box" | "break" | "const" | "continue" | "crate" | "do" - | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in" - | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "override" | "priv" - | "pub" | "ref" | "return" | "static" | "struct" | "super" | "trait" | "true" | "type" - | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while" | "yield" - | "try" | "async" | "await" | "dyn" => format!("r#{name}").into(), - "Self" | "self" => format!("{name}_").into(), - "_" => "unused".into(), - _ => trim_tick(name).into(), - } -} - fn mut_ptrs(pointers: usize) -> TokenStream { "*mut ".repeat(pointers).into() } diff --git a/crates/libs/bindgen/src/handles.rs b/crates/tools/riddle/src/rust/handles.rs similarity index 100% rename from crates/libs/bindgen/src/handles.rs rename to crates/tools/riddle/src/rust/handles.rs diff --git a/crates/libs/bindgen/src/implements.rs b/crates/tools/riddle/src/rust/implements.rs similarity index 95% rename from crates/libs/bindgen/src/implements.rs rename to crates/tools/riddle/src/rust/implements.rs index 275576eb93..8f43a95aeb 100644 --- a/crates/libs/bindgen/src/implements.rs +++ b/crates/tools/riddle/src/rust/implements.rs @@ -2,7 +2,7 @@ use super::*; pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { if gen.reader.type_def_kind(def) != TypeKind::Interface - || (!gen.component && !gen.reader.type_def_can_implement(def)) + || (!gen.implement && !gen.reader.type_def_can_implement(def)) { return quote! {}; } @@ -32,12 +32,12 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { let mut matches = quote! { iid == &<#type_ident as ::windows_core::ComInterface>::IID }; - if let Some(Type::TypeDef((def, _))) = vtables.last() { + if let Some(Type::TypeDef(def, _)) = vtables.last() { requires.combine(&gen_required_trait(gen, *def, &[])) } for def in &vtables { - if let Type::TypeDef((def, generics)) = def { + if let Type::TypeDef(def, generics) = def { let name = gen.type_def_name(*def, generics); matches.combine("e! { @@ -54,9 +54,9 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { // TODO: this awkward wrapping of TypeDefs needs fixing for interface in gen .reader - .type_interfaces(&Type::TypeDef((def, generics.to_vec()))) + .type_interfaces(&Type::TypeDef(def, generics.to_vec())) { - if let Type::TypeDef((def, generics)) = interface.ty { + if let Type::TypeDef(def, generics) = interface.ty { requires.combine(&gen_required_trait(gen, def, &generics)); } } @@ -109,7 +109,7 @@ pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream { match vtables.last() { Some(Type::IUnknown) => methods.combine("e! { base__: ::windows_core::IUnknown_Vtbl::new::(), }), Some(Type::IInspectable) => methods.combine("e! { base__: ::windows_core::IInspectable_Vtbl::new::(), }), - Some(Type::TypeDef((def, generics))) => { + Some(Type::TypeDef(def, generics)) => { let name = gen.type_def_name_imp(*def, generics, "_Vtbl"); if has_unknown_base { methods.combine("e! { base__: #name::new::(), }); diff --git a/crates/libs/bindgen/src/interfaces.rs b/crates/tools/riddle/src/rust/interfaces.rs similarity index 97% rename from crates/libs/bindgen/src/interfaces.rs rename to crates/tools/riddle/src/rust/interfaces.rs index 143e3bf552..bbce0052ba 100644 --- a/crates/libs/bindgen/src/interfaces.rs +++ b/crates/tools/riddle/src/rust/interfaces.rs @@ -32,7 +32,7 @@ fn gen_win_interface(gen: &Gen, def: TypeDef) -> TokenStream { let features = gen.cfg_features(&cfg); let interfaces = gen .reader - .type_interfaces(&Type::TypeDef((def, generics.to_vec()))); + .type_interfaces(&Type::TypeDef(def, generics.to_vec())); let vtables = gen.reader.type_def_vtables(def); let has_unknown_base = matches!(vtables.first(), Some(Type::IUnknown)); @@ -80,7 +80,7 @@ fn gen_win_interface(gen: &Gen, def: TypeDef) -> TokenStream { )); } for interface in &interfaces { - if let Type::TypeDef((def, generics)) = &interface.ty { + if let Type::TypeDef(def, generics) = &interface.ty { for method in gen.reader.type_def_methods(*def) { methods.combine(&winrt_methods::gen( gen, @@ -99,7 +99,7 @@ fn gen_win_interface(gen: &Gen, def: TypeDef) -> TokenStream { for ty in &vtables { match ty { Type::IUnknown | Type::IInspectable => {} - Type::TypeDef((def, _)) => { + Type::TypeDef(def, _) => { let kind = if gen.reader.type_def_type_name(*def) == TypeName::IDispatch { InterfaceKind::None } else { diff --git a/crates/libs/bindgen/src/iterators.rs b/crates/tools/riddle/src/rust/iterators.rs similarity index 98% rename from crates/libs/bindgen/src/iterators.rs rename to crates/tools/riddle/src/rust/iterators.rs index ca6b20f854..66f2a55a72 100644 --- a/crates/libs/bindgen/src/iterators.rs +++ b/crates/tools/riddle/src/rust/iterators.rs @@ -153,12 +153,12 @@ pub fn gen( let mut iterable = None; let interfaces = gen .reader - .type_interfaces(&Type::TypeDef((def, generics.to_vec()))); + .type_interfaces(&Type::TypeDef(def, generics.to_vec())); // If the class or interface is not one of the well-known collection interfaces, we then see whether it // implements any one of them. Here is where we favor IVectorView/IVector over IIterable. for interface in interfaces { - if let Type::TypeDef((interface, interface_generics)) = &interface.ty { + if let Type::TypeDef(interface, interface_generics) = &interface.ty { match gen.reader.type_def_type_name(*interface) { TypeName::IVectorView => { let item = gen.type_name(&interface_generics[0]); diff --git a/crates/libs/bindgen/src/method_names.rs b/crates/tools/riddle/src/rust/method_names.rs similarity index 93% rename from crates/libs/bindgen/src/method_names.rs rename to crates/tools/riddle/src/rust/method_names.rs index 9f5dad1c6c..97698777a6 100644 --- a/crates/libs/bindgen/src/method_names.rs +++ b/crates/tools/riddle/src/rust/method_names.rs @@ -20,7 +20,7 @@ impl MethodNames { pub fn add_vtable_types(&mut self, gen: &Gen, def: TypeDef) { for def in gen.reader.type_def_vtables(def) { - if let Type::TypeDef((def, _)) = def { + if let Type::TypeDef(def, _) = def { for method in gen.reader.type_def_methods(def) { self.add(gen, method); } diff --git a/crates/libs/bindgen/src/lib.rs b/crates/tools/riddle/src/rust/mod.rs similarity index 52% rename from crates/libs/bindgen/src/lib.rs rename to crates/tools/riddle/src/rust/mod.rs index 7712a73d63..28f98ff46f 100644 --- a/crates/libs/bindgen/src/lib.rs +++ b/crates/tools/riddle/src/rust/mod.rs @@ -1,7 +1,3 @@ -/*! -Learn more about Rust for Windows here: -*/ - mod classes; mod com_methods; mod constants; @@ -19,20 +15,140 @@ mod standalone; mod structs; mod try_format; mod winrt_methods; +use crate::{Error, Result, Tree}; +use rayon::prelude::*; + +pub fn from_reader( + reader: &metadata::Reader, + filter: &metadata::Filter, + mut config: std::collections::BTreeMap<&str, &str>, + output: &str, +) -> Result<()> { + let mut gen = Gen::new(reader, filter, output); + gen.package = config.remove("PACKAGE").is_some(); + gen.flatten = config.remove("FLATTEN").is_some(); + gen.std = config.remove("STD").is_some(); + gen.sys = gen.std || config.remove("SYS").is_some(); + gen.implement = config.remove("IMPLEMENT").is_some(); + + // TODO: get rid of this hack so it can work with any metadata + if gen.flatten { + gen.namespace = "Windows."; + } + + if gen.package && gen.flatten { + return Err(Error::new( + "cannot combine PACKAGE and FLATTEN configuration values", + )); + } + + if gen.implement && gen.sys { + return Err(Error::new( + "cannot combine IMPLEMENT and SYS configuration values", + )); + } + + if let Some((key, _)) = config.first_key_value() { + return Err(Error::new(&format!("invalid configuration value: `{key}`"))); + } + + if gen.package { + gen_package(&gen) + } else { + gen_file(&gen) + } +} + +fn gen_file(gen: &Gen) -> Result<()> { + // TODO: harmonize this output code so we don't need these two wildly differnt code paths + // there should be a simple way to generate the with or without namespaces. + + if gen.flatten { + // TODO: may need to harmonize right away since the filter doesn't map nicely to the standalone_imp... + // maybe just enumerate filter includes for now? + + // gen.namespace = "Windows."; -use metadata::reader::*; + let tokens = standalone::standalone_imp(gen, gen.filter.includes()); + crate::write_to_file(gen.output, tokens) + } else { + let mut tokens = String::new(); + let root = Tree::new(gen.reader, gen.filter); + + for tree in root.nested.values() { + tokens.push_str(&namespace(gen, tree)); + } + + crate::write_to_file(gen.output, tokens) + } +} + +fn gen_package(gen: &Gen) -> Result<()> { + let directory = crate::directory(gen.output); + let root = Tree::new(gen.reader, gen.filter); + let mut root_len = 0; + + for tree in root.nested.values() { + root_len = tree.namespace.len(); + _ = std::fs::remove_dir_all(format!("{directory}/{}", tree.namespace)); + } + + let trees = root.flatten(); + + trees.par_iter().try_for_each(|tree| { + let directory = format!("{directory}/{}", tree.namespace.replace('.', "/")); + let mut tokens = namespace(gen, tree); + if !gen.sys { + tokens.push_str("#[cfg(feature = \"implement\")]\n::core::include!(\"impl.rs\");\n"); + } + let output = format!("{directory}/mod.rs"); + crate::write_to_file(&output, tokens)?; + if !gen.sys { + let tokens = namespace_impl(gen, tree); + let output = format!("{directory}/impl.rs"); + crate::write_to_file(&output, tokens)?; + } + Ok::<(), Error>(()) + })?; + + let cargo_toml = format!("{}/Cargo.toml", crate::directory(directory)); + let mut toml = String::new(); + + for line in crate::read_file_lines(&cargo_toml)? { + toml.push_str(&line); + toml.push('\n'); + + if line == "# generated features" { + break; + } + } + + for tree in trees.iter().skip(1) { + let feature = tree.namespace[root_len + 1..].replace('.', "_"); + + if let Some(pos) = feature.rfind('_') { + let dependency = &feature[..pos]; + + toml.push_str(&format!("{feature} = [\"{dependency}\"]\n")); + } else { + toml.push_str(&format!("{feature} = []\n")); + } + } + + crate::write_to_file(&cargo_toml, toml) +} + +use crate::tokens::*; +use gen::*; +use metadata::*; use method_names::*; -pub use standalone::*; use std::collections::*; use std::fmt::Write; -use tokens::*; use try_format::*; -#[doc(hidden)] -pub use gen::*; - -#[doc(hidden)] -pub fn namespace(gen: &Gen, tree: &Tree) -> String { +fn namespace(gen: &Gen, tree: &Tree) -> String { + let gen = &mut gen.clone(); + gen.namespace = tree.namespace; let mut tokens = TokenStream::new(); if tree.namespace == "Windows" || !tree.namespace.starts_with("Windows.") { @@ -41,11 +157,19 @@ pub fn namespace(gen: &Gen, tree: &Tree) -> String { for (name, tree) in &tree.nested { let name = to_ident(name); - let namespace = tree.namespace[tree.namespace.find('.').unwrap() + 1..].replace('.', "_"); - if !gen.component { - tokens.combine("e! { #[cfg(feature = #namespace)] }); + let namespace_feature = + tree.namespace[tree.namespace.find('.').unwrap() + 1..].replace('.', "_"); + if gen.package { + tokens.combine("e! { + #[cfg(feature = #namespace_feature)] + pub mod #name; + }); + } else { + tokens.combine("e! { pub mod #name }); + tokens.push_str("{"); + tokens.push_str(&namespace(gen, tree)); + tokens.push_str("}"); } - tokens.combine("e! { pub mod #name; }); } let mut functions = BTreeMap::<&str, TokenStream>::new(); @@ -153,11 +277,17 @@ pub fn namespace(gen: &Gen, tree: &Tree) -> String { } tokens.combine(&extensions::gen_mod(gen, tree.namespace)); + + if gen.implement { + tokens.push_str(&namespace_impl(gen, tree)); + } + try_format(tokens.into_string()) } -#[doc(hidden)] -pub fn namespace_impl(gen: &Gen, tree: &Tree) -> String { +fn namespace_impl(gen: &Gen, tree: &Tree) -> String { + let gen = &mut gen.clone(); + gen.namespace = tree.namespace; let mut types = BTreeMap::<&str, TokenStream>::new(); for def in gen @@ -188,17 +318,17 @@ pub fn namespace_impl(gen: &Gen, tree: &Tree) -> String { try_format(tokens.into_string()) } -/// Generates bindings for a specific component namespace. -pub fn component(namespace: &str, files: &[File]) -> String { - let reader = &Reader::new(files); - let tree = reader.tree(namespace, &Default::default()); - let mut gen = Gen::new(reader); - gen.namespace = tree.namespace; - gen.component = true; - let mut bindings = crate::namespace(&gen, &tree); - bindings.push_str(&namespace_impl(&gen, &tree)); - try_format(bindings) -} +// /// Generates bindings for a specific component namespace. +// fn component(namespace_name: &str, files: &[File]) -> String { +// let reader = &Reader::new(files); +// let tree = reader.tree(namespace_name, &Default::default()); +// let mut gen = Gen::new(reader); +// gen.namespace = tree.namespace; +// gen.implement = true; +// let mut bindings = namespace(&gen, &tree); +// bindings.push_str(&namespace_impl(&gen, &tree)); +// try_format(bindings) +// } fn allow() -> TokenStream { quote! { diff --git a/crates/libs/bindgen/src/standalone.rs b/crates/tools/riddle/src/rust/standalone.rs similarity index 86% rename from crates/libs/bindgen/src/standalone.rs rename to crates/tools/riddle/src/rust/standalone.rs index bfff19a6e2..dd4ba69b94 100644 --- a/crates/libs/bindgen/src/standalone.rs +++ b/crates/tools/riddle/src/rust/standalone.rs @@ -1,37 +1,28 @@ use super::*; -/// Generates standalone Windows bindings based on the `windows` crate's bindings. -pub fn standalone_win(names: &[&str]) -> String { - let files = &File::with_default(&[]).unwrap(); - let reader = &Reader::new(files); - let gen = &mut Gen::new(reader); - standalone_imp(gen, names) -} - -/// Generates standalone Windows bindings based on the `windows-sys` crate's bindings. -pub fn standalone_sys(names: &[&str]) -> String { - let files = &File::with_default(&[]).unwrap(); - let reader = &Reader::new(files); - let gen = &mut Gen::new(reader); - gen.sys = true; - standalone_imp(gen, names) -} - -/// Generates standalone Windows bindings for the Rust Standard Library. -#[doc(hidden)] -pub fn standalone_std(names: &[&str]) -> String { - let files = &File::with_default(&[]).unwrap(); - let reader = &Reader::new(files); - let gen = &mut Gen::new(reader); - gen.std = true; - gen.sys = true; - standalone_imp(gen, names) -} - -fn standalone_imp(gen: &mut Gen, names: &[&str]) -> String { - gen.namespace = "Windows."; - gen.standalone = true; - +// /// Generates standalone Windows bindings based on the `windows` crate's bindings. +// pub fn standalone_win(reader: &Reader, names: &[&str]) -> String { +// let gen = &mut Gen::new(reader); +// standalone_imp(gen, names) +// } + +// /// Generates standalone Windows bindings based on the `windows-sys` crate's bindings. +// pub fn standalone_sys(reader: &Reader, names: &[&str]) -> String { +// let gen = &mut Gen::new(reader); +// gen.sys = true; +// standalone_imp(gen, names) +// } + +// /// Generates standalone Windows bindings for the Rust Standard Library. +// #[doc(hidden)] +// pub fn standalone_std(reader: &Reader, names: &[&str]) -> String { +// let gen = &mut Gen::new(reader); +// gen.std = true; +// gen.sys = true; +// standalone_imp(gen, names) +// } + +pub fn standalone_imp<'a, I: Iterator>(gen: &Gen, names: I) -> String { let mut types = BTreeSet::new(); let mut functions = BTreeSet::new(); let mut constants = BTreeSet::new(); @@ -43,7 +34,7 @@ fn standalone_imp(gen: &mut Gen, names: &[&str]) -> String { for def in gen.reader.get(type_name) { found = true; gen.reader - .type_collect_standalone(&Type::TypeDef((def, vec![])), &mut types); + .type_collect_standalone(&Type::TypeDef(def, vec![]), &mut types); } for method in gen @@ -147,7 +138,7 @@ fn standalone_imp(gen: &mut Gen, names: &[&str]) -> String { } }); } - Type::TypeDef((def, _)) => { + Type::TypeDef(def, _) => { let kind = gen.reader.type_def_kind(def); match kind { TypeKind::Class => { @@ -223,7 +214,7 @@ fn standalone_imp(gen: &mut Gen, names: &[&str]) -> String { } let mut tokens: TokenStream = format!( - r#"// Bindings generated by `windows-bindgen` {} + r#"// Bindings generated by `riddle` {} "#, std::env!("CARGO_PKG_VERSION") diff --git a/crates/libs/bindgen/src/structs.rs b/crates/tools/riddle/src/rust/structs.rs similarity index 99% rename from crates/libs/bindgen/src/structs.rs rename to crates/tools/riddle/src/rust/structs.rs index 2ca9588619..6b98bdc642 100644 --- a/crates/libs/bindgen/src/structs.rs +++ b/crates/tools/riddle/src/rust/structs.rs @@ -62,7 +62,7 @@ fn gen_struct_with_name(gen: &Gen, def: TypeDef, struct_name: &str, cfg: &Cfg) - && !flags.contains(TypeAttributes::WindowsRuntime) && !gen.reader.field_is_blittable(f, def) { - if let Type::Win32Array((ty, len)) = ty { + if let Type::Win32Array(ty, len) = ty { let ty = gen.type_default_name(&ty); quote! { pub #name: [::std::mem::ManuallyDrop<#ty>; #len], } } else { diff --git a/crates/libs/bindgen/src/try_format.rs b/crates/tools/riddle/src/rust/try_format.rs similarity index 100% rename from crates/libs/bindgen/src/try_format.rs rename to crates/tools/riddle/src/rust/try_format.rs diff --git a/crates/libs/bindgen/src/winrt_methods.rs b/crates/tools/riddle/src/rust/winrt_methods.rs similarity index 100% rename from crates/libs/bindgen/src/winrt_methods.rs rename to crates/tools/riddle/src/rust/winrt_methods.rs diff --git a/crates/libs/tokens/src/lib.rs b/crates/tools/riddle/src/tokens/mod.rs similarity index 65% rename from crates/libs/tokens/src/lib.rs rename to crates/tools/riddle/src/tokens/mod.rs index b9df954a93..e9e9e1a9bd 100644 --- a/crates/libs/tokens/src/lib.rs +++ b/crates/tools/riddle/src/tokens/mod.rs @@ -3,11 +3,7 @@ mod to_tokens; mod token_stream; -// Not public API. -#[doc(hidden)] -#[path = "runtime.rs"] -pub mod __private; - +pub mod runtime; pub use to_tokens::*; pub use token_stream::*; @@ -39,15 +35,17 @@ pub use token_stream::*; #[macro_export] macro_rules! quote { () => { - $crate::TokenStream::new() + $crate::tokens::TokenStream::new() }; ($($tt:tt)*) => {{ - let mut _s = $crate::TokenStream::new(); + let mut _s = $crate::tokens::TokenStream::new(); $crate::quote_each_token!(_s $($tt)*); _s }}; } +pub use quote; + /// Formatting macro for constructing a `TokenStream`. /// ///
@@ -128,7 +126,7 @@ macro_rules! quote_bind_into_iter { macro_rules! quote_bind_next_or_break { ($var:ident) => { let $var = match $var.next() { - Some(_x) => $crate::__private::RepInterp(_x), + Some(_x) => $crate::tokens::runtime::RepInterp(_x), None => break, }; }; @@ -170,10 +168,10 @@ macro_rules! quote_token_with_context { ($tokens:ident $b3:tt $b2:tt $b1:tt @ $a1:tt $a2:tt $a3:tt) => {}; ($tokens:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) * $a3:tt) => {{ - use $crate::__private::ext::*; - let has_iter = $crate::__private::ThereIsNoIteratorInRepetition; + use $crate::tokens::runtime::ext::*; + let has_iter = $crate::tokens::runtime::ThereIsNoIteratorInRepetition; $crate::pounded_var_names!(quote_bind_into_iter!(has_iter) () $($inner)*); - let _: $crate::__private::HasIterator = has_iter; + let _: $crate::tokens::runtime::HasIterator = has_iter; // This is `while true` instead of `loop` because if there are no // iterators used inside of this repetition then the body would not // contain any `break`, so the compiler would emit unreachable code @@ -189,11 +187,11 @@ macro_rules! quote_token_with_context { ($tokens:ident $b3:tt # ( $($inner:tt)* ) (*) $a1:tt $a2:tt $a3:tt) => {}; ($tokens:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) $sep:tt *) => {{ - use $crate::__private::ext::*; + use $crate::tokens::runtime::ext::*; let mut _i = 0usize; - let has_iter = $crate::__private::ThereIsNoIteratorInRepetition; + let has_iter = $crate::tokens::runtime::ThereIsNoIteratorInRepetition; $crate::pounded_var_names!(quote_bind_into_iter!(has_iter) () $($inner)*); - let _: $crate::__private::HasIterator = has_iter; + let _: $crate::tokens::runtime::HasIterator = has_iter; while true { $crate::pounded_var_names!(quote_bind_next_or_break!() () $($inner)*); if _i > 0 { @@ -212,7 +210,7 @@ macro_rules! quote_token_with_context { ($tokens:ident # ( $($inner:tt)* ) $sep:tt (*) $a1:tt $a2:tt $a3:tt) => {}; ($tokens:ident $b3:tt $b2:tt $b1:tt (#) $var:ident $a2:tt $a3:tt) => { - $crate::ToTokens::to_tokens(&$var, &mut $tokens); + $crate::tokens::ToTokens::to_tokens(&$var, &mut $tokens); }; ($tokens:ident $b3:tt $b2:tt # ($var:ident) $a1:tt $a2:tt $a3:tt) => {}; ($tokens:ident $b3:tt $b2:tt $b1:tt ($curr:tt) $a1:tt $a2:tt $a3:tt) => { @@ -224,210 +222,233 @@ macro_rules! quote_token_with_context { #[doc(hidden)] macro_rules! quote_token { ($tokens:ident ( $($inner:tt)* )) => { - $crate::__private::push_group( + $crate::tokens::runtime::push_group( &mut $tokens, - $crate::Delimiter::Parenthesis, + $crate::tokens::Delimiter::Parenthesis, $crate::quote!($($inner)*), ); }; ($tokens:ident [ $($inner:tt)* ]) => { - $crate::__private::push_group( + $crate::tokens::runtime::push_group( &mut $tokens, - $crate::Delimiter::Bracket, + $crate::tokens::Delimiter::Bracket, $crate::quote!($($inner)*), ); }; ($tokens:ident { $($inner:tt)* }) => { - $crate::__private::push_group( + $crate::tokens::runtime::push_group( &mut $tokens, - $crate::Delimiter::Brace, + $crate::tokens::Delimiter::Brace, $crate::quote!($($inner)*), ); }; ($tokens:ident +) => { - $crate::__private::push_add(&mut $tokens); + $crate::tokens::runtime::push_add(&mut $tokens); }; ($tokens:ident +=) => { - $crate::__private::push_add_eq(&mut $tokens); + $crate::tokens::runtime::push_add_eq(&mut $tokens); }; ($tokens:ident &) => { - $crate::__private::push_and(&mut $tokens); + $crate::tokens::runtime::push_and(&mut $tokens); }; ($tokens:ident &&) => { - $crate::__private::push_and_and(&mut $tokens); + $crate::tokens::runtime::push_and_and(&mut $tokens); }; ($tokens:ident &=) => { - $crate::__private::push_and_eq(&mut $tokens); + $crate::tokens::runtime::push_and_eq(&mut $tokens); }; ($tokens:ident @) => { - $crate::__private::push_at(&mut $tokens); + $crate::tokens::runtime::push_at(&mut $tokens); }; ($tokens:ident !) => { - $crate::__private::push_bang(&mut $tokens); + $crate::tokens::runtime::push_bang(&mut $tokens); }; ($tokens:ident ^) => { - $crate::__private::push_caret(&mut $tokens); + $crate::tokens::runtime::push_caret(&mut $tokens); }; ($tokens:ident ^=) => { - $crate::__private::push_caret_eq(&mut $tokens); + $crate::tokens::runtime::push_caret_eq(&mut $tokens); }; ($tokens:ident :) => { - $crate::__private::push_colon(&mut $tokens); + $crate::tokens::runtime::push_colon(&mut $tokens); }; ($tokens:ident ::) => { - $crate::__private::push_colon2(&mut $tokens); + $crate::tokens::runtime::push_colon2(&mut $tokens); }; ($tokens:ident ,) => { - $crate::__private::push_comma(&mut $tokens); + $crate::tokens::runtime::push_comma(&mut $tokens); }; ($tokens:ident /) => { - $crate::__private::push_div(&mut $tokens); + $crate::tokens::runtime::push_div(&mut $tokens); }; ($tokens:ident /=) => { - $crate::__private::push_div_eq(&mut $tokens); + $crate::tokens::runtime::push_div_eq(&mut $tokens); }; ($tokens:ident .) => { - $crate::__private::push_dot(&mut $tokens); + $crate::tokens::runtime::push_dot(&mut $tokens); }; ($tokens:ident ..) => { - $crate::__private::push_dot2(&mut $tokens); + $crate::tokens::runtime::push_dot2(&mut $tokens); }; ($tokens:ident ...) => { - $crate::__private::push_dot3(&mut $tokens); + $crate::tokens::runtime::push_dot3(&mut $tokens); }; ($tokens:ident ..=) => { - $crate::__private::push_dot_dot_eq(&mut $tokens); + $crate::tokens::runtime::push_dot_dot_eq(&mut $tokens); }; ($tokens:ident =) => { - $crate::__private::push_eq(&mut $tokens); + $crate::tokens::runtime::push_eq(&mut $tokens); }; ($tokens:ident ==) => { - $crate::__private::push_eq_eq(&mut $tokens); + $crate::tokens::runtime::push_eq_eq(&mut $tokens); }; ($tokens:ident >=) => { - $crate::__private::push_ge(&mut $tokens); + $crate::tokens::runtime::push_ge(&mut $tokens); }; ($tokens:ident >) => { - $crate::__private::push_gt(&mut $tokens); + $crate::tokens::runtime::push_gt(&mut $tokens); }; ($tokens:ident <=) => { - $crate::__private::push_le(&mut $tokens); + $crate::tokens::runtime::push_le(&mut $tokens); }; ($tokens:ident <) => { - $crate::__private::push_lt(&mut $tokens); + $crate::tokens::runtime::push_lt(&mut $tokens); }; ($tokens:ident *=) => { - $crate::__private::push_mul_eq(&mut $tokens); + $crate::tokens::runtime::push_mul_eq(&mut $tokens); }; ($tokens:ident !=) => { - $crate::__private::push_ne(&mut $tokens); + $crate::tokens::runtime::push_ne(&mut $tokens); }; ($tokens:ident |) => { - $crate::__private::push_or(&mut $tokens); + $crate::tokens::runtime::push_or(&mut $tokens); }; ($tokens:ident |=) => { - $crate::__private::push_or_eq(&mut $tokens); + $crate::tokens::runtime::push_or_eq(&mut $tokens); }; ($tokens:ident ||) => { - $crate::__private::push_or_or(&mut $tokens); + $crate::tokens::runtime::push_or_or(&mut $tokens); }; ($tokens:ident #) => { - $crate::__private::push_pound(&mut $tokens); + $crate::tokens::runtime::push_pound(&mut $tokens); }; ($tokens:ident ?) => { - $crate::__private::push_question(&mut $tokens); + $crate::tokens::runtime::push_question(&mut $tokens); }; ($tokens:ident ->) => { - $crate::__private::push_rarrow(&mut $tokens); + $crate::tokens::runtime::push_rarrow(&mut $tokens); }; ($tokens:ident <-) => { - $crate::__private::push_larrow(&mut $tokens); + $crate::tokens::runtime::push_larrow(&mut $tokens); }; ($tokens:ident %) => { - $crate::__private::push_rem(&mut $tokens); + $crate::tokens::runtime::push_rem(&mut $tokens); }; ($tokens:ident %=) => { - $crate::__private::push_rem_eq(&mut $tokens); + $crate::tokens::runtime::push_rem_eq(&mut $tokens); }; ($tokens:ident =>) => { - $crate::__private::push_fat_arrow(&mut $tokens); + $crate::tokens::runtime::push_fat_arrow(&mut $tokens); }; ($tokens:ident ;) => { - $crate::__private::push_semi(&mut $tokens); + $crate::tokens::runtime::push_semi(&mut $tokens); }; ($tokens:ident <<) => { - $crate::__private::push_shl(&mut $tokens); + $crate::tokens::runtime::push_shl(&mut $tokens); }; ($tokens:ident <<=) => { - $crate::__private::push_shl_eq(&mut $tokens); + $crate::tokens::runtime::push_shl_eq(&mut $tokens); }; ($tokens:ident >>) => { - $crate::__private::push_shr(&mut $tokens); + $crate::tokens::runtime::push_shr(&mut $tokens); }; ($tokens:ident >>=) => { - $crate::__private::push_shr_eq(&mut $tokens); + $crate::tokens::runtime::push_shr_eq(&mut $tokens); }; ($tokens:ident *) => { - $crate::__private::push_star(&mut $tokens); + $crate::tokens::runtime::push_star(&mut $tokens); }; ($tokens:ident -) => { - $crate::__private::push_sub(&mut $tokens); + $crate::tokens::runtime::push_sub(&mut $tokens); }; ($tokens:ident -=) => { - $crate::__private::push_sub_eq(&mut $tokens); + $crate::tokens::runtime::push_sub_eq(&mut $tokens); }; ($tokens:ident $ident:ident) => { - $crate::__private::push_ident(&mut $tokens, stringify!($ident)); + $crate::tokens::runtime::push_ident(&mut $tokens, stringify!($ident)); }; ($tokens:ident $other:tt) => { - $crate::__private::parse(&mut $tokens, stringify!($other)); + $crate::tokens::runtime::parse(&mut $tokens, stringify!($other)); }; } + +pub fn to_ident(name: &str) -> TokenStream { + // keywords list based on https://doc.rust-lang.org/reference/keywords.html + match name { + "abstract" | "as" | "become" | "box" | "break" | "const" | "continue" | "crate" | "do" + | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in" + | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "override" | "priv" + | "pub" | "ref" | "return" | "static" | "struct" | "super" | "trait" | "true" | "type" + | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while" | "yield" + | "try" | "async" | "await" | "dyn" => format!("r#{name}").into(), + "Self" | "self" => format!("{name}_").into(), + "_" => "unused".into(), + _ => trim_tick(name).into(), + } +} + +fn trim_tick(name: &str) -> &str { + if name.as_bytes().iter().rev().nth(1) == Some(&b'`') { + &name[..name.len() - 2] + } else { + name + } +} diff --git a/crates/libs/tokens/src/runtime.rs b/crates/tools/riddle/src/tokens/runtime.rs similarity index 99% rename from crates/libs/tokens/src/runtime.rs rename to crates/tools/riddle/src/tokens/runtime.rs index 413318bcc0..83bc42a040 100644 --- a/crates/libs/tokens/src/runtime.rs +++ b/crates/tools/riddle/src/tokens/runtime.rs @@ -1,4 +1,4 @@ -use crate::{Delimiter, ToTokens, TokenStream}; +use super::{Delimiter, ToTokens, TokenStream}; use core::ops::BitOr; pub struct HasIterator; // True @@ -40,8 +40,8 @@ impl BitOr for HasIterator { /// the returned value should be idempotent. pub mod ext { use super::RepInterp; + use super::ToTokens; use super::{HasIterator as HasIter, ThereIsNoIteratorInRepetition as DoesNotHaveIter}; - use crate::ToTokens; use core::slice; use std::collections::btree_set::{self, BTreeSet}; diff --git a/crates/libs/tokens/src/to_tokens.rs b/crates/tools/riddle/src/tokens/to_tokens.rs similarity index 99% rename from crates/libs/tokens/src/to_tokens.rs rename to crates/tools/riddle/src/tokens/to_tokens.rs index 42b2244445..95150fced0 100644 --- a/crates/libs/tokens/src/to_tokens.rs +++ b/crates/tools/riddle/src/tokens/to_tokens.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::rc::Rc; -use crate::{Literal, TokenStream}; +use super::{Literal, TokenStream}; /// Types that can be interpolated inside a `quote!` invocation. /// diff --git a/crates/libs/tokens/src/token_stream.rs b/crates/tools/riddle/src/tokens/token_stream.rs similarity index 93% rename from crates/libs/tokens/src/token_stream.rs rename to crates/tools/riddle/src/tokens/token_stream.rs index 923e5faa0e..b8ba00873f 100644 --- a/crates/libs/tokens/src/token_stream.rs +++ b/crates/tools/riddle/src/tokens/token_stream.rs @@ -136,7 +136,9 @@ pub struct Literal { macro_rules! unsuffixed { ($ty:ty => $name:ident) => { pub fn $name(n: $ty) -> Self { - Self { inner: n.to_string() } + Self { + inner: n.to_string(), + } } }; } @@ -149,7 +151,12 @@ impl Literal { unsuffixed!(u8 => u8_unsuffixed); pub fn byte_string(s: &[u8]) -> Self { - Self { inner: format!("b\"{}\"", core::str::from_utf8(s).expect("Could not turn bytes into byte literal")) } + Self { + inner: format!( + "b\"{}\"", + core::str::from_utf8(s).expect("Could not turn bytes into byte literal") + ), + } } pub fn as_str(&self) -> &str { diff --git a/crates/tools/riddle/src/tree.rs b/crates/tools/riddle/src/tree.rs new file mode 100644 index 0000000000..03a84b5996 --- /dev/null +++ b/crates/tools/riddle/src/tree.rs @@ -0,0 +1,46 @@ +#[derive(Debug)] +pub struct Tree<'a> { + pub namespace: &'a str, + pub nested: std::collections::BTreeMap<&'a str, Tree<'a>>, +} + +impl<'a> Tree<'a> { + pub fn new(reader: &'a metadata::Reader, filter: &'a metadata::Filter) -> Self { + let mut tree = Tree::from_namespace(""); + for ns in reader.namespaces() { + if filter.includes_namespace(ns) { + tree.insert_namespace(ns, 0); + } + } + tree + } + + fn from_namespace(namespace: &'a str) -> Self { + Self { + namespace, + nested: std::collections::BTreeMap::new(), + } + } + fn insert_namespace(&mut self, namespace: &'a str, pos: usize) -> &mut Self { + if let Some(next) = namespace[pos..].find('.') { + let next = pos + next; + self.nested + .entry(&namespace[pos..next]) + .or_insert_with(|| Self::from_namespace(&namespace[..next])) + .insert_namespace(namespace, next + 1) + } else { + self.nested + .entry(&namespace[pos..]) + .or_insert_with(|| Self::from_namespace(namespace)) + } + } + pub fn flatten(&self) -> Vec<&Self> { + let mut flatten = if self.namespace.is_empty() { + vec![] + } else { + vec![self] + }; + flatten.extend(self.nested.values().flat_map(|tree| tree.flatten())); + flatten + } +} diff --git a/crates/tools/riddle/src/winmd/mod.rs b/crates/tools/riddle/src/winmd/mod.rs new file mode 100644 index 0000000000..cb86273f30 --- /dev/null +++ b/crates/tools/riddle/src/winmd/mod.rs @@ -0,0 +1,4 @@ +mod to_winmd; +pub use to_winmd::from_reader; +pub mod writer; +pub use writer::*; diff --git a/crates/tools/riddle/src/winmd/to_winmd.rs b/crates/tools/riddle/src/winmd/to_winmd.rs new file mode 100644 index 0000000000..87d735b9e0 --- /dev/null +++ b/crates/tools/riddle/src/winmd/to_winmd.rs @@ -0,0 +1,87 @@ +use crate::winmd::{self, writer}; + +pub fn from_reader( + reader: &metadata::Reader, + filter: &metadata::Filter, + _config: std::collections::BTreeMap<&str, &str>, + output: &str, +) -> crate::Result<()> { + let mut writer = winmd::Writer::new(output); + + for def in reader.types(filter) { + let extends = if let Some(extends) = reader.type_def_extends(def) { + writer.insert_type_ref(extends.namespace, extends.name) + } else { + 0 + }; + + writer.tables.TypeDef.push(writer::TypeDef { + Extends: extends, + FieldList: writer.tables.Field.len() as _, + Flags: reader.type_def_flags(def).0, + MethodList: writer.tables.MethodDef.len() as _, + TypeName: writer.strings.insert(reader.type_def_name(def)), + TypeNamespace: writer.strings.insert(reader.type_def_namespace(def)), + }); + + for field in reader.type_def_fields(def) { + let ty = field_writer_type(reader, field, Some(def)); + let signature = writer.insert_field_sig(&ty); + + writer.tables.Field.push(writer::Field { + Flags: reader.field_flags(field).0, + Name: writer.strings.insert(reader.field_name(field)), + Signature: signature, + }); + } + } + + // TODO: In theory, `config` could instruct this function to balance the types across a number of winmd files + // like mdmerge supports for namespace-splitting. + crate::write_to_file(output, writer.into_stream()).map_err(|err| err.with_path(output)) +} + +fn field_writer_type( + reader: &metadata::Reader, + row: metadata::Field, + enclosing: Option, +) -> winmd::Type { + match reader.field_type(row, enclosing) { + metadata::Type::Void => winmd::Type::Void, + metadata::Type::Bool => winmd::Type::Bool, + metadata::Type::Char => winmd::Type::Char, + metadata::Type::I8 => winmd::Type::I8, + metadata::Type::U8 => winmd::Type::U8, + metadata::Type::I16 => winmd::Type::I16, + metadata::Type::U16 => winmd::Type::U16, + metadata::Type::I32 => winmd::Type::I32, + metadata::Type::U32 => winmd::Type::U32, + metadata::Type::I64 => winmd::Type::I64, + metadata::Type::U64 => winmd::Type::U64, + metadata::Type::F32 => winmd::Type::F32, + metadata::Type::F64 => winmd::Type::F64, + metadata::Type::ISize => winmd::Type::ISize, + metadata::Type::USize => winmd::Type::USize, + metadata::Type::String => winmd::Type::String, + metadata::Type::GUID => winmd::Type::GUID, + metadata::Type::IUnknown => winmd::Type::IUnknown, + metadata::Type::IInspectable => winmd::Type::IInspectable, + metadata::Type::HRESULT => winmd::Type::HRESULT, + metadata::Type::PSTR => winmd::Type::PSTR, + metadata::Type::PWSTR => winmd::Type::PWSTR, + metadata::Type::PCSTR => winmd::Type::PCSTR, + metadata::Type::PCWSTR => winmd::Type::PCWSTR, + metadata::Type::BSTR => winmd::Type::BSTR, + metadata::Type::TypeName => winmd::Type::TypeName, + // Type::TypeRef(TypeDefOrRef) => + // Type::GenericParam(GenericParam) => + // Type::TypeDef(TypeDef, Vec) => + // Type::MutPtr(Box, usize) => + // Type::ConstPtr(Box, usize) => + // Type::Win32Array(Box, usize) => + // Type::WinrtArray(Box) => + // Type::WinrtArrayRef(Box) => + // Type::ConstRef(Box) => + _ => unimplemented!(), + } +} diff --git a/crates/libs/metadata/src/writer/winmd/write/blobs.rs b/crates/tools/riddle/src/winmd/writer/blobs.rs similarity index 93% rename from crates/libs/metadata/src/writer/winmd/write/blobs.rs rename to crates/tools/riddle/src/winmd/writer/blobs.rs index 9b817836af..aed9d65ed9 100644 --- a/crates/libs/metadata/src/writer/winmd/write/blobs.rs +++ b/crates/tools/riddle/src/winmd/writer/blobs.rs @@ -8,7 +8,10 @@ pub struct Blobs { impl Default for Blobs { fn default() -> Self { - Self { map: Default::default(), stream: vec![0] } + Self { + map: Default::default(), + stream: vec![0], + } } } diff --git a/crates/libs/metadata/src/writer/winmd/write/codes.rs b/crates/tools/riddle/src/winmd/writer/codes.rs similarity index 100% rename from crates/libs/metadata/src/writer/winmd/write/codes.rs rename to crates/tools/riddle/src/winmd/writer/codes.rs diff --git a/crates/libs/metadata/src/writer/winmd/write/file.rs b/crates/tools/riddle/src/winmd/writer/file.rs similarity index 72% rename from crates/libs/metadata/src/writer/winmd/write/file.rs rename to crates/tools/riddle/src/winmd/writer/file.rs index 0fc3fb6b8d..e72cfcea8f 100644 --- a/crates/libs/metadata/src/writer/winmd/write/file.rs +++ b/crates/tools/riddle/src/winmd/writer/file.rs @@ -1,9 +1,13 @@ use super::*; +use metadata::imp::*; use std::mem::*; -pub fn write(mut tables: Vec, mut strings: Vec, mut blobs: Vec) -> Result> { - if [tables.len(), strings.len(), blobs.len()].iter().any(|len| *len > u32::MAX as _) { - return Err(Error::new("heap too large")); +pub fn write(mut tables: Vec, mut strings: Vec, mut blobs: Vec) -> Vec { + if [tables.len(), strings.len(), blobs.len()] + .iter() + .any(|len| *len > u32::MAX as _) + { + panic!("heap too large"); } unsafe { @@ -19,7 +23,8 @@ pub fn write(mut tables: Vec, mut strings: Vec, mut blobs: Vec) -> R file.Machine = IMAGE_FILE_MACHINE_I386; file.NumberOfSections = 1; file.SizeOfOptionalHeader = size_of::() as _; - file.Characteristics = IMAGE_FILE_DLL | IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_EXECUTABLE_IMAGE; + file.Characteristics = + IMAGE_FILE_DLL | IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_EXECUTABLE_IMAGE; let mut optional: IMAGE_OPTIONAL_HEADER32 = zeroed(); optional.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC; @@ -34,7 +39,9 @@ pub fn write(mut tables: Vec, mut strings: Vec, mut blobs: Vec) -> R optional.MinorSubsystemVersion = 2; optional.SizeOfHeaders = 512; optional.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI; - optional.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_NO_SEH | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; + optional.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_NX_COMPAT + | IMAGE_DLLCHARACTERISTICS_NO_SEH + | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; optional.SizeOfStackReserve = 0x100000; optional.SizeOfHeapReserve = 4096; optional.LoaderFlags = 0x100000; @@ -66,14 +73,25 @@ pub fn write(mut tables: Vec, mut strings: Vec, mut blobs: Vec) -> R type GuidsHeader = StreamHeader<8>; type BlobsHeader = StreamHeader<8>; - let size_of_stream_headers = size_of::() + size_of::() + size_of::() + size_of::(); - let size_of_image = optional.FileAlignment as usize + size_of::() + size_of::() + size_of_stream_headers + size_of_streams; + let size_of_stream_headers = size_of::() + + size_of::() + + size_of::() + + size_of::(); + let size_of_image = optional.FileAlignment as usize + + size_of::() + + size_of::() + + size_of_stream_headers + + size_of_streams; optional.SizeOfImage = round(size_of_image, optional.SectionAlignment as _) as _; section.Misc.VirtualSize = size_of_image as u32 - optional.FileAlignment; - section.SizeOfRawData = round(section.Misc.VirtualSize as _, optional.FileAlignment as _) as _; + section.SizeOfRawData = + round(section.Misc.VirtualSize as _, optional.FileAlignment as _) as _; - optional.DataDirectory[14] = IMAGE_DATA_DIRECTORY { VirtualAddress: SECTION_ALIGNMENT, Size: size_of::() as _ }; + optional.DataDirectory[14] = IMAGE_DATA_DIRECTORY { + VirtualAddress: SECTION_ALIGNMENT, + Size: size_of::() as _, + }; section.PointerToRawData = optional.FileAlignment; clr.MetaData.VirtualAddress = SECTION_ALIGNMENT + size_of::() as u32; clr.MetaData.Size = section.Misc.VirtualSize - size_of::() as u32; @@ -93,9 +111,18 @@ pub fn write(mut tables: Vec, mut strings: Vec, mut blobs: Vec) -> R let stream_offset = buffer.len() - metadata_offset + size_of_stream_headers; let tables_header = TablesHeader::new(stream_offset as _, tables.len() as _, b"#~\0\0"); - let strings_header = StringsHeader::new(tables_header.next_offset(), strings.len() as _, b"#Strings\0\0\0\0"); - let guids_header = GuidsHeader::new(strings_header.next_offset(), guids.len() as _, b"#GUID\0\0\0"); - let blobs_header = BlobsHeader::new(guids_header.next_offset(), blobs.len() as _, b"#Blob\0\0\0"); + let strings_header = StringsHeader::new( + tables_header.next_offset(), + strings.len() as _, + b"#Strings\0\0\0\0", + ); + let guids_header = GuidsHeader::new( + strings_header.next_offset(), + guids.len() as _, + b"#GUID\0\0\0", + ); + let blobs_header = + BlobsHeader::new(guids_header.next_offset(), blobs.len() as _, b"#Blob\0\0\0"); buffer.write_header(&tables_header); buffer.write_header(&strings_header); @@ -110,7 +137,7 @@ pub fn write(mut tables: Vec, mut strings: Vec, mut blobs: Vec) -> R assert_eq!(clr.MetaData.Size as usize, buffer.len() - metadata_offset); assert_eq!(size_of_image, buffer.len()); - Ok(buffer) + buffer } } @@ -125,7 +152,11 @@ struct StreamHeader { impl StreamHeader { fn new(offset: u32, size: u32, name: &[u8; LEN]) -> Self { - Self { offset, size, name: *name } + Self { + offset, + size, + name: *name, + } } fn next_offset(&self) -> u32 { self.offset + self.size diff --git a/crates/tools/riddle/src/winmd/writer/mod.rs b/crates/tools/riddle/src/winmd/writer/mod.rs new file mode 100644 index 0000000000..20dc39905f --- /dev/null +++ b/crates/tools/riddle/src/winmd/writer/mod.rs @@ -0,0 +1,390 @@ +mod blobs; +mod codes; +mod file; +mod strings; +mod tables; +mod traits; +mod r#type; + +use blobs::Blobs; +use codes::*; +use metadata::imp::*; +pub use r#type::*; +use std::collections::HashMap; +use strings::Strings; +pub use tables::*; +use traits::*; + +pub struct Writer { + pub blobs: Blobs, + pub strings: Strings, + pub tables: Tables, + pub module_scope: u32, + pub scopes: HashMap, + pub references: HashMap>, +} + +impl Writer { + pub fn new(name: &str) -> Self { + let mut writer = Self { + blobs: Default::default(), + strings: Default::default(), + tables: Default::default(), + module_scope: 0, + scopes: Default::default(), + references: Default::default(), + }; + + writer.tables.TypeDef.push(TypeDef { + TypeName: writer.strings.insert(""), + ..Default::default() + }); + let name = name + .rsplit_once(&['/', '\\']) + .map_or(name, |(_, name)| name); + writer.module_scope = ResolutionScope::Module(writer.tables.Module.push2(Module { + Name: writer.strings.insert(name), + Mvid: 1, + ..Default::default() + })) + .encode(); + let name = name.rsplit_once('.').map_or(name, |(_, name)| name); + + writer.tables.Assembly.push(Assembly { + Name: writer.strings.insert(name), + HashAlgId: 0x00008004, + MajorVersion: 0xFF, + MinorVersion: 0xFF, + BuildNumber: 0xFF, + RevisionNumber: 0xFF, + Flags: metadata::AssemblyFlags::WindowsRuntime.0, + ..Default::default() + }); + + // Some winmd parsers will fail to read without an `mscorlib` reference. The `insert_module_types` function will typically include it + // automatically but a minimal `Module` tree may not add this dependency. + writer.insert_scope("System"); + + writer + } + + pub fn into_stream(self) -> Vec { + file::write( + self.tables.into_stream(), + self.strings.into_stream(), + self.blobs.into_stream(), + ) + } + + // fn insert_module_types(&mut self, module: &'a Module) { + // for (name, def) in &module.types { + // self.insert_type_def(&module.namespace, name, def); + // } + // module.modules.values().for_each(|module| self.insert_module_types(module)); + // } + + // fn insert_type_def(&mut self, namespace: &'a str, name: &'a str, def: &'a [TypeDef]) { + // for def in def { + // let extends = if let Some(extends) = &def.extends { self.insert_type_ref(&extends.namespace, &extends.name) } else { 0 }; + // self.tables.TypeDef.push(TypeDef { + // Flags: def.flags.0, + // TypeName: self.strings.insert(name), + // TypeNamespace: self.strings.insert(namespace), + // Extends: extends, + // FieldList: self.tables.Field.len() as _, + // MethodList: self.tables.MethodDef.len() as _, + // }); + // for field in &def.fields { + // let blob = self.insert_field_sig(&field.ty); + // let parent = self.tables.Field.push2(Field { Flags: field.flags.0, Name: self.strings.insert(&field.name), Signature: blob }); + // if let Some(value) = &field.value { + // let blob = self.insert_value_blob(value); + // self.tables.Constant.push(Constant { Type: value.to_code(), Parent: HasConstant::Field(parent).encode(), Value: blob }); + // } + // } + // for method in &def.methods { + // let blob = self.insert_method_sig(method); + // self.tables.MethodDef.push(MethodDef { RVA: 0, ImplFlags: 0, Flags: method.flags.0, Name: self.strings.insert(&method.name), Signature: blob, ParamList: self.tables.Param.len() as _ }); + // for (sequence, param) in method.params.iter().enumerate() { + // self.tables.Param.push(Param { Flags: param.flags.0, Sequence: (sequence + 1) as _, Name: self.strings.insert(¶m.name) }); + // } + // } + // } + // } + + // fn insert_value_blob(&mut self, value: &Value) -> u32 { + // // TODO: can either cache in Writer, like we do for scopes and references, or regenerate each time. + // // Profile once we can stress test this with field/method signatures. + + // let blob = match value { + // Value::I8(value) => value.to_le_bytes().to_vec(), + // Value::U8(value) => value.to_le_bytes().to_vec(), + // Value::I16(value) => value.to_le_bytes().to_vec(), + // Value::U16(value) => value.to_le_bytes().to_vec(), + // Value::I32(value) => value.to_le_bytes().to_vec(), + // Value::U32(value) => value.to_le_bytes().to_vec(), + // Value::I64(value) => value.to_le_bytes().to_vec(), + // Value::U64(value) => value.to_le_bytes().to_vec(), + // Value::F32(value) => value.to_le_bytes().to_vec(), + // Value::F64(value) => value.to_le_bytes().to_vec(), + // Value::String(value) => { + // let mut blob = vec![]; + // usize_blob(value.len(), &mut blob); + // blob.extend_from_slice(value.as_bytes()); + // blob + // } + // _ => todo!("{:?}", value), + // }; + + // self.blobs.insert(&blob) + // } + + // fn insert_method_sig(&mut self, method: &'a Method) -> u32 { + // // TODO: can either cache in Writer, like we do for scopes and references, or regenerate each time. + // // Profile once we can stress test this with field/method signatures. + + // let mut blob = vec![method.call_flags.0]; + // usize_blob(method.params.len(), &mut blob); + // self.type_blob(&method.return_type.ty, &mut blob); + // for param in &method.params { + // self.type_blob(¶m.ty, &mut blob); + // } + + // self.blobs.insert(&blob) + // } + + pub fn insert_field_sig(&mut self, ty: &Type) -> u32 { + // TODO: can either cache in Writer, like we do for scopes and references, or regenerate each time. + // Profile once we can stress test this with field/method signatures. + + let mut blob = vec![0x6]; // FIELD + self.type_blob(ty, &mut blob); + + self.blobs.insert(&blob) + } + + fn insert_scope(&mut self, namespace: &str) -> u32 { + if let Some(scope) = self.scopes.get(namespace) { + *scope + } else if namespace == "System" { + let scope = ResolutionScope::AssemblyRef( + self.tables.AssemblyRef.push2(AssemblyRef { + Name: self.strings.insert("mscorlib"), + MajorVersion: 4, + PublicKeyOrToken: self + .blobs + .insert(&[0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89]), + ..Default::default() + }), + ) + .encode(); + self.scopes.insert(namespace.to_string(), scope); + scope + } else { + // TODO: may need to capture the original assembly info for external references. + let scope = ResolutionScope::AssemblyRef(self.tables.AssemblyRef.push2(AssemblyRef { + Name: self.strings.insert(namespace), + MajorVersion: 0xFF, + MinorVersion: 0xFF, + BuildNumber: 0xFF, + RevisionNumber: 0xFF, + Flags: metadata::AssemblyFlags::WindowsRuntime.0, + ..Default::default() + })) + .encode(); + self.scopes.insert(namespace.to_string(), scope); + scope + } + } + + pub fn insert_type_ref(&mut self, namespace: &str, name: &str) -> u32 { + if let Some(key) = self.references.get(namespace) { + if let Some(reference) = key.get(name) { + return *reference; + } + } + + // TODO? let scope = if self.module.contains_type(namespace, name) { self.module_scope } else { self.insert_scope(namespace) }; + let scope = self.module_scope; + + let reference = TypeDefOrRef::TypeRef(self.tables.TypeRef.push2(TypeRef { + TypeName: self.strings.insert(name), + TypeNamespace: self.strings.insert(namespace), + ResolutionScope: scope, + })) + .encode(); + self.references + .entry(namespace.to_string()) + .or_default() + .insert(name.to_string(), reference); + reference + } + + fn type_blob(&mut self, ty: &Type, blob: &mut Vec) { + match ty { + Type::Void => blob.push(ELEMENT_TYPE_VOID as _), + Type::Bool => blob.push(ELEMENT_TYPE_BOOLEAN as _), + Type::Char => blob.push(ELEMENT_TYPE_CHAR as _), + Type::I8 => blob.push(ELEMENT_TYPE_I1 as _), + Type::U8 => blob.push(ELEMENT_TYPE_U1 as _), + Type::I16 => blob.push(ELEMENT_TYPE_I2 as _), + Type::U16 => blob.push(ELEMENT_TYPE_U2 as _), + Type::I32 => blob.push(ELEMENT_TYPE_I4 as _), + Type::U32 => blob.push(ELEMENT_TYPE_U4 as _), + Type::I64 => blob.push(ELEMENT_TYPE_I8 as _), + Type::U64 => blob.push(ELEMENT_TYPE_U8 as _), + Type::F32 => blob.push(ELEMENT_TYPE_R4 as _), + Type::F64 => blob.push(ELEMENT_TYPE_R8 as _), + Type::ISize => blob.push(ELEMENT_TYPE_I as _), + Type::USize => blob.push(ELEMENT_TYPE_U as _), + Type::String => blob.push(ELEMENT_TYPE_STRING as _), + Type::IInspectable => blob.push(ELEMENT_TYPE_OBJECT as _), + Type::GUID => { + let code = self.insert_type_ref("System", "Guid"); + blob.push(ELEMENT_TYPE_VALUETYPE as _); + usize_blob(code as _, blob); + } + Type::HRESULT => { + let code = self.insert_type_ref("Windows.Foundation", "HResult"); + blob.push(ELEMENT_TYPE_VALUETYPE as _); + usize_blob(code as _, blob); + } + Type::TypeRef(ty) => { + let code = self.insert_type_ref(&ty.namespace, &ty.name); + blob.push(ELEMENT_TYPE_VALUETYPE as _); + usize_blob(code as _, blob); + } + Type::BSTR => { + let code = self.insert_type_ref("Windows.Win32.Foundation", "BSTR"); + blob.push(ELEMENT_TYPE_VALUETYPE as _); + usize_blob(code as _, blob); + } + Type::IUnknown => { + let code = self.insert_type_ref("Windows.Win32.Foundation", "IUnknown"); + blob.push(ELEMENT_TYPE_VALUETYPE as _); + usize_blob(code as _, blob); + } + Type::PCWSTR | Type::PWSTR => { + let code = self.insert_type_ref("Windows.Win32.Foundation", "PWSTR"); + blob.push(ELEMENT_TYPE_VALUETYPE as _); + usize_blob(code as _, blob); + } + Type::PCSTR | Type::PSTR => { + let code = self.insert_type_ref("Windows.Win32.Foundation", "PSTR"); + blob.push(ELEMENT_TYPE_VALUETYPE as _); + usize_blob(code as _, blob); + } + Type::ConstRef(ty) => { + usize_blob(ELEMENT_TYPE_CMOD_OPT as _, blob); + usize_blob( + self.insert_type_ref("System.Runtime.CompilerServices", "IsConst") as _, + blob, + ); + usize_blob(ELEMENT_TYPE_BYREF as _, blob); + self.type_blob(ty, blob); + } + Type::WinrtArrayRef(ty) => { + usize_blob(ELEMENT_TYPE_BYREF as _, blob); + usize_blob(ELEMENT_TYPE_SZARRAY as _, blob); + self.type_blob(ty, blob); + } + Type::WinrtArray(ty) => { + usize_blob(ELEMENT_TYPE_SZARRAY as _, blob); + self.type_blob(ty, blob); + } + Type::Win32Array(ty, bounds) => { + usize_blob(ELEMENT_TYPE_ARRAY as _, blob); + self.type_blob(ty, blob); + usize_blob(1, blob); // rank + usize_blob(1, blob); // count + usize_blob(*bounds as _, blob); + } + Type::TypeName => { + let code = self.insert_type_ref("System", "Type"); + blob.push(ELEMENT_TYPE_CLASS as _); + usize_blob(code as _, blob); + } + Type::MutPtr(ty, pointers) | Type::ConstPtr(ty, pointers) => { + for _ in 0..*pointers { + usize_blob(ELEMENT_TYPE_PTR as _, blob); + } + self.type_blob(ty, blob); + } + _ => todo!("{:?}", ty), + } + } +} + +fn round(size: usize, round: usize) -> usize { + let round = round - 1; + (size + round) & !round +} + +fn usize_blob(value: usize, blob: &mut Vec) { + // See II.23.2 in ECMA-335 + assert!(value < 0x20000000); + + if value < 0x80 { + blob.push(value as _); + } else if value < 0x4000 { + blob.push((0x80 | (value & 0x3F00) >> 8) as _); + blob.push((value & 0xFF) as _); + } else { + blob.push((0xC0 | (value & 0x1F000000) >> 24) as _); + blob.push(((value & 0xFF0000) >> 16) as _); + blob.push(((value & 0xFF00) >> 8) as _); + blob.push((value & 0xFF) as _); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_usize_blob() { + let mut blob = vec![]; + usize_blob(0, &mut blob); + usize_blob(1, &mut blob); + usize_blob(2, &mut blob); + + usize_blob(0x80 - 2, &mut blob); + usize_blob(0x80 - 1, &mut blob); + usize_blob(0x80, &mut blob); + usize_blob(0x80 + 1, &mut blob); + usize_blob(0x80 + 2, &mut blob); + + usize_blob(0x4000 - 2, &mut blob); + usize_blob(0x4000 - 1, &mut blob); + usize_blob(0x4000, &mut blob); + usize_blob(0x4000 + 1, &mut blob); + usize_blob(0x4000 + 2, &mut blob); + + usize_blob(0x20000000 - 3, &mut blob); + usize_blob(0x20000000 - 2, &mut blob); + usize_blob(0x20000000 - 1, &mut blob); + + let mut blob = metadata::Blob::new(0, &blob); + assert_eq!(blob.read_usize(), 0); + assert_eq!(blob.read_usize(), 1); + assert_eq!(blob.read_usize(), 2); + + assert_eq!(blob.read_usize(), 0x80 - 2); + assert_eq!(blob.read_usize(), 0x80 - 1); + assert_eq!(blob.read_usize(), 0x80); + assert_eq!(blob.read_usize(), 0x80 + 1); + assert_eq!(blob.read_usize(), 0x80 + 2); + + assert_eq!(blob.read_usize(), 0x4000 - 2); + assert_eq!(blob.read_usize(), 0x4000 - 1); + assert_eq!(blob.read_usize(), 0x4000); + assert_eq!(blob.read_usize(), 0x4000 + 1); + assert_eq!(blob.read_usize(), 0x4000 + 2); + + assert_eq!(blob.read_usize(), 0x20000000 - 3); + assert_eq!(blob.read_usize(), 0x20000000 - 2); + assert_eq!(blob.read_usize(), 0x20000000 - 1); + + assert_eq!(blob.slice.len(), 0); + } +} diff --git a/crates/libs/metadata/src/writer/winmd/write/strings.rs b/crates/tools/riddle/src/winmd/writer/strings.rs similarity index 66% rename from crates/libs/metadata/src/writer/winmd/write/strings.rs rename to crates/tools/riddle/src/winmd/writer/strings.rs index f473e8e762..278bd4bbca 100644 --- a/crates/libs/metadata/src/writer/winmd/write/strings.rs +++ b/crates/tools/riddle/src/winmd/writer/strings.rs @@ -1,24 +1,27 @@ use super::Write; use std::collections::hash_map::*; -pub struct Strings<'a> { - map: HashMap<&'a str, u32>, +pub struct Strings { + map: HashMap, stream: Vec, } -impl<'a> Default for Strings<'a> { +impl Default for Strings { fn default() -> Self { - Self { map: Default::default(), stream: vec![0] } + Self { + map: Default::default(), + stream: vec![0], + } } } -impl<'a> Strings<'a> { - pub fn insert(&mut self, value: &'a str) -> u32 { +impl Strings { + pub fn insert(&mut self, value: &str) -> u32 { if value.is_empty() { return 0; } - match self.map.entry(value) { + match self.map.entry(value.to_string()) { Entry::Vacant(entry) => { let offset = *entry.insert(self.stream.len() as _); self.stream.extend_from_slice(value.as_bytes()); diff --git a/crates/libs/metadata/src/writer/winmd/write/tables.rs b/crates/tools/riddle/src/winmd/writer/tables.rs similarity index 85% rename from crates/libs/metadata/src/writer/winmd/write/tables.rs rename to crates/tools/riddle/src/winmd/writer/tables.rs index 06b5dd62f2..3f5aec4a12 100644 --- a/crates/libs/metadata/src/writer/winmd/write/tables.rs +++ b/crates/tools/riddle/src/winmd/writer/tables.rs @@ -1,6 +1,7 @@ #![allow(non_snake_case)] -use super::{coded_index_size, Error, Result, Write}; +use super::Write; +use metadata::imp::coded_index_size; #[derive(Default)] pub struct Tables { @@ -175,14 +176,43 @@ pub struct TypeSpec { } impl Tables { - pub fn into_stream(self) -> Result> { - if [self.AssemblyRef.len(), self.ClassLayout.len(), self.Constant.len(), self.CustomAttribute.len(), self.Field.len(), self.GenericParam.len(), self.ImplMap.len(), self.InterfaceImpl.len(), self.MemberRef.len(), self.MethodDef.len(), self.Module.len(), self.ModuleRef.len(), self.NestedClass.len(), self.Param.len(), self.Property.len(), self.TypeDef.len(), self.TypeRef.len(), self.TypeSpec.len()].iter().any(|len| *len > u32::MAX as _) { - return Err(Error::new("metadata table too large")); + pub fn into_stream(self) -> Vec { + if [ + self.AssemblyRef.len(), + self.ClassLayout.len(), + self.Constant.len(), + self.CustomAttribute.len(), + self.Field.len(), + self.GenericParam.len(), + self.ImplMap.len(), + self.InterfaceImpl.len(), + self.MemberRef.len(), + self.MethodDef.len(), + self.Module.len(), + self.ModuleRef.len(), + self.NestedClass.len(), + self.Param.len(), + self.Property.len(), + self.TypeDef.len(), + self.TypeRef.len(), + self.TypeSpec.len(), + ] + .iter() + .any(|len| *len > u32::MAX as _) + { + panic!("metadata table too large"); } - let resolution_scope = coded_index_size(&[self.Module.len(), self.ModuleRef.len(), self.AssemblyRef.len(), self.TypeRef.len()]); - let type_def_or_ref = coded_index_size(&[self.TypeDef.len(), self.TypeRef.len(), self.TypeSpec.len()]); - let has_constant = coded_index_size(&[self.Field.len(), self.Param.len(), self.Property.len()]); + let resolution_scope = coded_index_size(&[ + self.Module.len(), + self.ModuleRef.len(), + self.AssemblyRef.len(), + self.TypeRef.len(), + ]); + let type_def_or_ref = + coded_index_size(&[self.TypeDef.len(), self.TypeRef.len(), self.TypeSpec.len()]); + let has_constant = + coded_index_size(&[self.Field.len(), self.Param.len(), self.Property.len()]); let valid_tables: u64 = 1 << 0 | // Module 1 << 0x01 | // TypeRef @@ -313,6 +343,6 @@ impl Tables { buffer.write_u32(x.HashValue); } - Ok(buffer.into_stream()) + buffer.into_stream() } } diff --git a/crates/libs/metadata/src/writer/winmd/write/traits.rs b/crates/tools/riddle/src/winmd/writer/traits.rs similarity index 91% rename from crates/libs/metadata/src/writer/winmd/write/traits.rs rename to crates/tools/riddle/src/winmd/writer/traits.rs index 2001755c98..f718d89bbf 100644 --- a/crates/libs/metadata/src/writer/winmd/write/traits.rs +++ b/crates/tools/riddle/src/winmd/writer/traits.rs @@ -13,7 +13,10 @@ pub trait Write { impl Write for Vec { unsafe fn write_header(&mut self, value: &T) { - self.extend_from_slice(std::slice::from_raw_parts(value as *const _ as _, std::mem::size_of::())); + self.extend_from_slice(std::slice::from_raw_parts( + value as *const _ as _, + std::mem::size_of::(), + )); } fn write_u8(&mut self, value: u8) { diff --git a/crates/tools/riddle/src/winmd/writer/type.rs b/crates/tools/riddle/src/winmd/writer/type.rs new file mode 100644 index 0000000000..d8d9521200 --- /dev/null +++ b/crates/tools/riddle/src/winmd/writer/type.rs @@ -0,0 +1,68 @@ +#![allow(clippy::upper_case_acronyms)] + +#[derive(Clone, Debug)] +pub struct TypeName { + pub namespace: String, + pub name: String, + pub generics: Vec, +} + +#[derive(Clone, Debug)] +pub enum Type { + Void, + Bool, + Char, + I8, + U8, + I16, + U16, + I32, + U32, + I64, + U64, + F32, + F64, + ISize, + USize, + String, + GUID, + IUnknown, + IInspectable, + HRESULT, + PSTR, + PWSTR, + PCSTR, + PCWSTR, + BSTR, + TypeName, // TODO: maybe call this DeferredTypeName since its used to indicate that the type name follows in the blob's value stream. + TypeRef(TypeName), + GenericParam(String), + MutPtr(Box, usize), + ConstPtr(Box, usize), + Win32Array(Box, usize), + WinrtArray(Box), + WinrtArrayRef(Box), + ConstRef(Box), +} + +impl Type { + pub fn into_mut_ptr(self) -> Self { + match self { + Self::MutPtr(ty, count) => Self::MutPtr(ty, count + 1), + Self::ConstPtr(ty, count) => Self::MutPtr(ty, count + 1), + _ => Self::MutPtr(Box::new(self), 1), + } + } + + pub fn into_const_ptr(self) -> Self { + match self { + Self::MutPtr(ty, count) => Self::ConstPtr(ty, count + 1), + Self::ConstPtr(ty, count) => Self::ConstPtr(ty, count + 1), + _ => Self::ConstPtr(Box::new(self), 1), + } + } + + pub fn into_array(self, len: usize) -> Self { + Self::Win32Array(Box::new(self), len) + } +} diff --git a/crates/libs/bindgen/rustfmt.toml b/crates/tools/rustfmt.toml similarity index 100% rename from crates/libs/bindgen/rustfmt.toml rename to crates/tools/rustfmt.toml diff --git a/crates/tools/sys/Cargo.toml b/crates/tools/sys/Cargo.toml index fc5c01707e..ca83803fc5 100644 --- a/crates/tools/sys/Cargo.toml +++ b/crates/tools/sys/Cargo.toml @@ -3,9 +3,3 @@ name = "tool_sys" version = "0.0.0" edition = "2018" publish = false - -[dependencies] -metadata = { package = "windows-metadata", path = "../../libs/metadata" } -bindgen = { package = "windows-bindgen", path = "../../libs/bindgen" } -lib = { package = "tool_lib", path = "../lib" } -rayon = "1.5.1" diff --git a/crates/tools/sys/bindings.txt b/crates/tools/sys/bindings.txt new file mode 100644 index 0000000000..9651f98ad8 --- /dev/null +++ b/crates/tools/sys/bindings.txt @@ -0,0 +1,114 @@ +-in crates/libs/metadata/default +-out crates/libs/sys/src/lib.rs +-config PACKAGE SYS + +-filter + Windows.Win32 + Windows.Wdk + !Windows.AI + !Windows.ApplicationModel + !Windows.Data + !Windows.Devices + !Windows.Embedded + !Windows.Foundation + !Windows.Gaming + !Windows.Globalization + !Windows.Graphics + !Windows.Management + !Windows.Media + !Windows.Networking + !Windows.Perception + !Windows.Phone + !Windows.Security + !Windows.Services + !Windows.Storage + !Windows.System + !Windows.UI + !Windows.Web + !Windows.Win32.AI + !Windows.Win32.Data.Xml + !Windows.Win32.Devices.DeviceAccess + !Windows.Win32.Devices.FunctionDiscovery + !Windows.Win32.Devices.Geolocation + !Windows.Win32.Devices.ImageAcquisition + !Windows.Win32.Foundation.Metadata + !Windows.Win32.Graphics.CompositionSwapchain + !Windows.Win32.Graphics.Direct2D + !Windows.Win32.Graphics.Direct2D.Common + !Windows.Win32.Graphics.Direct3D + !Windows.Win32.Graphics.Direct3D11 + !Windows.Win32.Graphics.Direct3D11on12 + !Windows.Win32.Graphics.Direct3D9on12 + !Windows.Win32.Graphics.DirectComposition + !Windows.Win32.Graphics.DirectDraw + !Windows.Win32.Graphics.DirectManipulation + !Windows.Win32.Graphics.DirectWrite + !Windows.Win32.Graphics.DXCore + !Windows.Win32.Graphics.Dxgi + !Windows.Win32.Graphics.Imaging + !Windows.Win32.Graphics.Imaging.D2D + !Windows.Win32.Media.Audio.Apo + !Windows.Win32.Media.Audio.DirectMusic + !Windows.Win32.Media.Audio.DirectSound + !Windows.Win32.Media.Audio.Endpoints + !Windows.Win32.Media.Audio.XAudio2 + !Windows.Win32.Media.DeviceManager + !Windows.Win32.Media.DirectShow + !Windows.Win32.Media.DirectShow.Tv + !Windows.Win32.Media.DirectShow.Xml + !Windows.Win32.Media.LibrarySharingServices + !Windows.Win32.Media.MediaFoundation + !Windows.Win32.Media.MediaPlayer + !Windows.Win32.Media.PictureAcquisition + !Windows.Win32.Media.Speech + !Windows.Win32.Networking.BackgroundIntelligentTransferService + !Windows.Win32.Networking.NetworkListManager + !Windows.Win32.Networking.RemoteDifferentialCompression + !Windows.Win32.NetworkManagement.MobileBroadband + !Windows.Win32.NetworkManagement.NetworkPolicyServer + !Windows.Win32.NetworkManagement.WindowsConnectNow + !Windows.Win32.Security.Authentication.Identity.Provider + !Windows.Win32.Security.Authorization.UI + !Windows.Win32.Security.ConfigurationSnapin + !Windows.Win32.Security.Tpm + !Windows.Win32.Storage.DataDeduplication + !Windows.Win32.Storage.EnhancedStorage + !Windows.Win32.Storage.FileServerResourceManager + !Windows.Win32.Storage.Packaging.Opc + !Windows.Win32.Storage.VirtualDiskService + !Windows.Win32.Storage.Vss + !Windows.Win32.Storage.Xps.Printing + !Windows.Win32.System.AssessmentTool + !Windows.Win32.System.Com.CallObj + !Windows.Win32.System.Com.ChannelCredentials + !Windows.Win32.System.Com.Events + !Windows.Win32.System.Com.UI + !Windows.Win32.System.Contacts + !Windows.Win32.System.DesktopSharing + !Windows.Win32.System.Diagnostics.ClrProfiling + !Windows.Win32.System.Diagnostics.Debug.ActiveScript + !Windows.Win32.System.Diagnostics.Debug.WebApp + !Windows.Win32.System.Mmc + !Windows.Win32.System.ParentalControls + !Windows.Win32.System.RealTimeCommunications + !Windows.Win32.System.RemoteAssistance + !Windows.Win32.System.ServerBackup + !Windows.Win32.System.SettingsManagementInfrastructure + !Windows.Win32.System.SideShow + !Windows.Win32.System.TaskScheduler + !Windows.Win32.System.TransactionServer + !Windows.Win32.System.UpdateAgent + !Windows.Win32.System.UpdateAssessment + !Windows.Win32.System.WindowsSync + !Windows.Win32.System.WinRT + !Windows.Win32.UI.Animation + !Windows.Win32.UI.Controls.RichEdit + !Windows.Win32.UI.Input.Ink + !Windows.Win32.UI.Input.Radial + !Windows.Win32.UI.LegacyWindowsEnvironmentFeatures + !Windows.Win32.UI.Notifications + !Windows.Win32.UI.Ribbon + !Windows.Win32.UI.Shell.Common + !Windows.Win32.UI.Wpf + !Windows.Win32.UI.Xaml + !Windows.Win32.Web.MsHtml diff --git a/crates/tools/sys/src/main.rs b/crates/tools/sys/src/main.rs index 57f5c954c5..c26d49f645 100644 --- a/crates/tools/sys/src/main.rs +++ b/crates/tools/sys/src/main.rs @@ -1,134 +1,14 @@ -use metadata::reader::*; -use rayon::prelude::*; -use std::io::prelude::*; - fn main() { - let time = std::time::Instant::now(); - let mut expect_namespace = false; - let mut namespace = String::new(); - for arg in std::env::args() { - match arg.as_str() { - "-n" => expect_namespace = true, - _ => { - if expect_namespace { - namespace = arg; - } - } - } - } - let mut output = std::path::PathBuf::from("crates/libs/sys/src/Windows"); - if namespace.is_empty() { - _ = std::fs::remove_dir_all(&output); - } - output.pop(); - let files = File::with_default(&[]).unwrap(); - let reader = &Reader::new(&files); - if !namespace.is_empty() { - let tree = reader.tree(&namespace, &Default::default()); - gen_tree(reader, &output, &tree); - return; - } - - let root = reader.tree("Windows", &filter(reader)); - let trees = root.flatten(); - trees.par_iter().for_each(|tree| gen_tree(reader, &output, tree)); - output.pop(); - output.push("Cargo.toml"); - let mut file = std::fs::File::create(&output).unwrap(); - - file.write_all( - r#" -[package] -name = "windows-sys" -version = "0.48.0" -authors = ["Microsoft"] -edition = "2018" -license = "MIT OR Apache-2.0" -description = "Rust for Windows" -repository = "https://github.com/microsoft/windows-rs" -readme = "../../../docs/readme.md" -rust-version = "1.48" -categories = ["os::windows-apis"] - -[package.metadata.docs.rs] -default-target = "x86_64-pc-windows-msvc" -targets = [] -all-features = true - -[dependencies.windows-targets] -version = "0.48.0" -path = "../targets" - -[features] -default = [] -"# - .as_bytes(), - ) - .unwrap(); - - // Skip the root Windows tree while writing features - for tree in trees.iter().skip(1) { - let feature = tree.namespace[root.namespace.len() + 1..].replace('.', "_"); - - if let Some(pos) = feature.rfind('_') { - let dependency = &feature[..pos]; - - file.write_all(format!("{feature} = [\"{dependency}\"]\n").as_bytes()).unwrap(); - } else { - file.write_all(format!("{feature} = []\n").as_bytes()).unwrap(); - } - } - - println!(" Finished in {:.2}s", time.elapsed().as_secs_f32()); -} - -fn gen_tree(reader: &Reader, output: &std::path::Path, tree: &Tree) { - let mut path = std::path::PathBuf::from(output); - path.push(tree.namespace.replace('.', "/")); - std::fs::create_dir_all(&path).unwrap(); - - let mut gen = bindgen::Gen::new(reader); - gen.namespace = tree.namespace; - gen.sys = true; - gen.doc = true; - let tokens = bindgen::namespace(&gen, tree); - std::fs::write(path.join("mod.rs"), tokens).unwrap(); -} - -fn filter<'a>(reader: &'a Reader) -> Filter<'a> { - let mut exclude = vec![]; - - for namespace in reader.namespaces() { - // Find any COM interfaces in this namespace. - let interfaces = reader.namespace_types(namespace, &Default::default()).any(|def| reader.type_def_kind(def) == TypeKind::Interface); - - // Find any interfaces-independent functions in this namespace. - let functions = reader.namespace_functions(namespace).any(|function| { - let cfg = reader.signature_cfg(&reader.method_def_signature(function, &[])); - if cfg.types.values().flatten().any(|def| reader.type_def_kind(*def) == TypeKind::Interface) { - return false; - } - if cfg.core_types.iter().any(|ty| matches!(ty, Type::IUnknown | Type::IInspectable)) { - return false; - } - true - }); - - if interfaces && !functions { - exclude.push(namespace); - } - } - - // Exclude additional namespace not covered by the hieuristic above. - const EXCLUDE: [&str; 13] = ["Windows.Win32.Graphics.Direct2D", "Windows.Win32.Graphics.DirectComposition", "Windows.Win32.Graphics.DirectDraw", "Windows.Win32.Graphics.DirectWrite", "Windows.Win32.Graphics.DXCore", "Windows.Win32.Graphics.Dxgi", "Windows.Win32.Graphics.Imaging", "Windows.Win32.Foundation.Metadata", "Windows.Win32.Media.Audio.DirectSound", "Windows.Win32.Media.DirectShow", "Windows.Win32.Media.MediaFoundation", "Windows.Win32.System.WinRT", "Windows.Win32.UI.Xaml"]; - - for namespace in &EXCLUDE { - if exclude.contains(namespace) { - panic!("already excluded `{}`", namespace); - } else { - exclude.push(namespace); - } - } - - Filter::new(&["Windows.Win32", "Windows.Wdk"], &exclude) + let mut command = std::process::Command::new("cargo.exe"); + + command.args([ + "run", + "-p", + "riddle", + "--", + "-etc", + "crates/tools/sys/bindings.txt", + ]); + + assert!(command.status().unwrap().success()); } diff --git a/crates/tools/windows/Cargo.toml b/crates/tools/windows/Cargo.toml index 130aa35e1e..98f4e3f045 100644 --- a/crates/tools/windows/Cargo.toml +++ b/crates/tools/windows/Cargo.toml @@ -3,9 +3,3 @@ name = "tool_windows" version = "0.0.0" edition = "2018" publish = false - -[dependencies] -metadata = { package = "windows-metadata", path = "../../libs/metadata" } -bindgen = { package = "windows-bindgen", path = "../../libs/bindgen" } -lib = { package = "tool_lib", path = "../lib" } -rayon = "1.5.1" diff --git a/crates/tools/windows/bindings.txt b/crates/tools/windows/bindings.txt new file mode 100644 index 0000000000..a55a735ddd --- /dev/null +++ b/crates/tools/windows/bindings.txt @@ -0,0 +1,20 @@ +-in crates/libs/metadata/default +-out crates/libs/windows/src/lib.rs +-config PACKAGE + +-filter + Windows + !Windows.AI.MachineLearning.Preview + !Windows.ApplicationModel.SocialInfo + !Windows.Devices.AllJoyn + !Windows.Devices.Perception + !Windows.Security.Authentication.Identity.Provider + !Windows.Services.Cortana + !Windows.System.Power.Diagnostics + !Windows.System.Preview + !Windows.UI.Xaml + !Windows.Win32.Foundation.Metadata + !Windows.Win32.System.Diagnostics.Debug.WebApp + !Windows.Win32.System.WinRT.Xaml + !Windows.Win32.Web.MsHtml + !Windows.Win32.UI.Xaml diff --git a/crates/tools/windows/src/main.rs b/crates/tools/windows/src/main.rs index ee57929085..2aaa83efbf 100644 --- a/crates/tools/windows/src/main.rs +++ b/crates/tools/windows/src/main.rs @@ -1,110 +1,14 @@ -use rayon::prelude::*; -use std::io::prelude::*; - -/// Namespaces to exclude from code generation for the `windows` crate. -const EXCLUDE_NAMESPACES: [&str; 14] = ["Windows.AI.MachineLearning.Preview", "Windows.ApplicationModel.SocialInfo", "Windows.Devices.AllJoyn", "Windows.Devices.Perception", "Windows.Security.Authentication.Identity.Provider", "Windows.Services.Cortana", "Windows.System.Power.Diagnostics", "Windows.System.Preview", "Windows.UI.Xaml", "Windows.Win32.Foundation.Metadata", "Windows.Win32.System.Diagnostics.Debug.WebApp", "Windows.Win32.System.WinRT.Xaml", "Windows.Win32.Web.MsHtml", "Windows.Win32.UI.Xaml"]; - fn main() { - let time = std::time::Instant::now(); - let mut expect_namespace = false; - let mut namespace = String::new(); - for arg in std::env::args() { - match arg.as_str() { - "-n" => expect_namespace = true, - _ => { - if expect_namespace { - namespace = arg; - } - } - } - } - let mut output = std::path::PathBuf::from("crates/libs/windows/src/Windows"); - if namespace.is_empty() { - _ = std::fs::remove_dir_all(&output); - } - output.pop(); - let files = metadata::reader::File::with_default(&[]).unwrap(); - let reader = &metadata::reader::Reader::new(&files); - if !namespace.is_empty() { - let tree = reader.tree(&namespace, &Default::default()); - gen_tree(reader, &output, &tree); - return; - } - let root = reader.tree("Windows", &metadata::reader::Filter::new(&["Windows"], &EXCLUDE_NAMESPACES)); - let trees = root.flatten(); - trees.par_iter().for_each(|tree| gen_tree(reader, &output, tree)); - output.pop(); - output.push("Cargo.toml"); - let mut file = std::fs::File::create(&output).unwrap(); - - file.write_all( - r#" -[package] -name = "windows" -version = "0.48.0" -authors = ["Microsoft"] -edition = "2018" -license = "MIT OR Apache-2.0" -description = "Rust for Windows" -repository = "https://github.com/microsoft/windows-rs" -documentation = "https://microsoft.github.io/windows-docs-rs/" -readme = "../../../docs/readme.md" -rust-version = "1.48" -categories = ["os::windows-apis"] - -[package.metadata.docs.rs] -default-target = "x86_64-pc-windows-msvc" -targets = [] -rustc-args = ["--cfg", "docsrs"] - -[dependencies] -windows-core = { path = "../core", version = "0.50.0" } -windows-targets = { path = "../targets", version = "0.48.0" } -windows-implement = { path = "../implement", version = "0.48.0", optional = true } -windows-interface = { path = "../interface", version = "0.48.0", optional = true } - -[features] -default = [] -deprecated = [] -implement = ["windows-implement", "windows-interface", "windows-core/implement"] -"# - .as_bytes(), - ) - .unwrap(); - - // Skip the root Windows tree while writing features - for tree in trees.iter().skip(1) { - let feature = tree.namespace[root.namespace.len() + 1..].replace('.', "_"); - - if let Some(pos) = feature.rfind('_') { - let dependency = &feature[..pos]; - - file.write_all(format!("{feature} = [\"{dependency}\"]\n").as_bytes()).unwrap(); - } else { - file.write_all(format!("{feature} = []\n").as_bytes()).unwrap(); - } - } - - println!(" Finished in {:.2}s", time.elapsed().as_secs_f32()); -} - -fn gen_tree(reader: &metadata::reader::Reader, output: &std::path::Path, tree: &metadata::reader::Tree) { - let mut path = std::path::PathBuf::from(output); - path.push(tree.namespace.replace('.', "/")); - std::fs::create_dir_all(&path).unwrap(); - - let mut gen = bindgen::Gen::new(reader); - gen.namespace = tree.namespace; - gen.doc = true; - let mut tokens = bindgen::namespace(&gen, tree); - - tokens.push_str( - r#"#[cfg(feature = "implement")] -::core::include!("impl.rs"); -"#, - ); - - std::fs::write(path.join("mod.rs"), tokens).unwrap(); - let tokens = bindgen::namespace_impl(&gen, tree); - std::fs::write(path.join("impl.rs"), tokens).unwrap(); + let mut command = std::process::Command::new("cargo.exe"); + + command.args([ + "run", + "-p", + "riddle", + "--", + "-etc", + "crates/tools/windows/bindings.txt", + ]); + + assert!(command.status().unwrap().success()); } diff --git a/crates/tools/yml/Cargo.toml b/crates/tools/yml/Cargo.toml index 28cb439027..5ec826c831 100644 --- a/crates/tools/yml/Cargo.toml +++ b/crates/tools/yml/Cargo.toml @@ -5,5 +5,4 @@ edition = "2018" publish = false [dependencies] -metadata = { package = "windows-metadata", path = "../../libs/metadata" } regex = "1.7" diff --git a/crates/tools/yml/src/main.rs b/crates/tools/yml/src/main.rs index 771087c6b9..24166672e5 100644 --- a/crates/tools/yml/src/main.rs +++ b/crates/tools/yml/src/main.rs @@ -130,7 +130,11 @@ fn find>(path: P, regex: &Regex, filter: bool) -> Vec { names.append(&mut find(file.path(), regex, filter)); } else if file.file_name() == "Cargo.toml" { let text = std::fs::read_to_string(file.path()).expect("Cargo.toml"); - let name = regex.captures(&text).expect("captures").get(1).expect("name"); + let name = regex + .captures(&text) + .expect("captures") + .get(1) + .expect("name"); if !filter || !name.as_str().ends_with("_x") { names.push(name.as_str().to_string()); } diff --git a/docs/publish.cmd b/docs/publish.cmd index ecd423713b..9ed830fece 100644 --- a/docs/publish.cmd +++ b/docs/publish.cmd @@ -7,11 +7,9 @@ :: cargo publish -p windows_x86_64_msvc :: cargo publish -p windows-targets -:: cargo publish -p windows-tokens :: cargo publish -p windows-metadata :: cargo publish -p windows-interface :: cargo publish -p windows-implement -:: cargo publish -p windows-bindgen +:: cargo publish -p windows-core :: cargo publish -p windows - :: cargo publish -p windows-sys