更新libclamav库1.0.0版本

This commit is contained in:
2023-01-14 18:28:39 +08:00
parent b879ee0b2e
commit 45fe15f472
8531 changed files with 1222046 additions and 177272 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,258 @@
## 0.20.0
* Add Builder::with_using_namespaces. (#688)
* Ignore PhantomPinned. (#695)
* Simplify Pin<T> to T. (#697)
* Update --pretty=expanded to -Zunpretty=expanded. (#706)
## 0.19.0
* Simplify types in generics (#663)
* Use --profile=check for macro expansion (#671)
* Use exported name to prefix enum variants (#672)
* Fix path attribute handling in inline submodules (#679)
* Fix a stack overflow with some recursive typedefs (#680)
## 0.18.0
* Simplify types in nested types such as pointed-to types and function signatures (#661)
## 0.17.0
* Add with_parse_extra_bindings to builder. (#645)
* Support NonZero and fix incorrect simplification of Option<ptr> into ptr. (#647)
* Deal with name conflicts correctly in declaration type resolution. (#651)
* Support pointers to ZSTs. (#656)
## 0.16.0
* Remove artificial restriction on lifetime parameters on enums (#604)
* Add an option for converting usize/isize into size_t/ptrdiff_t. (#606)
* Allow controlling the cargo profile used for expansion. (#607)
* Support wider range of expressions in enum discriminants (#614)
* Support generation of Cython bindings (#590)
* Fixed some issues with style=tag and recursive structs (#615)
* Default C style to Both (as specified in docs) (#615)
* Fix resolution of path dependencies from certain modules. (#629)
* Support inlined definitions for tuple variants with a single field in C (#631)
Thanks to all the awesome contributors that contributed to this release.
## 0.15.0
* Allow customizing mangling of generic parameters in C (#575)
* Box<T> simplifies to T* in C (4ce324c)
* ManuallyDrop<T> and MaybeUninit<T> simplify to T in C, and are opaque in C++ (0076a17)
* C++ supports a derive-ostream annotation to derive serialization of structs, unions and enums (#582)
* Large character constants have been fixed on Windows (#586)
* Constants are now generated for typedefs, etc (#589)
* The `sort_by` configuration option has been made to work for constants (#587)
* Default sort order is source order now (sort_by = "None"), and can be changed by the above option (#587)
## 0.14.6
* Fixed the builds with older versions of rustc.
## 0.14.5
* Add support to specify line ending style (#568)
* Add cbindgen:ptrs-as-arrays annotation to allow making function
arguments C/C++ arrays.
## 0.14.4
* Allow to override the mangling separator (#502)
* cbindgen now handles better having ZSTs in template parameters, and
default template parameters (#563).
* Support for annotating nonnull pointers (#558)
* Fixed bitflags that overflow a signed integer (#556)
* Support for wildcard argument names (#550)
* Support for the never return type, with configurable annotation (#549)
* Properly reject arrays as function arguments (#540)
## 0.14.3
* Introduce cbindgen:ignore comment annotation, to allow ignoring items or modules. (#519)
* Support for casts in constant expressions. (#526)
* Make a non-fatal error a warning message. (#535)
* Add a --metadata option to the CLI, to allow passing pre-computed cargo metadata. (#538)
## 0.14.2
* Fixed minimal dependency versions. (#507)
* Add an option to write pragma once. (#511)
* Fix submodule scanning for implicit Rust 2018 modules. (#512)
* Fix dependency parsing / scanning to handle target-specific versions. (#513)
* Use heck for case conversion. (#514)
* Add support for verbatim content after includes. (#416)
* Allow to add attributes to most generated functions. (#515)
## 0.14.1
* Handle mangling pointers. (#508)
* Unconditionally generate a return statement in partialeq implementations. (#509)
## 0.14.0
* Minor tweak at how [export.exclude] is handled to allow excluding
generic instantiations in C mode. (#501)
* Documented cpp_compat option. (#496)
* Fixed a panic when parsing associated constants for a built-in type. (#494)
## 0.13.2
* Constants now have suitable documentation. (#471)
* Fixed some C warnings by emitting void when there are no arguments. (#470)
* Avoids reading cargo.toml when not needed, which can cause panics in workspace situations.
* Only write `default` cases if the switch is not exhaustive. (#475)
* Some warnings have been refined. (#477)
* Code generation for static arrays has been fixed. (#479)
* Opt-in support for constexpr in constants. (#481)
* Fix C code generation and some warnings when extremely large constants are used. (#490)
* Proper escaping of enum variants and fields. (#483)
* Added support for RefCell (as an opaque type) and Cell. (#489)
## 0.13.1
* Support `#[cfg]` on individual enum variants. (#469)
## 0.13.0
* Support 'swift_name' attributes on generated functions (#449)
* Add [export.pre_body] to config (#452)
* Handle new line in doc attribute (#454)
* Add support for `Self` in tagged enums, structs and unions (#455, #455, #456)
* Make sentinel variant respect regular config (#459)
* Fix layout of tagged enums with size under some configurations (#463)
* Add an option to allow configuring the order of function names in generated headers (#466)
## 0.12.2
* Fixed version detection with lockfile v2. https://github.com/eqrion/cbindgen/pull/446
* Added support for export_name on functions. https://github.com/eqrion/cbindgen/pull/447
## 0.12.1
* Added support for #[repr*64)] on enums. https://github.com/eqrion/cbindgen/pull/441
* Added support to generate plain enums instead of enum classes for C++. https://github.com/eqrion/cbindgen/pull/443
* Fixed dependency resolution with lockfile v2. https://github.com/eqrion/cbindgen/pull/438
## 0.12.0
* Added support for #[repr(align)] and #[repr(packed)] on structs and unions. https://github.com/eqrion/cbindgen/pull/431
* Added support to generate copy-assignment operators for enums. https://github.com/eqrion/cbindgen/pull/434
## 0.11.1
* More binary operators and expressions are supported. https://github.com/eqrion/cbindgen/pull/425
* More built-in bitflags operators. https://github.com/eqrion/cbindgen/pull/426
## 0.11.0
* Made rust char map to uint32_t. https://github.com/eqrion/cbindgen/pull/424
## 0.10.1
* Improved error message for missing config file. https://github.com/eqrion/cbindgen/pull/422
* Add missing header for char32_t. https://github.com/eqrion/cbindgen/pull/414
## 0.10.0
* Initialize struct literal with list-initializer for C++11 standard. https://github.com/eqrion/cbindgen/pull/401
* Surround namespace with __cplusplus ifdef in cpp_compat mode. https://github.com/eqrion/cbindgen/pull/407
* Add support for --quiet flag. https://github.com/eqrion/cbindgen/pull/400
* Map char to char32_t. https://github.com/eqrion/cbindgen/pull/396
* Improve binding_crate_ref() error message. https://github.com/eqrion/cbindgen/pull/395
* avoid prematurely returning during expansion. https://github.com/eqrion/cbindgen/pull/389
* Add support for adding "using namespace" statements. https://github.com/eqrion/cbindgen/pull/387
## 0.9.1
* Various improvements to comment output. https://github.com/eqrion/cbindgen/pull/370 / https://github.com/eqrion/cbindgen/pull/375.
* Fixed expand when ran from build.rs. https://github.com/eqrion/cbindgen/pull/371
* More debugging output for expansion. https://github.com/eqrion/cbindgen/pull/383
* New option to add a default private constructor in C++ tagged enums. https://github.com/eqrion/cbindgen/pull/377
* Syn and related dependencies updated to 1.0. https://github.com/eqrion/cbindgen/pull/379
## 0.9.0
* Support to generate C headers with C++ compatibility. https://github.com/eqrion/cbindgen/pull/349
* Fix include guard generation when no_includes is set. https://github.com/eqrion/cbindgen/pull/352
* Fix crate parsing order so that types from the binding crate are preferred in presence of conflicting names. https://github.com/eqrion/cbindgen/pull/355
* Add extra_bindings option to generate bindings for dependencies. https://github.com/eqrion/cbindgen/pull/362
* Clap dependency is optional now so it's feasible to avoid building it if cbindgen is used as a library. https://github.com/eqrion/cbindgen/pull/363
## 0.8.7
* Require C++11 to run the test-suite (#341, test-only)
* Improve mangling error message (#340)
* Add the ability to automatically derive copy-constructors for tagged enums (#339)
* Use placement new for constructing in tagged unions' helper methods (#333)
## 0.8.6
* Fixed a panic when missing a lock file
## 0.8.5
* Improved support for Rust 2018 modules
* Add possibility to autogenerate tagged union destructors
## 0.8.4
* Support for package renaming in Cargo.toml
* Improved error messages for |cargo metadata| failures
* Replaced 'test.py' harness with |cargo test|
* Char constants will now be escaped properly
* Visibility of constants will now be respected
* Added a C99 doc comment style
## 0.8.2
* Improvements to bitflags parsing
## 0.8.1
* Support for manual parsing and expanding of bitflags macros
* Support for optional tagged enum casts with asserts
## 0.8.0
* support for 'includes' without default includes
* removed dependency on ancient serde_derive!
## 0.7.1
* Fix for crash when unwrapping unsupported type with associated constants
## 0.7.0
* support for libc::ssize_t
* fixed some warnings on nightly
* fix for transparent associated constants
* switched more API's to use AsRef<Path>
* impl std::error::Error for cbindgen::Error
* now support putting user-defined content in item bodies
## 0.6.8
* ptrdiff_t is now a recognized primitive type
* named function type arguments are now propagated
* fixes for struct literals in constants
* reserved C/C++ keywords are now escaped automatically
* negative enum discriminants are now supported
* fix for prefixing and formatting variant names
* fix for snake_case formatter
* syn update
* doxygen style comments are now output by default
* VaList is now a recognized primitive type
* fix for comment processing
* constant is propagated into array types
* references are now supported in IR, and used for some helper method generation
* assignment helper functions will generate array copies if necessary

440
clamav/libclamav_rust/.cargo/vendor/cbindgen/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,440 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "cbindgen"
version = "0.20.0"
dependencies = [
"clap",
"heck",
"indexmap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"serial_test",
"syn",
"tempfile",
"toml",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "getrandom"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itoa"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
[[package]]
name = "lock_api"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
]
[[package]]
name = "parking_lot"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
dependencies = [
"cfg-if",
"cloudabi",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "ppv-lite86"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
[[package]]
name = "proc-macro2"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serial_test"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b15f74add9a9d4a3eb2bf739c9a427d266d3895b53d992c3a7c234fec2ff1f1"
dependencies = [
"lazy_static",
"parking_lot",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65f59259be9fc1bf677d06cc1456e97756004a1a5a577480f71430bd7c17ba33"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "smallvec"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
"cfg-if",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "toml"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
dependencies = [
"serde",
]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@@ -0,0 +1,76 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "cbindgen"
version = "0.20.0"
authors = ["Emilio Cobos Álvarez <emilio@crisal.io>", "Jeff Muizelaar <jmuizelaar@mozilla.com>", "Kartikaya Gupta <kats@mozilla.com>", "Ryan Hunt <rhunt@eqrion.net>"]
exclude = ["tests/profile.rs"]
description = "A tool for generating C bindings to Rust code."
keywords = ["bindings", "ffi", "code-generation"]
categories = ["external-ffi-bindings", "development-tools::ffi"]
license = "MPL-2.0"
repository = "https://github.com/eqrion/cbindgen/"
[lib]
name = "cbindgen"
path = "src/lib.rs"
[[bin]]
name = "cbindgen"
path = "src/main.rs"
doc = false
required-features = ["clap"]
[dependencies.clap]
version = "2"
optional = true
[dependencies.heck]
version = "0.3"
[dependencies.indexmap]
version = "1"
[dependencies.log]
version = "0.4"
[dependencies.proc-macro2]
version = "1"
[dependencies.quote]
version = "1"
[dependencies.serde]
version = "1.0.103"
features = ["derive"]
default-features = false
[dependencies.serde_json]
version = "1.0"
[dependencies.syn]
version = "1.0.3"
features = ["clone-impls", "extra-traits", "full", "parsing", "printing"]
default-features = false
[dependencies.tempfile]
version = "3.0"
[dependencies.toml]
version = "0.5"
[dev-dependencies.serial_test]
version = "0.5.0"
[features]
default = ["clap"]
[badges.travis-ci]
repository = "eqrion/cbindgen"

View File

@@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@@ -0,0 +1,92 @@
# `cbindgen` &emsp; [![Build Status]][actions] [![Latest Version]][crates.io] [![Api Rustdoc]][rustdoc] [![Rust](https://img.shields.io/badge/rust-1.32%2B-blue.svg?maxAge=3600)](https://github.com/eqrion/cbindgen)
[Build Status]: https://github.com/eqrion/cbindgen/workflows/cbindgen/badge.svg
[actions]: https://github.com/eqrion/cbindgen/actions
[Latest Version]: https://img.shields.io/crates/v/cbindgen.svg
[crates.io]: https://crates.io/crates/cbindgen
[Api Rustdoc]: https://img.shields.io/badge/api-rustdoc-blue.svg
[rustdoc]: https://docs.rs/cbindgen
[Read the full user docs here!](docs.md)
cbindgen creates C/C++11 headers for Rust libraries which expose a public C API.
While you could do this by hand, it's not a particularly good use of your time.
It's also much more likely to be error-prone than machine-generated headers that
are based on your actual Rust code. The cbindgen developers have also worked
closely with the developers of Rust to ensure that the headers we generate
reflect actual guarantees about Rust's type layout and ABI.
C++ headers are nice because we can use operator overloads, constructors, enum
classes, and templates to make the API more ergonomic and Rust-like. C headers
are nice because you can be more confident that whoever you're interoperating
with can handle them. With cbindgen *you don't need to choose*! You can just
tell it to emit both from the same Rust library.
There are two ways to use cbindgen: as a standalone program, or as a library
(presumably in your build.rs). There isn't really much practical difference,
because cbindgen is a simple rust library with no interesting dependencies.
Using it as a program means people building your software will need it
installed. Using it in your library means people may have to build cbindgen more
frequently (e.g. every time they update their rust compiler).
It's worth noting that the development of cbindgen has been largely adhoc, as
features have been added to support the usecases of the maintainers. This means
cbindgen may randomly fail to support some particular situation simply because
no one has put in the effort to handle it yet. [Please file an issue if you run
into such a situation](https://github.com/eqrion/cbindgen/issues/new). Although
since we all have other jobs, you might need to do the implementation work too
:)
# Quick Start
To install cbindgen, you just need to run
```text
cargo install --force cbindgen
```
(--force just makes it update to the latest cbindgen if it's already installed)
To use cbindgen you need two things:
* A configuration (cbindgen.toml, which can be empty to start)
* A Rust crate with a public C API
Then all you need to do is run it:
```text
cbindgen --config cbindgen.toml --crate my_rust_library --output my_header.h
```
This produces a header file for C++. For C, add the `--lang c` switch.
See `cbindgen --help` for more options.
[Read the full user docs here!](docs.md)
[Get a template cbindgen.toml here.](template.toml)
# Examples
We don't currently have a nice tailored example application, but [the
tests](tests/rust/) contain plenty of interesting examples of our features.
You may also find it interesting to browse the projects that are using cbindgen
in production:
* [milksnake](https://github.com/getsentry/milksnake)
* [webrender](https://searchfox.org/mozilla-central/source/gfx/webrender_bindings) ([generated header](https://searchfox.org/mozilla-central/source/__GENERATED__/gfx/webrender_bindings/webrender_ffi_generated.h))
* [stylo](https://searchfox.org/mozilla-central/source/layout/style) ([generated header](https://searchfox.org/mozilla-central/source/__GENERATED__/layout/style/ServoStyleConsts.h))
* [wgpu-native](https://github.com/gfx-rs/wgpu-native) ([generated header](https://github.com/gfx-rs/wgpu-native/blob/master/ffi/wgpu.h))
* [etesync-rs](https://github.com/etesync/etesync-rs)
If you're using `cbindgen` and would like to be added to this list, please open
a pull request!
# Releases
cbindgen doesn't have a fixed release calendar, please file an issue requesting
a release if there's something fixed in trunk that you need released. Ping
`@emilio` for increased effect.

View File

@@ -0,0 +1,70 @@
fn generate_tests() {
use std::env;
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let mut dst = File::create(Path::new(&out_dir).join("tests.rs")).unwrap();
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let tests_dir = manifest_dir.join("tests").join("rust");
let tests = fs::read_dir(&tests_dir).unwrap();
let entries = tests.map(|t| t.expect("Couldn't read test file"));
println!("cargo:rerun-if-changed={}", tests_dir.display());
// Try to make a decent guess at where our binary will end up in.
//
// TODO(emilio): Ideally running tests will just use the library-version of
// cbindgen instead of the built binary.
let cbindgen_path = out_dir
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("cbindgen");
for entry in entries {
let path_segment = if entry.file_type().unwrap().is_file() {
match entry.path().extension().and_then(OsStr::to_str) {
Some("rs") => {}
_ => continue,
};
entry
.path()
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_owned()
} else {
entry.file_name().to_str().unwrap().to_owned()
};
let identifier = path_segment
.replace(|c| !char::is_alphanumeric(c), "_")
.replace("__", "_");
writeln!(
dst,
"test_file!({:?}, test_{}, {:?}, {:?});",
cbindgen_path,
identifier,
path_segment,
entry.path(),
)
.unwrap();
}
dst.flush().unwrap();
}
fn main() {
generate_tests();
}

View File

@@ -0,0 +1,24 @@
# Contributing
Thanks for wanting to contribute!
If you want help or mentorship, please file a Github issue and I'll be sure to provide guidance to the best of my ability.
Otherwise be sure to check out `internals.md` for an overview on the internals.
## Filing a pull request
Check out [Servo's Github workflow](https://github.com/servo/servo/wiki/Github-workflow) for an overview on creating a pull request.
Don't worry about requesting code review, as there is nothing formally setup for this repository. I try and review each pull request as soon as I can.
There is continuous integration setup for `cbindgen` using [travis](https://travis-ci.org/). It automatically runs `cargo test` which runs `cbindgen` against a series of rust files from `tests/rust/` and checks that the output compiles using `gcc` or `g++`.
In addition to a C/C++ compiler `cargo test` requires Python and Cython
(`python -m pip install Cython`) for checking Cython bindings generated from tests (`.pyx` files).
Please run `cargo test` before filing a pull request to be sure that all tests pass. This will also update the test expectations.
Rustfmt is also enforced by travis. To format your code install `rustfmt-preview` using `rustup component add rustfmt-preview` and then `cargo fmt`. Travis runs with rust nightly, so use `rustup run nightly -- cargo fmt` to guarantee consistent results.
Writing new tests with your pull requests is also appreciated.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
## Overview
`cbindgen` works in four phases:
1. *Parsing* - Crate information is gathered from `cargo`, and `rust` source files are read using `syn`
1. *Loading* - `syn` AST nodes are converted into an IR of `Item`s that loosely correspond to the C types that will be output
1. *Transformation* - Several passes are run that transform the IR. Some examples:
- Generic `type` aliases are used to specialize the type they refer to
- Annotations are transferred from `type` aliases to the item they refer to
- `Option<&T>` is converted to `*const T`
- `Option<&mut T>` is converted to `*mut T`
- Generic paths in struct fields, union variants, and static globals are collected and used to generate monomorphs of the structs or unions they refer to
- The items are sorted by dependencies and type and unused items are filtered out
1. *Writing* - The IR is pretty printed to a file or `stdout`
## Process Flow
The main interface for `cbindgen` is `bindgen::Builder` which accepts configuration options and either a crate directory to parse or a list of source files.
If a list of source files is given, then `bindgen::Builder` will parse them using `parser::parse_src` which will use `syn` to parse a specific file. No `extern crate` items will be followed for dependencies, but `mod` items will be attempted to be followed.
If a crate directory is given, then `bindgen::Builder` will use `cargo::Cargo` to load a dependency graph from `Cargo.toml`, `Cargo.lock`, and `cargo metadata`. Then `parser::parse_lib` will parse each crate, following `extern crate` items when `ParseConfig::parse_deps` is enabled and the crate is not in the whitelist or blacklist of crates. In addition `bindgen::Parser` may use `cargo expand` on a crate to expand macro definitions.
Once the `syn` nodes are collected by either method, they are given to `bindgen::Parse` which will perform *Loading* by creating a `ir::Item` for each `syn` node as appropriate.
`bindgen::Builder` will then convert the resulting `bindgen::Parse`'s into a `bindgen::Library` which is the driver of all of the *Transformation* passes.
// TODO - Talk more about passes
Then finally the `bindgen::Library` will create a `bindgen::Bindings` which contains the `ir::Item`'s that are ready to be written. The `bindgen::Bindings` can then be written to `stdout` or a specific file.

View File

@@ -0,0 +1,445 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path;
use std::rc::Rc;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::ir::{
Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct,
};
use crate::bindgen::writer::{Source, SourceWriter};
/// A bindings header that can be written.
pub struct Bindings {
pub config: Config,
/// The map from path to struct, used to lookup whether a given type is a
/// transparent struct. This is needed to generate code for constants.
struct_map: ItemMap<Struct>,
struct_fileds_memo: RefCell<HashMap<BindgenPath, Rc<Vec<String>>>>,
globals: Vec<Static>,
constants: Vec<Constant>,
items: Vec<ItemContainer>,
functions: Vec<Function>,
}
#[derive(PartialEq)]
enum NamespaceOperation {
Open,
Close,
}
impl Bindings {
pub(crate) fn new(
config: Config,
struct_map: ItemMap<Struct>,
constants: Vec<Constant>,
globals: Vec<Static>,
items: Vec<ItemContainer>,
functions: Vec<Function>,
) -> Bindings {
Bindings {
config,
struct_map,
struct_fileds_memo: Default::default(),
globals,
constants,
items,
functions,
}
}
// FIXME(emilio): What to do when the configuration doesn't match?
pub fn struct_is_transparent(&self, path: &BindgenPath) -> bool {
let mut any = false;
self.struct_map.for_items(path, |s| any |= s.is_transparent);
any
}
pub fn struct_exists(&self, path: &BindgenPath) -> bool {
let mut any = false;
self.struct_map.for_items(path, |_| any = true);
any
}
pub fn struct_field_names(&self, path: &BindgenPath) -> Rc<Vec<String>> {
let mut memos = self.struct_fileds_memo.borrow_mut();
if let Some(memo) = memos.get(path) {
return memo.clone();
}
let mut fields = Vec::<String>::new();
self.struct_map.for_items(path, |st| {
let mut pos: usize = 0;
for field in &st.fields {
if let Some(found_pos) = fields.iter().position(|v| *v == field.name) {
pos = found_pos + 1;
} else {
fields.insert(pos, field.name.clone());
pos += 1;
}
}
});
let fields = Rc::new(fields);
memos.insert(path.clone(), fields.clone());
fields
}
pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
// Don't compare files if we've never written this file before
if !path.as_ref().is_file() {
if let Some(parent) = path::Path::new(path.as_ref()).parent() {
fs::create_dir_all(parent).unwrap();
}
self.write(File::create(path).unwrap());
return true;
}
let mut new_file_contents = Vec::new();
self.write(&mut new_file_contents);
let mut old_file_contents = Vec::new();
{
let mut old_file = File::open(&path).unwrap();
old_file.read_to_end(&mut old_file_contents).unwrap();
}
if old_file_contents != new_file_contents {
let mut new_file = File::create(&path).unwrap();
new_file.write_all(&new_file_contents).unwrap();
true
} else {
false
}
}
pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) {
if let Some(ref f) = self.config.header {
out.new_line_if_not_start();
write!(out, "{}", f);
out.new_line();
}
if let Some(f) = self.config.include_guard() {
out.new_line_if_not_start();
write!(out, "#ifndef {}", f);
out.new_line();
write!(out, "#define {}", f);
out.new_line();
}
if self.config.pragma_once && self.config.language != Language::Cython {
out.new_line_if_not_start();
write!(out, "#pragma once");
out.new_line();
}
if self.config.include_version {
out.new_line_if_not_start();
write!(
out,
"/* Generated with cbindgen:{} */",
crate::bindgen::config::VERSION
);
out.new_line();
}
if let Some(ref f) = self.config.autogen_warning {
out.new_line_if_not_start();
write!(out, "{}", f);
out.new_line();
}
if self.config.no_includes
&& self.config.sys_includes().is_empty()
&& self.config.includes().is_empty()
&& (self.config.cython.cimports.is_empty() || self.config.language != Language::Cython)
&& self.config.after_includes.is_none()
{
return;
}
out.new_line_if_not_start();
if !self.config.no_includes {
match self.config.language {
Language::C => {
out.write("#include <stdarg.h>");
out.new_line();
out.write("#include <stdbool.h>");
out.new_line();
if self.config.usize_is_size_t {
out.write("#include <stddef.h>");
out.new_line();
}
out.write("#include <stdint.h>");
out.new_line();
out.write("#include <stdlib.h>");
out.new_line();
}
Language::Cxx => {
out.write("#include <cstdarg>");
out.new_line();
if self.config.usize_is_size_t {
out.write("#include <cstddef>");
out.new_line();
}
out.write("#include <cstdint>");
out.new_line();
out.write("#include <cstdlib>");
out.new_line();
out.write("#include <ostream>");
out.new_line();
out.write("#include <new>");
out.new_line();
if self.config.enumeration.cast_assert_name.is_none()
&& (self.config.enumeration.derive_mut_casts
|| self.config.enumeration.derive_const_casts)
{
out.write("#include <cassert>");
out.new_line();
}
}
Language::Cython => {
out.write(
"from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t",
);
out.new_line();
out.write(
"from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t",
);
out.new_line();
out.write("cdef extern from *");
out.open_brace();
out.write("ctypedef bint bool");
out.new_line();
out.write("ctypedef struct va_list");
out.new_line();
out.close_brace(false);
}
}
}
for include in self.config.sys_includes() {
write!(out, "#include <{}>", include);
out.new_line();
}
for include in self.config.includes() {
write!(out, "#include \"{}\"", include);
out.new_line();
}
if self.config.language == Language::Cython {
for (module, names) in &self.config.cython.cimports {
write!(out, "from {} cimport {}", module, names.join(", "));
out.new_line();
}
}
if let Some(ref line) = self.config.after_includes {
write!(out, "{}", line);
out.new_line();
}
}
pub fn write<F: Write>(&self, file: F) {
let mut out = SourceWriter::new(file, self);
self.write_headers(&mut out);
self.open_namespaces(&mut out);
for constant in &self.constants {
if constant.uses_only_primitive_types() {
out.new_line_if_not_start();
constant.write(&self.config, &mut out, None);
out.new_line();
}
}
for item in &self.items {
if item
.deref()
.annotations()
.bool("no-export")
.unwrap_or(false)
{
continue;
}
out.new_line_if_not_start();
match *item {
ItemContainer::Constant(..) => unreachable!(),
ItemContainer::Static(..) => unreachable!(),
ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
}
out.new_line();
}
for constant in &self.constants {
if !constant.uses_only_primitive_types() {
out.new_line_if_not_start();
constant.write(&self.config, &mut out, None);
out.new_line();
}
}
if !self.functions.is_empty() || !self.globals.is_empty() {
if self.config.cpp_compatible_c() {
out.new_line_if_not_start();
out.write("#ifdef __cplusplus");
}
if self.config.language == Language::Cxx {
if let Some(ref using_namespaces) = self.config.using_namespaces {
for namespace in using_namespaces {
out.new_line();
write!(out, "using namespace {};", namespace);
}
out.new_line();
}
}
if self.config.language == Language::Cxx || self.config.cpp_compatible_c() {
out.new_line();
out.write("extern \"C\" {");
out.new_line();
}
if self.config.cpp_compatible_c() {
out.write("#endif // __cplusplus");
out.new_line();
}
for global in &self.globals {
out.new_line_if_not_start();
global.write(&self.config, &mut out);
out.new_line();
}
for function in &self.functions {
out.new_line_if_not_start();
function.write(&self.config, &mut out);
out.new_line();
}
if self.config.cpp_compatible_c() {
out.new_line();
out.write("#ifdef __cplusplus");
}
if self.config.language == Language::Cxx || self.config.cpp_compatible_c() {
out.new_line();
out.write("} // extern \"C\"");
out.new_line();
}
if self.config.cpp_compatible_c() {
out.write("#endif // __cplusplus");
out.new_line();
}
}
if self.config.language == Language::Cython
&& self.globals.is_empty()
&& self.constants.is_empty()
&& self.items.is_empty()
&& self.functions.is_empty()
{
out.write("pass");
}
self.close_namespaces(&mut out);
if let Some(f) = self.config.include_guard() {
out.new_line_if_not_start();
if self.config.language == Language::C {
write!(out, "#endif /* {} */", f);
} else {
write!(out, "#endif // {}", f);
}
out.new_line();
}
if let Some(ref f) = self.config.trailer {
out.new_line_if_not_start();
write!(out, "{}", f);
if !f.ends_with('\n') {
out.new_line();
}
}
}
fn all_namespaces(&self) -> Vec<&str> {
if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() {
return vec![];
}
let mut ret = vec![];
if let Some(ref namespace) = self.config.namespace {
ret.push(&**namespace);
}
if let Some(ref namespaces) = self.config.namespaces {
for namespace in namespaces {
ret.push(&**namespace);
}
}
ret
}
fn open_close_namespaces<F: Write>(&self, op: NamespaceOperation, out: &mut SourceWriter<F>) {
if self.config.language == Language::Cython {
if op == NamespaceOperation::Open {
out.new_line();
let header = self.config.cython.header.as_deref().unwrap_or("*");
write!(out, "cdef extern from {}", header);
out.open_brace();
} else {
out.close_brace(false);
}
return;
}
let mut namespaces = self.all_namespaces();
if namespaces.is_empty() {
return;
}
if op == NamespaceOperation::Close {
namespaces.reverse();
}
if self.config.cpp_compatible_c() {
out.new_line_if_not_start();
out.write("#ifdef __cplusplus");
}
for namespace in namespaces {
out.new_line();
match op {
NamespaceOperation::Open => write!(out, "namespace {} {{", namespace),
NamespaceOperation::Close => write!(out, "}} // namespace {}", namespace),
}
}
out.new_line();
if self.config.cpp_compatible_c() {
out.write("#endif // __cplusplus");
out.new_line();
}
}
pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
self.open_close_namespaces(NamespaceOperation::Open, out);
}
pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
self.open_close_namespaces(NamespaceOperation::Close, out);
}
}

View File

@@ -0,0 +1,138 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use proc_macro2::TokenStream;
use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};
// $(#[$outer:meta])*
// ($($vis:tt)*) $BitFlags:ident: $T:ty {
// $(
// $(#[$inner:ident $($args:tt)*])*
// const $Flag:ident = $value:expr;
// )+
// }
#[derive(Debug)]
pub struct Bitflags {
attrs: Vec<syn::Attribute>,
vis: syn::Visibility,
struct_token: Token![struct],
name: syn::Ident,
colon_token: Token![:],
repr: syn::Type,
flags: Flags,
}
impl Bitflags {
pub fn expand(&self) -> (syn::ItemStruct, syn::ItemImpl) {
let Bitflags {
ref attrs,
ref vis,
ref name,
ref repr,
ref flags,
..
} = *self;
let struct_ = parse_quote! {
/// cbindgen:internal-derive-bitflags=true
#(#attrs)*
#vis struct #name {
bits: #repr,
}
};
let consts = flags.expand(name, repr);
let impl_ = parse_quote! {
impl #name {
#consts
}
};
(struct_, impl_)
}
}
impl Parse for Bitflags {
fn parse(input: ParseStream) -> ParseResult<Self> {
Ok(Self {
attrs: input.call(syn::Attribute::parse_outer)?,
vis: input.parse()?,
struct_token: input.parse()?,
name: input.parse()?,
colon_token: input.parse()?,
repr: input.parse()?,
flags: input.parse()?,
})
}
}
// $(#[$inner:ident $($args:tt)*])*
// const $Flag:ident = $value:expr;
#[derive(Debug)]
struct Flag {
attrs: Vec<syn::Attribute>,
const_token: Token![const],
name: syn::Ident,
equals_token: Token![=],
value: syn::Expr,
semicolon_token: Token![;],
}
impl Flag {
fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream {
let Flag {
ref attrs,
ref name,
ref value,
..
} = *self;
quote! {
#(#attrs)*
pub const #name : #struct_name = #struct_name { bits: (#value) as #repr };
}
}
}
impl Parse for Flag {
fn parse(input: ParseStream) -> ParseResult<Self> {
Ok(Self {
attrs: input.call(syn::Attribute::parse_outer)?,
const_token: input.parse()?,
name: input.parse()?,
equals_token: input.parse()?,
value: input.parse()?,
semicolon_token: input.parse()?,
})
}
}
#[derive(Debug)]
struct Flags(Vec<Flag>);
impl Parse for Flags {
fn parse(input: ParseStream) -> ParseResult<Self> {
let content;
let _ = braced!(content in input);
let mut flags = vec![];
while !content.is_empty() {
flags.push(content.parse()?);
}
Ok(Flags(flags))
}
}
impl Flags {
fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream {
let mut ts = quote! {};
for flag in &self.0 {
ts.extend(flag.expand(struct_name, repr));
}
ts
}
}
pub fn parse(tokens: TokenStream) -> ParseResult<Bitflags> {
let parser = Bitflags::parse;
parser.parse2(tokens)
}

View File

@@ -0,0 +1,396 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::path;
use crate::bindgen::bindings::Bindings;
use crate::bindgen::cargo::Cargo;
use crate::bindgen::config::{Braces, Config, Language, Profile, Style};
use crate::bindgen::error::Error;
use crate::bindgen::library::Library;
use crate::bindgen::parser::{self, Parse};
/// A builder for generating a bindings header.
#[derive(Debug, Clone)]
pub struct Builder {
config: Config,
srcs: Vec<path::PathBuf>,
lib: Option<(path::PathBuf, Option<String>)>,
lib_cargo: Option<Cargo>,
std_types: bool,
lockfile: Option<path::PathBuf>,
}
impl Builder {
#[allow(clippy::new_without_default)]
pub fn new() -> Builder {
Builder {
config: Config::default(),
srcs: Vec::new(),
lib: None,
lib_cargo: None,
std_types: true,
lockfile: None,
}
}
#[allow(unused)]
pub fn with_header<S: AsRef<str>>(mut self, header: S) -> Builder {
self.config.header = Some(String::from(header.as_ref()));
self
}
#[allow(unused)]
pub fn with_no_includes(mut self) -> Builder {
self.config.no_includes = true;
self
}
#[allow(unused)]
pub fn with_include<S: AsRef<str>>(mut self, include: S) -> Builder {
self.config.includes.push(String::from(include.as_ref()));
self
}
#[allow(unused)]
pub fn with_sys_include<S: AsRef<str>>(mut self, include: S) -> Builder {
self.config
.sys_includes
.push(String::from(include.as_ref()));
self
}
#[allow(unused)]
pub fn with_after_include<S: AsRef<str>>(mut self, line: S) -> Builder {
self.config.after_includes = Some(String::from(line.as_ref()));
self
}
#[allow(unused)]
pub fn with_trailer<S: AsRef<str>>(mut self, trailer: S) -> Builder {
self.config.trailer = Some(String::from(trailer.as_ref()));
self
}
#[allow(unused)]
pub fn with_include_guard<S: AsRef<str>>(mut self, include_guard: S) -> Builder {
self.config.include_guard = Some(String::from(include_guard.as_ref()));
self
}
#[allow(unused)]
pub fn with_pragma_once(mut self, pragma_once: bool) -> Builder {
self.config.pragma_once = pragma_once;
self
}
#[allow(unused)]
pub fn with_autogen_warning<S: AsRef<str>>(mut self, autogen_warning: S) -> Builder {
self.config.autogen_warning = Some(String::from(autogen_warning.as_ref()));
self
}
#[allow(unused)]
pub fn with_include_version(mut self, include_version: bool) -> Builder {
self.config.include_version = include_version;
self
}
#[allow(unused)]
pub fn with_namespace<S: AsRef<str>>(mut self, namespace: S) -> Builder {
self.config.namespace = Some(String::from(namespace.as_ref()));
self
}
#[allow(unused)]
pub fn with_namespaces<S: AsRef<str>>(mut self, namespaces: &[S]) -> Builder {
self.config.namespaces = Some(
namespaces
.iter()
.map(|x| String::from(x.as_ref()))
.collect(),
);
self
}
#[allow(unused)]
pub fn with_using_namespaces<S: AsRef<str>>(mut self, namespaces: &[S]) -> Builder {
self.config.using_namespaces = Some(
namespaces
.iter()
.map(|x| String::from(x.as_ref()))
.collect(),
);
self
}
#[allow(unused)]
pub fn with_braces(mut self, braces: Braces) -> Builder {
self.config.braces = braces;
self
}
#[allow(unused)]
pub fn with_line_length(mut self, line_length: usize) -> Builder {
self.config.line_length = line_length;
self
}
#[allow(unused)]
pub fn with_tab_width(mut self, tab_width: usize) -> Builder {
self.config.tab_width = tab_width;
self
}
#[allow(unused)]
pub fn with_language(mut self, language: Language) -> Builder {
self.config.language = language;
self
}
#[allow(unused)]
pub fn with_style(mut self, style: Style) -> Builder {
self.config.style = style;
self
}
#[allow(unused)]
pub fn include_item<S: AsRef<str>>(mut self, item_name: S) -> Builder {
self.config
.export
.include
.push(String::from(item_name.as_ref()));
self
}
#[allow(unused)]
pub fn exclude_item<S: AsRef<str>>(mut self, item_name: S) -> Builder {
self.config
.export
.exclude
.push(String::from(item_name.as_ref()));
self
}
#[allow(unused)]
pub fn rename_item<S: AsRef<str>>(mut self, from: S, to: S) -> Builder {
self.config
.export
.rename
.insert(String::from(from.as_ref()), String::from(to.as_ref()));
self
}
#[allow(unused)]
pub fn with_item_prefix<S: AsRef<str>>(mut self, prefix: S) -> Builder {
self.config.export.prefix = Some(String::from(prefix.as_ref()));
self
}
#[allow(unused)]
pub fn with_parse_deps(mut self, parse_deps: bool) -> Builder {
self.config.parse.parse_deps = parse_deps;
self
}
#[allow(unused)]
pub fn with_parse_include<S: AsRef<str>>(mut self, include: &[S]) -> Builder {
self.config.parse.include =
Some(include.iter().map(|x| String::from(x.as_ref())).collect());
self
}
#[allow(unused)]
pub fn with_parse_exclude<S: AsRef<str>>(mut self, exclude: &[S]) -> Builder {
self.config.parse.exclude = exclude.iter().map(|x| String::from(x.as_ref())).collect();
self
}
#[allow(unused)]
pub fn with_parse_expand<S: AsRef<str>>(mut self, expand: &[S]) -> Builder {
self.config.parse.expand.crates = expand.iter().map(|x| String::from(x.as_ref())).collect();
self
}
#[allow(unused)]
pub fn with_parse_expand_all_features(mut self, expand_all_features: bool) -> Builder {
self.config.parse.expand.all_features = expand_all_features;
self
}
#[allow(unused)]
pub fn with_parse_expand_default_features(mut self, expand_default_features: bool) -> Builder {
self.config.parse.expand.default_features = expand_default_features;
self
}
#[allow(unused)]
pub fn with_parse_expand_features<S: AsRef<str>>(mut self, expand_features: &[S]) -> Builder {
self.config.parse.expand.features = Some(
expand_features
.iter()
.map(|x| String::from(x.as_ref()))
.collect(),
);
self
}
#[allow(unused)]
pub fn with_parse_expand_profile(mut self, profile: Profile) -> Builder {
self.config.parse.expand.profile = profile;
self
}
#[allow(unused)]
pub fn with_parse_extra_bindings<S: AsRef<str>>(mut self, extra_bindings: &[S]) -> Builder {
self.config.parse.extra_bindings = extra_bindings
.iter()
.map(|x| String::from(x.as_ref()))
.collect();
self
}
#[allow(unused)]
pub fn with_only_target_dependencies(mut self, only_target_dependencies: bool) -> Builder {
self.config.only_target_dependencies = only_target_dependencies;
self
}
#[allow(unused)]
pub fn with_documentation(mut self, documentation: bool) -> Builder {
self.config.documentation = documentation;
self
}
#[allow(unused)]
pub fn with_target_os_define(mut self, platform: &str, preprocessor_define: &str) -> Builder {
self.config.defines.insert(
format!("target_os = {}", platform),
preprocessor_define.to_owned(),
);
self
}
#[allow(unused)]
pub fn with_define(mut self, key: &str, value: &str, preprocessor_define: &str) -> Builder {
self.config.defines.insert(
format!("{} = {}", key, value),
preprocessor_define.to_owned(),
);
self
}
#[allow(unused)]
pub fn with_config(mut self, config: Config) -> Builder {
self.config = config;
self
}
#[allow(unused)]
pub fn with_std_types(mut self, std_types: bool) -> Builder {
self.std_types = std_types;
self
}
#[allow(unused)]
pub fn with_src<P: AsRef<path::Path>>(mut self, src: P) -> Builder {
self.srcs.push(src.as_ref().to_owned());
self
}
#[allow(unused)]
pub fn with_crate<P: AsRef<path::Path>>(mut self, lib_dir: P) -> Builder {
debug_assert!(self.lib.is_none());
debug_assert!(self.lib_cargo.is_none());
self.lib = Some((path::PathBuf::from(lib_dir.as_ref()), None));
self
}
#[allow(unused)]
pub fn with_crate_and_name<P: AsRef<path::Path>, S: AsRef<str>>(
mut self,
lib_dir: P,
binding_lib_name: S,
) -> Builder {
debug_assert!(self.lib.is_none());
debug_assert!(self.lib_cargo.is_none());
self.lib = Some((
path::PathBuf::from(lib_dir.as_ref()),
Some(String::from(binding_lib_name.as_ref())),
));
self
}
#[allow(unused)]
pub(crate) fn with_cargo(mut self, lib: Cargo) -> Builder {
debug_assert!(self.lib.is_none());
debug_assert!(self.lib_cargo.is_none());
self.lib_cargo = Some(lib);
self
}
#[allow(unused)]
pub fn with_lockfile<P: AsRef<path::Path>>(mut self, lockfile: P) -> Builder {
debug_assert!(self.lockfile.is_none());
debug_assert!(self.lib_cargo.is_none());
self.lockfile = Some(path::PathBuf::from(lockfile.as_ref()));
self
}
pub fn generate(self) -> Result<Bindings, Error> {
let mut result = Parse::new();
if self.std_types {
result.add_std_types();
}
for x in &self.srcs {
result.extend_with(&parser::parse_src(x, &self.config)?);
}
if let Some((lib_dir, binding_lib_name)) = self.lib.clone() {
let lockfile = self.lockfile.as_ref().and_then(|p| p.to_str());
let cargo = Cargo::load(
&lib_dir,
lockfile,
binding_lib_name.as_deref(),
self.config.parse.parse_deps,
self.config.parse.clean,
self.config.only_target_dependencies,
/* existing_metadata = */ None,
)?;
result.extend_with(&parser::parse_lib(cargo, &self.config)?);
} else if let Some(cargo) = self.lib_cargo.clone() {
result.extend_with(&parser::parse_lib(cargo, &self.config)?);
}
Library::new(
self.config,
result.constants,
result.globals,
result.enums,
result.structs,
result.unions,
result.opaque_items,
result.typedefs,
result.functions,
)
.generate()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_style() {
assert_eq!(
Style::Tag,
Builder::new().with_style(Style::Tag).config.style
);
}
}

View File

@@ -0,0 +1,252 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::path::{Path, PathBuf};
use crate::bindgen::cargo::cargo_expand;
use crate::bindgen::cargo::cargo_lock::{self, Lock};
pub(crate) use crate::bindgen::cargo::cargo_metadata::PackageRef;
use crate::bindgen::cargo::cargo_metadata::{self, Metadata};
use crate::bindgen::cargo::cargo_toml;
use crate::bindgen::config::Profile;
use crate::bindgen::error::Error;
use crate::bindgen::ir::Cfg;
/// Parse a dependency string used in Cargo.lock
fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) {
let split: Vec<&str> = dep_string.split_whitespace().collect();
(split[0], split.get(1).cloned())
}
/// A collection of metadata for a library from cargo.
#[derive(Clone, Debug)]
pub(crate) struct Cargo {
manifest_path: PathBuf,
binding_crate_name: String,
lock: Option<Lock>,
metadata: Metadata,
clean: bool,
}
impl Cargo {
/// Gather metadata from cargo for a specific library and binding crate
/// name. If dependency finding isn't needed then Cargo.lock files don't
/// need to be parsed.
pub(crate) fn load(
crate_dir: &Path,
lock_file: Option<&str>,
binding_crate_name: Option<&str>,
use_cargo_lock: bool,
clean: bool,
only_target_dependencies: bool,
existing_metadata_file: Option<&Path>,
) -> Result<Cargo, Error> {
let toml_path = crate_dir.join("Cargo.toml");
let metadata =
cargo_metadata::metadata(&toml_path, existing_metadata_file, only_target_dependencies)
.map_err(|x| Error::CargoMetadata(toml_path.to_str().unwrap().to_owned(), x))?;
let lock_path = lock_file
.map(PathBuf::from)
.unwrap_or_else(|| Path::new(&metadata.workspace_root).join("Cargo.lock"));
let lock = if use_cargo_lock {
match cargo_lock::lock(&lock_path) {
Ok(lock) => Some(lock),
Err(x) => {
warn!("Couldn't load lock file {:?}: {:?}", lock_path, x);
None
}
}
} else {
None
};
// Use the specified binding crate name or infer it from the manifest
let binding_crate_name = match binding_crate_name {
Some(s) => s.to_owned(),
None => {
let manifest = cargo_toml::manifest(&toml_path)
.map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?;
manifest.package.name
}
};
Ok(Cargo {
manifest_path: toml_path,
binding_crate_name,
lock,
metadata,
clean,
})
}
pub(crate) fn binding_crate_name(&self) -> &str {
&self.binding_crate_name
}
pub(crate) fn binding_crate_ref(&self) -> PackageRef {
match self.find_pkg_ref(&self.binding_crate_name) {
Some(pkg_ref) => pkg_ref,
None => panic!(
"Unable to find {} for {:?}",
self.binding_crate_name, self.manifest_path
),
}
}
pub(crate) fn dependencies(&self, package: &PackageRef) -> Vec<(PackageRef, Option<Cfg>)> {
let lock = match self.lock {
Some(ref lock) => lock,
None => return vec![],
};
let mut dependencies = None;
// Find the dependencies listing in the lockfile
if let Some(ref root) = lock.root {
// If the version is not on the lockfile then it shouldn't be
// ambiguous.
if root.name == package.name
&& package
.version
.as_ref()
.map_or(true, |v| *v == root.version)
{
dependencies = root.dependencies.as_ref();
}
}
if dependencies.is_none() {
if let Some(ref lock_packages) = lock.package {
for lock_package in lock_packages {
if lock_package.name == package.name
&& package
.version
.as_ref()
.map_or(true, |v| *v == lock_package.version)
{
dependencies = lock_package.dependencies.as_ref();
break;
}
}
}
}
if dependencies.is_none() {
return vec![];
}
dependencies
.unwrap()
.iter()
.map(|dep| {
let (dep_name, dep_version) = parse_dep_string(dep);
// If a version was not specified find the only package with the name of the dependency
let dep_version = dep_version.or_else(|| {
let mut versions = self.metadata.packages.iter().filter_map(|package| {
if package.name_and_version.name != dep_name {
return None;
}
package.name_and_version.version.as_deref()
});
// If the iterator contains more items, meaning multiple versions of the same
// package are present, warn! amd abort.
let version = versions.next();
if versions.next().is_none() {
version
} else {
warn!("when looking for a version for package {}, multiple versions where found", dep_name);
None
}
});
// Try to find the cfgs in the Cargo.toml
let cfg = self
.metadata
.packages
.get(package)
.and_then(|meta_package| meta_package.dependencies.get(dep_name))
.and_then(|meta_dep| Cfg::load_metadata(meta_dep));
let package_ref = PackageRef {
name: dep_name.to_owned(),
version: dep_version.map(|v| v.to_owned()),
};
(package_ref, cfg)
})
.collect()
}
/// Finds the package reference in `cargo metadata` that has `package_name`
/// ignoring the version.
fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
for package in &self.metadata.packages {
if package.name_and_version.name == package_name {
return Some(package.name_and_version.clone());
}
}
None
}
/// Finds the directory for a specified package reference.
#[allow(unused)]
pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
self.metadata
.packages
.get(package)
.and_then(|meta_package| {
Path::new(&meta_package.manifest_path)
.parent()
.map(|x| x.to_owned())
})
}
/// Finds `src/lib.rs` for a specified package reference.
pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
let kind_lib = String::from("lib");
let kind_staticlib = String::from("staticlib");
let kind_rlib = String::from("rlib");
let kind_cdylib = String::from("cdylib");
let kind_dylib = String::from("dylib");
self.metadata
.packages
.get(package)
.and_then(|meta_package| {
for target in &meta_package.targets {
if target.kind.contains(&kind_lib)
|| target.kind.contains(&kind_staticlib)
|| target.kind.contains(&kind_rlib)
|| target.kind.contains(&kind_cdylib)
|| target.kind.contains(&kind_dylib)
{
return Some(PathBuf::from(&target.src_path));
}
}
None
})
}
pub(crate) fn expand_crate(
&self,
package: &PackageRef,
expand_all_features: bool,
expand_default_features: bool,
expand_features: &Option<Vec<String>>,
profile: Profile,
) -> Result<String, cargo_expand::Error> {
cargo_expand::expand(
&self.manifest_path,
&package.name,
package.version.as_deref(),
self.clean,
expand_all_features,
expand_default_features,
expand_features,
profile,
)
}
}

View File

@@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::bindgen::config::Profile;
use std::env;
use std::error;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::{from_utf8, Utf8Error};
extern crate tempfile;
use self::tempfile::Builder;
#[derive(Debug)]
/// Possible errors that can occur during `rustc -Zunpretty=expanded`.
pub enum Error {
/// Error during creation of temporary directory
Io(io::Error),
/// Output of `cargo metadata` was not valid utf8
Utf8(Utf8Error),
/// Error during execution of `cargo rustc -Zunpretty=expanded`
Compile(String),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::Utf8(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(ref err) => err.fmt(f),
Error::Utf8(ref err) => err.fmt(f),
Error::Compile(ref err) => write!(f, "{}", err),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Io(ref err) => Some(err),
Error::Utf8(ref err) => Some(err),
Error::Compile(..) => None,
}
}
}
/// Use rustc to expand and pretty print the crate into a single file,
/// removing any macros in the process.
#[allow(clippy::too_many_arguments)]
pub fn expand(
manifest_path: &Path,
crate_name: &str,
version: Option<&str>,
use_tempdir: bool,
expand_all_features: bool,
expand_default_features: bool,
expand_features: &Option<Vec<String>>,
profile: Profile,
) -> Result<String, Error> {
let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
let mut cmd = Command::new(cargo);
let mut _temp_dir = None; // drop guard
if use_tempdir {
_temp_dir = Some(Builder::new().prefix("cbindgen-expand").tempdir()?);
cmd.env("CARGO_TARGET_DIR", _temp_dir.unwrap().path());
} else if let Ok(ref path) = env::var("CARGO_EXPAND_TARGET_DIR") {
cmd.env("CARGO_TARGET_DIR", path);
} else if let Ok(ref path) = env::var("OUT_DIR") {
// When cbindgen was started programatically from a build.rs file, Cargo is running and
// locking the default target directory. In this case we need to use another directory,
// else we would end up in a deadlock. If Cargo is running `OUT_DIR` will be set, so we
// can use a directory relative to that.
cmd.env("CARGO_TARGET_DIR", PathBuf::from(path).join("expanded"));
}
// Set this variable so that we don't call it recursively if we expand a crate that is using
// cbindgen
cmd.env("_CBINDGEN_IS_RUNNING", "1");
cmd.arg("rustc");
cmd.arg("--lib");
// When build with the release profile we can't choose the `check` profile.
if profile != Profile::Release {
cmd.arg("--profile=check");
}
cmd.arg("--manifest-path");
cmd.arg(manifest_path);
if let Some(features) = expand_features {
cmd.arg("--features");
let mut features_str = String::new();
for (index, feature) in features.iter().enumerate() {
if index != 0 {
features_str.push(' ');
}
features_str.push_str(feature);
}
cmd.arg(features_str);
}
if expand_all_features {
cmd.arg("--all-features");
}
if !expand_default_features {
cmd.arg("--no-default-features");
}
match profile {
Profile::Debug => {}
Profile::Release => {
cmd.arg("--release");
}
}
cmd.arg("-p");
let mut package = crate_name.to_owned();
if let Some(version) = version {
package.push(':');
package.push_str(version);
}
cmd.arg(&package);
cmd.arg("--verbose");
cmd.arg("--");
cmd.arg("-Zunpretty=expanded");
info!("Command: {:?}", cmd);
let output = cmd.output()?;
let src = from_utf8(&output.stdout)?.to_owned();
let error = from_utf8(&output.stderr)?.to_owned();
if src.is_empty() {
Err(Error::Compile(error))
} else {
Ok(src)
}
}

View File

@@ -0,0 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
#[derive(Debug)]
/// Possible errors that can occur during Cargo.toml parsing.
pub enum Error {
/// Error during reading of Cargo.toml
Io(io::Error),
/// Deserialization error
Toml(toml::de::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl From<toml::de::Error> for Error {
fn from(err: toml::de::Error) -> Self {
Error::Toml(err)
}
}
#[derive(Clone, Deserialize, Debug)]
pub struct Lock {
pub root: Option<Package>,
pub package: Option<Vec<Package>>,
}
#[derive(Clone, Deserialize, Debug)]
pub struct Package {
pub name: String,
pub version: String,
/// A list of dependencies formatted like "NAME VERSION-OPT REGISTRY-OPT"
pub dependencies: Option<Vec<String>>,
}
/// Parse the Cargo.toml for a given path
pub fn lock(manifest_path: &Path) -> Result<Lock, Error> {
let mut s = String::new();
let mut f = File::open(manifest_path)?;
f.read_to_string(&mut s)?;
toml::from_str::<Lock>(&s).map_err(|x| x.into())
}

View File

@@ -0,0 +1,257 @@
#![deny(missing_docs)]
//! Structured access to the output of `cargo metadata`
//! Usually used from within a `cargo-*` executable
// Forked from `https://github.com/oli-obk/cargo_metadata`
// Modifications:
// 1. Remove `resolve` from Metadata because it was causing parse failures
// 2. Fix the `manifest-path` argument
// 3. Add `--all-features` argument
// 4. Remove the `--no-deps` argument
use std::borrow::{Borrow, Cow};
use std::collections::{HashMap, HashSet};
use std::env;
use std::error;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::io;
use std::path::Path;
use std::process::{Command, Output};
use std::str::Utf8Error;
#[derive(Clone, Deserialize, Debug)]
/// Starting point for metadata returned by `cargo metadata`
pub struct Metadata {
/// A list of all crates referenced by this crate (and the crate itself)
pub packages: HashSet<Package>,
version: usize,
/// path to the workspace containing the `Cargo.lock`
pub workspace_root: String,
}
/// A reference to a package including it's name and the specific version.
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct PackageRef {
pub name: String,
pub version: Option<String>,
}
#[derive(Clone, Deserialize, Debug)]
/// A crate
pub struct Package {
#[serde(flatten)]
pub name_and_version: PackageRef,
id: String,
source: Option<String>,
/// List of dependencies of this particular package
pub dependencies: HashSet<Dependency>,
/// Targets provided by the crate (lib, bin, example, test, ...)
pub targets: Vec<Target>,
features: HashMap<String, Vec<String>>,
/// path containing the `Cargo.toml`
pub manifest_path: String,
}
#[derive(Clone, Deserialize, Debug)]
/// A dependency of the main crate
pub struct Dependency {
/// Name as given in the `Cargo.toml`
pub name: String,
source: Option<String>,
/// Whether this is required or optional
pub req: String,
kind: Option<String>,
optional: bool,
uses_default_features: bool,
features: Vec<String>,
pub target: Option<String>,
}
#[derive(Clone, Deserialize, Debug)]
/// A single target (lib, bin, example, ...) provided by a crate
pub struct Target {
/// Name as given in the `Cargo.toml` or generated from the file name
pub name: String,
/// Kind of target ("bin", "example", "test", "bench", "lib")
pub kind: Vec<String>,
/// Almost the same as `kind`, except when an example is a library instad of an executable.
/// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example`
#[serde(default)]
pub crate_types: Vec<String>,
/// Path to the main source file of the target
pub src_path: String,
}
#[derive(Debug)]
/// Possible errors that can occur during metadata parsing.
pub enum Error {
/// Error during execution of `cargo metadata`
Io(io::Error),
/// Metadata extraction failure
Metadata(Output),
/// Output of `cargo metadata` was not valid utf8
Utf8(Utf8Error),
/// Deserialization error (structure of json did not match expected structure)
Json(serde_json::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::Utf8(err)
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::Json(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(ref err) => err.fmt(f),
Error::Metadata(_) => write!(f, "Metadata error"),
Error::Utf8(ref err) => err.fmt(f),
Error::Json(ref err) => err.fmt(f),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Io(ref err) => Some(err),
Error::Metadata(_) => None,
Error::Utf8(ref err) => Some(err),
Error::Json(ref err) => Some(err),
}
}
}
// Implementations that let us lookup Packages and Dependencies by name (string)
impl Borrow<PackageRef> for Package {
fn borrow(&self) -> &PackageRef {
&self.name_and_version
}
}
impl Hash for Package {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name_and_version.hash(state);
}
}
impl PartialEq for Package {
fn eq(&self, other: &Self) -> bool {
self.name_and_version == other.name_and_version
}
}
impl Eq for Package {}
impl Borrow<str> for Dependency {
fn borrow(&self) -> &str {
&self.name
}
}
impl Hash for Dependency {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl PartialEq for Dependency {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl Eq for Dependency {}
fn discover_target(manifest_path: &Path) -> Option<String> {
if let Ok(target) = std::env::var("TARGET") {
return Some(target);
}
// We must be running as a standalone script, not under cargo.
// Let's use the host platform instead.
// We figure out the host platform through rustc and use that.
// We unfortunatelly cannot go through cargo, since cargo rustc _also_ builds.
// If `rustc` fails to run, we just fall back to not passing --filter-platforms.
//
// NOTE: We set the current directory in case of rustup shenanigans.
let rustc = env::var("RUSTC").unwrap_or_else(|_| String::from("rustc"));
debug!("Discovering host platform by {:?}", rustc);
let rustc_output = Command::new(rustc)
.current_dir(manifest_path.parent().unwrap())
.arg("-vV")
.output();
let rustc_output = match rustc_output {
Ok(ref out) => String::from_utf8_lossy(&out.stdout),
Err(..) => return None,
};
let field = "host: ";
rustc_output.lines().find_map(|l| {
// XXX l.strip_prefix(field) re-implemented to preserve MSRV
if l.starts_with(field) {
Some(l[field.len()..].into())
} else {
None
}
})
}
/// The main entry point to obtaining metadata
pub fn metadata(
manifest_path: &Path,
existing_metadata_file: Option<&Path>,
only_target: bool,
) -> Result<Metadata, Error> {
let output;
let metadata = match existing_metadata_file {
Some(path) => Cow::Owned(std::fs::read_to_string(path)?),
None => {
let target = if only_target {
let target = discover_target(manifest_path);
if target.is_none() {
warn!(
"Failed to discover host platform for cargo metadata; \
will fetch dependencies for all platforms."
);
}
target
} else {
None
};
let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
let mut cmd = Command::new(cargo);
cmd.arg("metadata");
cmd.arg("--all-features");
cmd.arg("--format-version").arg("1");
if let Some(target) = target {
cmd.arg("--filter-platform").arg(target);
}
cmd.arg("--manifest-path");
cmd.arg(manifest_path);
output = cmd.output()?;
if !output.status.success() {
return Err(Error::Metadata(output));
}
Cow::Borrowed(std::str::from_utf8(&output.stdout)?)
}
};
let meta: Metadata = serde_json::from_str(&*metadata)?;
Ok(meta)
}

View File

@@ -0,0 +1,67 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::error;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
#[derive(Debug)]
/// Possible errors that can occur during Cargo.toml parsing.
pub enum Error {
/// Error during reading of Cargo.toml
Io(io::Error),
/// Deserialization error
Toml(toml::de::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl From<toml::de::Error> for Error {
fn from(err: toml::de::Error) -> Self {
Error::Toml(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(ref err) => err.fmt(f),
Error::Toml(ref err) => err.fmt(f),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Io(ref err) => Some(err),
Error::Toml(ref err) => Some(err),
}
}
}
#[derive(Clone, Deserialize, Debug)]
pub struct Manifest {
pub package: Package,
}
#[derive(Clone, Deserialize, Debug)]
pub struct Package {
pub name: String,
}
/// Parse the Cargo.toml for a given path
pub fn manifest(manifest_path: &Path) -> Result<Manifest, Error> {
let mut s = String::new();
let mut f = File::open(manifest_path)?;
f.read_to_string(&mut s)?;
toml::from_str::<Manifest>(&s).map_err(|x| x.into())
}

View File

@@ -0,0 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[allow(clippy::module_inception)]
mod cargo;
pub(crate) mod cargo_expand;
pub(crate) mod cargo_lock;
pub(crate) mod cargo_metadata;
pub(crate) mod cargo_toml;
pub(crate) use self::cargo::*;

View File

@@ -0,0 +1,325 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::declarationtyperesolver::DeclarationType;
use crate::bindgen::ir::{ArrayLength, Function, Type};
use crate::bindgen::writer::{ListType, SourceWriter};
use crate::bindgen::{Config, Language};
// This code is for translating Rust types into C declarations.
// See Section 6.7, Declarations, in the C standard for background.
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
enum CDeclarator {
Ptr {
is_const: bool,
is_nullable: bool,
is_ref: bool,
},
Array(String),
Func(Vec<(Option<String>, CDecl)>, bool),
}
impl CDeclarator {
fn is_ptr(&self) -> bool {
match self {
CDeclarator::Ptr { .. } | CDeclarator::Func(..) => true,
_ => false,
}
}
}
struct CDecl {
type_qualifers: String,
type_name: String,
type_generic_args: Vec<Type>,
declarators: Vec<CDeclarator>,
type_ctype: Option<DeclarationType>,
}
impl CDecl {
fn new() -> CDecl {
CDecl {
type_qualifers: String::new(),
type_name: String::new(),
type_generic_args: Vec::new(),
declarators: Vec::new(),
type_ctype: None,
}
}
fn from_type(t: &Type, config: &Config) -> CDecl {
let mut cdecl = CDecl::new();
cdecl.build_type(t, false, config);
cdecl
}
fn from_func_arg(t: &Type, array_length: Option<&str>, config: &Config) -> CDecl {
let mut cdecl = CDecl::new();
let length = match array_length {
Some(l) => l,
None => return CDecl::from_type(t, config),
};
let (ty, is_const) = match t {
Type::Ptr { ty, is_const, .. } => (ty, is_const),
_ => unreachable!(
"Should never have an array length for a non pointer type {:?}",
t
),
};
let ptr_as_array = Type::Array(ty.clone(), ArrayLength::Value(length.to_string()));
cdecl.build_type(&ptr_as_array, *is_const, config);
cdecl
}
fn from_func(f: &Function, layout_vertical: bool, config: &Config) -> CDecl {
let mut cdecl = CDecl::new();
cdecl.build_func(f, layout_vertical, config);
cdecl
}
fn build_func(&mut self, f: &Function, layout_vertical: bool, config: &Config) {
let args = f
.args
.iter()
.map(|arg| {
(
arg.name.clone(),
CDecl::from_func_arg(&arg.ty, arg.array_length.as_deref(), config),
)
})
.collect();
self.declarators
.push(CDeclarator::Func(args, layout_vertical));
self.build_type(&f.ret, false, config);
}
fn build_type(&mut self, t: &Type, is_const: bool, config: &Config) {
match t {
Type::Path(ref generic) => {
if is_const {
assert!(
self.type_qualifers.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_qualifers = "const".to_owned();
}
assert!(
self.type_name.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_name = generic.export_name().to_owned();
assert!(
self.type_generic_args.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_generic_args = generic.generics().to_owned();
self.type_ctype = generic.ctype().cloned();
}
Type::Primitive(ref p) => {
if is_const {
assert!(
self.type_qualifers.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_qualifers = "const".to_owned();
}
assert!(
self.type_name.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_name = p.to_repr_c(config).to_string();
}
Type::Ptr {
ref ty,
is_nullable,
is_const: ptr_is_const,
is_ref,
} => {
self.declarators.push(CDeclarator::Ptr {
is_const,
is_nullable: *is_nullable,
is_ref: *is_ref,
});
self.build_type(ty, *ptr_is_const, config);
}
Type::Array(ref t, ref constant) => {
let len = constant.as_str().to_owned();
self.declarators.push(CDeclarator::Array(len));
self.build_type(t, is_const, config);
}
Type::FuncPtr {
ref ret,
ref args,
is_nullable: _,
} => {
let args = args
.iter()
.map(|(ref name, ref ty)| (name.clone(), CDecl::from_type(ty, config)))
.collect();
self.declarators.push(CDeclarator::Ptr {
is_const: false,
is_nullable: true,
is_ref: false,
});
self.declarators.push(CDeclarator::Func(args, false));
self.build_type(ret, false, config);
}
}
}
fn write<F: Write>(&self, out: &mut SourceWriter<F>, ident: Option<&str>, config: &Config) {
// Write the type-specifier and type-qualifier first
if !self.type_qualifers.is_empty() {
write!(out, "{} ", self.type_qualifers);
}
if config.language != Language::Cython {
if let Some(ref ctype) = self.type_ctype {
write!(out, "{} ", ctype.to_str());
}
}
write!(out, "{}", self.type_name);
if !self.type_generic_args.is_empty() {
out.write("<");
out.write_horizontal_source_list(&self.type_generic_args, ListType::Join(", "));
out.write(">");
}
// When we have an identifier, put a space between the type and the declarators
if ident.is_some() {
out.write(" ");
}
// Write the left part of declarators before the identifier
let mut iter_rev = self.declarators.iter().rev().peekable();
#[allow(clippy::while_let_on_iterator)]
while let Some(declarator) = iter_rev.next() {
let next_is_pointer = iter_rev.peek().map_or(false, |x| x.is_ptr());
match *declarator {
CDeclarator::Ptr {
is_const,
is_nullable,
is_ref,
} => {
out.write(if is_ref { "&" } else { "*" });
if is_const {
out.write("const ");
}
if !is_nullable && !is_ref && config.language != Language::Cython {
if let Some(attr) = &config.pointer.non_null_attribute {
write!(out, "{} ", attr);
}
}
}
CDeclarator::Array(..) => {
if next_is_pointer {
out.write("(");
}
}
CDeclarator::Func(..) => {
if next_is_pointer {
out.write("(");
}
}
}
}
// Write the identifier
if let Some(ident) = ident {
write!(out, "{}", ident);
}
// Write the right part of declarators after the identifier
let mut iter = self.declarators.iter();
let mut last_was_pointer = false;
#[allow(clippy::while_let_on_iterator)]
while let Some(declarator) = iter.next() {
match *declarator {
CDeclarator::Ptr { .. } => {
last_was_pointer = true;
}
CDeclarator::Array(ref constant) => {
if last_was_pointer {
out.write(")");
}
write!(out, "[{}]", constant);
last_was_pointer = false;
}
CDeclarator::Func(ref args, layout_vertical) => {
if last_was_pointer {
out.write(")");
}
out.write("(");
if args.is_empty() && config.language == Language::C {
out.write("void");
}
if layout_vertical {
let align_length = out.line_length_for_align();
out.push_set_spaces(align_length);
for (i, &(ref arg_ident, ref arg_ty)) in args.iter().enumerate() {
if i != 0 {
out.write(",");
out.new_line();
}
// Convert &Option<String> to Option<&str>
let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
arg_ty.write(out, arg_ident, config);
}
out.pop_tab();
} else {
for (i, &(ref arg_ident, ref arg_ty)) in args.iter().enumerate() {
if i != 0 {
out.write(", ");
}
// Convert &Option<String> to Option<&str>
let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
arg_ty.write(out, arg_ident, config);
}
}
out.write(")");
last_was_pointer = true;
}
}
}
}
}
pub fn write_func<F: Write>(
out: &mut SourceWriter<F>,
f: &Function,
layout_vertical: bool,
config: &Config,
) {
CDecl::from_func(f, layout_vertical, config).write(out, Some(f.path().name()), config);
}
pub fn write_field<F: Write>(out: &mut SourceWriter<F>, t: &Type, ident: &str, config: &Config) {
CDecl::from_type(t, config).write(out, Some(ident), config);
}
pub fn write_type<F: Write>(out: &mut SourceWriter<F>, t: &Type, config: &Config) {
CDecl::from_type(t, config).write(out, None, config);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::bindgen::ir::Path;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
impl DeclarationType {
pub fn to_str(self) -> &'static str {
match self {
DeclarationType::Struct => "struct",
DeclarationType::Enum => "enum",
DeclarationType::Union => "union",
}
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum DeclarationType {
Struct,
Enum,
Union,
}
#[derive(Default)]
pub struct DeclarationTypeResolver {
types: HashMap<Path, Option<DeclarationType>>,
}
impl DeclarationTypeResolver {
fn insert(&mut self, path: &Path, ty: Option<DeclarationType>) {
if let Entry::Vacant(vacant_entry) = self.types.entry(path.clone()) {
vacant_entry.insert(ty);
}
}
pub fn add_enum(&mut self, path: &Path) {
self.insert(path, Some(DeclarationType::Enum));
}
pub fn add_struct(&mut self, path: &Path) {
self.insert(path, Some(DeclarationType::Struct));
}
pub fn add_union(&mut self, path: &Path) {
self.insert(path, Some(DeclarationType::Union));
}
pub fn add_none(&mut self, path: &Path) {
self.insert(path, None);
}
pub fn type_for(&self, path: &Path) -> Option<DeclarationType> {
*self.types.get(path)?
}
}

View File

@@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cmp::Ordering;
use std::collections::HashSet;
use crate::bindgen::ir::{ItemContainer, Path};
/// A dependency list is used for gathering what order to output the types.
#[derive(Default)]
pub struct Dependencies {
pub order: Vec<ItemContainer>,
pub items: HashSet<Path>,
}
impl Dependencies {
pub fn new() -> Dependencies {
Dependencies {
order: Vec::new(),
items: HashSet::new(),
}
}
pub fn sort(&mut self) {
// Sort untagged enums and opaque structs into their own layers because they don't
// depend on each other or anything else.
let ordering = |a: &ItemContainer, b: &ItemContainer| match (a, b) {
(&ItemContainer::Enum(ref x), &ItemContainer::Enum(ref y))
if x.tag.is_none() && y.tag.is_none() =>
{
x.path.cmp(&y.path)
}
(&ItemContainer::Enum(ref x), _) if x.tag.is_none() => Ordering::Less,
(_, &ItemContainer::Enum(ref x)) if x.tag.is_none() => Ordering::Greater,
(&ItemContainer::OpaqueItem(ref x), &ItemContainer::OpaqueItem(ref y)) => {
x.path.cmp(&y.path)
}
(&ItemContainer::OpaqueItem(_), _) => Ordering::Less,
(_, &ItemContainer::OpaqueItem(_)) => Ordering::Greater,
_ => Ordering::Equal,
};
self.order.sort_by(ordering);
}
}

View File

@@ -0,0 +1,88 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::error;
use std::fmt;
pub use crate::bindgen::cargo::cargo_expand::Error as CargoExpandError;
pub use crate::bindgen::cargo::cargo_metadata::Error as CargoMetadataError;
pub use crate::bindgen::cargo::cargo_toml::Error as CargoTomlError;
pub use syn::parse::Error as ParseError;
#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
pub enum Error {
CargoMetadata(String, CargoMetadataError),
CargoToml(String, CargoTomlError),
CargoExpand(String, CargoExpandError),
ParseSyntaxError {
crate_name: String,
src_path: String,
error: ParseError,
},
ParseCannotOpenFile {
crate_name: String,
src_path: String,
},
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::CargoMetadata(ref path, ref error) => write!(
f,
"Couldn't execute `cargo metadata` with manifest {:?}: {:?}",
path, error
),
Error::CargoToml(ref path, ref error) => {
write!(f, "Couldn't load manifest file {:?}: {:?}", path, error)
}
Error::CargoExpand(ref crate_name, ref error) => write!(
f,
"Parsing crate `{}`: couldn't run `cargo rustc -Zunpretty=expanded`: {:?}",
crate_name, error
),
Error::ParseSyntaxError {
ref crate_name,
ref src_path,
ref error,
} => {
write!(
f,
"Parsing crate `{}`:`{}`:\n{:?}",
crate_name, src_path, error
)?;
if !src_path.is_empty() {
write!(
f,
"\nTry running `rustc -Z parse-only {}` to see a nicer error message",
src_path,
)?
}
Ok(())
}
Error::ParseCannotOpenFile {
ref crate_name,
ref src_path,
} => write!(
f,
"Parsing crate `{}`: cannot open file `{}`.",
crate_name, src_path
),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::CargoMetadata(_, ref error) => Some(error),
Error::CargoToml(_, ref error) => Some(error),
Error::CargoExpand(_, ref error) => Some(error),
Error::ParseSyntaxError { ref error, .. } => Some(error),
Error::ParseCannotOpenFile { .. } => None,
}
}
}

View File

@@ -0,0 +1,179 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::str::FromStr;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::utilities::SynAttributeHelpers;
// A system for specifying properties on items. Annotations are
// given through document comments and parsed by this code.
//
// An annotation is in the form cbindgen:PROPERTY=VALUE
// Where PROPERTY depends on the item
// Where VALUE can be
// * list - [Item1, Item2, Item3, ...]
// * atom - Foo
// * bool - true,false
// Examples:
// * cbindgen:field-names=[mHandle, mNamespace]
// * cbindgen:function-postfix=WR_DESTRUCTOR_SAFE
/// A value specified by an annotation.
#[derive(Debug, Clone)]
pub enum AnnotationValue {
List(Vec<String>),
Atom(Option<String>),
Bool(bool),
}
/// A set of annotations specified by a document comment.
#[derive(Debug, Default, Clone)]
pub struct AnnotationSet {
annotations: HashMap<String, AnnotationValue>,
pub must_use: bool,
}
impl AnnotationSet {
pub fn new() -> AnnotationSet {
AnnotationSet {
annotations: HashMap::new(),
must_use: false,
}
}
pub fn is_empty(&self) -> bool {
self.annotations.is_empty() && !self.must_use
}
pub(crate) fn must_use(&self, config: &Config) -> bool {
self.must_use && config.language != Language::Cython
}
pub fn load(attrs: &[syn::Attribute]) -> Result<AnnotationSet, String> {
let lines = attrs.get_comment_lines();
let lines: Vec<&str> = lines
.iter()
.filter_map(|line| {
let line = line.trim_start();
if !line.starts_with("cbindgen:") {
return None;
}
Some(line)
})
.collect();
let must_use = attrs.has_attr_word("must_use");
let mut annotations = HashMap::new();
// Look at each line for an annotation
for line in lines {
debug_assert!(line.starts_with("cbindgen:"));
// Remove the "cbindgen:" prefix
let annotation = &line[9..];
// Split the annotation in two
let parts: Vec<&str> = annotation.split('=').map(|x| x.trim()).collect();
if parts.len() > 2 {
return Err(format!("Couldn't parse {}.", line));
}
// Grab the name that this annotation is modifying
let name = parts[0];
// If the annotation only has a name, assume it's setting a bool flag
if parts.len() == 1 {
annotations.insert(name.to_string(), AnnotationValue::Bool(true));
continue;
}
// Parse the value we're setting the name to
let value = parts[1];
if let Some(x) = parse_list(value) {
annotations.insert(name.to_string(), AnnotationValue::List(x));
continue;
}
if let Ok(x) = value.parse::<bool>() {
annotations.insert(name.to_string(), AnnotationValue::Bool(x));
continue;
}
annotations.insert(
name.to_string(),
if value.is_empty() {
AnnotationValue::Atom(None)
} else {
AnnotationValue::Atom(Some(value.to_string()))
},
);
}
Ok(AnnotationSet {
annotations,
must_use,
})
}
/// Adds an annotation value if none is specified.
pub fn add_default(&mut self, name: &str, value: AnnotationValue) {
if let Entry::Vacant(e) = self.annotations.entry(name.to_string()) {
e.insert(value);
}
}
pub fn list(&self, name: &str) -> Option<Vec<String>> {
match self.annotations.get(name) {
Some(&AnnotationValue::List(ref x)) => Some(x.clone()),
_ => None,
}
}
pub fn atom(&self, name: &str) -> Option<Option<String>> {
match self.annotations.get(name) {
Some(&AnnotationValue::Atom(ref x)) => Some(x.clone()),
_ => None,
}
}
pub fn bool(&self, name: &str) -> Option<bool> {
match self.annotations.get(name) {
Some(&AnnotationValue::Bool(ref x)) => Some(*x),
_ => None,
}
}
pub fn parse_atom<T>(&self, name: &str) -> Option<T>
where
T: Default + FromStr,
{
match self.annotations.get(name) {
Some(&AnnotationValue::Atom(ref x)) => Some(
x.as_ref()
.map_or(T::default(), |y| y.parse::<T>().ok().unwrap()),
),
_ => None,
}
}
}
/// Parse lists like "[x, y, z]". This is not implemented efficiently or well.
fn parse_list(list: &str) -> Option<Vec<String>> {
if list.len() < 2 {
return None;
}
match (list.chars().next(), list.chars().last()) {
(Some('['), Some(']')) => Some(
list[1..list.len() - 1]
.split(',')
.map(|x| x.trim().to_string())
.collect(),
),
_ => None,
}
}

View File

@@ -0,0 +1,365 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::fmt;
use std::io::Write;
use crate::bindgen::cargo::cargo_metadata::Dependency;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::writer::SourceWriter;
#[derive(PartialEq, Eq)]
enum DefineKey<'a> {
Boolean(&'a str),
Named(&'a str, &'a str),
}
impl<'a> DefineKey<'a> {
fn load(key: &str) -> DefineKey {
// TODO: dirty parser
if !key.contains('=') {
return DefineKey::Boolean(key);
}
let mut splits = key.trim().split('=');
let name = match splits.next() {
Some(n) => n.trim(),
None => return DefineKey::Boolean(key),
};
let value = match splits.next() {
Some(v) => v.trim(),
None => return DefineKey::Boolean(key),
};
if splits.next().is_some() {
return DefineKey::Boolean(key);
}
DefineKey::Named(name, value)
}
}
#[derive(Debug, Clone)]
pub enum Cfg {
Boolean(String),
Named(String, String),
Any(Vec<Cfg>),
All(Vec<Cfg>),
Not(Box<Cfg>),
}
impl fmt::Display for Cfg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Cfg::Boolean(key) => write!(f, "{}", key),
Cfg::Named(key, value) => write!(f, "{} = {:?}", key, value),
Cfg::Any(cfgs) => {
write!(f, "any(")?;
for (index, cfg) in cfgs.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", cfg)?;
}
write!(f, ")")
}
Cfg::All(cfgs) => {
write!(f, "all(")?;
for (index, cfg) in cfgs.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", cfg)?;
}
write!(f, ")")
}
Cfg::Not(cfg) => write!(f, "not({})", cfg),
}
}
}
impl Cfg {
pub fn join(cfgs: &[Cfg]) -> Option<Cfg> {
if cfgs.is_empty() {
None
} else {
Some(Cfg::All(cfgs.to_owned()))
}
}
pub fn append(parent: Option<&Cfg>, child: Option<Cfg>) -> Option<Cfg> {
match (parent, child) {
(None, None) => None,
(None, Some(child)) => Some(child),
(Some(parent), None) => Some(parent.clone()),
(Some(parent), Some(child)) => Some(Cfg::All(vec![parent.clone(), child])),
}
}
pub fn load(attrs: &[syn::Attribute]) -> Option<Cfg> {
let mut configs = Vec::new();
for attr in attrs {
if let Ok(syn::Meta::List(syn::MetaList { path, nested, .. })) = attr.parse_meta() {
if !path.is_ident("cfg") || nested.len() != 1 {
continue;
}
if let Some(config) = Cfg::load_single(nested.first().unwrap()) {
configs.push(config);
}
}
}
match configs.len() {
0 => None,
1 => Some(configs.pop().unwrap()),
_ => Some(Cfg::All(configs)),
}
}
pub fn load_metadata(dependency: &Dependency) -> Option<Cfg> {
let target = dependency.target.as_ref()?;
match syn::parse_str::<syn::Meta>(target) {
Ok(target) => {
// Parsing succeeded using the #[cfg] syntax
if let syn::Meta::List(syn::MetaList { path, nested, .. }) = target {
if !path.is_ident("cfg") || nested.len() != 1 {
return None;
}
Cfg::load_single(nested.first().unwrap())
} else {
None
}
}
Err(_) => {
// Parsing failed using #[cfg], this may be a literal target
// name
Cfg::load_single(&syn::NestedMeta::Lit(syn::Lit::Str(syn::LitStr::new(
target,
proc_macro2::Span::call_site(),
))))
}
}
}
fn load_single(item: &syn::NestedMeta) -> Option<Cfg> {
Some(match *item {
syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => {
Cfg::Boolean(format!("{}", path.segments.first().unwrap().ident))
}
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
ref path,
lit: syn::Lit::Str(ref value),
..
})) => Cfg::Named(
format!("{}", path.segments.first().unwrap().ident),
value.value(),
),
syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList {
ref path,
ref nested,
..
})) => {
if path.is_ident("any") {
Cfg::Any(Cfg::load_list(nested.iter())?)
} else if path.is_ident("all") {
Cfg::All(Cfg::load_list(nested.iter())?)
} else if path.is_ident("not") {
if nested.len() != 1 {
return None;
}
Cfg::Not(Box::new(Cfg::load_single(&nested[0])?))
} else {
return None;
}
}
_ => return None,
})
}
fn load_list<'a, I: Iterator<Item = &'a syn::NestedMeta>>(attrs: I) -> Option<Vec<Cfg>> {
let mut configs = Vec::new();
for attr in attrs {
configs.push(Cfg::load_single(attr)?);
}
if configs.is_empty() {
None
} else {
Some(configs)
}
}
}
pub trait ToCondition: Sized {
fn to_condition(&self, config: &Config) -> Option<Condition>;
}
impl<'a> ToCondition for Option<Cfg> {
fn to_condition(&self, config: &Config) -> Option<Condition> {
self.as_ref()?.to_condition(config)
}
}
impl<'a> ToCondition for Cfg {
fn to_condition(&self, config: &Config) -> Option<Condition> {
match *self {
Cfg::Boolean(ref cfg_name) => {
let define = config
.defines
.iter()
.find(|(key, ..)| DefineKey::Boolean(cfg_name) == DefineKey::load(key));
if let Some((_, define)) = define {
Some(Condition::Define(define.to_owned()))
} else {
warn!(
"Missing `[defines]` entry for `{}` in cbindgen config.",
self,
);
None
}
}
Cfg::Named(ref cfg_name, ref cfg_value) => {
let define = config.defines.iter().find(|(key, ..)| {
DefineKey::Named(cfg_name, cfg_value) == DefineKey::load(key)
});
if let Some((_, define)) = define {
Some(Condition::Define(define.to_owned()))
} else {
warn!(
"Missing `[defines]` entry for `{}` in cbindgen config.",
self,
);
None
}
}
Cfg::Any(ref children) => {
let conditions: Vec<_> = children
.iter()
.filter_map(|x| x.to_condition(config))
.collect();
match conditions.len() {
0 => None,
1 => conditions.into_iter().next(),
_ => Some(Condition::Any(conditions)),
}
}
Cfg::All(ref children) => {
let cfgs: Vec<_> = children
.iter()
.filter_map(|x| x.to_condition(config))
.collect();
match cfgs.len() {
0 => None,
1 => cfgs.into_iter().next(),
_ => Some(Condition::All(cfgs)),
}
}
Cfg::Not(ref child) => child
.to_condition(config)
.map(|cfg| Condition::Not(Box::new(cfg))),
}
}
}
#[derive(Debug, Clone)]
pub enum Condition {
Define(String),
Any(Vec<Condition>),
All(Vec<Condition>),
Not(Box<Condition>),
}
impl Condition {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
match *self {
Condition::Define(ref define) => {
if config.language == Language::Cython {
write!(out, "{}", define);
} else {
out.write("defined(");
write!(out, "{}", define);
out.write(")");
}
}
Condition::Any(ref conditions) => {
out.write("(");
for (i, condition) in conditions.iter().enumerate() {
if i != 0 {
out.write(if config.language == Language::Cython {
" or "
} else {
" || "
});
}
condition.write(config, out);
}
out.write(")");
}
Condition::All(ref conditions) => {
out.write("(");
for (i, condition) in conditions.iter().enumerate() {
if i != 0 {
out.write(if config.language == Language::Cython {
" and "
} else {
" && "
});
}
condition.write(config, out);
}
out.write(")");
}
Condition::Not(ref condition) => {
out.write(if config.language == Language::Cython {
"not "
} else {
"!"
});
condition.write(config, out);
}
}
}
}
pub trait ConditionWrite {
fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
}
impl ConditionWrite for Option<Condition> {
fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if let Some(ref cfg) = *self {
if config.language == Language::Cython {
out.write("IF ");
cfg.write(config, out);
out.open_brace();
} else {
out.push_set_spaces(0);
out.write("#if ");
cfg.write(config, out);
out.pop_set_spaces();
out.new_line();
}
}
}
fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if self.is_some() {
if config.language == Language::Cython {
out.close_brace(false);
} else {
out.new_line();
out.push_set_spaces(0);
out.write("#endif");
out.pop_set_spaces();
}
}
}
}

View File

@@ -0,0 +1,603 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::collections::HashMap;
use std::io::Write;
use syn::{self, UnOp};
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
Struct, ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::writer::{Source, SourceWriter};
use crate::bindgen::Bindings;
#[derive(Debug, Clone)]
pub enum Literal {
Expr(String),
Path(String),
PostfixUnaryOp {
op: &'static str,
value: Box<Literal>,
},
BinOp {
left: Box<Literal>,
op: &'static str,
right: Box<Literal>,
},
Struct {
path: Path,
export_name: String,
fields: HashMap<String, Literal>,
},
Cast {
ty: Type,
value: Box<Literal>,
},
}
impl Literal {
fn replace_self_with(&mut self, self_ty: &Path) {
match *self {
Literal::PostfixUnaryOp { ref mut value, .. } => {
value.replace_self_with(self_ty);
}
Literal::BinOp {
ref mut left,
ref mut right,
..
} => {
left.replace_self_with(self_ty);
right.replace_self_with(self_ty);
}
Literal::Struct {
ref mut path,
ref mut export_name,
ref mut fields,
} => {
if path.replace_self_with(self_ty) {
*export_name = self_ty.name().to_owned();
}
for ref mut expr in fields.values_mut() {
expr.replace_self_with(self_ty);
}
}
Literal::Cast {
ref mut ty,
ref mut value,
} => {
ty.replace_self_with(self_ty);
value.replace_self_with(self_ty);
}
Literal::Expr(..) | Literal::Path(..) => {}
}
}
fn is_valid(&self, bindings: &Bindings) -> bool {
match *self {
Literal::Expr(..) => true,
Literal::Path(..) => true,
Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings),
Literal::BinOp {
ref left,
ref right,
..
} => left.is_valid(bindings) && right.is_valid(bindings),
Literal::Struct { ref path, .. } => bindings.struct_exists(path),
Literal::Cast { ref value, .. } => value.is_valid(bindings),
}
}
pub fn uses_only_primitive_types(&self) -> bool {
match self {
Literal::Expr(..) => true,
Literal::Path(..) => true,
Literal::PostfixUnaryOp { ref value, .. } => value.uses_only_primitive_types(),
Literal::BinOp {
ref left,
ref right,
..
} => left.uses_only_primitive_types() & right.uses_only_primitive_types(),
Literal::Struct { .. } => false,
Literal::Cast { ref value, ref ty } => {
value.uses_only_primitive_types() && ty.is_primitive_or_ptr_primitive()
}
}
}
}
impl Literal {
pub fn rename_for_config(&mut self, config: &Config) {
match self {
Literal::Struct {
ref mut export_name,
fields,
..
} => {
config.export.rename(export_name);
for lit in fields.values_mut() {
lit.rename_for_config(config);
}
}
Literal::Path(ref mut name) => {
config.export.rename(name);
}
Literal::PostfixUnaryOp { ref mut value, .. } => {
value.rename_for_config(config);
}
Literal::BinOp {
ref mut left,
ref mut right,
..
} => {
left.rename_for_config(config);
right.rename_for_config(config);
}
Literal::Expr(_) => {}
Literal::Cast {
ref mut ty,
ref mut value,
} => {
ty.rename_for_config(config, &GenericParams::default());
value.rename_for_config(config);
}
}
}
// Translate from full blown `syn::Expr` into a simpler `Literal` type
pub fn load(expr: &syn::Expr) -> Result<Literal, String> {
match *expr {
// Match binary expressions of the form `a * b`
syn::Expr::Binary(ref bin_expr) => {
let l = Self::load(&bin_expr.left)?;
let r = Self::load(&bin_expr.right)?;
let op = match bin_expr.op {
syn::BinOp::Add(..) => "+",
syn::BinOp::Sub(..) => "-",
syn::BinOp::Mul(..) => "*",
syn::BinOp::Div(..) => "/",
syn::BinOp::Rem(..) => "%",
syn::BinOp::And(..) => "&&",
syn::BinOp::Or(..) => "||",
syn::BinOp::BitXor(..) => "^",
syn::BinOp::BitAnd(..) => "&",
syn::BinOp::BitOr(..) => "|",
syn::BinOp::Shl(..) => "<<",
syn::BinOp::Shr(..) => ">>",
syn::BinOp::Eq(..) => "==",
syn::BinOp::Lt(..) => "<",
syn::BinOp::Le(..) => "<=",
syn::BinOp::Ne(..) => "!=",
syn::BinOp::Ge(..) => ">=",
syn::BinOp::Gt(..) => ">",
syn::BinOp::AddEq(..) => "+=",
syn::BinOp::SubEq(..) => "-=",
syn::BinOp::MulEq(..) => "*=",
syn::BinOp::DivEq(..) => "/=",
syn::BinOp::RemEq(..) => "%=",
syn::BinOp::BitXorEq(..) => "^=",
syn::BinOp::BitAndEq(..) => "&=",
syn::BinOp::BitOrEq(..) => "|=",
syn::BinOp::ShlEq(..) => ">>=",
syn::BinOp::ShrEq(..) => "<<=",
};
Ok(Literal::BinOp {
left: Box::new(l),
op,
right: Box::new(r),
})
}
// Match literals like true, 'a', 32 etc
syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
match lit {
syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
0..=255 => format!("'{}'", value.value().escape_default()),
other_code => format!(r"U'\U{:08X}'", other_code),
})),
syn::Lit::Int(ref value) => {
if value.base10_parse::<i64>().is_err() {
Ok(Literal::Expr(format!("{}ULL", value.base10_digits())))
} else {
Ok(Literal::Expr(value.base10_digits().to_string()))
}
}
syn::Lit::Float(ref value) => {
Ok(Literal::Expr(value.base10_digits().to_string()))
}
syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!("{}", value.value))),
// TODO: Add support for byte string and Verbatim
_ => Err(format!("Unsupported literal expression. {:?}", *lit)),
}
}
syn::Expr::Struct(syn::ExprStruct {
ref path,
ref fields,
..
}) => {
let struct_name = path.segments[0].ident.to_string();
let mut field_map = HashMap::<String, Literal>::default();
for field in fields {
let ident = match field.member {
syn::Member::Named(ref name) => name.to_string(),
syn::Member::Unnamed(ref index) => format!("_{}", index.index),
};
let key = ident.to_string();
let value = Literal::load(&field.expr)?;
field_map.insert(key, value);
}
Ok(Literal::Struct {
path: Path::new(struct_name.clone()),
export_name: struct_name,
fields: field_map,
})
}
syn::Expr::Unary(syn::ExprUnary {
ref op, ref expr, ..
}) => match *op {
UnOp::Neg(_) => {
let val = Self::load(expr)?;
Ok(Literal::PostfixUnaryOp {
op: "-",
value: Box::new(val),
})
}
_ => Err(format!("Unsupported Unary expression. {:?}", *op)),
},
// Match identifiers, like `5 << SHIFT`
syn::Expr::Path(syn::ExprPath {
path: syn::Path { ref segments, .. },
..
}) => {
// Handle only the simplest identifiers and error for anything else.
if segments.len() == 1 {
Ok(Literal::Path(format!("{}", segments.last().unwrap().ident)))
} else {
Err(format!("Unsupported path expression. {:?}", *segments))
}
}
syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr),
syn::Expr::Cast(syn::ExprCast {
ref expr, ref ty, ..
}) => {
let val = Self::load(expr)?;
match Type::load(ty)? {
Some(ty) => Ok(Literal::Cast {
ty,
value: Box::new(val),
}),
None => Err("Cannot cast to zero sized type.".to_owned()),
}
}
_ => Err(format!("Unsupported expression. {:?}", *expr)),
}
}
pub(crate) fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
match self {
Literal::Expr(v) => match (&**v, config.language) {
("true", Language::Cython) => write!(out, "True"),
("false", Language::Cython) => write!(out, "False"),
(v, _) => write!(out, "{}", v),
},
Literal::Path(v) => write!(out, "{}", v),
Literal::PostfixUnaryOp { op, ref value } => {
write!(out, "{}", op);
value.write(config, out);
}
Literal::BinOp {
ref left,
op,
ref right,
} => {
write!(out, "(");
left.write(config, out);
write!(out, " {} ", op);
right.write(config, out);
write!(out, ")");
}
Literal::Cast { ref ty, ref value } => {
out.write(if config.language == Language::Cython {
"<"
} else {
"("
});
ty.write(config, out);
out.write(if config.language == Language::Cython {
">"
} else {
")"
});
value.write(config, out);
}
Literal::Struct {
export_name,
fields,
path,
} => {
match config.language {
Language::C => write!(out, "({})", export_name),
Language::Cxx => write!(out, "{}", export_name),
Language::Cython => write!(out, "<{}>", export_name),
}
write!(out, "{{ ");
let mut is_first_field = true;
// In C++, same order as defined is required.
let ordered_fields = out.bindings().struct_field_names(path);
for ordered_key in ordered_fields.iter() {
if let Some(lit) = fields.get(ordered_key) {
if !is_first_field {
write!(out, ", ");
} else {
is_first_field = false;
}
match config.language {
Language::Cxx => write!(out, "/* .{} = */ ", ordered_key),
Language::C => write!(out, ".{} = ", ordered_key),
Language::Cython => {}
}
lit.write(config, out);
}
}
write!(out, " }}");
}
}
}
}
#[derive(Debug, Clone)]
pub struct Constant {
pub path: Path,
pub export_name: String,
pub ty: Type,
pub value: Literal,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
pub associated_to: Option<Path>,
}
impl Constant {
pub fn load(
path: Path,
mod_cfg: Option<&Cfg>,
ty: &syn::Type,
expr: &syn::Expr,
attrs: &[syn::Attribute],
associated_to: Option<Path>,
) -> Result<Constant, String> {
let ty = Type::load(ty)?;
let mut ty = match ty {
Some(ty) => ty,
None => {
return Err("Cannot have a zero sized const definition.".to_owned());
}
};
let mut lit = Literal::load(expr)?;
if let Some(ref associated_to) = associated_to {
ty.replace_self_with(associated_to);
lit.replace_self_with(associated_to);
}
Ok(Constant::new(
path,
ty,
lit,
Cfg::append(mod_cfg, Cfg::load(attrs)),
AnnotationSet::load(attrs)?,
Documentation::load(attrs),
associated_to,
))
}
pub fn new(
path: Path,
ty: Type,
value: Literal,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
associated_to: Option<Path>,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
ty,
value,
cfg,
annotations,
documentation,
associated_to,
}
}
pub fn uses_only_primitive_types(&self) -> bool {
self.value.uses_only_primitive_types() && self.ty.is_primitive_or_ptr_primitive()
}
}
impl Item for Constant {
fn path(&self) -> &Path {
&self.path
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.ty.add_dependencies(library, out);
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Constant(self.clone())
}
fn rename_for_config(&mut self, config: &Config) {
if self.associated_to.is_none() {
config.export.rename(&mut self.export_name);
}
self.value.rename_for_config(config);
self.ty.rename_for_config(config, &GenericParams::default()); // FIXME: should probably propagate something here
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ty.resolve_declaration_types(resolver);
}
}
impl Constant {
pub fn write_declaration<F: Write>(
&self,
config: &Config,
out: &mut SourceWriter<F>,
associated_to_struct: &Struct,
) {
debug_assert!(self.associated_to.is_some());
debug_assert!(config.language == Language::Cxx);
debug_assert!(!associated_to_struct.is_transparent);
debug_assert!(config.structure.associated_constants_in_body);
debug_assert!(config.constant.allow_static_const);
if let Type::Ptr { is_const: true, .. } = self.ty {
out.write("static ");
} else {
out.write("static const ");
}
self.ty.write(config, out);
write!(out, " {};", self.export_name())
}
pub fn write<F: Write>(
&self,
config: &Config,
out: &mut SourceWriter<F>,
associated_to_struct: Option<&Struct>,
) {
if let Some(assoc) = associated_to_struct {
if assoc.is_generic() {
return; // Not tested / implemented yet, so bail out.
}
}
if !self.value.is_valid(out.bindings()) {
return;
}
let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent);
let in_body = associated_to_struct.is_some()
&& config.language == Language::Cxx
&& config.structure.associated_constants_in_body
&& config.constant.allow_static_const
&& !associated_to_transparent;
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
let name = if in_body {
Cow::Owned(format!(
"{}::{}",
associated_to_struct.unwrap().export_name(),
self.export_name(),
))
} else if self.associated_to.is_none() {
Cow::Borrowed(self.export_name())
} else {
let associated_name = match associated_to_struct {
Some(s) => Cow::Borrowed(s.export_name()),
None => {
let mut name = self.associated_to.as_ref().unwrap().name().to_owned();
config.export.rename(&mut name);
Cow::Owned(name)
}
};
Cow::Owned(format!("{}_{}", associated_name, self.export_name()))
};
let value = match self.value {
Literal::Struct {
ref fields,
ref path,
..
} if out.bindings().struct_is_transparent(path) => fields.iter().next().unwrap().1,
_ => &self.value,
};
self.documentation.write(config, out);
let allow_constexpr = if let Type::Primitive(..) = self.ty {
config.constant.allow_constexpr
} else {
false
};
match config.language {
Language::Cxx if config.constant.allow_static_const || allow_constexpr => {
if allow_constexpr {
out.write("constexpr ")
}
if config.constant.allow_static_const {
out.write(if in_body { "inline " } else { "static " });
}
if let Type::Ptr { is_const: true, .. } = self.ty {
// Nothing.
} else {
out.write("const ");
}
self.ty.write(config, out);
write!(out, " {} = ", name);
value.write(config, out);
write!(out, ";");
}
Language::Cxx | Language::C => {
write!(out, "#define {} ", name);
value.write(config, out);
}
Language::Cython => {
out.write("const ");
self.ty.write(config, out);
// For extern Cython declarations the initializer is ignored,
// but still useful as documentation, so we write it as a comment.
write!(out, " {} # = ", name);
value.write(config, out);
}
}
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,106 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::config::{Config, DocumentationStyle, Language};
use crate::bindgen::utilities::SynAttributeHelpers;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Documentation {
pub doc_comment: Vec<String>,
}
impl Documentation {
pub fn load(attrs: &[syn::Attribute]) -> Self {
let doc = attrs
.get_comment_lines()
.into_iter()
.filter(|x| !x.trim_start().starts_with("cbindgen:"))
.collect();
Documentation { doc_comment: doc }
}
pub fn simple(line: &str) -> Self {
Documentation {
doc_comment: vec![line.to_owned()],
}
}
pub fn none() -> Self {
Documentation {
doc_comment: Vec::new(),
}
}
}
impl Source for Documentation {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if self.doc_comment.is_empty() || !config.documentation {
return;
}
// Cython uses Python-style comments, so `documentation_style` is not relevant.
if config.language == Language::Cython {
for line in &self.doc_comment {
write!(out, "#{}", line);
out.new_line();
}
return;
}
let style = match config.documentation_style {
DocumentationStyle::Auto if config.language == Language::C => DocumentationStyle::Doxy,
DocumentationStyle::Auto if config.language == Language::Cxx => DocumentationStyle::Cxx,
DocumentationStyle::Auto => DocumentationStyle::C, // Fallback if `Language` gets extended.
other => other,
};
// Following these documents for style conventions:
// https://en.wikibooks.org/wiki/C++_Programming/Code/Style_Conventions/Comments
// https://www.cs.cmu.edu/~410/doc/doxygen.html
match style {
DocumentationStyle::C => {
out.write("/*");
out.new_line();
}
DocumentationStyle::Doxy => {
out.write("/**");
out.new_line();
}
_ => (),
}
for line in &self.doc_comment {
match style {
DocumentationStyle::C => out.write(""),
DocumentationStyle::Doxy => out.write(" *"),
DocumentationStyle::C99 => out.write("//"),
DocumentationStyle::Cxx => out.write("///"),
DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered
}
write!(out, "{}", line);
out.new_line();
}
match style {
DocumentationStyle::C => {
out.write(" */");
out.new_line();
}
DocumentationStyle::Doxy => {
out.write(" */");
out.new_line();
}
_ => (),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
use std::io::Write;
use crate::bindgen::cdecl;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::ir::{AnnotationSet, Cfg, ConditionWrite};
use crate::bindgen::ir::{Documentation, Path, ToCondition, Type};
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Field {
pub name: String,
pub ty: Type,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl Field {
pub fn from_name_and_type(name: String, ty: Type) -> Field {
Field {
name,
ty,
cfg: None,
annotations: AnnotationSet::new(),
documentation: Documentation::none(),
}
}
pub fn load(field: &syn::Field, self_path: &Path) -> Result<Option<Field>, String> {
Ok(if let Some(mut ty) = Type::load(&field.ty)? {
ty.replace_self_with(self_path);
Some(Field {
name: field
.ident
.as_ref()
.ok_or_else(|| "field is missing identifier".to_string())?
.to_string(),
ty,
cfg: Cfg::load(&field.attrs),
annotations: AnnotationSet::load(&field.attrs)?,
documentation: Documentation::load(&field.attrs),
})
} else {
None
})
}
}
impl Source for Field {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
// Cython doesn't support conditional fields.
let condition = self.cfg.to_condition(config);
if config.language != Language::Cython {
condition.write_before(config, out);
}
self.documentation.write(config, out);
cdecl::write_field(out, &self.ty, &self.name, config);
// Cython extern declarations don't manage layouts, layouts are defined entierly by the
// corresponding C code. So we can omit bitfield sizes which are not supported by Cython.
if config.language != Language::Cython {
if let Some(bitfield) = self.annotations.atom("bitfield") {
write!(out, ": {}", bitfield.unwrap_or_default());
}
}
if config.language != Language::Cython {
condition.write_after(config, out);
// FIXME(#634): `write_vertical_source_list` should support
// configuring list elements natively. For now we print a newline
// here to avoid printing `#endif;` with semicolon.
if condition.is_some() {
out.new_line();
}
}
}
}

View File

@@ -0,0 +1,400 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::io::Write;
use crate::bindgen::cdecl;
use crate::bindgen::config::{Config, Language, Layout};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, GenericPath, Path, PrimitiveType,
ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::reserved;
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct FunctionArgument {
pub name: Option<String>,
pub ty: Type,
pub array_length: Option<String>,
}
#[derive(Debug, Clone)]
pub struct Function {
pub path: Path,
/// Path to the self-type of the function
/// If the function is a method, this will contain the path of the type in the impl block
pub self_type_path: Option<Path>,
pub ret: Type,
pub args: Vec<FunctionArgument>,
pub extern_decl: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
pub never_return: bool,
}
impl Function {
pub fn load(
path: Path,
self_type_path: Option<&Path>,
sig: &syn::Signature,
extern_decl: bool,
attrs: &[syn::Attribute],
mod_cfg: Option<&Cfg>,
) -> Result<Function, String> {
let mut args = sig.inputs.iter().try_skip_map(|x| x.as_argument())?;
let mut never_return = false;
let mut ret = match sig.output {
syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
syn::ReturnType::Type(_, ref ty) => {
if let syn::Type::Never(_) = ty.as_ref() {
never_return = true;
Type::Primitive(PrimitiveType::Void)
} else {
Type::load(ty)?.unwrap_or(Type::Primitive(PrimitiveType::Void))
}
}
};
if let Some(self_path) = self_type_path {
for arg in &mut args {
arg.ty.replace_self_with(self_path);
}
ret.replace_self_with(self_path);
}
Ok(Function {
path,
self_type_path: self_type_path.cloned(),
ret,
args,
extern_decl,
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
annotations: AnnotationSet::load(attrs)?,
documentation: Documentation::load(attrs),
never_return,
})
}
pub(crate) fn never_return(&self, config: &Config) -> bool {
self.never_return && config.language != Language::Cython
}
pub fn swift_name(&self, config: &Config) -> Option<String> {
if config.language == Language::Cython {
return None;
}
// If the symbol name starts with the type name, separate the two components with '.'
// so that Swift recognises the association between the method and the type
let (ref type_prefix, ref type_name) = match self.self_type_path {
Some(ref type_name) => {
let type_name = type_name.to_string();
if !self.path.name().starts_with(&type_name) {
return Some(self.path.to_string());
}
(format!("{}.", type_name), type_name)
}
None => ("".to_string(), "".to_string()),
};
let item_name = self
.path
.name()
.trim_start_matches(type_name)
.trim_start_matches('_');
let item_args = {
let mut items = Vec::with_capacity(self.args.len());
for arg in self.args.iter() {
items.push(format!("{}:", arg.name.as_ref()?.as_str()));
}
items.join("")
};
Some(format!("{}{}({})", type_prefix, item_name, item_args))
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn simplify_standard_types(&mut self, config: &Config) {
self.ret.simplify_standard_types(config);
for arg in &mut self.args {
arg.ty.simplify_standard_types(config);
}
}
pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.ret.add_dependencies(library, out);
for arg in &self.args {
arg.ty.add_dependencies(library, out);
}
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
self.ret.add_monomorphs(library, out);
for arg in &self.args {
arg.ty.add_monomorphs(library, out);
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
self.ret.mangle_paths(monomorphs);
for arg in &mut self.args {
arg.ty.mangle_paths(monomorphs);
}
}
pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ret.resolve_declaration_types(resolver);
for arg in &mut self.args {
arg.ty.resolve_declaration_types(resolver);
}
}
pub fn rename_for_config(&mut self, config: &Config) {
// Rename the types used in arguments
let generic_params = Default::default();
self.ret.rename_for_config(config, &generic_params);
// Apply rename rules to argument names
let rules = self
.annotations
.parse_atom::<RenameRule>("rename-all")
.unwrap_or(config.function.rename_args);
if let Some(r) = rules.not_none() {
let args = std::mem::take(&mut self.args);
self.args = args
.into_iter()
.map(|arg| {
let name = arg
.name
.map(|n| r.apply(&n, IdentifierType::FunctionArg).into_owned());
FunctionArgument {
name,
ty: arg.ty,
array_length: None,
}
})
.collect()
}
// Escape C/C++ reserved keywords used in argument names, and
// recursively rename argument types.
for arg in &mut self.args {
arg.ty.rename_for_config(config, &generic_params);
if let Some(ref mut name) = arg.name {
reserved::escape(name);
}
}
// Save the array length of the pointer arguments which need to use
// the C-array notation
if let Some(tuples) = self.annotations.list("ptrs-as-arrays") {
let mut ptrs_as_arrays: HashMap<String, String> = HashMap::new();
for str_tuple in tuples {
let parts: Vec<&str> = str_tuple[1..str_tuple.len() - 1]
.split(';')
.map(|x| x.trim())
.collect();
if parts.len() != 2 {
warn!(
"{:?} does not follow the correct syntax, so the annotation is being ignored",
parts
);
continue;
}
ptrs_as_arrays.insert(parts[0].to_string(), parts[1].to_string());
}
for arg in &mut self.args {
match arg.ty {
Type::Ptr { .. } => {}
_ => continue,
}
let name = match arg.name {
Some(ref name) => name,
None => continue,
};
arg.array_length = ptrs_as_arrays.get(name).cloned();
}
}
}
}
impl Source for Function {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
fn write_1<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
let prefix = config.function.prefix(&func.annotations);
let postfix = config.function.postfix(&func.annotations);
let condition = func.cfg.to_condition(config);
condition.write_before(config, out);
func.documentation.write(config, out);
if func.extern_decl {
out.write("extern ");
} else {
if let Some(ref prefix) = prefix {
write!(out, "{} ", prefix);
}
if func.annotations.must_use(config) {
if let Some(ref anno) = config.function.must_use {
write!(out, "{} ", anno);
}
}
}
cdecl::write_func(out, func, false, config);
if !func.extern_decl {
if let Some(ref postfix) = postfix {
write!(out, " {}", postfix);
}
}
if let Some(ref swift_name_macro) = config.function.swift_name_macro {
if let Some(swift_name) = func.swift_name(config) {
write!(out, " {}({})", swift_name_macro, swift_name);
}
}
if func.never_return(config) {
if let Some(ref no_return_attr) = config.function.no_return {
out.write_fmt(format_args!(" {}", no_return_attr));
}
}
out.write(";");
condition.write_after(config, out);
}
fn write_2<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
let prefix = config.function.prefix(&func.annotations);
let postfix = config.function.postfix(&func.annotations);
let condition = func.cfg.to_condition(config);
condition.write_before(config, out);
func.documentation.write(config, out);
if func.extern_decl {
out.write("extern ");
} else {
if let Some(ref prefix) = prefix {
write!(out, "{}", prefix);
out.new_line();
}
if func.annotations.must_use(config) {
if let Some(ref anno) = config.function.must_use {
write!(out, "{}", anno);
out.new_line();
}
}
}
cdecl::write_func(out, func, true, config);
if !func.extern_decl {
if let Some(ref postfix) = postfix {
out.new_line();
write!(out, "{}", postfix);
}
}
if let Some(ref swift_name_macro) = config.function.swift_name_macro {
if let Some(swift_name) = func.swift_name(config) {
write!(out, " {}({})", swift_name_macro, swift_name);
}
}
if func.never_return(config) {
if let Some(ref no_return_attr) = config.function.no_return {
out.write_fmt(format_args!(" {}", no_return_attr));
}
}
out.write(";");
condition.write_after(config, out);
}
let option_1 = out.measure(|out| write_1(self, config, out));
if (config.function.args == Layout::Auto && option_1 <= config.line_length)
|| config.function.args == Layout::Horizontal
{
write_1(self, config, out);
} else {
write_2(self, config, out);
}
}
}
trait SynFnArgHelpers {
fn as_argument(&self) -> Result<Option<FunctionArgument>, String>;
}
fn gen_self_type(receiver: &syn::Receiver) -> Type {
let self_ty = Type::Path(GenericPath::self_path());
if receiver.reference.is_none() {
return self_ty;
}
let is_const = receiver.mutability.is_none();
Type::Ptr {
ty: Box::new(self_ty),
is_const,
is_nullable: false,
is_ref: false,
}
}
impl SynFnArgHelpers for syn::FnArg {
fn as_argument(&self) -> Result<Option<FunctionArgument>, String> {
match *self {
syn::FnArg::Typed(syn::PatType {
ref pat, ref ty, ..
}) => {
let name = match **pat {
syn::Pat::Wild(..) => None,
syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => Some(ident.to_string()),
_ => {
return Err(format!(
"Parameter has an unsupported argument name: {:?}",
pat
))
}
};
let ty = match Type::load(ty)? {
Some(x) => x,
None => return Ok(None),
};
if let Type::Array(..) = ty {
return Err("Array as function arguments are not supported".to_owned());
}
Ok(Some(FunctionArgument {
name,
ty,
array_length: None,
}))
}
syn::FnArg::Receiver(ref receiver) => Ok(Some(FunctionArgument {
name: Some("self".to_string()),
ty: gen_self_type(receiver),
array_length: None,
})),
}
}
}

View File

@@ -0,0 +1,169 @@
use std::io::Write;
use std::ops::Deref;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver};
use crate::bindgen::ir::{Path, Type};
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Default, Debug, Clone)]
pub struct GenericParams(pub Vec<Path>);
impl GenericParams {
pub fn new(generics: &syn::Generics) -> Self {
GenericParams(
generics
.params
.iter()
.filter_map(|x| match *x {
syn::GenericParam::Type(syn::TypeParam { ref ident, .. }) => {
Some(Path::new(ident.to_string()))
}
_ => None,
})
.collect(),
)
}
fn write_internal<F: Write>(
&self,
config: &Config,
out: &mut SourceWriter<F>,
with_default: bool,
) {
if !self.0.is_empty() && config.language == Language::Cxx {
out.write("template<");
for (i, item) in self.0.iter().enumerate() {
if i != 0 {
out.write(", ");
}
write!(out, "typename {}", item);
if with_default {
write!(out, " = void");
}
}
out.write(">");
out.new_line();
}
}
pub fn write_with_default<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
self.write_internal(config, out, true);
}
}
impl Deref for GenericParams {
type Target = [Path];
fn deref(&self) -> &[Path] {
&self.0
}
}
impl Source for GenericParams {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
self.write_internal(config, out, false);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct GenericPath {
path: Path,
export_name: String,
generics: Vec<Type>,
ctype: Option<DeclarationType>,
}
impl GenericPath {
pub fn new(path: Path, generics: Vec<Type>) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generics,
ctype: None,
}
}
pub fn self_path() -> Self {
Self::new(Path::new("Self"), vec![])
}
pub fn replace_self_with(&mut self, self_ty: &Path) {
if self.path.replace_self_with(self_ty) {
self.export_name = self_ty.name().to_owned();
}
// Caller deals with generics.
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn generics(&self) -> &[Type] {
&self.generics
}
pub fn generics_mut(&mut self) -> &mut [Type] {
&mut self.generics
}
pub fn ctype(&self) -> Option<&DeclarationType> {
self.ctype.as_ref()
}
pub fn name(&self) -> &str {
self.path.name()
}
pub fn export_name(&self) -> &str {
&self.export_name
}
pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) {
for generic in &mut self.generics {
generic.rename_for_config(config, generic_params);
}
if !generic_params.contains(&self.path) {
config.export.rename(&mut self.export_name);
}
}
pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ctype = resolver.type_for(&self.path);
}
pub fn load(path: &syn::Path) -> Result<Self, String> {
assert!(
!path.segments.is_empty(),
"{:?} doesn't have any segments",
path
);
let last_segment = path.segments.last().unwrap();
let name = last_segment.ident.to_string();
let path = Path::new(name);
let phantom_data_path = Path::new("PhantomData");
if path == phantom_data_path {
return Ok(Self::new(path, Vec::new()));
}
let generics = match last_segment.arguments {
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
ref args,
..
}) => args.iter().try_skip_map(|x| match *x {
syn::GenericArgument::Type(ref x) => Type::load(x),
syn::GenericArgument::Lifetime(_) => Ok(None),
_ => Err(format!("can't handle generic argument {:?}", x)),
})?,
syn::PathArguments::Parenthesized(_) => {
return Err("Path contains parentheses.".to_owned());
}
_ => Vec::new(),
};
Ok(Self::new(path, generics))
}
}

View File

@@ -0,0 +1,117 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::cdecl;
use crate::bindgen::config::Config;
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, Item, ItemContainer, Path, Type};
use crate::bindgen::library::Library;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Static {
pub path: Path,
pub export_name: String,
pub ty: Type,
pub mutable: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl Static {
pub fn load(item: &syn::ItemStatic, mod_cfg: Option<&Cfg>) -> Result<Static, String> {
let ty = Type::load(&item.ty)?;
if ty.is_none() {
return Err("Cannot have a zero sized static definition.".to_owned());
}
Ok(Static::new(
Path::new(item.ident.to_string()),
ty.unwrap(),
item.mutability.is_some(),
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
AnnotationSet::load(&item.attrs)?,
Documentation::load(&item.attrs),
))
}
pub fn new(
path: Path,
ty: Type,
mutable: bool,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
ty,
mutable,
cfg,
annotations,
documentation,
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
self.ty.simplify_standard_types(config);
}
}
impl Item for Static {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Static(self.clone())
}
fn rename_for_config(&mut self, config: &Config) {
self.ty.rename_for_config(config, &Default::default());
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ty.resolve_declaration_types(resolver);
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.ty.add_dependencies(library, out);
}
}
impl Source for Static {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
out.write("extern ");
if let Type::Ptr { is_const: true, .. } = self.ty {
} else if !self.mutable {
out.write("const ");
}
cdecl::write_field(out, &self.ty, &self.export_name, config);
out.write(";");
}
}

View File

@@ -0,0 +1,244 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use indexmap::IndexMap;
use std::mem;
use crate::bindgen::config::Config;
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, Constant, Enum, OpaqueItem, Path, Static, Struct, Type, Typedef, Union,
};
use crate::bindgen::library::Library;
use crate::bindgen::monomorph::Monomorphs;
/// An item is any type of rust item besides a function
pub trait Item {
fn path(&self) -> &Path;
fn name(&self) -> &str {
self.path().name()
}
fn export_name(&self) -> &str {
self.name()
}
fn cfg(&self) -> Option<&Cfg>;
fn annotations(&self) -> &AnnotationSet;
fn annotations_mut(&mut self) -> &mut AnnotationSet;
fn container(&self) -> ItemContainer;
fn collect_declaration_types(&self, _resolver: &mut DeclarationTypeResolver) {
unimplemented!()
}
fn resolve_declaration_types(&mut self, _resolver: &DeclarationTypeResolver) {
unimplemented!()
}
fn rename_for_config(&mut self, _config: &Config) {}
fn add_dependencies(&self, _library: &Library, _out: &mut Dependencies) {}
fn instantiate_monomorph(&self, _generics: &[Type], _library: &Library, _out: &mut Monomorphs) {
unreachable!("Cannot instantiate {} as a generic.", self.name())
}
}
#[derive(Debug, Clone)]
pub enum ItemContainer {
Constant(Constant),
Static(Static),
OpaqueItem(OpaqueItem),
Struct(Struct),
Union(Union),
Enum(Enum),
Typedef(Typedef),
}
impl ItemContainer {
pub fn deref(&self) -> &dyn Item {
match *self {
ItemContainer::Constant(ref x) => x,
ItemContainer::Static(ref x) => x,
ItemContainer::OpaqueItem(ref x) => x,
ItemContainer::Struct(ref x) => x,
ItemContainer::Union(ref x) => x,
ItemContainer::Enum(ref x) => x,
ItemContainer::Typedef(ref x) => x,
}
}
}
#[derive(Debug, Clone)]
pub enum ItemValue<T: Item> {
Cfg(Vec<T>),
Single(T),
}
#[derive(Debug, Clone)]
pub struct ItemMap<T: Item> {
data: IndexMap<Path, ItemValue<T>>,
}
impl<T: Item> Default for ItemMap<T> {
fn default() -> ItemMap<T> {
ItemMap {
data: Default::default(),
}
}
}
impl<T: Item + Clone> ItemMap<T> {
pub fn rebuild(&mut self) {
let old = mem::take(self);
old.for_all_items(|x| {
self.try_insert(x.clone());
});
}
pub fn try_insert(&mut self, item: T) -> bool {
match (item.cfg().is_some(), self.data.get_mut(item.path())) {
(true, Some(&mut ItemValue::Cfg(ref mut items))) => {
items.push(item);
return true;
}
(false, Some(&mut ItemValue::Cfg(_))) => {
return false;
}
(true, Some(&mut ItemValue::Single(_))) => {
return false;
}
(false, Some(&mut ItemValue::Single(_))) => {
return false;
}
_ => {}
}
let path = item.path().clone();
if item.cfg().is_some() {
self.data.insert(path, ItemValue::Cfg(vec![item]));
} else {
self.data.insert(path, ItemValue::Single(item));
}
true
}
pub fn extend_with(&mut self, other: &ItemMap<T>) {
other.for_all_items(|x| {
self.try_insert(x.clone());
});
}
pub fn to_vec(&self) -> Vec<T> {
let mut result = Vec::with_capacity(self.data.len());
for container in self.data.values() {
match *container {
ItemValue::Cfg(ref items) => result.extend_from_slice(items),
ItemValue::Single(ref item) => {
result.push(item.clone());
}
}
}
result
}
pub fn get_items(&self, path: &Path) -> Option<Vec<ItemContainer>> {
Some(match *self.data.get(path)? {
ItemValue::Cfg(ref items) => items.iter().map(|x| x.container()).collect(),
ItemValue::Single(ref item) => vec![item.container()],
})
}
pub fn filter<F>(&mut self, callback: F)
where
F: Fn(&T) -> bool,
{
let data = mem::take(&mut self.data);
for (name, container) in data {
match container {
ItemValue::Cfg(items) => {
let mut new_items = Vec::new();
for item in items {
if !callback(&item) {
new_items.push(item);
}
}
if !new_items.is_empty() {
self.data.insert(name, ItemValue::Cfg(new_items));
}
}
ItemValue::Single(item) => {
if !callback(&item) {
self.data.insert(name, ItemValue::Single(item));
}
}
}
}
}
pub fn for_all_items<F>(&self, mut callback: F)
where
F: FnMut(&T),
{
for container in self.data.values() {
match *container {
ItemValue::Cfg(ref items) => {
for item in items {
callback(item);
}
}
ItemValue::Single(ref item) => callback(item),
}
}
}
pub fn for_all_items_mut<F>(&mut self, mut callback: F)
where
F: FnMut(&mut T),
{
for container in self.data.values_mut() {
match *container {
ItemValue::Cfg(ref mut items) => {
for item in items {
callback(item);
}
}
ItemValue::Single(ref mut item) => callback(item),
}
}
}
pub fn for_items<F>(&self, path: &Path, mut callback: F)
where
F: FnMut(&T),
{
match self.data.get(path) {
Some(&ItemValue::Cfg(ref items)) => {
for item in items {
callback(item);
}
}
Some(&ItemValue::Single(ref item)) => {
callback(item);
}
None => {}
}
}
pub fn for_items_mut<F>(&mut self, path: &Path, mut callback: F)
where
F: FnMut(&mut T),
{
match self.data.get_mut(path) {
Some(&mut ItemValue::Cfg(ref mut items)) => {
for item in items {
callback(item);
}
}
Some(&mut ItemValue::Single(ref mut item)) => {
callback(item);
}
None => {}
}
}
}

View File

@@ -0,0 +1,39 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub mod annotation;
pub mod cfg;
pub mod constant;
pub mod documentation;
pub mod enumeration;
pub mod field;
pub mod function;
pub mod generic_path;
pub mod global;
pub mod item;
pub mod opaque;
pub mod path;
pub mod repr;
pub mod structure;
pub mod ty;
pub mod typedef;
pub mod union;
pub use self::annotation::{AnnotationSet, AnnotationValue};
pub use self::cfg::*;
pub use self::constant::*;
pub use self::documentation::Documentation;
pub use self::enumeration::*;
pub use self::field::*;
pub use self::function::*;
pub use self::generic_path::*;
pub use self::global::*;
pub use self::item::*;
pub use self::opaque::*;
pub use self::path::*;
pub use self::repr::*;
pub use self::structure::*;
pub use self::ty::*;
pub use self::typedef::*;
pub use self::union::*;

View File

@@ -0,0 +1,176 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::mangle;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct OpaqueItem {
pub path: Path,
pub export_name: String,
pub generic_params: GenericParams,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl OpaqueItem {
pub fn load(
path: Path,
generics: &syn::Generics,
attrs: &[syn::Attribute],
mod_cfg: Option<&Cfg>,
) -> Result<OpaqueItem, String> {
Ok(Self::new(
path,
GenericParams::new(generics),
Cfg::append(mod_cfg, Cfg::load(attrs)),
AnnotationSet::load(attrs).unwrap_or_else(|_| AnnotationSet::new()),
Documentation::load(attrs),
))
}
pub fn new(
path: Path,
generic_params: GenericParams,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> OpaqueItem {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generic_params,
cfg,
annotations,
documentation,
}
}
}
impl Item for OpaqueItem {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::OpaqueItem(self.clone())
}
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
resolver.add_struct(&self.path);
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.export_name);
}
fn add_dependencies(&self, _: &Library, _: &mut Dependencies) {}
fn instantiate_monomorph(
&self,
generic_values: &[Type],
library: &Library,
out: &mut Monomorphs,
) {
assert!(
!self.generic_params.is_empty(),
"{} is not generic",
self.path
);
// We can be instantiated with less generic params because of default
// template parameters, or because of empty types that we remove during
// parsing (`()`).
assert!(
self.generic_params.len() >= generic_values.len(),
"{} has {} params but is being instantiated with {} values",
self.path,
self.generic_params.len(),
generic_values.len(),
);
let mangled_path = mangle::mangle_path(
&self.path,
generic_values,
&library.get_config().export.mangle,
);
let monomorph = OpaqueItem::new(
mangled_path,
GenericParams::default(),
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
);
out.insert_opaque(self, monomorph, generic_values.to_owned());
}
}
impl Source for OpaqueItem {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
self.documentation.write(config, out);
self.generic_params.write_with_default(config, out);
match config.language {
Language::C if config.style.generate_typedef() => {
write!(
out,
"typedef struct {} {};",
self.export_name(),
self.export_name()
);
}
Language::C | Language::Cxx => {
write!(out, "struct {};", self.export_name());
}
Language::Cython => {
write!(
out,
"{}struct {}",
config.style.cython_def(),
self.export_name()
);
out.open_brace();
out.write("pass");
out.close_brace(false);
}
}
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cmp::Ordering;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Path {
name: String,
}
impl Path {
pub fn new<T>(name: T) -> Self
where
String: From<T>,
{
Self { name: name.into() }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn replace_self_with(&mut self, path: &Self) -> bool {
if self.name() != "Self" {
return false;
}
*self = path.clone();
true
}
}
impl PartialOrd for Path {
fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Path {
fn cmp(&self, other: &Path) -> Ordering {
self.name.cmp(&other.name)
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}

View File

@@ -0,0 +1,184 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::bindgen::ir::ty::{IntKind, PrimitiveType};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ReprStyle {
Rust,
C,
Transparent,
}
impl Default for ReprStyle {
fn default() -> Self {
ReprStyle::Rust
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ReprType {
kind: IntKind,
signed: bool,
}
impl ReprType {
pub(crate) fn to_primitive(self) -> PrimitiveType {
PrimitiveType::Integer {
kind: self.kind,
signed: self.signed,
zeroable: true,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ReprAlign {
Packed,
Align(u64),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub struct Repr {
pub style: ReprStyle,
pub ty: Option<ReprType>,
pub align: Option<ReprAlign>,
}
impl Repr {
pub fn load(attrs: &[syn::Attribute]) -> Result<Repr, String> {
let ids = attrs
.iter()
.filter_map(|attr| {
if let syn::Meta::List(syn::MetaList { path, nested, .. }) =
attr.parse_meta().ok()?
{
if path.is_ident("repr") {
return Some(nested.into_iter().collect::<Vec<_>>());
}
}
None
})
.flatten()
.filter_map(|meta| match meta {
syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
Some((path.segments.first().unwrap().ident.to_string(), None))
}
syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList { path, nested, .. })) => {
Some((
path.segments.first().unwrap().ident.to_string(),
Some(
nested
.iter()
.filter_map(|meta| match meta {
// Only used for #[repr(align(...))].
syn::NestedMeta::Lit(syn::Lit::Int(literal)) => {
Some(literal.base10_digits().to_string())
}
// Only single levels of nesting supported at the moment.
_ => None,
})
.collect::<Vec<_>>(),
),
))
}
_ => None,
});
let mut repr = Repr::default();
for id in ids {
let (int_kind, signed) = match (id.0.as_ref(), id.1) {
("u8", None) => (IntKind::B8, false),
("u16", None) => (IntKind::B16, false),
("u32", None) => (IntKind::B32, false),
("u64", None) => (IntKind::B64, false),
("usize", None) => (IntKind::Size, false),
("i8", None) => (IntKind::B8, true),
("i16", None) => (IntKind::B16, true),
("i32", None) => (IntKind::B32, true),
("i64", None) => (IntKind::B64, true),
("isize", None) => (IntKind::Size, true),
("C", None) => {
repr.style = ReprStyle::C;
continue;
}
("transparent", None) => {
repr.style = ReprStyle::Transparent;
continue;
}
("packed", args) => {
// #[repr(packed(n))] not supported because of some open questions about how
// to calculate the native alignment of types. See eqrion/cbindgen#433.
if args.is_some() {
return Err(
"Not-yet-implemented #[repr(packed(...))] encountered.".to_string()
);
}
let align = ReprAlign::Packed;
// Only permit a single alignment-setting repr.
if let Some(old_align) = repr.align {
return Err(format!(
"Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
old_align, align
));
}
repr.align = Some(align);
continue;
}
("align", Some(args)) => {
// #[repr(align(...))] only allows a single argument.
if args.len() != 1 {
return Err(format!(
"Unsupported #[repr(align({}))], align must have exactly one argument.",
args.join(", ")
));
}
// Must be a positive integer.
let align = match args.first().unwrap().parse::<u64>() {
Ok(align) => align,
Err(_) => {
return Err(format!("Non-numeric #[repr(align({}))].", args.join(", ")))
}
};
// Must be a power of 2.
if !align.is_power_of_two() || align == 0 {
return Err(format!("Invalid alignment to #[repr(align({}))].", align));
}
// Only permit a single alignment-setting repr.
if let Some(old_align) = repr.align {
return Err(format!(
"Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
old_align,
ReprAlign::Align(align)
));
}
repr.align = Some(ReprAlign::Align(align));
continue;
}
(path, args) => match args {
None => return Err(format!("Unsupported #[repr({})].", path)),
Some(args) => {
return Err(format!(
"Unsupported #[repr({}({}))].",
path,
args.join(", ")
));
}
},
};
let ty = ReprType {
kind: int_kind,
signed,
};
if let Some(old_ty) = repr.ty {
return Err(format!(
"Conflicting #[repr(...)] type hints {:?} and {:?}.",
old_ty, ty
));
}
repr.ty = Some(ty);
}
Ok(repr)
}
}

View File

@@ -0,0 +1,710 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::config::{Config, Language, LayoutConfig};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Constant, Documentation, Field, GenericParams, Item,
ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition, Type, Typedef,
};
use crate::bindgen::library::Library;
use crate::bindgen::mangle;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::reserved;
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{ListType, Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Struct {
pub path: Path,
pub export_name: String,
pub generic_params: GenericParams,
pub fields: Vec<Field>,
/// Whether there's a tag field on the body of this struct. When this is
/// true, is_enum_variant_body is also guaranteed to be true.
pub has_tag_field: bool,
/// Whether this is an enum variant body.
pub is_enum_variant_body: bool,
pub alignment: Option<ReprAlign>,
pub is_transparent: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
pub associated_constants: Vec<Constant>,
}
impl Struct {
/// Whether this struct can derive operator== / operator!=.
pub fn can_derive_eq(&self) -> bool {
!self.fields.is_empty() && self.fields.iter().all(|x| x.ty.can_cmp_eq())
}
pub fn add_associated_constant(&mut self, c: Constant) {
self.associated_constants.push(c);
}
pub fn load(
layout_config: &LayoutConfig,
item: &syn::ItemStruct,
mod_cfg: Option<&Cfg>,
) -> Result<Self, String> {
let repr = Repr::load(&item.attrs)?;
let is_transparent = match repr.style {
ReprStyle::C => false,
ReprStyle::Transparent => true,
_ => {
return Err("Struct is not marked #[repr(C)] or #[repr(transparent)].".to_owned());
}
};
let path = Path::new(item.ident.to_string());
// Ensure we can safely represent the struct given the configuration.
if let Some(align) = repr.align {
layout_config.ensure_safe_to_represent(&align)?;
}
let fields = match item.fields {
syn::Fields::Unit => Vec::new(),
syn::Fields::Named(ref fields) => fields
.named
.iter()
.try_skip_map(|field| Field::load(field, &path))?,
syn::Fields::Unnamed(ref fields) => {
let mut out = Vec::new();
let mut current = 0;
for field in fields.unnamed.iter() {
if let Some(mut ty) = Type::load(&field.ty)? {
ty.replace_self_with(&path);
out.push(Field {
name: format!("{}", current),
ty,
cfg: Cfg::load(&field.attrs),
annotations: AnnotationSet::load(&field.attrs)?,
documentation: Documentation::load(&field.attrs),
});
current += 1;
}
}
out
}
};
let has_tag_field = false;
let is_enum_variant_body = false;
Ok(Struct::new(
path,
GenericParams::new(&item.generics),
fields,
has_tag_field,
is_enum_variant_body,
repr.align,
is_transparent,
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
AnnotationSet::load(&item.attrs)?,
Documentation::load(&item.attrs),
))
}
#[allow(clippy::too_many_arguments)]
pub fn new(
path: Path,
generic_params: GenericParams,
fields: Vec<Field>,
has_tag_field: bool,
is_enum_variant_body: bool,
alignment: Option<ReprAlign>,
is_transparent: bool,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generic_params,
fields,
has_tag_field,
is_enum_variant_body,
alignment,
is_transparent,
cfg,
annotations,
documentation,
associated_constants: vec![],
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
for field in &mut self.fields {
field.ty.simplify_standard_types(config);
}
}
pub fn is_generic(&self) -> bool {
self.generic_params.len() > 0
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
// Generic structs can instantiate monomorphs only once they've been
// instantiated. See `instantiate_monomorph` for more details.
if self.is_generic() {
return;
}
for field in &self.fields {
field.ty.add_monomorphs(library, out);
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
for field in &mut self.fields {
field.ty.mangle_paths(monomorphs);
}
}
pub fn specialize(
&self,
generic_values: &[Type],
mappings: &[(&Path, &Type)],
config: &Config,
) -> Self {
let mangled_path = mangle::mangle_path(&self.path, generic_values, &config.export.mangle);
Struct::new(
mangled_path,
GenericParams::default(),
self.fields
.iter()
.map(|field| Field {
name: field.name.clone(),
ty: field.ty.specialize(mappings),
cfg: field.cfg.clone(),
annotations: field.annotations.clone(),
documentation: field.documentation.clone(),
})
.collect(),
self.has_tag_field,
self.is_enum_variant_body,
self.alignment,
self.is_transparent,
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
)
}
fn emit_bitflags_binop<F: Write>(
&self,
operator: char,
other: &str,
out: &mut SourceWriter<F>,
) {
out.new_line();
write!(
out,
"{} operator{}(const {}& {}) const",
self.export_name(),
operator,
self.export_name(),
other
);
out.open_brace();
write!(
out,
"return {{static_cast<decltype(bits)>(this->bits {} {}.bits)}};",
operator, other
);
out.close_brace(false);
out.new_line();
write!(
out,
"{}& operator{}=(const {}& {})",
self.export_name(),
operator,
self.export_name(),
other
);
out.open_brace();
write!(out, "*this = (*this {} {});", operator, other);
out.new_line();
write!(out, "return *this;");
out.close_brace(false);
}
}
impl Item for Struct {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Struct(self.clone())
}
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
if self.is_transparent {
resolver.add_none(&self.path);
} else {
resolver.add_struct(&self.path);
}
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
for field in &mut self.fields {
field.ty.resolve_declaration_types(resolver);
}
}
fn rename_for_config(&mut self, config: &Config) {
// Rename the name of the struct
if !(self.has_tag_field && config.language == Language::Cxx) {
config.export.rename(&mut self.export_name);
}
// Rename the types used in fields
{
let fields = self
.fields
.iter_mut()
.skip(if self.has_tag_field { 1 } else { 0 });
for field in fields {
field.ty.rename_for_config(config, &self.generic_params);
}
}
// Apply renaming rules to fields in the following order
// 1. `cbindgen::field-names` annotation
// 2. `cbindgen::rename-all` annotation
// 3. config struct rename rule
// If the struct is a tuple struct and we have not renamed the
// fields, then prefix each of them with an underscore.
// If any field is a reserved keyword, then postfix it with an
// underscore.
// Scope for mutable borrow of fields
{
let names = self.fields.iter_mut().map(|field| &mut field.name);
let field_rules = self
.annotations
.parse_atom::<RenameRule>("rename-all")
.unwrap_or(config.structure.rename_fields);
if let Some(o) = self.annotations.list("field-names") {
for (dest, src) in names.zip(o) {
*dest = src;
}
} else if let Some(r) = field_rules.not_none() {
for name in names {
*name = r.apply(name, IdentifierType::StructMember).into_owned();
}
} else {
// If we don't have any rules for a tuple struct, prefix them with
// an underscore so it still compiles.
for name in names {
if name.starts_with(|c: char| c.is_ascii_digit()) {
name.insert(0, '_');
}
}
}
}
for field in &mut self.fields {
reserved::escape(&mut field.name);
}
for c in self.associated_constants.iter_mut() {
c.rename_for_config(config);
}
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
let mut fields = self.fields.iter();
// If there is a tag field, skip it
if self.has_tag_field {
fields.next();
}
for field in fields {
field
.ty
.add_dependencies_ignoring_generics(&self.generic_params, library, out);
}
for c in &self.associated_constants {
c.add_dependencies(library, out);
}
}
fn instantiate_monomorph(
&self,
generic_values: &[Type],
library: &Library,
out: &mut Monomorphs,
) {
assert!(
self.generic_params.len() > 0,
"{} is not generic",
self.path
);
assert!(
self.generic_params.len() == generic_values.len(),
"{} has {} params but is being instantiated with {} values",
self.path,
self.generic_params.len(),
generic_values.len(),
);
let mappings = self
.generic_params
.iter()
.zip(generic_values.iter())
.collect::<Vec<_>>();
let monomorph = self.specialize(generic_values, &mappings, library.get_config());
out.insert_struct(library, self, monomorph, generic_values.to_owned());
}
}
impl Source for Struct {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if self.is_transparent {
let typedef = Typedef {
path: self.path.clone(),
export_name: self.export_name.to_owned(),
generic_params: self.generic_params.clone(),
aliased: self.fields[0].ty.clone(),
cfg: self.cfg.clone(),
annotations: self.annotations.clone(),
documentation: self.documentation.clone(),
};
typedef.write(config, out);
for constant in &self.associated_constants {
out.new_line();
constant.write(config, out, Some(self));
}
return;
}
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
self.documentation.write(config, out);
if !self.is_enum_variant_body {
self.generic_params.write(config, out);
}
// The following results in
// C++ or C with Tag as style:
// struct Name {
// C with Type only style:
// typedef struct {
// C with Both as style:
// typedef struct Name {
match config.language {
Language::C if config.style.generate_typedef() => out.write("typedef "),
Language::C | Language::Cxx => {}
Language::Cython => out.write(config.style.cython_def()),
}
// Cython extern declarations don't manage layouts, layouts are defined entierly by the
// corresponding C code. So this `packed` is only for documentation, and missing
// `aligned(n)` is also not a problem.
if config.language == Language::Cython {
if let Some(align) = self.alignment {
match align {
ReprAlign::Packed => out.write("packed "),
ReprAlign::Align(_) => {} // Not supported
}
}
}
out.write("struct");
if config.language != Language::Cython {
if let Some(align) = self.alignment {
match align {
ReprAlign::Packed => {
if let Some(ref anno) = config.layout.packed {
write!(out, " {}", anno);
}
}
ReprAlign::Align(n) => {
if let Some(ref anno) = config.layout.aligned_n {
write!(out, " {}({})", anno, n);
}
}
}
}
}
if self.annotations.must_use(config) {
if let Some(ref anno) = config.structure.must_use {
write!(out, " {}", anno);
}
}
if config.language != Language::C || config.style.generate_tag() {
write!(out, " {}", self.export_name());
}
out.open_brace();
// Emit the pre_body section, if relevant
if let Some(body) = config.export.pre_body(&self.path) {
out.write_raw_block(body);
out.new_line();
}
out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
if config.language == Language::Cython && self.fields.is_empty() {
out.write("pass");
}
if config.language == Language::Cxx {
let mut wrote_start_newline = false;
if config.structure.derive_constructor(&self.annotations) && !self.fields.is_empty() {
if !wrote_start_newline {
wrote_start_newline = true;
out.new_line();
}
out.new_line();
let arg_renamer = |name: &str| {
config
.function
.rename_args
.apply(name, IdentifierType::FunctionArg)
.into_owned()
};
write!(out, "{}(", self.export_name());
let vec: Vec<_> = self
.fields
.iter()
.map(|field| {
Field::from_name_and_type(
// const-ref args to constructor
format!("const& {}", arg_renamer(&field.name)),
field.ty.clone(),
)
})
.collect();
out.write_vertical_source_list(&vec[..], ListType::Join(","));
write!(out, ")");
out.new_line();
write!(out, " : ");
let vec: Vec<_> = self
.fields
.iter()
.map(|field| format!("{}({})", field.name, arg_renamer(&field.name)))
.collect();
out.write_vertical_source_list(&vec[..], ListType::Join(","));
out.new_line();
write!(out, "{{}}");
out.new_line();
}
let other = config
.function
.rename_args
.apply("other", IdentifierType::FunctionArg);
if self
.annotations
.bool("internal-derive-bitflags")
.unwrap_or(false)
{
if !wrote_start_newline {
wrote_start_newline = true;
out.new_line();
}
out.new_line();
write!(out, "explicit operator bool() const");
out.open_brace();
write!(out, "return !!bits;");
out.close_brace(false);
out.new_line();
write!(out, "{} operator~() const", self.export_name());
out.open_brace();
write!(out, "return {{static_cast<decltype(bits)>(~bits)}};");
out.close_brace(false);
self.emit_bitflags_binop('|', &other, out);
self.emit_bitflags_binop('&', &other, out);
self.emit_bitflags_binop('^', &other, out);
}
// Generate a serializer function that allows dumping this struct
// to an std::ostream. It's defined as a friend function inside the
// struct definition, and doesn't need the `inline` keyword even
// though it's implemented right in the generated header file.
if config.structure.derive_ostream(&self.annotations) {
if !wrote_start_newline {
wrote_start_newline = true;
out.new_line();
}
out.new_line();
let stream = config
.function
.rename_args
.apply("stream", IdentifierType::FunctionArg);
let instance = config
.function
.rename_args
.apply("instance", IdentifierType::FunctionArg);
write!(
out,
"friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
stream,
self.export_name(),
instance,
);
out.open_brace();
write!(out, "return {} << \"{{ \"", stream);
let vec: Vec<_> = self
.fields
.iter()
.map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name))
.collect();
out.write_vertical_source_list(&vec[..], ListType::Join(" << \", \""));
out.write(" << \" }\";");
out.close_brace(false);
}
let skip_fields = if self.has_tag_field { 1 } else { 0 };
macro_rules! emit_op {
($op_name:expr, $op:expr, $conjuc:expr) => {{
if !wrote_start_newline {
#[allow(unused_assignments)]
{
wrote_start_newline = true;
}
out.new_line();
}
out.new_line();
if let Some(Some(attrs)) =
self.annotations.atom(concat!($op_name, "-attributes"))
{
write!(out, "{} ", attrs);
}
write!(
out,
"bool operator{}(const {}& {}) const",
$op,
self.export_name(),
other
);
out.open_brace();
out.write("return ");
let vec: Vec<_> = self
.fields
.iter()
.skip(skip_fields)
.map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name))
.collect();
out.write_vertical_source_list(
&vec[..],
ListType::Join(&format!(" {}", $conjuc)),
);
out.write(";");
out.close_brace(false);
}};
}
if config.structure.derive_eq(&self.annotations) && self.can_derive_eq() {
emit_op!("eq", "==", "&&");
}
if config.structure.derive_neq(&self.annotations) && self.can_derive_eq() {
emit_op!("neq", "!=", "||");
}
if config.structure.derive_lt(&self.annotations)
&& self.fields.len() == 1
&& self.fields[0].ty.can_cmp_order()
{
emit_op!("lt", "<", "&&");
}
if config.structure.derive_lte(&self.annotations)
&& self.fields.len() == 1
&& self.fields[0].ty.can_cmp_order()
{
emit_op!("lte", "<=", "&&");
}
if config.structure.derive_gt(&self.annotations)
&& self.fields.len() == 1
&& self.fields[0].ty.can_cmp_order()
{
emit_op!("gt", ">", "&&");
}
if config.structure.derive_gte(&self.annotations)
&& self.fields.len() == 1
&& self.fields[0].ty.can_cmp_order()
{
emit_op!("gte", ">=", "&&");
}
}
// Emit the post_body section, if relevant
if let Some(body) = config.export.post_body(&self.path) {
out.new_line();
out.write_raw_block(body);
}
if config.language == Language::Cxx
&& config.structure.associated_constants_in_body
&& config.constant.allow_static_const
{
for constant in &self.associated_constants {
out.new_line();
constant.write_declaration(config, out, self);
}
}
if config.language == Language::C && config.style.generate_typedef() {
out.close_brace(false);
write!(out, " {};", self.export_name());
} else {
out.close_brace(true);
}
for constant in &self.associated_constants {
out.new_line();
constant.write(config, out, Some(self));
}
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,954 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::io::Write;
use crate::bindgen::cdecl;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{GenericParams, GenericPath, Path};
use crate::bindgen::library::Library;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum PrimitiveType {
Void,
Bool,
Char,
SChar,
UChar,
Char32,
Float,
Double,
VaList,
PtrDiffT,
Integer {
zeroable: bool,
signed: bool,
kind: IntKind,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum IntKind {
Short,
Int,
Long,
LongLong,
SizeT,
Size,
B8,
B16,
B32,
B64,
}
impl PrimitiveType {
pub fn maybe(path: &str) -> Option<PrimitiveType> {
Some(match path {
"c_void" => PrimitiveType::Void,
"c_char" => PrimitiveType::Char,
"c_schar" => PrimitiveType::SChar,
"c_uchar" => PrimitiveType::UChar,
"c_float" => PrimitiveType::Float,
"c_double" => PrimitiveType::Double,
"ptrdiff_t" => PrimitiveType::PtrDiffT,
"VaList" => PrimitiveType::VaList,
"bool" => PrimitiveType::Bool,
"char" => PrimitiveType::Char32,
"f32" => PrimitiveType::Float,
"f64" => PrimitiveType::Double,
_ => {
let (kind, signed) = match path {
"c_short" => (IntKind::Short, true),
"c_int" => (IntKind::Int, true),
"c_long" => (IntKind::Long, true),
"c_longlong" => (IntKind::LongLong, true),
"ssize_t" => (IntKind::SizeT, true),
"c_ushort" => (IntKind::Short, false),
"c_uint" => (IntKind::Int, false),
"c_ulong" => (IntKind::Long, false),
"c_ulonglong" => (IntKind::LongLong, false),
"size_t" => (IntKind::SizeT, false),
"isize" | "intptr_t" => (IntKind::Size, true),
"usize" | "uintptr_t" => (IntKind::Size, false),
"u8" | "uint8_t" => (IntKind::B8, false),
"u16" | "uint16_t" => (IntKind::B16, false),
"u32" | "uint32_t" => (IntKind::B32, false),
"u64" | "uint64_t" => (IntKind::B64, false),
"i8" | "int8_t" => (IntKind::B8, true),
"i16" | "int16_t" => (IntKind::B16, true),
"i32" | "int32_t" => (IntKind::B32, true),
"i64" | "int64_t" => (IntKind::B64, true),
_ => return None,
};
PrimitiveType::Integer {
zeroable: true,
signed,
kind,
}
}
})
}
pub fn to_repr_rust(&self) -> &'static str {
match *self {
PrimitiveType::Bool => "bool",
PrimitiveType::Void => "c_void",
PrimitiveType::Char => "c_char",
PrimitiveType::SChar => "c_schar",
PrimitiveType::UChar => "c_uchar",
PrimitiveType::Char32 => "char",
PrimitiveType::Integer {
kind,
signed,
zeroable: _,
} => match kind {
IntKind::Short => {
if signed {
"c_short"
} else {
"c_ushort"
}
}
IntKind::Int => {
if signed {
"c_int"
} else {
"c_uint"
}
}
IntKind::Long => {
if signed {
"c_long"
} else {
"c_ulong"
}
}
IntKind::LongLong => {
if signed {
"c_longlong"
} else {
"c_ulonglong"
}
}
IntKind::SizeT => {
if signed {
"ssize_t"
} else {
"size_t"
}
}
IntKind::Size => {
if signed {
"isize"
} else {
"usize"
}
}
IntKind::B8 => {
if signed {
"i8"
} else {
"u8"
}
}
IntKind::B16 => {
if signed {
"i16"
} else {
"u16"
}
}
IntKind::B32 => {
if signed {
"i32"
} else {
"u32"
}
}
IntKind::B64 => {
if signed {
"i64"
} else {
"u64"
}
}
},
PrimitiveType::Float => "f32",
PrimitiveType::Double => "f64",
PrimitiveType::PtrDiffT => "ptrdiff_t",
PrimitiveType::VaList => "va_list",
}
}
pub fn to_repr_c(&self, config: &Config) -> &'static str {
match *self {
PrimitiveType::Void => "void",
PrimitiveType::Bool => "bool",
PrimitiveType::Char => "char",
PrimitiveType::SChar => "signed char",
PrimitiveType::UChar => "unsigned char",
// NOTE: It'd be nice to use a char32_t, but:
//
// * uchar.h is not present on mac (see #423).
//
// * char32_t isn't required to be compatible with Rust's char, as
// the C++ spec only requires it to be the same size as
// uint_least32_t, which is _not_ guaranteed to be 4-bytes.
//
PrimitiveType::Char32 => "uint32_t",
PrimitiveType::Integer {
kind,
signed,
zeroable: _,
} => match kind {
IntKind::Short => {
if signed {
"short"
} else {
"unsigned short"
}
}
IntKind::Int => {
if signed {
"int"
} else {
"unsigned int"
}
}
IntKind::Long => {
if signed {
"long"
} else {
"unsigned long"
}
}
IntKind::LongLong => {
if signed {
"long long"
} else {
"unsigned long long"
}
}
IntKind::SizeT => {
if signed {
"ssize_t"
} else {
"size_t"
}
}
IntKind::Size => {
if config.usize_is_size_t {
if signed {
"ptrdiff_t"
} else {
"size_t"
}
} else if signed {
"intptr_t"
} else {
"uintptr_t"
}
}
IntKind::B8 => {
if signed {
"int8_t"
} else {
"uint8_t"
}
}
IntKind::B16 => {
if signed {
"int16_t"
} else {
"uint16_t"
}
}
IntKind::B32 => {
if signed {
"int32_t"
} else {
"uint32_t"
}
}
IntKind::B64 => {
if signed {
"int64_t"
} else {
"uint64_t"
}
}
},
PrimitiveType::Float => "float",
PrimitiveType::Double => "double",
PrimitiveType::PtrDiffT => "ptrdiff_t",
PrimitiveType::VaList => "va_list",
}
}
fn can_cmp_order(&self) -> bool {
match *self {
PrimitiveType::Bool => false,
_ => true,
}
}
fn can_cmp_eq(&self) -> bool {
true
}
}
// The `U` part of `[T; U]`
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ArrayLength {
Name(String),
Value(String),
}
impl ArrayLength {
pub fn as_str(&self) -> &str {
match self {
ArrayLength::Name(ref string) | ArrayLength::Value(ref string) => string,
}
}
fn rename_for_config(&mut self, config: &Config) {
if let ArrayLength::Name(ref mut name) = self {
config.export.rename(name);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Type {
Ptr {
ty: Box<Type>,
is_const: bool,
is_nullable: bool,
// FIXME: This is a bit of a hack, this is only to get us to codegen
// `T&` / `const T&`, but we should probably pass that down as an option
// to code generation or something.
is_ref: bool,
},
Path(GenericPath),
Primitive(PrimitiveType),
Array(Box<Type>, ArrayLength),
FuncPtr {
ret: Box<Type>,
args: Vec<(Option<String>, Type)>,
is_nullable: bool,
},
}
impl Type {
pub fn const_ref_to(ty: &Self) -> Self {
Type::Ptr {
ty: Box::new(ty.clone()),
is_const: true,
is_nullable: false,
is_ref: true,
}
}
pub fn load(ty: &syn::Type) -> Result<Option<Type>, String> {
let converted = match *ty {
syn::Type::Reference(ref reference) => {
let converted = Type::load(&reference.elem)?;
let converted = match converted {
Some(converted) => converted,
None => Type::Primitive(PrimitiveType::Void),
};
// TODO(emilio): we could make these use is_ref: true.
let is_const = reference.mutability.is_none();
Type::Ptr {
ty: Box::new(converted),
is_const,
is_nullable: false,
is_ref: false,
}
}
syn::Type::Ptr(ref pointer) => {
let converted = Type::load(&pointer.elem)?;
let converted = match converted {
Some(converted) => converted,
None => Type::Primitive(PrimitiveType::Void),
};
let is_const = pointer.mutability.is_none();
Type::Ptr {
ty: Box::new(converted),
is_const,
is_nullable: true,
is_ref: false,
}
}
syn::Type::Path(ref path) => {
let generic_path = GenericPath::load(&path.path)?;
if generic_path.name() == "PhantomData" || generic_path.name() == "PhantomPinned" {
return Ok(None);
}
if let Some(prim) = PrimitiveType::maybe(generic_path.name()) {
if !generic_path.generics().is_empty() {
return Err("Primitive has generics.".to_owned());
}
Type::Primitive(prim)
} else {
Type::Path(generic_path)
}
}
syn::Type::Array(syn::TypeArray {
ref elem,
len: syn::Expr::Path(ref path),
..
}) => {
let converted = Type::load(elem)?;
let converted = match converted {
Some(converted) => converted,
None => return Err("Cannot have an array of zero sized types.".to_owned()),
};
let generic_path = GenericPath::load(&path.path)?;
let len = ArrayLength::Name(generic_path.export_name().to_owned());
Type::Array(Box::new(converted), len)
}
syn::Type::Array(syn::TypeArray {
ref elem,
len:
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Int(ref len),
..
}),
..
}) => {
let converted = Type::load(elem)?;
let converted = match converted {
Some(converted) => converted,
None => return Err("Cannot have an array of zero sized types.".to_owned()),
};
let len = ArrayLength::Value(len.base10_digits().to_string());
// panic!("panic -> value: {:?}", len);
Type::Array(Box::new(converted), len)
}
syn::Type::BareFn(ref function) => {
let mut wildcard_counter = 0;
let args = function.inputs.iter().try_skip_map(|x| {
Type::load(&x.ty).map(|opt_ty| {
opt_ty.map(|ty| {
(
x.name.as_ref().map(|(ref ident, _)| {
if ident == "_" {
wildcard_counter += 1;
if wildcard_counter == 1 {
"_".to_owned()
} else {
format!("_{}", wildcard_counter - 1)
}
} else {
ident.to_string()
}
}),
ty,
)
})
})
})?;
let ret = match function.output {
syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
syn::ReturnType::Type(_, ref ty) => {
if let Some(x) = Type::load(ty)? {
x
} else {
Type::Primitive(PrimitiveType::Void)
}
}
};
Type::FuncPtr {
ret: Box::new(ret),
args,
is_nullable: false,
}
}
syn::Type::Tuple(ref tuple) => {
if tuple.elems.is_empty() {
return Ok(None);
}
return Err("Tuples are not supported types.".to_owned());
}
_ => return Err(format!("Unsupported type: {:?}", ty)),
};
Ok(Some(converted))
}
pub fn is_primitive_or_ptr_primitive(&self) -> bool {
match *self {
Type::Primitive(..) => true,
Type::Ptr { ref ty, .. } => match ty.as_ref() {
Type::Primitive(..) => true,
_ => false,
},
_ => false,
}
}
pub fn make_zeroable(&self) -> Option<Self> {
let (kind, signed) = match *self {
Type::Primitive(PrimitiveType::Integer {
zeroable: false,
kind,
signed,
}) => (kind, signed),
_ => return None,
};
Some(Type::Primitive(PrimitiveType::Integer {
kind,
signed,
zeroable: true,
}))
}
pub fn make_nullable(&self) -> Option<Self> {
match *self {
Type::Ptr {
ref ty,
is_const,
is_ref,
is_nullable: false,
} => Some(Type::Ptr {
ty: ty.clone(),
is_const,
is_ref,
is_nullable: true,
}),
Type::FuncPtr {
ref ret,
ref args,
is_nullable: false,
} => Some(Type::FuncPtr {
ret: ret.clone(),
args: args.clone(),
is_nullable: true,
}),
_ => None,
}
}
fn nonzero_to_primitive(&self) -> Option<Self> {
let path = match *self {
Type::Path(ref p) => p,
_ => return None,
};
if !path.generics().is_empty() {
return None;
}
let name = path.name();
if !name.starts_with("NonZero") {
return None;
}
let (kind, signed) = match path.name() {
"NonZeroU8" => (IntKind::B8, false),
"NonZeroU16" => (IntKind::B16, false),
"NonZeroU32" => (IntKind::B32, false),
"NonZeroU64" => (IntKind::B64, false),
"NonZeroUSize" => (IntKind::Size, false),
"NonZeroI8" => (IntKind::B8, true),
"NonZeroI16" => (IntKind::B16, true),
"NonZeroI32" => (IntKind::B32, true),
"NonZeroI64" => (IntKind::B64, true),
"NonZeroISize" => (IntKind::Size, true),
_ => return None,
};
Some(Type::Primitive(PrimitiveType::Integer {
zeroable: false,
signed,
kind,
}))
}
fn simplified_type(&self, config: &Config) -> Option<Self> {
let path = match *self {
Type::Path(ref p) => p,
_ => return None,
};
if path.generics().is_empty() {
return self.nonzero_to_primitive();
}
if path.generics().len() != 1 {
return None;
}
let unsimplified_generic = &path.generics()[0];
let generic = match unsimplified_generic.simplified_type(config) {
Some(generic) => Cow::Owned(generic),
None => Cow::Borrowed(unsimplified_generic),
};
match path.name() {
"Option" => {
if let Some(nullable) = generic.make_nullable() {
return Some(nullable);
}
if let Some(zeroable) = generic.make_zeroable() {
return Some(zeroable);
}
None
}
"NonNull" => Some(Type::Ptr {
ty: Box::new(generic.into_owned()),
is_const: false,
is_nullable: false,
is_ref: false,
}),
"Box" if config.language != Language::Cxx => Some(Type::Ptr {
ty: Box::new(generic.into_owned()),
is_const: false,
is_nullable: false,
is_ref: false,
}),
"Cell" => Some(generic.into_owned()),
"ManuallyDrop" | "MaybeUninit" | "Pin" if config.language != Language::Cxx => {
Some(generic.into_owned())
}
_ => None,
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
self.visit_types(|ty| ty.simplify_standard_types(config));
if let Some(ty) = self.simplified_type(config) {
*self = ty;
}
}
pub fn replace_self_with(&mut self, self_ty: &Path) {
if let Type::Path(ref mut generic_path) = *self {
generic_path.replace_self_with(self_ty);
}
self.visit_types(|ty| ty.replace_self_with(self_ty))
}
fn visit_types(&mut self, mut visitor: impl FnMut(&mut Type)) {
match *self {
Type::Array(ref mut ty, ..) | Type::Ptr { ref mut ty, .. } => visitor(ty),
Type::Path(ref mut path) => {
for generic in path.generics_mut() {
visitor(generic);
}
}
Type::Primitive(..) => {}
Type::FuncPtr {
ref mut ret,
ref mut args,
..
} => {
visitor(ret);
for arg in args {
visitor(&mut arg.1)
}
}
}
}
pub fn get_root_path(&self) -> Option<Path> {
let mut current = self;
loop {
match *current {
Type::Ptr { ref ty, .. } => current = ty,
Type::Path(ref generic) => {
return Some(generic.path().clone());
}
Type::Primitive(..) => {
return None;
}
Type::Array(..) => {
return None;
}
Type::FuncPtr { .. } => {
return None;
}
};
}
}
pub fn specialize(&self, mappings: &[(&Path, &Type)]) -> Type {
match *self {
Type::Ptr {
ref ty,
is_const,
is_nullable,
is_ref,
} => Type::Ptr {
ty: Box::new(ty.specialize(mappings)),
is_const,
is_nullable,
is_ref,
},
Type::Path(ref generic_path) => {
for &(param, value) in mappings {
if generic_path.path() == param {
return value.clone();
}
}
let specialized = GenericPath::new(
generic_path.path().clone(),
generic_path
.generics()
.iter()
.map(|x| x.specialize(mappings))
.collect(),
);
Type::Path(specialized)
}
Type::Primitive(ref primitive) => Type::Primitive(primitive.clone()),
Type::Array(ref ty, ref constant) => {
Type::Array(Box::new(ty.specialize(mappings)), constant.clone())
}
Type::FuncPtr {
ref ret,
ref args,
is_nullable,
} => Type::FuncPtr {
ret: Box::new(ret.specialize(mappings)),
args: args
.iter()
.cloned()
.map(|(name, ty)| (name, ty.specialize(mappings)))
.collect(),
is_nullable,
},
}
}
pub fn add_dependencies_ignoring_generics(
&self,
generic_params: &GenericParams,
library: &Library,
out: &mut Dependencies,
) {
match *self {
Type::Ptr { ref ty, .. } => {
ty.add_dependencies_ignoring_generics(generic_params, library, out);
}
Type::Path(ref generic) => {
for generic_value in generic.generics() {
generic_value.add_dependencies_ignoring_generics(generic_params, library, out);
}
let path = generic.path();
if !generic_params.contains(path) {
if let Some(items) = library.get_items(path) {
if !out.items.contains(path) {
out.items.insert(path.clone());
for item in &items {
item.deref().add_dependencies(library, out);
}
for item in items {
out.order.push(item);
}
}
} else {
warn!(
"Can't find {}. This usually means that this type was incompatible or \
not found.",
path
);
}
}
}
Type::Primitive(_) => {}
Type::Array(ref ty, _) => {
ty.add_dependencies_ignoring_generics(generic_params, library, out);
}
Type::FuncPtr {
ref ret, ref args, ..
} => {
ret.add_dependencies_ignoring_generics(generic_params, library, out);
for (_, ref arg) in args {
arg.add_dependencies_ignoring_generics(generic_params, library, out);
}
}
}
}
pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.add_dependencies_ignoring_generics(&GenericParams::default(), library, out)
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
match *self {
Type::Ptr { ref ty, .. } => {
ty.add_monomorphs(library, out);
}
Type::Path(ref generic) => {
if generic.generics().is_empty() || out.contains(generic) {
return;
}
let path = generic.path();
if let Some(items) = library.get_items(path) {
for item in items {
item.deref()
.instantiate_monomorph(generic.generics(), library, out);
}
}
}
Type::Primitive(_) => {}
Type::Array(ref ty, _) => {
ty.add_monomorphs(library, out);
}
Type::FuncPtr {
ref ret, ref args, ..
} => {
ret.add_monomorphs(library, out);
for (_, ref arg) in args {
arg.add_monomorphs(library, out);
}
}
}
}
pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) {
match *self {
Type::Ptr { ref mut ty, .. } => {
ty.rename_for_config(config, generic_params);
}
Type::Path(ref mut ty) => {
ty.rename_for_config(config, generic_params);
}
Type::Primitive(_) => {}
Type::Array(ref mut ty, ref mut len) => {
ty.rename_for_config(config, generic_params);
len.rename_for_config(config);
}
Type::FuncPtr {
ref mut ret,
ref mut args,
..
} => {
ret.rename_for_config(config, generic_params);
for (_, arg) in args {
arg.rename_for_config(config, generic_params);
}
}
}
}
pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
match *self {
Type::Ptr { ref mut ty, .. } => {
ty.resolve_declaration_types(resolver);
}
Type::Path(ref mut generic_path) => {
generic_path.resolve_declaration_types(resolver);
}
Type::Primitive(_) => {}
Type::Array(ref mut ty, _) => {
ty.resolve_declaration_types(resolver);
}
Type::FuncPtr {
ref mut ret,
ref mut args,
..
} => {
ret.resolve_declaration_types(resolver);
for (_, ref mut arg) in args {
arg.resolve_declaration_types(resolver);
}
}
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
match *self {
Type::Ptr { ref mut ty, .. } => {
ty.mangle_paths(monomorphs);
}
Type::Path(ref mut generic_path) => {
if generic_path.generics().is_empty() {
return;
}
if let Some(mangled_path) = monomorphs.mangle_path(generic_path) {
*generic_path = GenericPath::new(mangled_path.clone(), vec![]);
} else {
warn!(
"Cannot find a mangling for generic path {:?}. This usually means that a \
type referenced by this generic was incompatible or not found.",
generic_path
);
}
}
Type::Primitive(_) => {}
Type::Array(ref mut ty, _) => {
ty.mangle_paths(monomorphs);
}
Type::FuncPtr {
ref mut ret,
ref mut args,
..
} => {
ret.mangle_paths(monomorphs);
for (_, ref mut arg) in args {
arg.mangle_paths(monomorphs);
}
}
}
}
pub fn can_cmp_order(&self) -> bool {
match *self {
// FIXME: Shouldn't this look at ty.can_cmp_order() as well?
Type::Ptr { is_ref, .. } => !is_ref,
Type::Path(..) => true,
Type::Primitive(ref p) => p.can_cmp_order(),
Type::Array(..) => false,
Type::FuncPtr { .. } => false,
}
}
pub fn can_cmp_eq(&self) -> bool {
match *self {
Type::Ptr { ref ty, is_ref, .. } => !is_ref || ty.can_cmp_eq(),
Type::Path(..) => true,
Type::Primitive(ref p) => p.can_cmp_eq(),
Type::Array(..) => false,
Type::FuncPtr { .. } => true,
}
}
}
impl Source for String {
fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
write!(out, "{}", self);
}
}
impl Source for Type {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
cdecl::write_type(out, self, config);
}
}

View File

@@ -0,0 +1,223 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::io::Write;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericParams, Item, ItemContainer,
Path, ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::mangle;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::writer::{Source, SourceWriter};
/// A type alias that is represented as a C typedef
#[derive(Debug, Clone)]
pub struct Typedef {
pub path: Path,
pub export_name: String,
pub generic_params: GenericParams,
pub aliased: Type,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl Typedef {
pub fn load(item: &syn::ItemType, mod_cfg: Option<&Cfg>) -> Result<Typedef, String> {
if let Some(x) = Type::load(&item.ty)? {
let path = Path::new(item.ident.to_string());
Ok(Typedef::new(
path,
GenericParams::new(&item.generics),
x,
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
AnnotationSet::load(&item.attrs)?,
Documentation::load(&item.attrs),
))
} else {
Err("Cannot have a typedef of a zero sized type.".to_owned())
}
}
pub fn new(
path: Path,
generic_params: GenericParams,
aliased: Type,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generic_params,
aliased,
cfg,
annotations,
documentation,
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
self.aliased.simplify_standard_types(config);
}
pub fn transfer_annotations(&mut self, out: &mut HashMap<Path, AnnotationSet>) {
if self.annotations.is_empty() {
return;
}
if let Some(alias_path) = self.aliased.get_root_path() {
if out.contains_key(&alias_path) {
warn!(
"Multiple typedef's with annotations for {}. Ignoring annotations from {}.",
alias_path, self.path
);
return;
}
out.insert(alias_path, self.annotations.clone());
self.annotations = AnnotationSet::new();
}
}
pub fn is_generic(&self) -> bool {
self.generic_params.len() > 0
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
// Generic structs can instantiate monomorphs only once they've been
// instantiated. See `instantiate_monomorph` for more details.
if self.is_generic() {
return;
}
self.aliased.add_monomorphs(library, out);
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
self.aliased.mangle_paths(monomorphs);
}
}
impl Item for Typedef {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Typedef(self.clone())
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.export_name);
self.aliased.rename_for_config(config, &self.generic_params);
}
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
resolver.add_none(&self.path);
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.aliased.resolve_declaration_types(resolver);
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.aliased
.add_dependencies_ignoring_generics(&self.generic_params, library, out);
}
fn instantiate_monomorph(
&self,
generic_values: &[Type],
library: &Library,
out: &mut Monomorphs,
) {
assert!(
self.generic_params.len() > 0,
"{} is not generic",
self.path
);
assert!(
self.generic_params.len() == generic_values.len(),
"{} has {} params but is being instantiated with {} values",
self.path,
self.generic_params.len(),
generic_values.len(),
);
let mappings = self
.generic_params
.iter()
.zip(generic_values.iter())
.collect::<Vec<_>>();
let mangled_path = mangle::mangle_path(
&self.path,
generic_values,
&library.get_config().export.mangle,
);
let monomorph = Typedef::new(
mangled_path,
GenericParams::default(),
self.aliased.specialize(&mappings),
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
);
out.insert_typedef(library, self, monomorph, generic_values.to_owned());
}
}
impl Source for Typedef {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
self.documentation.write(config, out);
self.generic_params.write(config, out);
match config.language {
Language::Cxx => {
write!(out, "using {} = ", self.export_name());
self.aliased.write(config, out);
}
Language::C | Language::Cython => {
write!(out, "{} ", config.language.typedef());
Field::from_name_and_type(self.export_name().to_owned(), self.aliased.clone())
.write(config, out);
}
}
out.write(";");
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,351 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::config::{Config, Language, LayoutConfig};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericParams, Item, ItemContainer,
Path, Repr, ReprAlign, ReprStyle, ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::mangle;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{ListType, Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Union {
pub path: Path,
pub export_name: String,
pub generic_params: GenericParams,
pub fields: Vec<Field>,
pub tuple_union: bool,
pub alignment: Option<ReprAlign>,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl Union {
pub fn load(
layout_config: &LayoutConfig,
item: &syn::ItemUnion,
mod_cfg: Option<&Cfg>,
) -> Result<Union, String> {
let repr = Repr::load(&item.attrs)?;
if repr.style != ReprStyle::C {
return Err("Union is not marked #[repr(C)].".to_owned());
}
// Ensure we can safely represent the union given the configuration.
if let Some(align) = repr.align {
layout_config.ensure_safe_to_represent(&align)?;
}
let path = Path::new(item.ident.to_string());
let (fields, tuple_union) = {
let out = item
.fields
.named
.iter()
.try_skip_map(|field| Field::load(field, &path))?;
(out, false)
};
Ok(Union::new(
path,
GenericParams::new(&item.generics),
fields,
repr.align,
tuple_union,
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
AnnotationSet::load(&item.attrs)?,
Documentation::load(&item.attrs),
))
}
#[allow(clippy::too_many_arguments)]
pub fn new(
path: Path,
generic_params: GenericParams,
fields: Vec<Field>,
alignment: Option<ReprAlign>,
tuple_union: bool,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generic_params,
fields,
tuple_union,
alignment,
cfg,
annotations,
documentation,
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
for field in &mut self.fields {
field.ty.simplify_standard_types(config);
}
}
pub fn is_generic(&self) -> bool {
self.generic_params.len() > 0
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
// Generic unions can instantiate monomorphs only once they've been
// instantiated. See `instantiate_monomorph` for more details.
if self.is_generic() {
return;
}
for field in &self.fields {
field.ty.add_monomorphs(library, out);
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
for field in &mut self.fields {
field.ty.mangle_paths(monomorphs);
}
}
}
impl Item for Union {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Union(self.clone())
}
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
resolver.add_union(&self.path);
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
for field in &mut self.fields {
field.ty.resolve_declaration_types(resolver);
}
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.export_name);
for field in &mut self.fields {
field.ty.rename_for_config(config, &self.generic_params);
}
let rules = self
.annotations
.parse_atom::<RenameRule>("rename-all")
.unwrap_or(config.structure.rename_fields);
if let Some(o) = self.annotations.list("field-names") {
let mut overriden_fields = Vec::new();
for (i, field) in self.fields.iter().enumerate() {
if i >= o.len() {
overriden_fields.push(field.clone());
} else {
overriden_fields.push(Field {
name: o[i].clone(),
ty: field.ty.clone(),
cfg: field.cfg.clone(),
annotations: field.annotations.clone(),
documentation: field.documentation.clone(),
});
}
}
self.fields = overriden_fields;
} else if let Some(r) = rules.not_none() {
self.fields = self
.fields
.iter()
.map(|field| Field {
name: r
.apply(&field.name, IdentifierType::StructMember)
.into_owned(),
ty: field.ty.clone(),
cfg: field.cfg.clone(),
annotations: field.annotations.clone(),
documentation: field.documentation.clone(),
})
.collect();
} else if self.tuple_union {
// If we don't have any rules for a tuple union, prefix them with
// an underscore so it still compiles
for field in &mut self.fields {
field.name.insert(0, '_');
}
}
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
for field in &self.fields {
field
.ty
.add_dependencies_ignoring_generics(&self.generic_params, library, out);
}
}
fn instantiate_monomorph(
&self,
generic_values: &[Type],
library: &Library,
out: &mut Monomorphs,
) {
assert!(
self.generic_params.len() > 0,
"{} is not generic",
self.path
);
assert!(
self.generic_params.len() == generic_values.len(),
"{} has {} params but is being instantiated with {} values",
self.path,
self.generic_params.len(),
generic_values.len(),
);
let mappings = self
.generic_params
.iter()
.zip(generic_values.iter())
.collect::<Vec<_>>();
let mangled_path = mangle::mangle_path(
&self.path,
generic_values,
&library.get_config().export.mangle,
);
let monomorph = Union::new(
mangled_path,
GenericParams::default(),
self.fields
.iter()
.map(|field| Field {
name: field.name.clone(),
ty: field.ty.specialize(&mappings),
cfg: field.cfg.clone(),
annotations: field.annotations.clone(),
documentation: field.documentation.clone(),
})
.collect(),
self.alignment,
self.tuple_union,
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
);
out.insert_union(library, self, monomorph, generic_values.to_owned());
}
}
impl Source for Union {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
self.documentation.write(config, out);
self.generic_params.write(config, out);
// The following results in
// C++ or C with Tag as style:
// union Name {
// C with Type only style:
// typedef union {
// C with Both as style:
// typedef union Name {
match config.language {
Language::C if config.style.generate_typedef() => out.write("typedef "),
Language::C | Language::Cxx => {}
Language::Cython => out.write(config.style.cython_def()),
}
out.write("union");
// Cython supports `packed` on structs (see comments there), but not on unions.
if config.language != Language::Cython {
if let Some(align) = self.alignment {
match align {
ReprAlign::Packed => {
if let Some(ref anno) = config.layout.packed {
write!(out, " {}", anno);
}
}
ReprAlign::Align(n) => {
if let Some(ref anno) = config.layout.aligned_n {
write!(out, " {}({})", anno, n);
}
}
}
}
}
if config.language != Language::C || config.style.generate_tag() {
write!(out, " {}", self.export_name);
}
out.open_brace();
// Emit the pre_body section, if relevant
if let Some(body) = config.export.pre_body(&self.path) {
out.write_raw_block(body);
out.new_line();
}
out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
if config.language == Language::Cython && self.fields.is_empty() {
out.write("pass");
}
// Emit the post_body section, if relevant
if let Some(body) = config.export.post_body(&self.path) {
out.new_line();
out.write_raw_block(body);
}
if config.language == Language::C && config.style.generate_typedef() {
out.close_brace(false);
write!(out, " {};", self.export_name);
} else {
out.close_brace(true);
}
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,436 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use crate::bindgen::bindings::Bindings;
use crate::bindgen::config::{Config, Language, SortKey};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::error::Error;
use crate::bindgen::ir::{Constant, Enum, Function, Item, ItemContainer, ItemMap};
use crate::bindgen::ir::{OpaqueItem, Path, Static, Struct, Typedef, Union};
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::ItemType;
#[derive(Debug, Clone)]
pub struct Library {
config: Config,
constants: ItemMap<Constant>,
globals: ItemMap<Static>,
enums: ItemMap<Enum>,
structs: ItemMap<Struct>,
unions: ItemMap<Union>,
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
functions: Vec<Function>,
}
impl Library {
#[allow(clippy::too_many_arguments)]
pub fn new(
config: Config,
constants: ItemMap<Constant>,
globals: ItemMap<Static>,
enums: ItemMap<Enum>,
structs: ItemMap<Struct>,
unions: ItemMap<Union>,
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
functions: Vec<Function>,
) -> Library {
Library {
config,
constants,
globals,
enums,
structs,
unions,
opaque_items,
typedefs,
functions,
}
}
pub fn generate(mut self) -> Result<Bindings, Error> {
self.transfer_annotations();
self.simplify_standard_types();
match self.config.function.sort_by.unwrap_or(self.config.sort_by) {
SortKey::Name => self.functions.sort_by(|x, y| x.path.cmp(&y.path)),
SortKey::None => { /* keep input order */ }
}
if self.config.language != Language::Cxx {
self.instantiate_monomorphs();
}
self.remove_excluded();
if self.config.language == Language::C {
self.resolve_declaration_types();
}
self.rename_items();
let mut dependencies = Dependencies::new();
for function in &self.functions {
function.add_dependencies(&self, &mut dependencies);
}
self.globals.for_all_items(|global| {
global.add_dependencies(&self, &mut dependencies);
});
self.constants.for_all_items(|constant| {
constant.add_dependencies(&self, &mut dependencies);
});
for name in &self.config.export.include {
let path = Path::new(name.clone());
if let Some(items) = self.get_items(&path) {
if dependencies.items.insert(path) {
for item in &items {
item.deref().add_dependencies(&self, &mut dependencies);
}
for item in items {
dependencies.order.push(item);
}
}
}
}
dependencies.sort();
let items = dependencies.order;
let constants = if self.config.export.should_generate(ItemType::Constants) {
let mut constants = self.constants.to_vec();
match self.config.constant.sort_by.unwrap_or(self.config.sort_by) {
SortKey::Name => constants.sort_by(|x, y| x.path.cmp(&y.path)),
SortKey::None => { /* keep input order */ }
}
constants
} else {
vec![]
};
let globals = if self.config.export.should_generate(ItemType::Globals) {
let mut globals = self.globals.to_vec();
match self.config.constant.sort_by.unwrap_or(self.config.sort_by) {
SortKey::Name => globals.sort_by(|x, y| x.path.cmp(&y.path)),
SortKey::None => { /* keep input order */ }
}
globals
} else {
vec![]
};
let functions = if self.config.export.should_generate(ItemType::Functions) {
self.functions
} else {
vec![]
};
Ok(Bindings::new(
self.config,
self.structs,
constants,
globals,
items,
functions,
))
}
pub fn get_items(&self, p: &Path) -> Option<Vec<ItemContainer>> {
macro_rules! find {
($field:ident, $kind:ident) => {
if self.config.export.should_generate(ItemType::$kind) {
if let Some(x) = self.$field.get_items(p) {
return Some(x);
}
}
};
}
find!(enums, Enums);
find!(structs, Structs);
find!(unions, Unions);
find!(opaque_items, OpaqueItems);
find!(typedefs, Typedefs);
None
}
pub fn get_config(&self) -> &Config {
&self.config
}
fn remove_excluded(&mut self) {
let config = &self.config;
// FIXME: interpret `config.export.exclude` as `Path`s.
self.functions
.retain(|x| !config.export.exclude.iter().any(|y| y == x.path().name()));
self.enums
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.structs
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.unions
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.opaque_items
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.typedefs
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.globals
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.constants
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
}
fn transfer_annotations(&mut self) {
let mut annotations = HashMap::new();
self.typedefs.for_all_items_mut(|x| {
x.transfer_annotations(&mut annotations);
});
for (alias_path, annotations) in annotations {
// TODO
let mut transferred = false;
self.enums.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
self.structs.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
self.unions.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
self.opaque_items.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
self.typedefs.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
}
}
fn rename_items(&mut self) {
let config = &self.config;
self.globals
.for_all_items_mut(|x| x.rename_for_config(config));
self.globals.rebuild();
self.constants
.for_all_items_mut(|x| x.rename_for_config(config));
self.constants.rebuild();
self.structs
.for_all_items_mut(|x| x.rename_for_config(config));
self.structs.rebuild();
self.unions
.for_all_items_mut(|x| x.rename_for_config(config));
self.unions.rebuild();
self.enums
.for_all_items_mut(|x| x.rename_for_config(config));
self.enums.rebuild();
self.opaque_items
.for_all_items_mut(|x| x.rename_for_config(config));
self.opaque_items.rebuild();
self.typedefs
.for_all_items_mut(|x| x.rename_for_config(config));
self.typedefs.rebuild();
for item in &mut self.functions {
item.rename_for_config(&self.config);
}
}
fn resolve_declaration_types(&mut self) {
if !self.config.style.generate_tag() {
return;
}
let mut resolver = DeclarationTypeResolver::default();
self.structs.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.enums.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.unions.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.typedefs.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
// NOTE: Intentionally last, so that in case there's an opaque type
// which is conflicting with a non-opaque one, the later wins.
self.opaque_items.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.enums
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.structs
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.unions
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.typedefs
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.globals
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
for item in &mut self.functions {
item.resolve_declaration_types(&resolver);
}
}
fn simplify_standard_types(&mut self) {
let config = &self.config;
self.structs.for_all_items_mut(|x| {
x.simplify_standard_types(config);
});
self.unions.for_all_items_mut(|x| {
x.simplify_standard_types(config);
});
self.globals.for_all_items_mut(|x| {
x.simplify_standard_types(config);
});
self.typedefs.for_all_items_mut(|x| {
x.simplify_standard_types(config);
});
for x in &mut self.functions {
x.simplify_standard_types(config);
}
}
fn instantiate_monomorphs(&mut self) {
// Collect a list of monomorphs
let mut monomorphs = Monomorphs::default();
self.structs.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
self.unions.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
self.enums.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
self.typedefs.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
for x in &self.functions {
x.add_monomorphs(self, &mut monomorphs);
}
// Insert the monomorphs into self
for monomorph in monomorphs.drain_structs() {
self.structs.try_insert(monomorph);
}
for monomorph in monomorphs.drain_unions() {
self.unions.try_insert(monomorph);
}
for monomorph in monomorphs.drain_opaques() {
self.opaque_items.try_insert(monomorph);
}
for monomorph in monomorphs.drain_typedefs() {
self.typedefs.try_insert(monomorph);
}
for monomorph in monomorphs.drain_enums() {
self.enums.try_insert(monomorph);
}
// Remove structs and opaque items that are generic
self.opaque_items.filter(|x| x.generic_params.len() > 0);
self.structs.filter(|x| x.generic_params.len() > 0);
self.unions.filter(|x| x.generic_params.len() > 0);
self.enums.filter(|x| x.generic_params.len() > 0);
self.typedefs.filter(|x| x.generic_params.len() > 0);
// Mangle the paths that remain
self.unions
.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
self.structs
.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
self.enums
.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
self.typedefs
.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
for x in &mut self.functions {
x.mangle_paths(&monomorphs);
}
}
}

View File

@@ -0,0 +1,291 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::bindgen::config::MangleConfig;
use crate::bindgen::ir::{Path, Type};
use crate::bindgen::rename::IdentifierType;
pub fn mangle_path(path: &Path, generic_values: &[Type], config: &MangleConfig) -> Path {
Path::new(mangle_name(path.name(), generic_values, config))
}
pub fn mangle_name(name: &str, generic_values: &[Type], config: &MangleConfig) -> String {
Mangler::new(name, generic_values, /* last = */ true, config).mangle()
}
enum Separator {
OpeningAngleBracket = 1,
Comma,
ClosingAngleBracket,
BeginMutPtr,
BeginConstPtr,
BeginFn,
BetweenFnArg,
EndFn,
}
struct Mangler<'a> {
input: &'a str,
generic_values: &'a [Type],
output: String,
last: bool,
config: &'a MangleConfig,
}
impl<'a> Mangler<'a> {
fn new(
input: &'a str,
generic_values: &'a [Type],
last: bool,
config: &'a MangleConfig,
) -> Self {
Self {
input,
generic_values,
output: String::new(),
last,
config,
}
}
fn mangle(mut self) -> String {
self.mangle_internal();
self.output
}
fn push(&mut self, id: Separator) {
let count = id as usize;
let separator = if self.config.remove_underscores {
""
} else {
"_"
};
self.output.extend(std::iter::repeat(separator).take(count));
}
fn append_mangled_type(&mut self, ty: &Type, last: bool) {
match *ty {
Type::Path(ref generic) => {
let sub_path =
Mangler::new(generic.export_name(), generic.generics(), last, self.config)
.mangle();
self.output.push_str(
&self
.config
.rename_types
.apply(&sub_path, IdentifierType::Type),
);
}
Type::Primitive(ref primitive) => {
self.output.push_str(
&self
.config
.rename_types
.apply(primitive.to_repr_rust(), IdentifierType::Type),
);
}
Type::Ptr {
ref ty, is_const, ..
} => {
self.push(if is_const {
Separator::BeginConstPtr
} else {
Separator::BeginMutPtr
});
self.append_mangled_type(&**ty, last);
}
Type::FuncPtr {
ref ret, ref args, ..
} => {
self.push(Separator::BeginFn);
self.append_mangled_type(&**ret, args.is_empty());
for (i, arg) in args.iter().enumerate() {
self.push(Separator::BetweenFnArg);
let last = last && i == args.len() - 1;
self.append_mangled_type(&arg.1, last);
}
if !self.last {
self.push(Separator::EndFn);
}
}
Type::Array(..) => {
unimplemented!(
"Unable to mangle generic parameter {:?} for '{}'",
ty,
self.input
);
}
}
}
fn mangle_internal(&mut self) {
debug_assert!(self.output.is_empty());
self.output = self.input.to_owned();
if self.generic_values.is_empty() {
return;
}
self.push(Separator::OpeningAngleBracket);
for (i, ty) in self.generic_values.iter().enumerate() {
if i != 0 {
self.push(Separator::Comma);
}
let last = self.last && i == self.generic_values.len() - 1;
self.append_mangled_type(ty, last);
}
// Skip writing the trailing '>' mangling when possible
if !self.last {
self.push(Separator::ClosingAngleBracket)
}
}
}
#[test]
fn generics() {
use crate::bindgen::ir::{GenericPath, PrimitiveType};
use crate::bindgen::rename::RenameRule::{self, PascalCase};
fn float() -> Type {
Type::Primitive(PrimitiveType::Float)
}
fn c_char() -> Type {
Type::Primitive(PrimitiveType::Char)
}
fn path(path: &str) -> Type {
generic_path(path, &[])
}
fn generic_path(path: &str, generics: &[Type]) -> Type {
let path = Path::new(path);
let generic_path = GenericPath::new(path, generics.to_owned());
Type::Path(generic_path)
}
// Foo<f32> => Foo_f32
assert_eq!(
mangle_path(&Path::new("Foo"), &[float()], &MangleConfig::default()),
Path::new("Foo_f32")
);
// Foo<Bar<f32>> => Foo_Bar_f32
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[float()])],
&MangleConfig::default(),
),
Path::new("Foo_Bar_f32")
);
// Foo<Bar> => Foo_Bar
assert_eq!(
mangle_path(&Path::new("Foo"), &[path("Bar")], &MangleConfig::default()),
Path::new("Foo_Bar")
);
// Foo<Bar> => FooBar
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[path("Bar")],
&MangleConfig {
remove_underscores: true,
rename_types: RenameRule::None,
}
),
Path::new("FooBar")
);
// Foo<Bar<f32>> => FooBarF32
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[float()])],
&MangleConfig {
remove_underscores: true,
rename_types: PascalCase,
},
),
Path::new("FooBarF32")
);
// Foo<Bar<c_char>> => FooBarCChar
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[c_char()])],
&MangleConfig {
remove_underscores: true,
rename_types: PascalCase,
},
),
Path::new("FooBarCChar")
);
// Foo<Bar<T>> => Foo_Bar_T
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[path("T")])],
&MangleConfig::default(),
),
Path::new("Foo_Bar_T")
);
// Foo<Bar<T>, E> => Foo_Bar_T_____E
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[path("T")]), path("E")],
&MangleConfig::default(),
),
Path::new("Foo_Bar_T_____E")
);
// Foo<Bar<T>, Bar<E>> => Foo_Bar_T_____Bar_E
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[
generic_path("Bar", &[path("T")]),
generic_path("Bar", &[path("E")]),
],
&MangleConfig::default(),
),
Path::new("Foo_Bar_T_____Bar_E")
);
// Foo<Bar<T>, E> => FooBarTE
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[path("T")]), path("E")],
&MangleConfig {
remove_underscores: true,
rename_types: PascalCase,
},
),
Path::new("FooBarTE")
);
// Foo<Bar<T>, Bar<E>> => FooBarTBarE
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[
generic_path("Bar", &[path("T")]),
generic_path("Bar", &[path("E")]),
],
&MangleConfig {
remove_underscores: true,
rename_types: PascalCase,
},
),
Path::new("FooBarTBarE")
);
}

View File

@@ -0,0 +1,65 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// A helper macro for deriving deserialize for an enum to be used in toml-rs.
/// This macro works be relying on an existing FromStr implementation for the
/// desired type.
macro_rules! deserialize_enum_str {
($name:ident) => {
impl<'de> ::serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
struct Visitor;
impl<'de> ::serde::de::Visitor<'de> for Visitor {
type Value = $name;
fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.write_str("$name")
}
fn visit_str<E>(self, v: &str) -> Result<$name, E>
where
E: ::serde::de::Error,
{
match v.parse::<$name>() {
Ok(v) => Ok(v),
Err(m) => Err(E::custom(m)),
}
}
}
deserializer.deserialize_str(Visitor)
}
}
};
}
mod bindings;
mod bitflags;
mod builder;
mod cargo;
mod cdecl;
mod config;
mod declarationtyperesolver;
mod dependencies;
mod error;
mod ir;
mod library;
mod mangle;
mod monomorph;
mod parser;
mod rename;
mod reserved;
mod utilities;
mod writer;
#[allow(unused)]
pub(crate) use self::cargo::*;
pub use self::bindings::Bindings;
pub use self::builder::Builder;
pub use self::config::Profile; // disambiguate with cargo::Profile
pub use self::config::*;
pub use self::error::Error;

View File

@@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::mem;
use crate::bindgen::ir::{Enum, GenericPath, OpaqueItem, Path, Struct, Type, Typedef, Union};
use crate::bindgen::library::Library;
#[derive(Default, Clone, Debug)]
pub struct Monomorphs {
replacements: HashMap<GenericPath, Path>,
opaques: Vec<OpaqueItem>,
structs: Vec<Struct>,
unions: Vec<Union>,
typedefs: Vec<Typedef>,
enums: Vec<Enum>,
}
impl Monomorphs {
pub fn contains(&self, path: &GenericPath) -> bool {
self.replacements.contains_key(path)
}
pub fn insert_struct(
&mut self,
library: &Library,
generic: &Struct,
monomorph: Struct,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
monomorph.add_monomorphs(library, self);
self.structs.push(monomorph);
}
pub fn insert_enum(
&mut self,
library: &Library,
generic: &Enum,
monomorph: Enum,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
monomorph.add_monomorphs(library, self);
self.enums.push(monomorph);
}
pub fn insert_union(
&mut self,
library: &Library,
generic: &Union,
monomorph: Union,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
monomorph.add_monomorphs(library, self);
self.unions.push(monomorph);
}
pub fn insert_opaque(
&mut self,
generic: &OpaqueItem,
monomorph: OpaqueItem,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
self.opaques.push(monomorph);
}
pub fn insert_typedef(
&mut self,
library: &Library,
generic: &Typedef,
monomorph: Typedef,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
monomorph.add_monomorphs(library, self);
self.typedefs.push(monomorph);
}
pub fn mangle_path(&self, path: &GenericPath) -> Option<&Path> {
self.replacements.get(path)
}
pub fn drain_opaques(&mut self) -> Vec<OpaqueItem> {
mem::take(&mut self.opaques)
}
pub fn drain_structs(&mut self) -> Vec<Struct> {
mem::take(&mut self.structs)
}
pub fn drain_unions(&mut self) -> Vec<Union> {
mem::take(&mut self.unions)
}
pub fn drain_typedefs(&mut self) -> Vec<Typedef> {
mem::take(&mut self.typedefs)
}
pub fn drain_enums(&mut self) -> Vec<Enum> {
mem::take(&mut self.enums)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::str::FromStr;
/// The type of identifier to be renamed.
#[derive(Debug, Clone, Copy)]
pub enum IdentifierType<'a> {
StructMember,
EnumVariant { prefix: &'a str },
FunctionArg,
Type,
Enum,
}
impl<'a> IdentifierType<'a> {
fn to_str(self) -> &'static str {
match self {
IdentifierType::StructMember => "m",
IdentifierType::EnumVariant { .. } => "",
IdentifierType::FunctionArg => "a",
IdentifierType::Type => "",
IdentifierType::Enum => "",
}
}
}
/// A rule to apply to an identifier when generating bindings.
#[derive(Debug, Clone, Copy)]
pub enum RenameRule {
/// Do not apply any renaming. The default.
None,
/// Converts the identifier to PascalCase and adds a context dependent prefix
GeckoCase,
/// Converts the identifier to lower case.
LowerCase,
/// Converts the identifier to upper case.
UpperCase,
/// Converts the identifier to PascalCase.
PascalCase,
/// Converts the identifier to camelCase.
CamelCase,
/// Converts the identifier to snake_case.
SnakeCase,
/// Converts the identifier to SCREAMING_SNAKE_CASE.
ScreamingSnakeCase,
/// Converts the identifier to SCREAMING_SNAKE_CASE and prefixes enum variants
/// with the enum name.
QualifiedScreamingSnakeCase,
}
impl RenameRule {
pub(crate) fn not_none(self) -> Option<Self> {
match self {
RenameRule::None => None,
other => Some(other),
}
}
/// Applies the rename rule to a string
pub fn apply<'a>(self, text: &'a str, context: IdentifierType) -> Cow<'a, str> {
use heck::*;
if text.is_empty() {
return Cow::Borrowed(text);
}
Cow::Owned(match self {
RenameRule::None => return Cow::Borrowed(text),
RenameRule::GeckoCase => context.to_str().to_owned() + &text.to_camel_case(),
RenameRule::LowerCase => text.to_lowercase(),
RenameRule::UpperCase => text.to_uppercase(),
RenameRule::PascalCase => text.to_camel_case(),
RenameRule::CamelCase => text.to_mixed_case(),
RenameRule::SnakeCase => text.to_snake_case(),
RenameRule::ScreamingSnakeCase => text.to_shouty_snake_case(),
RenameRule::QualifiedScreamingSnakeCase => {
let mut result = String::new();
if let IdentifierType::EnumVariant { prefix } = context {
result.push_str(
&RenameRule::ScreamingSnakeCase.apply(prefix, IdentifierType::Enum),
);
result.push('_');
}
result.push_str(&RenameRule::ScreamingSnakeCase.apply(text, context));
result
}
})
}
}
impl Default for RenameRule {
fn default() -> RenameRule {
RenameRule::None
}
}
impl FromStr for RenameRule {
type Err = String;
fn from_str(s: &str) -> Result<RenameRule, Self::Err> {
match s {
"none" => Ok(RenameRule::None),
"None" => Ok(RenameRule::None),
"mGeckoCase" => Ok(RenameRule::GeckoCase),
"GeckoCase" => Ok(RenameRule::GeckoCase),
"gecko_case" => Ok(RenameRule::GeckoCase),
"lowercase" => Ok(RenameRule::LowerCase),
"LowerCase" => Ok(RenameRule::LowerCase),
"lower_case" => Ok(RenameRule::LowerCase),
"UPPERCASE" => Ok(RenameRule::UpperCase),
"UpperCase" => Ok(RenameRule::UpperCase),
"upper_case" => Ok(RenameRule::UpperCase),
"PascalCase" => Ok(RenameRule::PascalCase),
"pascal_case" => Ok(RenameRule::PascalCase),
"camelCase" => Ok(RenameRule::CamelCase),
"CamelCase" => Ok(RenameRule::CamelCase),
"camel_case" => Ok(RenameRule::CamelCase),
"snake_case" => Ok(RenameRule::SnakeCase),
"SnakeCase" => Ok(RenameRule::SnakeCase),
"SCREAMING_SNAKE_CASE" => Ok(RenameRule::ScreamingSnakeCase),
"ScreamingSnakeCase" => Ok(RenameRule::ScreamingSnakeCase),
"screaming_snake_case" => Ok(RenameRule::ScreamingSnakeCase),
"QUALIFIED_SCREAMING_SNAKE_CASE" => Ok(RenameRule::QualifiedScreamingSnakeCase),
"QualifiedScreamingSnakeCase" => Ok(RenameRule::QualifiedScreamingSnakeCase),
"qualified_screaming_snake_case" => Ok(RenameRule::QualifiedScreamingSnakeCase),
_ => Err(format!("Unrecognized RenameRule: '{}'.", s)),
}
}
}
deserialize_enum_str!(RenameRule);

View File

@@ -0,0 +1,91 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// Taken from `https://en.cppreference.com/w/cpp/keyword`
/// Some experimental keywords were filtered out and the resulting list was
/// sorted using a rust program.
const RESERVED_KEYWORDS: &[&str] = &[
"alignas",
"alignof",
"auto",
"bool",
"break",
"case",
"catch",
"char",
"char16_t",
"char32_t",
"char8_t",
"class",
"const",
"const_cast",
"consteval",
"constexpr",
"continue",
"decltype",
"default",
"delete",
"do",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
"float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
"mutable",
"namespace",
"new",
"noexcept",
"nullptr",
"operator",
"private",
"protected",
"public",
"register",
"reinterpret_cast",
"return",
"short",
"signed",
"sizeof",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
"thread_local",
"throw",
"true",
"try",
"typedef",
"typename",
"union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
"while",
];
pub fn escape(rust_identifier: &mut String) {
if RESERVED_KEYWORDS
.binary_search(&rust_identifier.as_ref())
.is_ok()
{
rust_identifier.push('_');
}
}

View File

@@ -0,0 +1,293 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![allow(clippy::redundant_closure_call)]
pub trait IterHelpers: Iterator {
fn try_skip_map<F, T, E>(&mut self, f: F) -> Result<Vec<T>, E>
where
F: FnMut(&Self::Item) -> Result<Option<T>, E>;
}
impl<I> IterHelpers for I
where
I: Iterator,
{
fn try_skip_map<F, T, E>(&mut self, mut f: F) -> Result<Vec<T>, E>
where
F: FnMut(&Self::Item) -> Result<Option<T>, E>,
{
let mut out = Vec::new();
for item in self {
if let Some(x) = f(&item)? {
out.push(x);
}
}
Ok(out)
}
}
pub trait SynItemFnHelpers: SynAttributeHelpers {
fn exported_name(&self) -> Option<String>;
}
impl SynItemFnHelpers for syn::ItemFn {
fn exported_name(&self) -> Option<String> {
self.attrs
.attr_name_value_lookup("export_name")
.or_else(|| {
if self.is_no_mangle() {
Some(self.sig.ident.to_string())
} else {
None
}
})
}
}
impl SynItemFnHelpers for syn::ImplItemMethod {
fn exported_name(&self) -> Option<String> {
self.attrs
.attr_name_value_lookup("export_name")
.or_else(|| {
if self.is_no_mangle() {
Some(self.sig.ident.to_string())
} else {
None
}
})
}
}
/// Returns whether this attribute causes us to skip at item. This basically
/// checks for `#[cfg(test)]`, `#[test]`, `/// cbindgen::ignore` and
/// variations thereof.
fn is_skip_item_attr(attr: &syn::Meta) -> bool {
match *attr {
syn::Meta::Path(ref path) => {
// TODO(emilio): It'd be great if rustc allowed us to use a syntax
// like `#[cbindgen::ignore]` or such.
path.is_ident("test")
}
syn::Meta::List(ref list) => {
if !list.path.is_ident("cfg") {
return false;
}
list.nested.iter().any(|nested| match *nested {
syn::NestedMeta::Meta(ref meta) => is_skip_item_attr(meta),
syn::NestedMeta::Lit(..) => false,
})
}
syn::Meta::NameValue(ref name_value) => {
if name_value.path.is_ident("doc") {
if let syn::Lit::Str(ref content) = name_value.lit {
// FIXME(emilio): Maybe should use the general annotation
// mechanism, but it seems overkill for this.
if content.value().trim() == "cbindgen:ignore" {
return true;
}
}
}
false
}
}
}
pub trait SynAttributeHelpers {
/// Returns the list of attributes for an item.
fn attrs(&self) -> &[syn::Attribute];
/// Searches for attributes like `#[test]`.
/// Example:
/// - `item.has_attr_word("test")` => `#[test]`
fn has_attr_word(&self, name: &str) -> bool {
self.attrs()
.iter()
.filter_map(|x| x.parse_meta().ok())
.any(|attr| {
if let syn::Meta::Path(ref path) = attr {
path.is_ident(name)
} else {
false
}
})
}
fn is_no_mangle(&self) -> bool {
self.has_attr_word("no_mangle")
}
/// Sees whether we should skip parsing a given item.
fn should_skip_parsing(&self) -> bool {
for attr in self.attrs() {
let meta = match attr.parse_meta() {
Ok(attr) => attr,
Err(..) => return false,
};
if is_skip_item_attr(&meta) {
return true;
}
}
false
}
fn attr_name_value_lookup(&self, name: &str) -> Option<String> {
self.attrs()
.iter()
.filter_map(|attr| {
let attr = attr.parse_meta().ok()?;
if let syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(lit),
..
}) = attr
{
if path.is_ident(name) {
return Some(lit.value());
}
}
None
})
.next()
}
fn get_comment_lines(&self) -> Vec<String> {
let mut comment = Vec::new();
for attr in self.attrs() {
if attr.style == syn::AttrStyle::Outer {
if let Ok(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(content),
..
})) = attr.parse_meta()
{
if path.is_ident("doc") {
comment.extend(split_doc_attr(&content.value()));
}
}
}
}
comment
}
}
macro_rules! syn_item_match_helper {
($s:ident => has_attrs: |$i:ident| $a:block, otherwise: || $b:block) => {
match *$s {
syn::Item::Const(ref $i) => $a,
syn::Item::Enum(ref $i) => $a,
syn::Item::ExternCrate(ref $i) => $a,
syn::Item::Fn(ref $i) => $a,
syn::Item::ForeignMod(ref $i) => $a,
syn::Item::Impl(ref $i) => $a,
syn::Item::Macro(ref $i) => $a,
syn::Item::Macro2(ref $i) => $a,
syn::Item::Mod(ref $i) => $a,
syn::Item::Static(ref $i) => $a,
syn::Item::Struct(ref $i) => $a,
syn::Item::Trait(ref $i) => $a,
syn::Item::Type(ref $i) => $a,
syn::Item::Union(ref $i) => $a,
syn::Item::Use(ref $i) => $a,
syn::Item::TraitAlias(ref $i) => $a,
syn::Item::Verbatim(_) => $b,
_ => panic!("Unhandled syn::Item: {:?}", $s),
}
};
}
impl SynAttributeHelpers for syn::Item {
fn attrs(&self) -> &[syn::Attribute] {
syn_item_match_helper!(self =>
has_attrs: |item| { &item.attrs },
otherwise: || { &[] }
)
}
}
macro_rules! impl_syn_item_helper {
($t:ty) => {
impl SynAttributeHelpers for $t {
fn attrs(&self) -> &[syn::Attribute] {
&self.attrs
}
}
};
}
impl_syn_item_helper!(syn::ItemExternCrate);
impl_syn_item_helper!(syn::ItemUse);
impl_syn_item_helper!(syn::ItemStatic);
impl_syn_item_helper!(syn::ItemConst);
impl_syn_item_helper!(syn::ItemFn);
impl_syn_item_helper!(syn::ImplItemMethod);
impl_syn_item_helper!(syn::ItemMod);
impl_syn_item_helper!(syn::ItemForeignMod);
impl_syn_item_helper!(syn::ItemType);
impl_syn_item_helper!(syn::ItemStruct);
impl_syn_item_helper!(syn::ItemEnum);
impl_syn_item_helper!(syn::ItemUnion);
impl_syn_item_helper!(syn::ItemTrait);
impl_syn_item_helper!(syn::ItemImpl);
impl_syn_item_helper!(syn::ItemMacro);
impl_syn_item_helper!(syn::ItemMacro2);
impl_syn_item_helper!(syn::ItemTraitAlias);
/// Helper function for accessing Abi information
pub trait SynAbiHelpers {
fn is_c(&self) -> bool;
fn is_omitted(&self) -> bool;
}
impl SynAbiHelpers for Option<syn::Abi> {
fn is_c(&self) -> bool {
if let Some(ref abi) = *self {
if let Some(ref lit_string) = abi.name {
return lit_string.value() == "C";
}
}
false
}
fn is_omitted(&self) -> bool {
if let Some(ref abi) = *self {
abi.name.is_none()
} else {
false
}
}
}
impl SynAbiHelpers for syn::Abi {
fn is_c(&self) -> bool {
if let Some(ref lit_string) = self.name {
lit_string.value() == "C"
} else {
false
}
}
fn is_omitted(&self) -> bool {
self.name.is_none()
}
}
impl SynAttributeHelpers for [syn::Attribute] {
fn attrs(&self) -> &[syn::Attribute] {
self
}
}
fn split_doc_attr(input: &str) -> Vec<String> {
input
// Convert two newline (indicate "new paragraph") into two line break.
.replace("\n\n", " \n \n")
// Convert newline after two spaces (indicate "line break") into line break.
.split(" \n")
// Convert single newline (indicate hard-wrapped) into space.
.map(|s| s.replace('\n', " "))
.map(|s| s.trim_end().to_string())
.collect()
}

View File

@@ -0,0 +1,258 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cmp;
use std::io;
use std::io::Write;
use crate::bindgen::config::{Braces, Config, Language};
use crate::bindgen::Bindings;
/// A type of way to format a list.
pub enum ListType<'a> {
/// Join each adjacent item with a str.
Join(&'a str),
/// End each item with a str.
Cap(&'a str),
}
/// An empty file used for creating a null source writer and measuring line
/// metrics for various code layouts.
pub struct NullFile;
impl Write for NullFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
/// A utility wrapper to write unbuffered data and correctly adjust positions.
struct InnerWriter<'a, 'b: 'a, F: 'a + Write>(&'a mut SourceWriter<'b, F>);
impl<'a, 'b, F: Write> Write for InnerWriter<'a, 'b, F> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let writer = &mut self.0;
if !writer.line_started {
for _ in 0..writer.spaces() {
write!(writer.out, " ").unwrap();
}
writer.line_started = true;
writer.line_length += writer.spaces();
}
let written = writer.out.write(buf)?;
writer.line_length += written;
writer.max_line_length = cmp::max(writer.max_line_length, writer.line_length);
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
self.0.out.flush()
}
}
/// A utility writer for generating code easier.
pub struct SourceWriter<'a, F: Write> {
out: F,
bindings: &'a Bindings,
spaces: Vec<usize>,
line_started: bool,
line_length: usize,
line_number: usize,
max_line_length: usize,
}
pub type MeasureWriter<'a> = SourceWriter<'a, NullFile>;
impl<'a, F: Write> SourceWriter<'a, F> {
pub fn new(out: F, bindings: &'a Bindings) -> Self {
SourceWriter {
out,
bindings,
spaces: vec![0],
line_started: false,
line_length: 0,
line_number: 1,
max_line_length: 0,
}
}
pub fn bindings(&self) -> &Bindings {
self.bindings
}
/// Takes a function that writes source and returns the maximum line length
/// written.
pub fn measure<T>(&self, func: T) -> usize
where
T: Fn(&mut MeasureWriter),
{
let mut measurer = SourceWriter {
out: NullFile,
bindings: self.bindings,
spaces: self.spaces.clone(),
line_started: self.line_started,
line_length: self.line_length,
line_number: self.line_number,
max_line_length: self.line_length,
};
func(&mut measurer);
measurer.max_line_length
}
fn spaces(&self) -> usize {
*self.spaces.last().unwrap()
}
pub fn push_set_spaces(&mut self, spaces: usize) {
self.spaces.push(spaces);
}
pub fn pop_set_spaces(&mut self) {
self.pop_tab()
}
pub fn line_length_for_align(&self) -> usize {
if self.line_started {
self.line_length
} else {
self.line_length + self.spaces()
}
}
pub fn push_tab(&mut self) {
let spaces = self.spaces() - (self.spaces() % self.bindings.config.tab_width)
+ self.bindings.config.tab_width;
self.spaces.push(spaces);
}
pub fn pop_tab(&mut self) {
assert!(!self.spaces.is_empty());
self.spaces.pop();
}
pub fn new_line(&mut self) {
self.out
.write_all(self.bindings.config.line_endings.as_str().as_bytes())
.unwrap();
self.line_started = false;
self.line_length = 0;
self.line_number += 1;
}
pub fn new_line_if_not_start(&mut self) {
if self.line_number != 1 {
self.new_line();
}
}
pub fn open_brace(&mut self) {
match self.bindings.config.language {
Language::Cxx | Language::C => match self.bindings.config.braces {
Braces::SameLine => {
self.write(" {");
self.push_tab();
self.new_line();
}
Braces::NextLine => {
self.new_line();
self.write("{");
self.push_tab();
self.new_line();
}
},
Language::Cython => {
self.write(":");
self.new_line();
self.push_tab();
}
}
}
pub fn close_brace(&mut self, semicolon: bool) {
self.pop_tab();
match self.bindings.config.language {
Language::Cxx | Language::C => {
self.new_line();
if semicolon {
self.write("};");
} else {
self.write("}");
}
}
Language::Cython => {}
}
}
pub fn write(&mut self, text: &'static str) {
write!(self, "{}", text);
}
pub fn write_raw_block(&mut self, block: &str) {
self.line_started = true;
write!(self, "{}", block);
}
pub fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) {
InnerWriter(self).write_fmt(fmt).unwrap();
}
pub fn write_horizontal_source_list<'b, S: Source>(
&mut self,
items: &[S],
list_type: ListType<'b>,
) {
for (i, item) in items.iter().enumerate() {
item.write(&self.bindings.config, self);
match list_type {
ListType::Join(text) => {
if i != items.len() - 1 {
write!(self, "{}", text);
}
}
ListType::Cap(text) => {
write!(self, "{}", text);
}
}
}
}
pub fn write_vertical_source_list<'b, S: Source>(
&mut self,
items: &[S],
list_type: ListType<'b>,
) {
let align_length = self.line_length_for_align();
self.push_set_spaces(align_length);
for (i, item) in items.iter().enumerate() {
item.write(&self.bindings.config, self);
match list_type {
ListType::Join(text) => {
if i != items.len() - 1 {
write!(self, "{}", text);
}
}
ListType::Cap(text) => {
write!(self, "{}", text);
}
}
if i != items.len() - 1 {
self.new_line();
}
}
self.pop_tab();
}
}
pub trait Source {
fn write<F: Write>(&self, config: &Config, _: &mut SourceWriter<F>);
}

View File

@@ -0,0 +1,41 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[macro_use]
extern crate log;
extern crate proc_macro2;
#[macro_use]
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate toml;
mod bindgen;
pub use crate::bindgen::*;
use std::path::Path;
/// A utility function for build scripts to generate bindings for a crate, using
/// a `cbindgen.toml` if it exists.
pub fn generate<P: AsRef<Path>>(crate_dir: P) -> Result<Bindings, Error> {
let config = Config::from_root_or_default(crate_dir.as_ref());
generate_with_config(crate_dir, config)
}
/// A utility function for build scripts to generate bindings for a crate with a
/// custom config.
pub fn generate_with_config<P: AsRef<Path>>(
crate_dir: P,
config: Config,
) -> Result<Bindings, Error> {
Builder::new()
.with_config(config)
.with_crate(crate_dir)
.generate()
}

View File

@@ -0,0 +1,105 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io;
use std::io::Write;
use log::*;
pub struct TraceLogger;
pub struct WarnLogger;
pub struct InfoLogger;
pub struct ErrorLogger;
impl TraceLogger {
pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&InfoLogger)?;
log::set_max_level(LevelFilter::Trace);
Ok(())
}
}
impl log::Log for TraceLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Trace
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("{}: {}", record.level(), record.args());
}
}
fn flush(&self) {
io::stderr().flush().unwrap();
}
}
impl WarnLogger {
pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&InfoLogger)?;
log::set_max_level(LevelFilter::Warn);
Ok(())
}
}
impl log::Log for WarnLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Warn
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("{}: {}", record.level(), record.args());
}
}
fn flush(&self) {
io::stderr().flush().unwrap();
}
}
impl ErrorLogger {
pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&InfoLogger)?;
log::set_max_level(LevelFilter::Error);
Ok(())
}
}
impl log::Log for ErrorLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Error
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("{}: {}", record.level(), record.args());
}
}
fn flush(&self) {
io::stderr().flush().unwrap();
}
}
impl InfoLogger {
pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&InfoLogger)?;
log::set_max_level(LevelFilter::Info);
Ok(())
}
}
impl log::Log for InfoLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("{}: {}", record.level(), record.args());
}
}
fn flush(&self) {
io::stderr().flush().unwrap();
}
}

View File

@@ -0,0 +1,314 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::env;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;
extern crate clap;
#[macro_use]
extern crate log;
extern crate proc_macro2;
#[macro_use]
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate toml;
use clap::{App, Arg, ArgMatches};
mod bindgen;
mod logging;
use crate::bindgen::{Bindings, Builder, Cargo, Config, Error, Profile, Style};
fn apply_config_overrides<'a>(config: &mut Config, matches: &ArgMatches<'a>) {
// We allow specifying a language to override the config default. This is
// used by compile-tests.
if let Some(lang) = matches.value_of("lang") {
config.language = match lang.parse() {
Ok(lang) => lang,
Err(reason) => {
error!("{}", reason);
return;
}
}
}
if matches.is_present("cpp-compat") {
config.cpp_compat = true;
}
if matches.is_present("only-target-dependencies") {
config.only_target_dependencies = true;
}
if let Some(style) = matches.value_of("style") {
config.style = match style {
"Both" => Style::Both,
"both" => Style::Both,
"Tag" => Style::Tag,
"tag" => Style::Tag,
"Type" => Style::Type,
"type" => Style::Type,
_ => {
error!("Unknown style specified.");
return;
}
}
}
if let Some(profile) = matches.value_of("profile") {
config.parse.expand.profile = match Profile::from_str(profile) {
Ok(p) => p,
Err(e) => {
error!("{}", e);
return;
}
}
}
if matches.is_present("d") {
config.parse.parse_deps = true;
}
}
fn load_bindings<'a>(input: &Path, matches: &ArgMatches<'a>) -> Result<Bindings, Error> {
// If a file is specified then we load it as a single source
if !input.is_dir() {
// Load any config specified or search in the input directory
let mut config = match matches.value_of("config") {
Some(c) => Config::from_file(c).unwrap(),
None => Config::from_root_or_default(input),
};
apply_config_overrides(&mut config, matches);
return Builder::new()
.with_config(config)
.with_src(input)
.generate();
}
// We have to load a whole crate, so we use cargo to gather metadata
let lib = Cargo::load(
input,
matches.value_of("lockfile"),
matches.value_of("crate"),
true,
matches.is_present("clean"),
matches.is_present("only-target-dependencies"),
matches.value_of("metadata").map(Path::new),
)?;
// Load any config specified or search in the binding crate directory
let mut config = match matches.value_of("config") {
Some(c) => Config::from_file(c).unwrap(),
None => {
let binding_crate_dir = lib.find_crate_dir(&lib.binding_crate_ref());
if let Some(binding_crate_dir) = binding_crate_dir {
Config::from_root_or_default(&binding_crate_dir)
} else {
// This shouldn't happen
Config::from_root_or_default(input)
}
}
};
apply_config_overrides(&mut config, matches);
Builder::new()
.with_config(config)
.with_cargo(lib)
.generate()
}
fn main() {
let matches = App::new("cbindgen")
.version(bindgen::VERSION)
.about("Generate C bindings for a Rust library")
.arg(
Arg::with_name("v")
.short("v")
.multiple(true)
.help("Enable verbose logging"),
)
.arg(
Arg::with_name("verify")
.long("verify")
.help("Generate bindings and compare it to the existing bindings file and error if they are different"),
)
.arg(
Arg::with_name("config")
.short("c")
.long("config")
.value_name("PATH")
.help("Specify path to a `cbindgen.toml` config to use"),
)
.arg(
Arg::with_name("lang")
.short("l")
.long("lang")
.value_name("LANGUAGE")
.help("Specify the language to output bindings in")
.possible_values(&["c++", "C++", "c", "C", "cython", "Cython"]),
)
.arg(
Arg::with_name("cpp-compat")
.long("cpp-compat")
.help("Whether to add C++ compatibility to generated C bindings")
)
.arg(
Arg::with_name("only-target-dependencies")
.long("only-target-dependencies")
.help("Only fetch dependencies needed by the target platform. \
The target platform defaults to the host platform; set TARGET to override.")
)
.arg(
Arg::with_name("style")
.short("s")
.long("style")
.value_name("STYLE")
.help("Specify the declaration style to use for bindings")
.possible_values(&["Both", "both", "Tag", "tag", "Type", "type"]),
)
.arg(
Arg::with_name("d")
.short("d")
.long("parse-dependencies")
.help("Whether to parse dependencies when generating bindings"),
)
.arg(
Arg::with_name("clean")
.long("clean")
.help(
"Whether to use a new temporary directory for expanding macros. \
Affects performance, but might be required in certain build processes.")
.required(false)
)
.arg(
Arg::with_name("INPUT")
.help(
"A crate directory or source file to generate bindings for. \
In general this is the folder where the Cargo.toml file of \
source Rust library resides.")
.required(false)
.index(1),
)
.arg(
Arg::with_name("crate")
.long("crate")
.value_name("CRATE_NAME")
.help(
"If generating bindings for a crate, \
the specific crate to generate bindings for",
)
.required(false),
)
.arg(
Arg::with_name("out")
.short("o")
.long("output")
.value_name("PATH")
.help("The file to output the bindings to")
.required(false),
)
.arg(
Arg::with_name("lockfile")
.long("lockfile")
.value_name("PATH")
.help(
"Specify the path to the Cargo.lock file explicitly. If this \
is not specified, the Cargo.lock file is searched for in the \
same folder as the Cargo.toml file. This option is useful for \
projects that use workspaces.")
.required(false),
)
.arg(
Arg::with_name("metadata")
.long("metadata")
.value_name("PATH")
.help(
"Specify the path to the output of a `cargo metadata` \
command that allows to get dependency information. \
This is useful because cargo metadata may be the longest \
part of cbindgen runtime, and you may want to share it \
across cbindgen invocations. By default cbindgen will run \
`cargo metadata --all-features --format-version 1 \
--manifest-path <path/to/crate/Cargo.toml>"
)
.required(false),
)
.arg(
Arg::with_name("profile")
.long("profile")
.value_name("PROFILE")
.help(
"Specify the profile to use when expanding macros. \
Has no effect otherwise."
)
.possible_values(&["Debug", "debug", "Release", "release"]),
)
.arg(
Arg::with_name("quiet")
.short("q")
.long("quiet")
.help("Report errors only (overrides verbosity options).")
.required(false),
)
.get_matches();
if !matches.is_present("out") && matches.is_present("verify") {
error!(
"Cannot verify bindings against `stdout`, please specify a file to compare against."
);
std::process::exit(2);
}
// Initialize logging
if matches.is_present("quiet") {
logging::ErrorLogger::init().unwrap();
} else {
match matches.occurrences_of("v") {
0 => logging::WarnLogger::init().unwrap(),
1 => logging::InfoLogger::init().unwrap(),
_ => logging::TraceLogger::init().unwrap(),
}
}
// Find the input directory
let input = match matches.value_of("INPUT") {
Some(input) => PathBuf::from(input),
None => env::current_dir().unwrap(),
};
let bindings = match load_bindings(&input, &matches) {
Ok(bindings) => bindings,
Err(msg) => {
error!("{}", msg);
error!("Couldn't generate bindings for {}.", input.display());
std::process::exit(1);
}
};
// Write the bindings file
match matches.value_of("out") {
Some(file) => {
let changed = bindings.write_to_file(file);
if matches.is_present("verify") && changed {
error!("Bindings changed: {}", file);
std::process::exit(2);
}
}
_ => {
bindings.write(io::stdout());
}
}
}

View File

@@ -0,0 +1,151 @@
# This is a template cbindgen.toml file with all of the default values.
# Some values are commented out because their absence is the real default.
#
# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
# for detailed documentation of every option here.
language = "C++"
############## Options for Wrapping the Contents of the Header #################
# header = "/* Text to put at the beginning of the generated file. Probably a license. */"
# trailer = "/* Text to put at the end of the generated file */"
# include_guard = "my_bindings_h"
# pragma_once = true
# autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
include_version = false
# namespace = "my_namespace"
namespaces = []
using_namespaces = []
sys_includes = []
includes = []
no_includes = false
after_includes = ""
############################ Code Style Options ################################
braces = "SameLine"
line_length = 100
tab_width = 2
documentation = true
documentation_style = "auto"
line_endings = "LF" # also "CR", "CRLF", "Native"
############################# Codegen Options ##################################
style = "both"
sort_by = "Name" # default for `fn.sort_by` and `const.sort_by`
usize_is_size_t = true
[defines]
# "target_os = freebsd" = "DEFINE_FREEBSD"
# "feature = serde" = "DEFINE_SERDE"
[export]
include = []
exclude = []
# prefix = "CAPI_"
item_types = []
renaming_overrides_prefixing = false
[export.rename]
[export.body]
[export.mangle]
[fn]
rename_args = "None"
# must_use = "MUST_USE_FUNC"
# no_return = "NO_RETURN"
# prefix = "START_FUNC"
# postfix = "END_FUNC"
args = "auto"
sort_by = "Name"
[struct]
rename_fields = "None"
# must_use = "MUST_USE_STRUCT"
derive_constructor = false
derive_eq = false
derive_neq = false
derive_lt = false
derive_lte = false
derive_gt = false
derive_gte = false
[enum]
rename_variants = "None"
# must_use = "MUST_USE_ENUM"
add_sentinel = false
prefix_with_name = false
derive_helper_methods = false
derive_const_casts = false
derive_mut_casts = false
# cast_assert_name = "ASSERT"
derive_tagged_enum_destructor = false
derive_tagged_enum_copy_constructor = false
enum_class = true
private_default_tagged_enum_constructor = false
[const]
allow_static_const = true
allow_constexpr = false
sort_by = "Name"
[macro_expansion]
bitflags = false
############## Options for How Your Rust library Should Be Parsed ##############
[parse]
parse_deps = false
# include = []
exclude = []
clean = false
extra_bindings = []
[parse.expand]
crates = []
all_features = false
default_features = true
features = []

View File

@@ -0,0 +1,37 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum Status {
Ok,
Err,
};
typedef uint32_t Status;
typedef struct Dep {
int32_t a;
float b;
} Dep;
typedef struct Foo_i32 {
int32_t a;
int32_t b;
struct Dep c;
} Foo_i32;
typedef struct Foo_i32 IntFoo;
typedef struct Foo_f64 {
double a;
double b;
struct Dep c;
} Foo_f64;
typedef struct Foo_f64 DoubleFoo;
typedef int32_t Unit;
typedef Status SpecialStatus;
void root(IntFoo x, DoubleFoo y, Unit z, SpecialStatus w);

View File

@@ -0,0 +1,51 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum Status
#ifdef __cplusplus
: uint32_t
#endif // __cplusplus
{
Ok,
Err,
};
#ifndef __cplusplus
typedef uint32_t Status;
#endif // __cplusplus
typedef struct Dep {
int32_t a;
float b;
} Dep;
typedef struct Foo_i32 {
int32_t a;
int32_t b;
struct Dep c;
} Foo_i32;
typedef struct Foo_i32 IntFoo;
typedef struct Foo_f64 {
double a;
double b;
struct Dep c;
} Foo_f64;
typedef struct Foo_f64 DoubleFoo;
typedef int32_t Unit;
typedef Status SpecialStatus;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(IntFoo x, DoubleFoo y, Unit z, SpecialStatus w);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,37 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum Status {
Ok,
Err,
};
typedef uint32_t Status;
typedef struct {
int32_t a;
float b;
} Dep;
typedef struct {
int32_t a;
int32_t b;
Dep c;
} Foo_i32;
typedef Foo_i32 IntFoo;
typedef struct {
double a;
double b;
Dep c;
} Foo_f64;
typedef Foo_f64 DoubleFoo;
typedef int32_t Unit;
typedef Status SpecialStatus;
void root(IntFoo x, DoubleFoo y, Unit z, SpecialStatus w);

View File

@@ -0,0 +1,51 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum Status
#ifdef __cplusplus
: uint32_t
#endif // __cplusplus
{
Ok,
Err,
};
#ifndef __cplusplus
typedef uint32_t Status;
#endif // __cplusplus
typedef struct {
int32_t a;
float b;
} Dep;
typedef struct {
int32_t a;
int32_t b;
Dep c;
} Foo_i32;
typedef Foo_i32 IntFoo;
typedef struct {
double a;
double b;
Dep c;
} Foo_f64;
typedef Foo_f64 DoubleFoo;
typedef int32_t Unit;
typedef Status SpecialStatus;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(IntFoo x, DoubleFoo y, Unit z, SpecialStatus w);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,36 @@
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
enum class Status : uint32_t {
Ok,
Err,
};
struct Dep {
int32_t a;
float b;
};
template<typename X>
struct Foo {
X a;
X b;
Dep c;
};
using IntFoo = Foo<int32_t>;
using DoubleFoo = Foo<double>;
using Unit = int32_t;
using SpecialStatus = Status;
extern "C" {
void root(IntFoo x, DoubleFoo y, Unit z, SpecialStatus w);
} // extern "C"

View File

@@ -0,0 +1,36 @@
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
cdef enum:
Ok,
Err,
ctypedef uint32_t Status;
ctypedef struct Dep:
int32_t a;
float b;
ctypedef struct Foo_i32:
int32_t a;
int32_t b;
Dep c;
ctypedef Foo_i32 IntFoo;
ctypedef struct Foo_f64:
double a;
double b;
Dep c;
ctypedef Foo_f64 DoubleFoo;
ctypedef int32_t Unit;
ctypedef Status SpecialStatus;
void root(IntFoo x, DoubleFoo y, Unit z, SpecialStatus w);

View File

@@ -0,0 +1,37 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum Status {
Ok,
Err,
};
typedef uint32_t Status;
struct Dep {
int32_t a;
float b;
};
struct Foo_i32 {
int32_t a;
int32_t b;
struct Dep c;
};
typedef struct Foo_i32 IntFoo;
struct Foo_f64 {
double a;
double b;
struct Dep c;
};
typedef struct Foo_f64 DoubleFoo;
typedef int32_t Unit;
typedef Status SpecialStatus;
void root(IntFoo x, DoubleFoo y, Unit z, SpecialStatus w);

View File

@@ -0,0 +1,51 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum Status
#ifdef __cplusplus
: uint32_t
#endif // __cplusplus
{
Ok,
Err,
};
#ifndef __cplusplus
typedef uint32_t Status;
#endif // __cplusplus
struct Dep {
int32_t a;
float b;
};
struct Foo_i32 {
int32_t a;
int32_t b;
struct Dep c;
};
typedef struct Foo_i32 IntFoo;
struct Foo_f64 {
double a;
double b;
struct Dep c;
};
typedef struct Foo_f64 DoubleFoo;
typedef int32_t Unit;
typedef Status SpecialStatus;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(IntFoo x, DoubleFoo y, Unit z, SpecialStatus w);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,36 @@
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
cdef enum:
Ok,
Err,
ctypedef uint32_t Status;
cdef struct Dep:
int32_t a;
float b;
cdef struct Foo_i32:
int32_t a;
int32_t b;
Dep c;
ctypedef Foo_i32 IntFoo;
cdef struct Foo_f64:
double a;
double b;
Dep c;
ctypedef Foo_f64 DoubleFoo;
ctypedef int32_t Unit;
ctypedef Status SpecialStatus;
void root(IntFoo x, DoubleFoo y, Unit z, SpecialStatus w);

View File

@@ -0,0 +1,65 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum C {
X = 2,
Y,
};
typedef uint32_t C;
typedef struct A {
int32_t m0;
} A;
typedef struct B {
int32_t x;
float y;
} B;
enum F_Tag {
Foo,
Bar,
Baz,
};
typedef uint8_t F_Tag;
typedef struct Bar_Body {
F_Tag tag;
uint8_t x;
int16_t y;
} Bar_Body;
typedef union F {
F_Tag tag;
struct {
F_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
} F;
enum H_Tag {
Hello,
There,
Everyone,
};
typedef uint8_t H_Tag;
typedef struct There_Body {
uint8_t x;
int16_t y;
} There_Body;
typedef struct H {
H_Tag tag;
union {
struct {
int16_t hello;
};
There_Body there;
};
} H;
void root(struct A x, struct B y, C z, union F f, struct H h);

View File

@@ -0,0 +1,91 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum C
#ifdef __cplusplus
: uint32_t
#endif // __cplusplus
{
X = 2,
Y,
};
#ifndef __cplusplus
typedef uint32_t C;
#endif // __cplusplus
typedef struct A {
int32_t m0;
} A;
typedef struct B {
int32_t x;
float y;
} B;
enum F_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
Foo,
Bar,
Baz,
};
#ifndef __cplusplus
typedef uint8_t F_Tag;
#endif // __cplusplus
typedef struct Bar_Body {
F_Tag tag;
uint8_t x;
int16_t y;
} Bar_Body;
typedef union F {
F_Tag tag;
struct {
F_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
} F;
enum H_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
Hello,
There,
Everyone,
};
#ifndef __cplusplus
typedef uint8_t H_Tag;
#endif // __cplusplus
typedef struct There_Body {
uint8_t x;
int16_t y;
} There_Body;
typedef struct H {
H_Tag tag;
union {
struct {
int16_t hello;
};
There_Body there;
};
} H;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(struct A x, struct B y, C z, union F f, struct H h);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,65 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum C {
X = 2,
Y,
};
typedef uint32_t C;
typedef struct {
int32_t m0;
} A;
typedef struct {
int32_t x;
float y;
} B;
enum F_Tag {
Foo,
Bar,
Baz,
};
typedef uint8_t F_Tag;
typedef struct {
F_Tag tag;
uint8_t x;
int16_t y;
} Bar_Body;
typedef union {
F_Tag tag;
struct {
F_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
} F;
enum H_Tag {
Hello,
There,
Everyone,
};
typedef uint8_t H_Tag;
typedef struct {
uint8_t x;
int16_t y;
} There_Body;
typedef struct {
H_Tag tag;
union {
struct {
int16_t hello;
};
There_Body there;
};
} H;
void root(A x, B y, C z, F f, H h);

View File

@@ -0,0 +1,91 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum C
#ifdef __cplusplus
: uint32_t
#endif // __cplusplus
{
X = 2,
Y,
};
#ifndef __cplusplus
typedef uint32_t C;
#endif // __cplusplus
typedef struct {
int32_t m0;
} A;
typedef struct {
int32_t x;
float y;
} B;
enum F_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
Foo,
Bar,
Baz,
};
#ifndef __cplusplus
typedef uint8_t F_Tag;
#endif // __cplusplus
typedef struct {
F_Tag tag;
uint8_t x;
int16_t y;
} Bar_Body;
typedef union {
F_Tag tag;
struct {
F_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
} F;
enum H_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
Hello,
There,
Everyone,
};
#ifndef __cplusplus
typedef uint8_t H_Tag;
#endif // __cplusplus
typedef struct {
uint8_t x;
int16_t y;
} There_Body;
typedef struct {
H_Tag tag;
union {
struct {
int16_t hello;
};
There_Body there;
};
} H;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(A x, B y, C z, F f, H h);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,152 @@
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
enum class C : uint32_t {
X = 2,
Y,
};
struct A {
int32_t m0;
A(int32_t const& m0)
: m0(m0)
{}
bool operator<(const A& other) const {
return m0 < other.m0;
}
bool operator<=(const A& other) const {
return m0 <= other.m0;
}
};
struct B {
int32_t x;
float y;
};
union F {
enum class Tag : uint8_t {
Foo,
Bar,
Baz,
};
struct Foo_Body {
Tag tag;
int16_t _0;
};
struct Bar_Body {
Tag tag;
uint8_t x;
int16_t y;
};
struct {
Tag tag;
};
Foo_Body foo;
Bar_Body bar;
static F Foo(const int16_t &_0) {
F result;
::new (&result.foo._0) (int16_t)(_0);
result.tag = Tag::Foo;
return result;
}
bool IsFoo() const {
return tag == Tag::Foo;
}
static F Bar(const uint8_t &x,
const int16_t &y) {
F result;
::new (&result.bar.x) (uint8_t)(x);
::new (&result.bar.y) (int16_t)(y);
result.tag = Tag::Bar;
return result;
}
bool IsBar() const {
return tag == Tag::Bar;
}
static F Baz() {
F result;
result.tag = Tag::Baz;
return result;
}
bool IsBaz() const {
return tag == Tag::Baz;
}
};
struct H {
enum class Tag : uint8_t {
Hello,
There,
Everyone,
};
struct Hello_Body {
int16_t _0;
};
struct There_Body {
uint8_t x;
int16_t y;
};
Tag tag;
union {
Hello_Body hello;
There_Body there;
};
static H Hello(const int16_t &_0) {
H result;
::new (&result.hello._0) (int16_t)(_0);
result.tag = Tag::Hello;
return result;
}
bool IsHello() const {
return tag == Tag::Hello;
}
static H There(const uint8_t &x,
const int16_t &y) {
H result;
::new (&result.there.x) (uint8_t)(x);
::new (&result.there.y) (int16_t)(y);
result.tag = Tag::There;
return result;
}
bool IsThere() const {
return tag == Tag::There;
}
static H Everyone() {
H result;
result.tag = Tag::Everyone;
return result;
}
bool IsEveryone() const {
return tag == Tag::Everyone;
}
};
extern "C" {
void root(A x, B y, C z, F f, H h);
} // extern "C"

View File

@@ -0,0 +1,53 @@
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
cdef enum:
X # = 2,
Y,
ctypedef uint32_t C;
ctypedef struct A:
int32_t m0;
ctypedef struct B:
int32_t x;
float y;
cdef enum:
Foo,
Bar,
Baz,
ctypedef uint8_t F_Tag;
ctypedef struct Bar_Body:
F_Tag tag;
uint8_t x;
int16_t y;
ctypedef union F:
F_Tag tag;
F_Tag foo_tag;
int16_t foo;
Bar_Body bar;
cdef enum:
Hello,
There,
Everyone,
ctypedef uint8_t H_Tag;
ctypedef struct There_Body:
uint8_t x;
int16_t y;
ctypedef struct H:
H_Tag tag;
int16_t hello;
There_Body there;
void root(A x, B y, C z, F f, H h);

View File

@@ -0,0 +1,65 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum C {
X = 2,
Y,
};
typedef uint32_t C;
struct A {
int32_t m0;
};
struct B {
int32_t x;
float y;
};
enum F_Tag {
Foo,
Bar,
Baz,
};
typedef uint8_t F_Tag;
struct Bar_Body {
F_Tag tag;
uint8_t x;
int16_t y;
};
union F {
F_Tag tag;
struct {
F_Tag foo_tag;
int16_t foo;
};
struct Bar_Body bar;
};
enum H_Tag {
Hello,
There,
Everyone,
};
typedef uint8_t H_Tag;
struct There_Body {
uint8_t x;
int16_t y;
};
struct H {
H_Tag tag;
union {
struct {
int16_t hello;
};
struct There_Body there;
};
};
void root(struct A x, struct B y, C z, union F f, struct H h);

View File

@@ -0,0 +1,91 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum C
#ifdef __cplusplus
: uint32_t
#endif // __cplusplus
{
X = 2,
Y,
};
#ifndef __cplusplus
typedef uint32_t C;
#endif // __cplusplus
struct A {
int32_t m0;
};
struct B {
int32_t x;
float y;
};
enum F_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
Foo,
Bar,
Baz,
};
#ifndef __cplusplus
typedef uint8_t F_Tag;
#endif // __cplusplus
struct Bar_Body {
F_Tag tag;
uint8_t x;
int16_t y;
};
union F {
F_Tag tag;
struct {
F_Tag foo_tag;
int16_t foo;
};
struct Bar_Body bar;
};
enum H_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
Hello,
There,
Everyone,
};
#ifndef __cplusplus
typedef uint8_t H_Tag;
#endif // __cplusplus
struct There_Body {
uint8_t x;
int16_t y;
};
struct H {
H_Tag tag;
union {
struct {
int16_t hello;
};
struct There_Body there;
};
};
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(struct A x, struct B y, C z, union F f, struct H h);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,53 @@
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
cdef enum:
X # = 2,
Y,
ctypedef uint32_t C;
cdef struct A:
int32_t m0;
cdef struct B:
int32_t x;
float y;
cdef enum:
Foo,
Bar,
Baz,
ctypedef uint8_t F_Tag;
cdef struct Bar_Body:
F_Tag tag;
uint8_t x;
int16_t y;
cdef union F:
F_Tag tag;
F_Tag foo_tag;
int16_t foo;
Bar_Body bar;
cdef enum:
Hello,
There,
Everyone,
ctypedef uint8_t H_Tag;
cdef struct There_Body:
uint8_t x;
int16_t y;
cdef struct H:
H_Tag tag;
int16_t hello;
There_Body there;
void root(A x, B y, C z, F f, H h);

View File

@@ -0,0 +1,19 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef enum Foo_Tag {
A,
} Foo_Tag;
typedef struct Foo {
Foo_Tag tag;
union {
struct {
float a[20];
};
};
} Foo;
void root(struct Foo a);

View File

@@ -0,0 +1,27 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef enum Foo_Tag {
A,
} Foo_Tag;
typedef struct Foo {
Foo_Tag tag;
union {
struct {
float a[20];
};
};
} Foo;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(struct Foo a);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,19 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef enum {
A,
} Foo_Tag;
typedef struct {
Foo_Tag tag;
union {
struct {
float a[20];
};
};
} Foo;
void root(Foo a);

View File

@@ -0,0 +1,27 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef enum {
A,
} Foo_Tag;
typedef struct {
Foo_Tag tag;
union {
struct {
float a[20];
};
};
} Foo;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(Foo a);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,39 @@
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
struct Foo {
enum class Tag {
A,
};
struct A_Body {
float _0[20];
};
Tag tag;
union {
A_Body a;
};
static Foo A(const float (&_0)[20]) {
Foo result;
for (int i = 0; i < 20; i++) {
::new (&result.a._0[i]) (float)(_0[i]);
}
result.tag = Tag::A;
return result;
}
bool IsA() const {
return tag == Tag::A;
}
};
extern "C" {
void root(Foo a);
} // extern "C"

View File

@@ -0,0 +1,16 @@
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
ctypedef enum Foo_Tag:
A,
ctypedef struct Foo:
Foo_Tag tag;
float a[20];
void root(Foo a);

View File

@@ -0,0 +1,19 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum Foo_Tag {
A,
};
struct Foo {
enum Foo_Tag tag;
union {
struct {
float a[20];
};
};
};
void root(struct Foo a);

View File

@@ -0,0 +1,27 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum Foo_Tag {
A,
};
struct Foo {
enum Foo_Tag tag;
union {
struct {
float a[20];
};
};
};
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(struct Foo a);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,16 @@
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
cdef enum Foo_Tag:
A,
cdef struct Foo:
Foo_Tag tag;
float a[20];
void root(Foo a);

View File

@@ -0,0 +1,78 @@
#define MY_ASSERT(...) do { } while (0)
#define MY_ATTRS __attribute((noinline))
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct I I;
enum H_Tag {
H_Foo,
H_Bar,
H_Baz,
};
typedef uint8_t H_Tag;
typedef struct H_Bar_Body {
uint8_t x;
int16_t y;
} H_Bar_Body;
typedef struct H {
H_Tag tag;
union {
struct {
int16_t foo;
};
H_Bar_Body bar;
};
} H;
enum J_Tag {
J_Foo,
J_Bar,
J_Baz,
};
typedef uint8_t J_Tag;
typedef struct J_Bar_Body {
uint8_t x;
int16_t y;
} J_Bar_Body;
typedef struct J {
J_Tag tag;
union {
struct {
int16_t foo;
};
J_Bar_Body bar;
};
} J;
enum K_Tag {
K_Foo,
K_Bar,
K_Baz,
};
typedef uint8_t K_Tag;
typedef struct K_Bar_Body {
K_Tag tag;
uint8_t x;
int16_t y;
} K_Bar_Body;
typedef union K {
K_Tag tag;
struct {
K_Tag foo_tag;
int16_t foo;
};
K_Bar_Body bar;
} K;
void foo(struct H h, struct I i, struct J j, union K k);

View File

@@ -0,0 +1,104 @@
#define MY_ASSERT(...) do { } while (0)
#define MY_ATTRS __attribute((noinline))
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct I I;
enum H_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
H_Foo,
H_Bar,
H_Baz,
};
#ifndef __cplusplus
typedef uint8_t H_Tag;
#endif // __cplusplus
typedef struct H_Bar_Body {
uint8_t x;
int16_t y;
} H_Bar_Body;
typedef struct H {
H_Tag tag;
union {
struct {
int16_t foo;
};
H_Bar_Body bar;
};
} H;
enum J_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
J_Foo,
J_Bar,
J_Baz,
};
#ifndef __cplusplus
typedef uint8_t J_Tag;
#endif // __cplusplus
typedef struct J_Bar_Body {
uint8_t x;
int16_t y;
} J_Bar_Body;
typedef struct J {
J_Tag tag;
union {
struct {
int16_t foo;
};
J_Bar_Body bar;
};
} J;
enum K_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
K_Foo,
K_Bar,
K_Baz,
};
#ifndef __cplusplus
typedef uint8_t K_Tag;
#endif // __cplusplus
typedef struct K_Bar_Body {
K_Tag tag;
uint8_t x;
int16_t y;
} K_Bar_Body;
typedef union K {
K_Tag tag;
struct {
K_Tag foo_tag;
int16_t foo;
};
K_Bar_Body bar;
} K;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void foo(struct H h, struct I i, struct J j, union K k);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,78 @@
#define MY_ASSERT(...) do { } while (0)
#define MY_ATTRS __attribute((noinline))
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct I I;
enum H_Tag {
H_Foo,
H_Bar,
H_Baz,
};
typedef uint8_t H_Tag;
typedef struct {
uint8_t x;
int16_t y;
} H_Bar_Body;
typedef struct {
H_Tag tag;
union {
struct {
int16_t foo;
};
H_Bar_Body bar;
};
} H;
enum J_Tag {
J_Foo,
J_Bar,
J_Baz,
};
typedef uint8_t J_Tag;
typedef struct {
uint8_t x;
int16_t y;
} J_Bar_Body;
typedef struct {
J_Tag tag;
union {
struct {
int16_t foo;
};
J_Bar_Body bar;
};
} J;
enum K_Tag {
K_Foo,
K_Bar,
K_Baz,
};
typedef uint8_t K_Tag;
typedef struct {
K_Tag tag;
uint8_t x;
int16_t y;
} K_Bar_Body;
typedef union {
K_Tag tag;
struct {
K_Tag foo_tag;
int16_t foo;
};
K_Bar_Body bar;
} K;
void foo(H h, I i, J j, K k);

View File

@@ -0,0 +1,104 @@
#define MY_ASSERT(...) do { } while (0)
#define MY_ATTRS __attribute((noinline))
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct I I;
enum H_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
H_Foo,
H_Bar,
H_Baz,
};
#ifndef __cplusplus
typedef uint8_t H_Tag;
#endif // __cplusplus
typedef struct {
uint8_t x;
int16_t y;
} H_Bar_Body;
typedef struct {
H_Tag tag;
union {
struct {
int16_t foo;
};
H_Bar_Body bar;
};
} H;
enum J_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
J_Foo,
J_Bar,
J_Baz,
};
#ifndef __cplusplus
typedef uint8_t J_Tag;
#endif // __cplusplus
typedef struct {
uint8_t x;
int16_t y;
} J_Bar_Body;
typedef struct {
J_Tag tag;
union {
struct {
int16_t foo;
};
J_Bar_Body bar;
};
} J;
enum K_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
K_Foo,
K_Bar,
K_Baz,
};
#ifndef __cplusplus
typedef uint8_t K_Tag;
#endif // __cplusplus
typedef struct {
K_Tag tag;
uint8_t x;
int16_t y;
} K_Bar_Body;
typedef union {
K_Tag tag;
struct {
K_Tag foo_tag;
int16_t foo;
};
K_Bar_Body bar;
} K;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void foo(H h, I i, J j, K k);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,250 @@
#define MY_ASSERT(...) do { } while (0)
#define MY_ATTRS __attribute((noinline))
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
struct I;
struct H {
enum class Tag : uint8_t {
H_Foo,
H_Bar,
H_Baz,
};
struct H_Foo_Body {
int16_t _0;
};
struct H_Bar_Body {
uint8_t x;
int16_t y;
};
Tag tag;
union {
H_Foo_Body foo;
H_Bar_Body bar;
};
static H H_Foo(const int16_t &_0) {
H result;
::new (&result.foo._0) (int16_t)(_0);
result.tag = Tag::H_Foo;
return result;
}
bool IsH_Foo() const {
return tag == Tag::H_Foo;
}
const int16_t& AsH_Foo() const {
MY_ASSERT(IsH_Foo());
return foo._0;
}
MY_ATTRS int16_t& AsH_Foo() {
MY_ASSERT(IsH_Foo());
return foo._0;
}
static H H_Bar(const uint8_t &x,
const int16_t &y) {
H result;
::new (&result.bar.x) (uint8_t)(x);
::new (&result.bar.y) (int16_t)(y);
result.tag = Tag::H_Bar;
return result;
}
bool IsH_Bar() const {
return tag == Tag::H_Bar;
}
MY_ATTRS const H_Bar_Body& AsH_Bar() const {
MY_ASSERT(IsH_Bar());
return bar;
}
H_Bar_Body& AsH_Bar() {
MY_ASSERT(IsH_Bar());
return bar;
}
static H H_Baz() {
H result;
result.tag = Tag::H_Baz;
return result;
}
MY_ATTRS bool IsH_Baz() const {
return tag == Tag::H_Baz;
}
};
struct J {
enum class Tag : uint8_t {
J_Foo,
J_Bar,
J_Baz,
};
struct J_Foo_Body {
int16_t _0;
};
struct J_Bar_Body {
uint8_t x;
int16_t y;
};
Tag tag;
union {
J_Foo_Body foo;
J_Bar_Body bar;
};
static J J_Foo(const int16_t &_0) {
J result;
::new (&result.foo._0) (int16_t)(_0);
result.tag = Tag::J_Foo;
return result;
}
bool IsJ_Foo() const {
return tag == Tag::J_Foo;
}
const int16_t& AsJ_Foo() const {
MY_ASSERT(IsJ_Foo());
return foo._0;
}
int16_t& AsJ_Foo() {
MY_ASSERT(IsJ_Foo());
return foo._0;
}
static J J_Bar(const uint8_t &x,
const int16_t &y) {
J result;
::new (&result.bar.x) (uint8_t)(x);
::new (&result.bar.y) (int16_t)(y);
result.tag = Tag::J_Bar;
return result;
}
bool IsJ_Bar() const {
return tag == Tag::J_Bar;
}
const J_Bar_Body& AsJ_Bar() const {
MY_ASSERT(IsJ_Bar());
return bar;
}
J_Bar_Body& AsJ_Bar() {
MY_ASSERT(IsJ_Bar());
return bar;
}
static J J_Baz() {
J result;
result.tag = Tag::J_Baz;
return result;
}
bool IsJ_Baz() const {
return tag == Tag::J_Baz;
}
};
union K {
enum class Tag : uint8_t {
K_Foo,
K_Bar,
K_Baz,
};
struct K_Foo_Body {
Tag tag;
int16_t _0;
};
struct K_Bar_Body {
Tag tag;
uint8_t x;
int16_t y;
};
struct {
Tag tag;
};
K_Foo_Body foo;
K_Bar_Body bar;
static K K_Foo(const int16_t &_0) {
K result;
::new (&result.foo._0) (int16_t)(_0);
result.tag = Tag::K_Foo;
return result;
}
bool IsK_Foo() const {
return tag == Tag::K_Foo;
}
const int16_t& AsK_Foo() const {
MY_ASSERT(IsK_Foo());
return foo._0;
}
int16_t& AsK_Foo() {
MY_ASSERT(IsK_Foo());
return foo._0;
}
static K K_Bar(const uint8_t &x,
const int16_t &y) {
K result;
::new (&result.bar.x) (uint8_t)(x);
::new (&result.bar.y) (int16_t)(y);
result.tag = Tag::K_Bar;
return result;
}
bool IsK_Bar() const {
return tag == Tag::K_Bar;
}
const K_Bar_Body& AsK_Bar() const {
MY_ASSERT(IsK_Bar());
return bar;
}
K_Bar_Body& AsK_Bar() {
MY_ASSERT(IsK_Bar());
return bar;
}
static K K_Baz() {
K result;
result.tag = Tag::K_Baz;
return result;
}
bool IsK_Baz() const {
return tag == Tag::K_Baz;
}
};
extern "C" {
void foo(H h, I i, J j, K k);
} // extern "C"

View File

@@ -0,0 +1,63 @@
#define MY_ASSERT(...) do { } while (0)
#define MY_ATTRS __attribute((noinline))
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
ctypedef struct I:
pass
cdef enum:
H_Foo,
H_Bar,
H_Baz,
ctypedef uint8_t H_Tag;
ctypedef struct H_Bar_Body:
uint8_t x;
int16_t y;
ctypedef struct H:
H_Tag tag;
int16_t foo;
H_Bar_Body bar;
cdef enum:
J_Foo,
J_Bar,
J_Baz,
ctypedef uint8_t J_Tag;
ctypedef struct J_Bar_Body:
uint8_t x;
int16_t y;
ctypedef struct J:
J_Tag tag;
int16_t foo;
J_Bar_Body bar;
cdef enum:
K_Foo,
K_Bar,
K_Baz,
ctypedef uint8_t K_Tag;
ctypedef struct K_Bar_Body:
K_Tag tag;
uint8_t x;
int16_t y;
ctypedef union K:
K_Tag tag;
K_Tag foo_tag;
int16_t foo;
K_Bar_Body bar;
void foo(H h, I i, J j, K k);

View File

@@ -0,0 +1,78 @@
#define MY_ASSERT(...) do { } while (0)
#define MY_ATTRS __attribute((noinline))
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
struct I;
enum H_Tag {
H_Foo,
H_Bar,
H_Baz,
};
typedef uint8_t H_Tag;
struct H_Bar_Body {
uint8_t x;
int16_t y;
};
struct H {
H_Tag tag;
union {
struct {
int16_t foo;
};
struct H_Bar_Body bar;
};
};
enum J_Tag {
J_Foo,
J_Bar,
J_Baz,
};
typedef uint8_t J_Tag;
struct J_Bar_Body {
uint8_t x;
int16_t y;
};
struct J {
J_Tag tag;
union {
struct {
int16_t foo;
};
struct J_Bar_Body bar;
};
};
enum K_Tag {
K_Foo,
K_Bar,
K_Baz,
};
typedef uint8_t K_Tag;
struct K_Bar_Body {
K_Tag tag;
uint8_t x;
int16_t y;
};
union K {
K_Tag tag;
struct {
K_Tag foo_tag;
int16_t foo;
};
struct K_Bar_Body bar;
};
void foo(struct H h, struct I i, struct J j, union K k);

View File

@@ -0,0 +1,104 @@
#define MY_ASSERT(...) do { } while (0)
#define MY_ATTRS __attribute((noinline))
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
struct I;
enum H_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
H_Foo,
H_Bar,
H_Baz,
};
#ifndef __cplusplus
typedef uint8_t H_Tag;
#endif // __cplusplus
struct H_Bar_Body {
uint8_t x;
int16_t y;
};
struct H {
H_Tag tag;
union {
struct {
int16_t foo;
};
struct H_Bar_Body bar;
};
};
enum J_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
J_Foo,
J_Bar,
J_Baz,
};
#ifndef __cplusplus
typedef uint8_t J_Tag;
#endif // __cplusplus
struct J_Bar_Body {
uint8_t x;
int16_t y;
};
struct J {
J_Tag tag;
union {
struct {
int16_t foo;
};
struct J_Bar_Body bar;
};
};
enum K_Tag
#ifdef __cplusplus
: uint8_t
#endif // __cplusplus
{
K_Foo,
K_Bar,
K_Baz,
};
#ifndef __cplusplus
typedef uint8_t K_Tag;
#endif // __cplusplus
struct K_Bar_Body {
K_Tag tag;
uint8_t x;
int16_t y;
};
union K {
K_Tag tag;
struct {
K_Tag foo_tag;
int16_t foo;
};
struct K_Bar_Body bar;
};
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void foo(struct H h, struct I i, struct J j, union K k);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,63 @@
#define MY_ASSERT(...) do { } while (0)
#define MY_ATTRS __attribute((noinline))
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
cdef struct I:
pass
cdef enum:
H_Foo,
H_Bar,
H_Baz,
ctypedef uint8_t H_Tag;
cdef struct H_Bar_Body:
uint8_t x;
int16_t y;
cdef struct H:
H_Tag tag;
int16_t foo;
H_Bar_Body bar;
cdef enum:
J_Foo,
J_Bar,
J_Baz,
ctypedef uint8_t J_Tag;
cdef struct J_Bar_Body:
uint8_t x;
int16_t y;
cdef struct J:
J_Tag tag;
int16_t foo;
J_Bar_Body bar;
cdef enum:
K_Foo,
K_Bar,
K_Baz,
ctypedef uint8_t K_Tag;
cdef struct K_Bar_Body:
K_Tag tag;
uint8_t x;
int16_t y;
cdef union K:
K_Tag tag;
K_Tag foo_tag;
int16_t foo;
K_Bar_Body bar;
void foo(H h, I i, J j, K k);

View File

@@ -0,0 +1,6 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define Foo_FOO 42

View File

@@ -0,0 +1,7 @@
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
static const uint32_t Foo_FOO = 42;

View File

@@ -0,0 +1,9 @@
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
const uint32_t Foo_FOO # = 42

View File

@@ -0,0 +1,12 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct Foo {
} Foo;
#define Foo_GA 10
#define Foo_ZO 3.14
void root(struct Foo x);

View File

@@ -0,0 +1,20 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct Foo {
} Foo;
#define Foo_GA 10
#define Foo_ZO 3.14
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(struct Foo x);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,12 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct {
} Foo;
#define Foo_GA 10
#define Foo_ZO 3.14
void root(Foo x);

View File

@@ -0,0 +1,20 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct {
} Foo;
#define Foo_GA 10
#define Foo_ZO 3.14
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void root(Foo x);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -0,0 +1,17 @@
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
struct Foo {
};
static const int32_t Foo_GA = 10;
static const float Foo_ZO = 3.14;
extern "C" {
void root(Foo x);
} // extern "C"

View File

@@ -0,0 +1,14 @@
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list
cdef extern from *:
ctypedef struct Foo:
pass
const int32_t Foo_GA # = 10
const float Foo_ZO # = 3.14
void root(Foo x);

Some files were not shown because too many files have changed in this diff Show More