mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
629 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31efd0cf72 | |||
| 6b79e4cd76 | |||
| ee2da72685 | |||
| 6180a528a7 | |||
| 69ef08d4d2 | |||
| f7f04d5180 | |||
| e10623d658 | |||
| c3ab89ba66 | |||
| fa500639f0 | |||
| 3d2574a855 | |||
| 003935db47 | |||
| fbed171f90 | |||
| 298d48f708 | |||
| 4ff677f5fc | |||
| 5b920321dd | |||
| 6bb99783e9 | |||
| 547f82acc1 | |||
| 9ea98e3f1e | |||
| 0ad03bee98 | |||
| ba7c331984 | |||
| 53dea3166a | |||
| 129dec43ed | |||
| a7f58e57ec | |||
| e6871525c4 | |||
| c4956dbf49 | |||
| d0ea428e23 | |||
| 3e87d3ea9b | |||
| 0f8bde4b4e | |||
| b9c9dfbd03 | |||
| 71824297f6 | |||
| c0a8064228 | |||
| 84efb8bf95 | |||
| cf687a6ac6 | |||
| ea85edb242 | |||
| 788175e0d6 | |||
| 90ef25a6ee | |||
| ac72f6acae | |||
| 4444ac4ea3 | |||
| 5f950e30ec | |||
| 311aa5f6e1 | |||
| 739d5f5262 | |||
| 375028e13f | |||
| 46e949c184 | |||
| 62b53e218c | |||
| 0393406b9f | |||
| 272cfdb89b | |||
| 8e244a25ea | |||
| ef8b8e11ec | |||
| 2543414726 | |||
| ee112568d6 | |||
| 69d3ddec29 | |||
| 5b0d811c5a | |||
| 19536db976 | |||
| fc69551f87 | |||
| a68b0b6905 | |||
| 1ec1cc9922 | |||
| fd68b033f0 | |||
| c3817409a8 | |||
| 863d8092d6 | |||
| 24b6ff3c20 | |||
| bc36261f5c | |||
| bcb9a4d220 | |||
| 9caf3542d9 | |||
| cb45db05fa | |||
| d1cb2a9a96 | |||
| aa3f6ba46c | |||
| cc57bed4c8 | |||
| b5d38ab2e3 | |||
| cd4091f64d | |||
| 91e069a77e | |||
| cacd3dc70f | |||
| 6f6cda0099 | |||
| d51ce86142 | |||
| aa061bce6f | |||
| 1c6b6ad801 | |||
| 031854fa2a | |||
| 5fd96c7908 | |||
| 7a3f19b857 | |||
| 55a5d7ef10 | |||
| af31dbd180 | |||
| 9ae3cb0a7c | |||
| fc1c80c142 | |||
| ee7ced6334 | |||
| d8307d0e4c | |||
| bf9fcc5aca | |||
| 7ce6b97da9 | |||
| 4891a6ac10 | |||
| 4ef0d583fc | |||
| 30b0122f5d | |||
| c31c72076f | |||
| 3bfaee4ecc | |||
| 89b68e2a24 | |||
| cbf805f492 | |||
| c926327a6a | |||
| 7c099500d0 | |||
| 8c889cc353 | |||
| 958abd050e | |||
| 92a8203e1c | |||
| b34b90eca3 | |||
| 7aad0adcb4 | |||
| a3cd25db7a | |||
| a4bfeb7752 | |||
| 0fda512938 | |||
| 58e0661086 | |||
| c7094d470f | |||
| a7d0691e10 | |||
| 05778fbe6f | |||
| b34d67ec41 | |||
| 46ee7ea772 | |||
| 15738dda1f | |||
| 1f32754f05 | |||
| c2eab173c5 | |||
| bb311349ec | |||
| 928e1d9221 | |||
| 5b8681dcf2 | |||
| 4e9a17707b | |||
| 6066089cc1 | |||
| a8d5f23818 | |||
| 5ec4e0338a | |||
| d27d1365b5 | |||
| 76b896edcc | |||
| ddebf56b41 | |||
| 4f56575e05 | |||
| 35fa76263e | |||
| 446d63a77e | |||
| 8d8d521721 | |||
| 0d31a1caa6 | |||
| 3dc58cdfc9 | |||
| 5724b5f112 | |||
| d64d9719c6 | |||
| 75475fc9a8 | |||
| f8fe9246bb | |||
| 5c54361236 | |||
| ec227f9056 | |||
| 93a1a55aaa | |||
| a020b2b5b2 | |||
| 2c756e5958 | |||
| c702077028 | |||
| 08545224f4 | |||
| e122f90837 | |||
| c638d90b02 | |||
| 930fd9c00f | |||
| 2a8db87132 | |||
| cbae4fe59c | |||
| c68e3c4f41 | |||
| 02d4ceff34 | |||
| 98339c57e6 | |||
| 2331995e28 | |||
| c07bdce250 | |||
| 084a85c3d8 | |||
| 735aa22be9 | |||
| 5479546b27 | |||
| 179c54f297 | |||
| f9ae4bf05f | |||
| 9e16e18132 | |||
| 7535a23fa2 | |||
| 3787ff9e8c | |||
| 0a2a70c15a | |||
| 81d7c81532 | |||
| 0453029765 | |||
| 7b777d074e | |||
| c6cd1c53c3 | |||
| 669349d704 | |||
| c086c144d0 | |||
| 640cb2c182 | |||
| 03a4068d55 | |||
| 9f6c78532f | |||
| 8aecc83f53 | |||
| 4891b6535c | |||
| fa343c2c69 | |||
| 6d5e735bed | |||
| ac315fd80b | |||
| f95161c6e0 | |||
| 4bc846a119 | |||
| a3ca95fc8f | |||
| ae4897ab2e | |||
| 4ac87c7208 | |||
| 04d8106676 | |||
| b6cdf32f16 | |||
| ad9bc36764 | |||
| 4b8c26e682 | |||
| 7839c4438c | |||
| 529361fcbc | |||
| 4dddf3c18d | |||
| fb0c0d9ee9 | |||
| 235c32006c | |||
| b65901e444 | |||
| 91fe02da45 | |||
| 8274b5fa88 | |||
| 762e677a70 | |||
| 5b5f1e4669 | |||
| e582e7c57f | |||
| 3714da5ec8 | |||
| ca69be07ff | |||
| 16951e3628 | |||
| 8bb2b444ab | |||
| fc1570d2d7 | |||
| fce85381c6 | |||
| 3088516851 | |||
| 5162a0f46e | |||
| 640d27697d | |||
| da4404baa5 | |||
| 7c2e9b5a7c | |||
| 5db545e7b4 | |||
| b88228b3d4 | |||
| 8677b57847 | |||
| 7543b0674e | |||
| 8e111058c3 | |||
| e1c0aa8491 | |||
| 7957c6868d | |||
| 9c86eefb76 | |||
| 7272e40c23 | |||
| c6ef393ce9 | |||
| d25f2fc07c | |||
| 104e242ddd | |||
| 825bdbfa04 | |||
| 1634c43f0a | |||
| d6b27de34e | |||
| 4018a17e26 | |||
| 5ebbc0868c | |||
| 3774296835 | |||
| ece66c46bf | |||
| 66b4a865c2 | |||
| 21149106ee | |||
| 74bebe6da3 | |||
| 9891e86d16 | |||
| e3c5cfdf19 | |||
| 4977b91a98 | |||
| 6317b8e0c8 | |||
| ecc09c4387 | |||
| 7bfd32750d | |||
| 23d4e2519b | |||
| 2857cb76c6 | |||
| fdc50bffc9 | |||
| 9931709ecd | |||
| 8092f00930 | |||
| d2e87943ac | |||
| 26b9bdb362 | |||
| c58f67b140 | |||
| 0c08cdaf7c | |||
| 10999babe0 | |||
| f0a995a357 | |||
| aa47324ee9 | |||
| ac6a391426 | |||
| 8e14b6e40b | |||
| c117a4c1af | |||
| 4082b354fe | |||
| 3641c98959 | |||
| b7d170ab9b | |||
| 5acf9d758d | |||
| 833790967b | |||
| a86d6ab330 | |||
| 2f8755dcc7 | |||
| 94415065c0 | |||
| bba644e83f | |||
| d5173380e3 | |||
| d9941ef409 | |||
| c715aec1f7 | |||
| cd3f45f31f | |||
| b4892c228c | |||
| 7a75c73052 | |||
| 658f2a13ea | |||
| c1395ab543 | |||
| cd6d86a5ce | |||
| 8b2d067196 | |||
| 07a5538e50 | |||
| d17dc54645 | |||
| baa8895cfb | |||
| 550d6b2991 | |||
| b3649a44e0 | |||
| 313117095c | |||
| 7221051683 | |||
| 1eb2ecb3b0 | |||
| 24b0672d99 | |||
| 08c7429531 | |||
| aa05eb4c81 | |||
| 4aa178fcc0 | |||
| 98ca880f8a | |||
| fe21ef43ba | |||
| 7d3704c222 | |||
| 496c8fa0e9 | |||
| 9ce0e11518 | |||
| b77836920a | |||
| f1ceaf0ff1 | |||
| 884a025b52 | |||
| c23fa5aa6c | |||
| 4f1d2abbcb | |||
| a25e81036e | |||
| b46cad1db1 | |||
| f2d48ce296 | |||
| 0fa39d431d | |||
| 0aa86c4d47 | |||
| ea2faa3755 | |||
| 59c9abbac7 | |||
| 8f3de8aa19 | |||
| 317ce7caa6 | |||
| 3a44729a48 | |||
| 3d46fad459 | |||
| ffc4bd599c | |||
| 1c969da286 | |||
| 45fd2fa40a | |||
| 806bd202d6 | |||
| a644049087 | |||
| ad31bed1db | |||
| 62f0bb97b0 | |||
| f67f8646ae | |||
| fa217d3706 | |||
| b62f2ee0f7 | |||
| 6e6c73e4c7 | |||
| 508517c45e | |||
| a79840afc9 | |||
| 39afe4c6f7 | |||
| 038cc5f974 | |||
| 59b14000f3 | |||
| 849206ef9d | |||
| 80fff4f2e7 | |||
| 9734146313 | |||
| 58be624222 | |||
| 3d43103431 | |||
| 5a22437d5f | |||
| 83c075c72b | |||
| 270b98a429 | |||
| 0ee3324462 | |||
| 8a9c4f0b15 | |||
| 34db5f985e | |||
| 020e8a78a8 | |||
| 3ca7b4942e | |||
| 1754226c74 | |||
| 3014c4d7a1 | |||
| 908f37656a | |||
| f4d783cb41 | |||
| f5982bc204 | |||
| e30b425224 | |||
| 512921404c | |||
| dfd82edc42 | |||
| 75a15ceabf | |||
| 60730fd068 | |||
| df778b7b33 | |||
| 45299c0ef1 | |||
| 38eec1236c | |||
| 145c5b316b | |||
| e97e69a309 | |||
| 2ac7b23596 | |||
| 8200bee467 | |||
| fe6ab250bf | |||
| 804972b099 | |||
| 727f99ee4d | |||
| 3c801e7b17 | |||
| a38e484fe9 | |||
| eed48889cd | |||
| b5896173fd | |||
| 205989f569 | |||
| 12472de1d2 | |||
| a68708c12e | |||
| 4c0474d573 | |||
| 60e859f643 | |||
| 9ed0d90746 | |||
| 514ec24252 | |||
| 244e6c9c12 | |||
| 4749e3a22a | |||
| cf153f38de | |||
| c0a0983025 | |||
| 69ff0c5509 | |||
| bf79d6c212 | |||
| 0b9a85e183 | |||
| 59974d7bde | |||
| 61e846326c | |||
| 3547985bb0 | |||
| 4c92580201 | |||
| e3f34f319c | |||
| b16f3895a0 | |||
| a4c919231c | |||
| e4d6e92287 | |||
| 642201a7e0 | |||
| c36808b251 | |||
| 2022de2156 | |||
| 66e01548ce | |||
| 93b505cff9 | |||
| 44f5688c32 | |||
| 2bee5ed33a | |||
| 2d775695ef | |||
| 34476ebf53 | |||
| 5043447f23 | |||
| b879abc418 | |||
| a1e39a8620 | |||
| f5021daaea | |||
| ec0fb7614e | |||
| 04c0763146 | |||
| 7d0aa7c01b | |||
| 2a3980ef62 | |||
| adb979761e | |||
| 37d722d71c | |||
| 6e3af593f6 | |||
| 54c14cc5b8 | |||
| 7f0de5790a | |||
| 6488477bc4 | |||
| 53b7b5b70b | |||
| fc159e0c46 | |||
| a802276c56 | |||
| 65b816f2f0 | |||
| e2b3464ec9 | |||
| 89cf5bf362 | |||
| bb0a6070c4 | |||
| 60f1c16ddb | |||
| 17809a390c | |||
| 7662a7e4ff | |||
| d769a95fc5 | |||
| 0e4476c2e3 | |||
| d48a2b3f6c | |||
| b3592bc23e | |||
| 052310e93d | |||
| 09eb7f251b | |||
| c137da7618 | |||
| c0c6a33f94 | |||
| 0cb0a345dd | |||
| 3e479be4e5 | |||
| 170aa53e29 | |||
| 021ee946fc | |||
| 0b928fdfee | |||
| e858384cd4 | |||
| c062cddd87 | |||
| 94a79656ad | |||
| cd0c8a4584 | |||
| 4fff14a144 | |||
| 196c09a0d6 | |||
| 114f072269 | |||
| b3211f13ee | |||
| 1f1463c482 | |||
| bec40ee5ea | |||
| dc94d51d97 | |||
| 3a096ae64a | |||
| a85e757d4d | |||
| 312886846c | |||
| cdc1c8415e | |||
| f2142731fd | |||
| 1dd62c5ffc | |||
| 95d8b2fd0f | |||
| 51a2959312 | |||
| 28bcc73f2c | |||
| 8b71f94141 | |||
| 3bda1351c3 | |||
| 4daa631178 | |||
| 9f5325ef2f | |||
| 6fb65a8fbe | |||
| f6dff65d9b | |||
| 4fa9aa1e5b | |||
| e1c075fa51 | |||
| df9251fb52 | |||
| ead6be4a52 | |||
| 4f48c7e5dc | |||
| fac39a2f46 | |||
| 483e6be207 | |||
| 0e030d21b0 | |||
| 355a0606c3 | |||
| 2277ee4860 | |||
| 8a4977e8e7 | |||
| 128c357e07 | |||
| 9f0fc27c52 | |||
| bfd1c29c0a | |||
| 5fca2db6d3 | |||
| 8ecbf5b76a | |||
| d037c31b4d | |||
| 4fe89c9d45 | |||
| 389526bb80 | |||
| 4adc3116f9 | |||
| 8e0bdc9934 | |||
| 7dc6e4c132 | |||
| 44b8c8b7a6 | |||
| 6b8b79266f | |||
| 08ab685d8d | |||
| 057deb0169 | |||
| 44533d2c9d | |||
| 54c7a2d191 | |||
| c38a1f060b | |||
| 5dca743b0c | |||
| 01c1952c9f | |||
| dbc3dd95d4 | |||
| a9b0cdfc03 | |||
| 1c20494158 | |||
| c9294ad642 | |||
| 3a71bfb8a0 | |||
| 24e14c4874 | |||
| 20826a69ae | |||
| 5127903c38 | |||
| 541139b944 | |||
| 925a2816cc | |||
| b3b8d79446 | |||
| 85f17a269d | |||
| b169031d4e | |||
| 399e469328 | |||
| 1367a033d7 | |||
| c1168d3ec1 | |||
| b05698d55b | |||
| aeacf6cacc | |||
| 1f0e81c9a1 | |||
| c2bfc9ec52 | |||
| 9fdba541e9 | |||
| cf0524aa23 | |||
| 03b6dfb2c3 | |||
| cf1cb31150 | |||
| 7d586f52f2 | |||
| e85818e199 | |||
| 16bec29274 | |||
| 5a135a331a | |||
| e7b712e29f | |||
| d5483988d2 | |||
| 0f5c68dcf8 | |||
| 8ab0ccf11c | |||
| 9596f2e9ee | |||
| 1dc32452e6 | |||
| 9785722d61 | |||
| c905a34b1d | |||
| a1089dbf95 | |||
| baf25e263f | |||
| 3abf73dee5 | |||
| 4adebd31f9 | |||
| cea2d7fd15 | |||
| e0224ab159 | |||
| 6dee339783 | |||
| b674d7906d | |||
| 3d7796de55 | |||
| 4306e6e978 | |||
| 68e65a8ffe | |||
| 2efc637ab9 | |||
| 22e748557c | |||
| 77effb5055 | |||
| 1c66a02878 | |||
| 1ac98e7d16 | |||
| bbd2fe06e1 | |||
| d951cb503f | |||
| bbd2488f79 | |||
| 3253ae8f4a | |||
| fdda0d3724 | |||
| a1d385c7b7 | |||
| 21b834decc | |||
| 4daa7de997 | |||
| bd68a155c0 | |||
| 993aaf44c7 | |||
| 54cbc62727 | |||
| 2d6a0fdf9c | |||
| d0cbd32ad2 | |||
| be64706cff | |||
| c178bc0a55 | |||
| 0fccdfed5c | |||
| aaf0a5e44a | |||
| 98888883bc | |||
| 65e72f39ae | |||
| f9d1213c4b | |||
| dc10c80e61 | |||
| 23b9cefdca | |||
| 15dc0e9f23 | |||
| 0c53e09e30 | |||
| cdbf04f50c | |||
| ba324b4f54 | |||
| 3e83753466 | |||
| 288934c82c | |||
| 483bc80fc4 | |||
| 354c69acc4 | |||
| 6fa0a78ec0 | |||
| f66932e8d2 | |||
| da6d5a93c9 | |||
| 1be927bc5d | |||
| 0848ddcdf7 | |||
| e182d474e0 | |||
| 0f937b0a03 | |||
| cd9fc1d08f | |||
| d9aac08b81 | |||
| 9c1669020b | |||
| 742307a267 | |||
| 781ded573a | |||
| 6a647f58be | |||
| 4bc6501d2e | |||
| 8d80321738 | |||
| a0d37fd182 | |||
| 03787668fd | |||
| 3059f82552 | |||
| 22fbcfd9fb | |||
| 304880bfd1 | |||
| 33c276d0b4 | |||
| 87d027e8ac | |||
| d7db1b7f2e | |||
| 22c973af02 | |||
| 8c18fa1764 | |||
| 5394faca16 | |||
| 4a3f6d60ad | |||
| 68b60e2a0a | |||
| 888bd77e60 | |||
| 949906f9f7 | |||
| 94f01e597c | |||
| b8e3290f35 | |||
| 03ab828342 | |||
| b66bff9155 | |||
| f52abf919e | |||
| b790b525c1 | |||
| f5182e0584 | |||
| 47c8300ccf | |||
| 8339621f9c | |||
| 16cbb442e5 | |||
| 88da28a68d | |||
| a62061f453 | |||
| cb3d58b1c7 | |||
| fa46720f5a | |||
| 72b9209ae9 | |||
| 0cb7058476 | |||
| ac8b3424d1 | |||
| 2cb4b49d0d | |||
| d67332a3ac | |||
| 22c6e9a7e7 | |||
| 1d4a135e8e | |||
| 9d28b790e7 | |||
| 61c285746c | |||
| a13c188de3 | |||
| c60f633a62 | |||
| 56abc4a700 | |||
| 9b4e3a1598 | |||
| 0aa30226df | |||
| fdb5724053 | |||
| b532f86674 | |||
| c44463f030 | |||
| de38e299fc | |||
| 7526773a90 | |||
| bf79d9e75d | |||
| 02c08c6350 | |||
| 05eb20f9c6 | |||
| 9716918517 | |||
| c88417a3b6 | |||
| 6e95386f30 | |||
| 6807dfa22e | |||
| f27c49f931 |
@@ -6,18 +6,18 @@ jobs:
|
||||
name: coverage
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: xd009642/tarpaulin
|
||||
image: xd009642/tarpaulin:develop-nightly
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@main
|
||||
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua54,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||
cargo +nightly tarpaulin --verbose --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
|
||||
|
||||
- name: Upload report to codecov.io
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: false
|
||||
|
||||
+97
-46
@@ -7,18 +7,18 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -27,10 +27,11 @@ jobs:
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.4-dev liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
@@ -44,23 +45,23 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -71,18 +72,18 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
name: Cross-compile to armv7-unknown-linux-gnueabihf
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -93,7 +94,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -102,33 +103,34 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" --tests -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" --tests -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
@@ -137,24 +139,52 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
os: [ubuntu-latest]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: -Z sanitizer=address
|
||||
|
||||
test_with_memory_limit:
|
||||
name: Test with memory limit
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with forced memory limit
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: --cfg=force_memory_limit
|
||||
|
||||
test_modules:
|
||||
name: Test modules
|
||||
@@ -162,21 +192,21 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest]
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
@@ -195,7 +225,7 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- name: Install Rust & Lua
|
||||
run: |
|
||||
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
|
||||
@@ -204,30 +234,51 @@ jobs:
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-22.04
|
||||
test_wasm32_emscripten:
|
||||
name: Test on wasm32-unknown-emscripten
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luau]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-emscripten
|
||||
- name: Install Emscripten
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends emscripten
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rustfmt
|
||||
- run: cargo fmt -- --check
|
||||
|
||||
clippy:
|
||||
name: Clippy check
|
||||
runs-on: ubuntu-22.04
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: clippy
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
reporter: 'github-pr-review'
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
@@ -4,3 +4,4 @@ Cargo.lock
|
||||
|
||||
.vscode/
|
||||
.DS_Store
|
||||
.stignore
|
||||
|
||||
+302
-4
@@ -1,3 +1,301 @@
|
||||
## v0.10.5 (May 24th, 2025)
|
||||
|
||||
- mlua-sys is back to 0.6.x (Luau 0.663)
|
||||
- Reverted: Trigger abort when Luau userdata destructors are panic (requires new mlua-sys)
|
||||
- Reverted: Added large (52bit) integers support for Luau (breaking change)
|
||||
|
||||
## v0.10.4 (May 5th, 2025)
|
||||
|
||||
_yanked_ because of semver-breaking changes
|
||||
|
||||
- Luau updated to 0.672
|
||||
- New serde option `encode_empty_tables_as_array` to serialize empty tables as arrays
|
||||
- Added `WeakLua` and `Lua::weak()` to create weak references to Lua state
|
||||
- Trigger abort when Luau userdata destructors are panic (Luau GC does not support it)
|
||||
- Added `AnyUserData::type_id()` method to get the type id of the userdata
|
||||
- Added `Chunk::name()`, `Chunk::environment()` and `Chunk::mode()` functions
|
||||
- Support borrowing underlying wrapped types for `UserDataRef` and `UserDataRefMut` (under `userdata-wrappers` feature)
|
||||
- Added large (52bit) integers support for Luau
|
||||
- Enable `serde` for `bstr` if `serialize` feature flag is enabled
|
||||
- Recursive warnings (Lua 5.4) are no longer allowed
|
||||
- Implemented `IntoLua`/`FromLua` for `BorrowedString` and `BorrowedBytes`
|
||||
- Implemented `IntoLua`/`FromLua` for `char`
|
||||
- Enable `Thread::reset()` for all Lua versions (limited support for 5.1-5.3)
|
||||
- Bugfixes and improvements
|
||||
|
||||
## v0.10.3 (Jan 27th, 2025)
|
||||
|
||||
- Set `Default` for `Value` to be `Nil`
|
||||
- Allow exhaustive match on `Value` (#502)
|
||||
- Add `Table::set_safeenv` method (Luau)
|
||||
|
||||
## v0.10.2 (Dec 1st, 2024)
|
||||
|
||||
- Switch proc-macro-error to proc-macro-error2 (#493)
|
||||
- Do not allow Lua to run GC finalizers on ref thread (#491)
|
||||
- Fix chunks loading in Luau when memory limit is enforced (#488)
|
||||
- Added `String::wrap` method to wrap arbitrary `AsRef<[u8]>` into `impl IntoLua`
|
||||
- Better FreeBSD/OpenBSD support (thanks to cos)
|
||||
- Delay "any" userdata metatable creation until first instance is created (#482)
|
||||
- Reduce amount of generated code for `UserData` (less generics)
|
||||
|
||||
## v0.10.1 (Nov 9th, 2024)
|
||||
|
||||
- Minimal Luau updated to 0.650
|
||||
- Added Luau native vector library support (this can change behavior if you use `vector` function!)
|
||||
- Added Lua `String::display` method
|
||||
- Improved pretty-printing for Lua tables (#478)
|
||||
- Added `Scope::create_any_userdata` to create Lua objects from any non-`'static` Rust types
|
||||
- Added `AnyUserData::destroy` method
|
||||
- New `userdata-wrappers` feature to `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` (similar to v0.9)
|
||||
- `UserDataRef` in `send` mode now uses shared lock if `T: Sync` (and exclusive lock otherwise)
|
||||
- Added `Scope::add_destructor` to attach custom destructors
|
||||
- Added `Lua::try_app_data_ref` and `Lua::try_app_data_mut` methods
|
||||
- Added `From<Vec>` and `Into<Vec>` support to `MultiValue` and `Variadic` types
|
||||
- Bug fixes and improvements (#477 #479)
|
||||
|
||||
## v0.10.0 (Oct 25th, 2024)
|
||||
|
||||
Changes since v0.10.0-rc.1
|
||||
|
||||
- Added `error-send` feature flag (disabled by default) to require `Send + Sync` for `Error`
|
||||
- Some performance improvements
|
||||
|
||||
## v0.10.0-rc.1
|
||||
|
||||
- `Lua::scope` is back
|
||||
- Support yielding from hooks for Lua 5.3+
|
||||
- Support setting metatable for Lua builtin types (number/string/function/etc)
|
||||
- Added `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` traits for using in `Function::wrap`
|
||||
- Added `Error::chain` method to return iterator over nested errors
|
||||
- Added `Lua::exec_raw` helper to execute low-level Lua C API code
|
||||
- Added `Either<L, R>` enum to combine two types into a single one
|
||||
- Added a new `Buffer` type for Luau
|
||||
- Added `Value::is_error` and `Value::as_error` helpers
|
||||
- Added `Value::Other` variant to represent unknown Lua types (eg LuaJIT CDATA)
|
||||
- Added (optional) `anyhow` feature to implement `IntoLua` for `anyhow::Error`
|
||||
- Added `IntoLua`/`FromLua` for `OsString`/`OsStr` and `PathBuf`/`Path`
|
||||
|
||||
## v0.10.0-beta.2
|
||||
|
||||
- Updated `ThreadStatus` enum to include `Running` and `Finished` variants.
|
||||
- `Error::CoroutineInactive` renamed to `Error::CoroutineUnresumable`.
|
||||
- `IntoLua`/`IntoLuaMulti` now uses `impl trait` syntax for args (shorten from `a.get::<_, T>` to `a.get::<T>`).
|
||||
- Removed undocumented `Lua::into_static`/`from_static` methods.
|
||||
- Futures now require `Send` bound if `send` feature is enabled.
|
||||
- Dropped lifetime from `UserDataMethods` and `UserDataFields` traits.
|
||||
- `Compiler::compile()` now returns `Result` (Luau).
|
||||
- Removed `Clone` requirement from `UserDataFields::add_field()`.
|
||||
- `TableExt` and `AnyUserDataExt` traits were combined into `ObjectLike` trait.
|
||||
- Disabled `send` feature in module mode (since we don't have exclusive access to Lua).
|
||||
- `Chunk::set_environment()` takes `Table` instead of `IntoLua` type.
|
||||
- Reduced the compile time contribution of `next_key_seed` and `next_value_seed`.
|
||||
- Reduced the compile time contribution of `serde_userdata`.
|
||||
- Performance improvements.
|
||||
|
||||
## v0.10.0-beta.1
|
||||
|
||||
- Dropped `'lua` lifetime (subtypes now store a weak reference to Lua)
|
||||
- Removed (experimental) owned types (they no longer needed)
|
||||
- Make Lua types truly `Send` and `Sync` (when enabling `send` feature flag)
|
||||
- Removed `UserData` impl for Rc/Arc types ("any" userdata functions can be used instead)
|
||||
- `Lua::replace_registry_value` takes `&mut RegistryKey`
|
||||
- `Lua::scope` temporary disabled (will be re-added in the next release)
|
||||
|
||||
## v0.9.9
|
||||
|
||||
- Minimal Luau updated to 0.629
|
||||
- Fixed bug when attempting to reset or resume already running coroutines (#416).
|
||||
- Added `RegistryKey::id()` method to get the underlying Lua registry key id.
|
||||
|
||||
## v0.9.8
|
||||
|
||||
- Fixed serializing same table multiple times (#408)
|
||||
- Use `mlua-sys` v0.6 (to support Luau 0.624+)
|
||||
- Fixed cross compilation of windows dlls from unix (#394)
|
||||
|
||||
## v0.9.7
|
||||
|
||||
- Implemented `IntoLua` for `RegistryKey`
|
||||
- Mark `__idiv` metamethod as available for luau
|
||||
- Added `Function::deep_clone()` method (Luau)
|
||||
- Added `SerializeOptions::detect_serde_json_arbitrary_precision` option
|
||||
- Added `Lua::create_buffer()` method (Luau)
|
||||
- Support serializing buffer type as a byte slice (Luau)
|
||||
- Perf: Implemented `push_into_stack`/`from_stack` for `Option<T>`
|
||||
- Added `Lua::create_ser_any_userdata()` method
|
||||
|
||||
## v0.9.6
|
||||
|
||||
- Added `to_pointer` function to `Function`/`Table`/`Thread`
|
||||
- Implemented `IntoLua` for `&Value`
|
||||
- Implemented `FromLua` for `RegistryKey`
|
||||
- Faster (~5%) table array traversal during serialization
|
||||
- Some performance improvements for bool/int types
|
||||
|
||||
## v0.9.5
|
||||
|
||||
- Minimal Luau updated to 0.609
|
||||
- Luau max stack size increased to 1M (from 100K)
|
||||
- Implemented `IntoLua` for refs to `String`/`Table`/`Function`/`AnyUserData`/`Thread` + `RegistryKey`
|
||||
- Implemented `IntoLua` and `FromLua` for `OwnedThread`/`OwnedString`
|
||||
- Fixed `FromLua` derive proc macro to cover more cases
|
||||
|
||||
## v0.9.4
|
||||
|
||||
- Fixed loading all-in-one modules under mixed states (eg. main state and coroutines)
|
||||
|
||||
## v0.9.3
|
||||
|
||||
- WebAssembly support (`wasm32-unknown-emscripten` target)
|
||||
- Performance improvements (faster Lua function calls for lua51/jit/luau)
|
||||
|
||||
## v0.9.2
|
||||
|
||||
- Added binary modules support to Luau
|
||||
- Added Luau package module (uses `StdLib::PACKAGE`) with loaders (follows lua5.1 interface)
|
||||
- Added support of Luau 0.601+ buffer type (represented as userdata in Rust)
|
||||
- LuaJIT `cdata` type is also represented as userdata in Rust (instead of panic)
|
||||
- Vendored LuaJIT switched to rolling vanilla (from openresty)
|
||||
- Added `Table::for_each` method for fast table pairs traversal (faster than `pairs`)
|
||||
- Performance improvements around table traversal (and faster serialization)
|
||||
- Bug fixes and improvements
|
||||
|
||||
## v0.9.1
|
||||
|
||||
- impl Default for Lua
|
||||
- impl IntoLuaMulti for `std::result::Result<(), E>`
|
||||
- Fix using wrong userdata index after processing Variadic args (#311)
|
||||
|
||||
## v0.9.0
|
||||
|
||||
Changes since v0.9.0-rc.3
|
||||
|
||||
- Improved non-static (scoped) userdata support
|
||||
- Added `Scope::create_any_userdata()` method
|
||||
- Added `Lua::set_vector_metatable()` method (`unstable` feature flag)
|
||||
- Added `OwnedThread` type (`unstable` feature flag)
|
||||
- Minimal Luau updated to 0.590
|
||||
- Added new option `sort_keys` to `DeserializeOptions` (`Lua::from_value()` method)
|
||||
- Changed `Table::raw_len()` output type to `usize`
|
||||
- Helper functions for `Value` (eg: `Value::as_number()`/`Value::as_string`/etc)
|
||||
- Performance improvements
|
||||
|
||||
## v0.9.0-rc.3
|
||||
|
||||
- Minimal Luau updated to 0.588
|
||||
|
||||
## v0.9.0-rc.2
|
||||
|
||||
- Added `#[derive(FromLua)]` macro to opt-in into `FromLua<T> where T: 'static + Clone` (userdata type).
|
||||
- Support vendored module mode for windows (raw-dylib linking, Rust 1.71+)
|
||||
- `module` and `vendored` features are now mutually exclusive
|
||||
- Use `C-unwind` ABI (Rust 1.71+)
|
||||
- Changed `AsChunk` trait to support capturing wrapped Lua types
|
||||
|
||||
## v0.9.0-rc.1
|
||||
|
||||
- `UserDataMethods::add_async_method()` takes `&T` instead of cloning `T`
|
||||
- Implemented `PartialEq<[T]>` for tables
|
||||
- Added Luau 4-dimensional vectors support (`luau-vector4` feature)
|
||||
- `Table::sequence_values()` iterator no longer uses any metamethods (`Table::raw_sequence_values()` is deprecated)
|
||||
- Added `Table:is_empty()` function that checks both hash and array parts
|
||||
- Refactored Debug interface
|
||||
- Re-exported `ffi` (`mlua-sys`) crate for easier writing of unsafe code
|
||||
- Refactored Lua 5.4 warnings interface
|
||||
- Take `&str` as function name in `TableExt` and `AnyUserDataExt` traits
|
||||
- Added module attribule `skip_memory_check` to improve performance
|
||||
- Added `AnyUserData::wrap()` to provide more easy way of creating _any_ userdata in Lua
|
||||
|
||||
## v0.9.0-beta.3
|
||||
|
||||
- Added `OwnedAnyUserData::take()`
|
||||
- Switch to `DeserializeOwned`
|
||||
- Overwrite error context when called multiple times
|
||||
- New feature flag `luau-jit` to enable (experimental) Luau codegen backend
|
||||
- Set `__name` field in userdata metatable
|
||||
- Added `Value::to_string()` method similar to `luaL_tolstring`
|
||||
- Lua 5.4.6
|
||||
- Application data container now allows to mutably and immutably borrow different types at the same time
|
||||
- Performance optimizations
|
||||
- Support getting and setting environment for Lua functions.
|
||||
- Added `UserDataFields::add_field()` method to add static fields to UserData
|
||||
|
||||
Breaking changes:
|
||||
- Require environment to be a `Table` instead of `Value` in Chunks.
|
||||
- `AsChunk::env()` renamed to `AsChunk::environment()`
|
||||
|
||||
## v0.9.0-beta.2
|
||||
|
||||
New features:
|
||||
- Added `Thread::set_hook()` function to set hook on threads
|
||||
- Added pretty print to the Debug formatting to Lua `Value` and `Table`
|
||||
- ffi layer moved to `mlua-sys` crate
|
||||
- Added OwnedString (unstable)
|
||||
|
||||
Breaking changes:
|
||||
- Refactor `HookTriggers` (make it const)
|
||||
|
||||
## v0.9.0-beta.1
|
||||
|
||||
New features:
|
||||
- Owned Lua types (unstable feature flag)
|
||||
- New functions `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async`
|
||||
- `Lua::register_userdata_type()` to register a custom userdata types (without requiring `UserData` trait)
|
||||
- `Lua::create_any_userdata()`
|
||||
- Added `create_userdata_ref`/`create_userdata_ref_mut` for scopes
|
||||
- Added `AnyUserDataExt` trait with auxiliary functions for `AnyUserData`
|
||||
- Added `UserDataRef` and `UserDataRefMut` type wrapped that implement `FromLua`
|
||||
- Improved error handling:
|
||||
* Improved error reporting when calling Rust functions from Lua.
|
||||
* Added `Error::BadArgument` to help identify bad argument position or name
|
||||
* Added `ErrorContext` extension trait to attach additional context to `Error`
|
||||
|
||||
Breaking changes:
|
||||
- Refactored `AsChunk` trait
|
||||
- `ToLua`/`ToLuaMulti` renamed to `IntoLua`/`IntoLuaMulti`
|
||||
- Renamed `to_lua_err` to `into_lua_err`
|
||||
- Removed `FromLua` impl for `T: UserData+Clone`
|
||||
- Removed `Lua::async_scope`
|
||||
- Added `&Lua` arg to Luau interrupt callback
|
||||
|
||||
Other:
|
||||
- Better Debug for String
|
||||
- Allow deserializing values from serializable UserData using `Lua::from_value()` method
|
||||
- Added `Table::clear()` method
|
||||
- Added `Error::downcast_ref()` method
|
||||
- Support setting memory limit for Lua 5.1/JIT/Luau
|
||||
- Support setting module name in `#[lua_module(name = "...")]` macro
|
||||
- Minor fixes and improvements
|
||||
|
||||
## v0.8.10
|
||||
|
||||
- Update to Luau 0.590 (luau0-src to 0.7.x)
|
||||
- Fix loading luau code starting with \t
|
||||
- Pin lua-src and luajit-src versions
|
||||
|
||||
## v0.8.9
|
||||
|
||||
- Update minimal (vendored) Lua 5.4 to 5.4.6
|
||||
- Use `lua_closethread` instead of `lua_resetthread` in vendored mode (Lua 5.4.6)
|
||||
- Allow deserializing Lua null into unit (`()`) or unit struct.
|
||||
|
||||
## v0.8.8
|
||||
|
||||
- Fix potential deadlock when trying to reuse dropped registry keys.
|
||||
- Optimize userdata methods call when __index and fields_getters are nil
|
||||
|
||||
## v0.8.7
|
||||
|
||||
- Minimum Luau updated to 0.555 (`LUAI_MAXCSTACK` limit increased to 100000)
|
||||
- `_VERSION` in Luau now includes version number
|
||||
- Fixed lifetime of `DebugNames` in `Debug::names()` and `DebugSource` in `Debug::source()`
|
||||
- Fixed subtraction overflow when calculating index for `MultiValue::get()`
|
||||
|
||||
## v0.8.6
|
||||
|
||||
- Fixed bug when recycled Registry slot can be set to Nil
|
||||
|
||||
## v0.8.5
|
||||
|
||||
- Fixed potential unsoundness when using `Layout::from_size_align_unchecked` and Rust 1.65+
|
||||
@@ -31,7 +329,7 @@
|
||||
|
||||
## v0.8.0
|
||||
Changes since 0.7.4
|
||||
- Roblox Luau support
|
||||
- Luau support
|
||||
- Removed C glue
|
||||
- Added async support to `__index` and `__newindex` metamethods
|
||||
- Added `Function::info()` to get information about functions (#149).
|
||||
@@ -81,7 +379,7 @@ Breaking changes:
|
||||
|
||||
## v0.8.0-beta.1
|
||||
|
||||
- Roblox Luau support
|
||||
- Luau support
|
||||
- Refactored ffi module. C glue is no longer required
|
||||
- Added async support to `__index` and `__newindex` metamethods
|
||||
|
||||
@@ -194,7 +492,7 @@ Breaking changes:
|
||||
- [**Breaking**] Removed `AnyUserData::has_metamethod()`
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads.
|
||||
It's possible to attach a new function to a thread (coroutine).
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optionally capturing Rust variables.
|
||||
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
|
||||
|
||||
## v0.6.0-beta.1
|
||||
@@ -250,7 +548,7 @@ Breaking changes:
|
||||
|
||||
- Lua 5.4 support with `MetaMethod::Close`.
|
||||
- `lua53` feature is disabled by default. Now preferred Lua version have to be chosen explicitly.
|
||||
- Provide safety guaraness for Lua state, which means that potenially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equalient for the previous `Lua::new()` function is `Lua::unsafe_new()`.
|
||||
- Provide safety guarantees for Lua state, which means that potentially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equivalent to the previous `Lua::new()` function is `Lua::unsafe_new()`.
|
||||
- New `send` feature to require `Send`.
|
||||
- New `module` feature, that disables linking to Lua Core Libraries. Required for modules.
|
||||
- Don't allow `'callback` outlive `'lua` in `Lua::create_function()` to fix [the unsoundness](tests/compile/static_callback_args.rs).
|
||||
|
||||
+60
-44
@@ -1,84 +1,100 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.8.5" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
version = "0.10.5" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.79.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
keywords = ["lua", "luajit", "luau", "async", "scripting"]
|
||||
categories = ["api-bindings", "asynchronous"]
|
||||
license = "MIT"
|
||||
links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
|
||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
|
||||
with async/await features and support of writing native Lua modules in Rust.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot"]
|
||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"mlua_derive",
|
||||
"mlua-sys",
|
||||
]
|
||||
|
||||
[features]
|
||||
lua54 = []
|
||||
lua53 = []
|
||||
lua52 = []
|
||||
lua51 = []
|
||||
luajit = []
|
||||
luajit52 = ["luajit"]
|
||||
luau = ["luau0-src"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
module = ["mlua_derive"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde"]
|
||||
lua54 = ["ffi/lua54"]
|
||||
lua53 = ["ffi/lua53"]
|
||||
lua52 = ["ffi/lua52"]
|
||||
lua51 = ["ffi/lua51"]
|
||||
luajit = ["ffi/luajit"]
|
||||
luajit52 = ["luajit", "ffi/luajit52"]
|
||||
luau = ["ffi/luau", "dep:libloading"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["dep:futures-util"]
|
||||
send = ["parking_lot/send_guard", "error-send"]
|
||||
error-send = []
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
anyhow = ["dep:anyhow", "error-send"]
|
||||
userdata-wrappers = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.8.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.0" }
|
||||
mlua_derive = { version = "=0.10.1", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||
either = "1.0"
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "1.0"
|
||||
futures-core = { version = "0.3.5", optional = true }
|
||||
futures-task = { version = "0.3.5", optional = true }
|
||||
futures-util = { version = "0.3.5", optional = true }
|
||||
rustc-hash = "2.0"
|
||||
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
|
||||
serde = { version = "1.0", optional = true }
|
||||
erased-serde = { version = "0.3", optional = true }
|
||||
parking_lot = { version = "0.12", optional = true }
|
||||
erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
rustversion = "1.0"
|
||||
|
||||
[build-dependencies]
|
||||
cc = { version = "1.0" }
|
||||
pkg-config = { version = "0.3.17" }
|
||||
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.4.0", optional = true }
|
||||
ffi = { package = "mlua-sys", version = "0.6.8", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "10.0"
|
||||
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
futures-timer = "3.0"
|
||||
hyper = { version = "1.2", features = ["full"] }
|
||||
hyper-util = { version = "0.1.3", features = ["full"] }
|
||||
http-body-util = "0.1.1"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
tokio = { version = "1.0", features = ["macros", "rt", "time"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
|
||||
maplit = "1.0"
|
||||
tempfile = "3"
|
||||
static_assertions = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
rustyline = "15.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(tarpaulin_include)'] }
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
required-features = ["async"]
|
||||
|
||||
[[bench]]
|
||||
name = "serde"
|
||||
harness = false
|
||||
required-features = ["serialize"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
required-features = ["async", "macros"]
|
||||
@@ -89,11 +105,11 @@ required-features = ["async", "serialize", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_server"
|
||||
required-features = ["async", "macros"]
|
||||
required-features = ["async", "macros", "send"]
|
||||
|
||||
[[example]]
|
||||
name = "async_tcp_server"
|
||||
required-features = ["async", "macros"]
|
||||
required-features = ["async", "macros", "send"]
|
||||
|
||||
[[example]]
|
||||
name = "guided_tour"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# mlua
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io] ![MSRV]
|
||||
|
||||
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||
[Build Status]: https://github.com/mlua-rs/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/mlua-rs/mlua/actions
|
||||
[Latest Version]: https://img.shields.io/crates/v/mlua.svg
|
||||
[crates.io]: https://crates.io/crates/mlua
|
||||
[API Documentation]: https://docs.rs/mlua/badge.svg
|
||||
[docs.rs]: https://docs.rs/mlua
|
||||
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/khvzak/mlua
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.56+-brightgreen.svg?&logo=rust
|
||||
[Coverage Status]: https://codecov.io/gh/mlua-rs/mlua/branch/main/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/mlua-rs/mlua
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.79+-brightgreen.svg?&logo=rust
|
||||
|
||||
[Guided Tour] | [Benchmarks] | [FAQ]
|
||||
|
||||
@@ -17,15 +17,21 @@
|
||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||
[FAQ]: FAQ.md
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> See v0.10 [release notes](https://github.com/mlua-rs/mlua/blob/main/docs/release_notes/v0.10.md).
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
|
||||
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Roblox Luau] and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Luau] and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
[Roblox Luau]: https://luau-lang.org
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua/Luau versions excluding JIT.
|
||||
|
||||
[GitHub Actions]: https://github.com/mlua-rs/mlua/actions
|
||||
[Luau]: https://luau.org
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -34,34 +40,37 @@ Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT
|
||||
`mlua` uses feature flags to reduce the amount of dependencies, compiled code and allow to choose only required set of features.
|
||||
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||
|
||||
* `lua54`: activate Lua [5.4] support
|
||||
* `lua53`: activate Lua [5.3] support
|
||||
* `lua52`: activate Lua [5.2] support
|
||||
* `lua51`: activate Lua [5.1] support
|
||||
* `luajit`: activate [LuaJIT] support
|
||||
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
|
||||
* `luau`: activate [Luau] support (auto vendored mode)
|
||||
* `lua54`: enable Lua [5.4] support
|
||||
* `lua53`: enable Lua [5.3] support
|
||||
* `lua52`: enable Lua [5.2] support
|
||||
* `lua51`: enable Lua [5.1] support
|
||||
* `luajit`: enable [LuaJIT] support
|
||||
* `luajit52`: enable [LuaJIT] support with partial compatibility with Lua 5.2
|
||||
* `luau`: enable [Luau] support (auto vendored mode)
|
||||
* `luau-jit`: enable [Luau] support with JIT backend.
|
||||
* `luau-vector4`: enable [Luau] support with 4-dimensional vector.
|
||||
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `error-send`: make `mlua:Error: Send + Sync`
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
* `parking_lot`: support UserData types wrapped in [parking_lot]'s primitives (`Arc<Mutex>` and `Arc<RwLock>`)
|
||||
* `anyhow`: enable `anyhow::Error` conversion into Lua
|
||||
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
[5.2]: https://www.lua.org/manual/5.2/manual.html
|
||||
[5.1]: https://www.lua.org/manual/5.1/manual.html
|
||||
[LuaJIT]: https://luajit.org/
|
||||
[Luau]: https://github.com/Roblox/luau
|
||||
[lua-src]: https://github.com/khvzak/lua-src-rs
|
||||
[luajit-src]: https://github.com/khvzak/luajit-src-rs
|
||||
[Luau]: https://github.com/luau-lang/luau
|
||||
[lua-src]: https://github.com/mlua-rs/lua-src-rs
|
||||
[luajit-src]: https://github.com/mlua-rs/luajit-src-rs
|
||||
[tokio]: https://github.com/tokio-rs/tokio
|
||||
[async-std]: https://github.com/async-rs/async-std
|
||||
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
[serde]: https://github.com/serde-rs/serde
|
||||
[parking_lot]: https://github.com/Amanieu/parking_lot
|
||||
|
||||
### Async/await support
|
||||
|
||||
@@ -75,6 +84,20 @@ This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6
|
||||
- [HTTP Server](examples/async_http_server.rs)
|
||||
- [TCP Server](examples/async_tcp_server.rs)
|
||||
|
||||
|
||||
**shell command examples**:
|
||||
```shell
|
||||
# async http client (hyper)
|
||||
cargo run --example async_http_client --features=lua54,async,macros
|
||||
|
||||
# async http client (reqwest)
|
||||
cargo run --example async_http_reqwest --features=lua54,async,macros,serialize
|
||||
|
||||
# async http server
|
||||
cargo run --example async_http_server --features=lua54,async,macros,send
|
||||
curl -v http://localhost:3000
|
||||
```
|
||||
|
||||
### Serialization (serde) support
|
||||
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
|
||||
@@ -91,12 +114,12 @@ You have to enable one of the features: `lua54`, `lua53`, `lua52`, `lua51`, `lua
|
||||
|
||||
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
|
||||
To achieve this, mlua supports `LUA_INC`, `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
|
||||
To achieve this, mlua supports `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
|
||||
`LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive).
|
||||
|
||||
An example how to use them:
|
||||
``` sh
|
||||
my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
||||
my_project $ LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
||||
```
|
||||
|
||||
`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
@@ -110,7 +133,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.8", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.10", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -145,7 +168,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.8", features = ["lua54", "vendored", "module"] }
|
||||
mlua = { version = "0.10", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -175,7 +198,7 @@ $ lua5.4 -e 'require("my_module").hello("world")'
|
||||
hello, world!
|
||||
```
|
||||
|
||||
On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config` with the following content:
|
||||
On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config.toml` with the following content:
|
||||
``` toml
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = [
|
||||
@@ -190,20 +213,22 @@ rustflags = [
|
||||
]
|
||||
```
|
||||
On Linux you can build modules normally with `cargo build --release`.
|
||||
Vendored and non-vendored builds are supported for these OS.
|
||||
|
||||
On Windows `vendored` mode for modules is not supported since you need to link to a Lua dll.
|
||||
Easiest way is to use either MinGW64 (as part of [MSYS2](https://github.com/msys2/msys2) package) with `pkg-config` or
|
||||
MSVC with `LUA_INC` / `LUA_LIB` / `LUA_LIB_NAME` environment variables.
|
||||
On Windows the target module will be linked with `lua5x.dll` library (depending on your feature flags).
|
||||
Your main application should provide this library.
|
||||
|
||||
More details about compiling and linking Lua modules can be found on the [Building Modules](http://lua-users.org/wiki/BuildingModules) page.
|
||||
Module builds don't require Lua lib or headers to be installed on the system.
|
||||
|
||||
### Publishing to luarocks.org
|
||||
|
||||
There is a LuaRocks build backend for mlua modules [`luarocks-build-rust-mlua`].
|
||||
|
||||
Modules written in Rust and published to luarocks:
|
||||
- [`decasify`](https://github.com/alerque/decasify)
|
||||
- [`lua-ryaml`](https://github.com/khvzak/lua-ryaml)
|
||||
- [`tiktoken_core`](https://github.com/gptlang/lua-tiktoken)
|
||||
- [`toml-edit`](https://github.com/vhyrro/toml-edit.lua)
|
||||
- [`typst-lua`](https://github.com/rousbound/typst-lua)
|
||||
|
||||
[`luarocks-build-rust-mlua`]: https://luarocks.org/modules/khvzak/luarocks-build-rust-mlua
|
||||
|
||||
@@ -266,7 +291,7 @@ Please check the [Luau Sandboxing] page if you are interested in running untrust
|
||||
|
||||
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
|
||||
|
||||
[Luau Sandboxing]: https://luau-lang.org/sandbox
|
||||
[Luau Sandboxing]: https://luau.org/sandbox
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+252
-151
@@ -1,5 +1,7 @@
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::task;
|
||||
|
||||
@@ -10,10 +12,10 @@ fn collect_gc_twice(lua: &Lua) {
|
||||
lua.gc_collect().unwrap();
|
||||
}
|
||||
|
||||
fn create_table(c: &mut Criterion) {
|
||||
fn table_create_empty(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table empty]", |b| {
|
||||
c.bench_function("table [create empty]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
@@ -24,71 +26,55 @@ fn create_table(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn create_array(c: &mut Criterion) {
|
||||
fn table_create_array(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [array] 10", |b| {
|
||||
c.bench_function("table [create array]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table = lua.create_table().unwrap();
|
||||
for i in 1..=10 {
|
||||
table.set(i, i).unwrap();
|
||||
}
|
||||
lua.create_sequence_from(1..=10).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_string_table(c: &mut Criterion) {
|
||||
fn table_create_hash(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table string] 10", |b| {
|
||||
c.bench_function("table [create hash]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table = lua.create_table().unwrap();
|
||||
for &s in &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] {
|
||||
let s = lua.create_string(s).unwrap();
|
||||
table.set(s.clone(), s).unwrap();
|
||||
}
|
||||
lua.create_table_from(
|
||||
["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
|
||||
.into_iter()
|
||||
.map(|s| (s, s)),
|
||||
)
|
||||
.unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_function(c: &mut Criterion) {
|
||||
fn table_get_set(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [function] 10", |b| {
|
||||
c.bench_function("table [get and set]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
for i in 0..10 {
|
||||
lua.create_function(move |_, ()| Ok(i)).unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_lua_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("call Lua function [sum] 3 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function(a, b, c) return a + b + c end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
lua.create_table().unwrap()
|
||||
},
|
||||
|function| {
|
||||
for i in 0..10 {
|
||||
let _result: i64 = function.call((i, i + 1, i + 2)).unwrap();
|
||||
|table| {
|
||||
for (i, s) in ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
table.raw_set(s, i).unwrap();
|
||||
assert_eq!(table.raw_get::<usize>(s).unwrap(), i);
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
@@ -96,197 +82,303 @@ fn call_lua_function(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn call_sum_callback(c: &mut Criterion) {
|
||||
fn table_traversal_pairs(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
|
||||
|
||||
c.bench_function("table [traversal pairs]", |b| {
|
||||
b.iter_batched(
|
||||
|| lua.globals(),
|
||||
|globals| {
|
||||
for kv in globals.pairs::<String, LuaValue>() {
|
||||
let (_k, _v) = kv.unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn table_traversal_for_each(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("table [traversal for_each]", |b| {
|
||||
b.iter_batched(
|
||||
|| lua.globals(),
|
||||
|globals| globals.for_each::<String, LuaValue>(|_k, _v| Ok(())),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn table_traversal_sequence(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua.create_sequence_from(1..1000).unwrap();
|
||||
|
||||
c.bench_function("table [traversal sequence]", |b| {
|
||||
b.iter_batched(
|
||||
|| table.clone(),
|
||||
|table| {
|
||||
for v in table.sequence_values::<i32>() {
|
||||
let _i = v.unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_create(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("function [create Rust]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
lua.create_function(|_, ()| Ok(123)).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_call_sum(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let sum = lua
|
||||
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b - c))
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
c.bench_function("call Rust callback [sum] 3 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
c.bench_function("function [call Rust sum]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(sum.call::<i64>((10, 20, 30)).unwrap(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_async_sum_callback(c: &mut Criterion) {
|
||||
let options = LuaOptions::new().thread_cache_size(1024);
|
||||
fn function_call_lua_sum(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let sum = lua
|
||||
.load("function(a, b, c) return a + b - c end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("function [call Lua sum]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(sum.call::<i64>((10, 20, 30)).unwrap(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_call_concat(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let concat = lua
|
||||
.create_function(|_, (a, b): (LuaString, LuaString)| Ok(format!("{}{}", a.to_str()?, b.to_str()?)))
|
||||
.unwrap();
|
||||
let i = AtomicUsize::new(0);
|
||||
|
||||
c.bench_function("function [call Rust concat string]", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
i.fetch_add(1, Ordering::Relaxed)
|
||||
},
|
||||
|i| {
|
||||
assert_eq!(concat.call::<LuaString>(("num:", i)).unwrap(), format!("num:{i}"));
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_call_lua_concat(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let concat = lua
|
||||
.load("function(a, b) return a..b end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
let i = AtomicUsize::new(0);
|
||||
|
||||
c.bench_function("function [call Lua concat string]", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
i.fetch_add(1, Ordering::Relaxed)
|
||||
},
|
||||
|i| {
|
||||
assert_eq!(concat.call::<LuaString>(("num:", i)).unwrap(), format!("num:{i}"));
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_async_call_sum(c: &mut Criterion) {
|
||||
let options = LuaOptions::new().thread_pool_size(1024);
|
||||
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
|
||||
let callback = lua
|
||||
|
||||
let sum = lua
|
||||
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
|
||||
task::yield_now().await;
|
||||
Ok(a + b + c)
|
||||
Ok(a + b - c)
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
c.bench_function("call async Rust callback [sum] 3 10", |b| {
|
||||
c.bench_function("function [async call Rust sum]", |b| {
|
||||
let rt = Runtime::new().unwrap();
|
||||
b.to_async(rt).iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| async move {
|
||||
function.call_async::<_, ()>(()).await.unwrap();
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| async {
|
||||
assert_eq!(sum.call_async::<i64>((10, 20, 30)).await.unwrap(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_concat_callback(c: &mut Criterion) {
|
||||
fn registry_value_create(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
.create_function(|_, (a, b): (LuaString, LuaString)| {
|
||||
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
lua.gc_stop();
|
||||
|
||||
c.bench_function("call Rust callback [concat string] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback('a', tostring(i)) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
},
|
||||
c.bench_function("registry value [create]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| lua.create_registry_value("hello").unwrap(),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_registry_values(c: &mut Criterion) {
|
||||
fn registry_value_get(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
lua.gc_stop();
|
||||
|
||||
c.bench_function("create [registry value] 10", |b| {
|
||||
let value = lua.create_registry_value("hello").unwrap();
|
||||
|
||||
c.bench_function("registry value [get]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
for _ in 0..10 {
|
||||
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
|
||||
}
|
||||
lua.expire_registry_values();
|
||||
assert_eq!(lua.registry_value::<LuaString>(&value).unwrap(), "hello");
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_userdata(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
fn userdata_create(c: &mut Criterion) {
|
||||
struct UserData(#[allow(unused)] i64);
|
||||
impl LuaUserData for UserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table userdata] 10", |b| {
|
||||
c.bench_function("userdata [create]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table: LuaTable = lua.create_table().unwrap();
|
||||
for i in 1..11 {
|
||||
table.set(i, UserData(i)).unwrap();
|
||||
}
|
||||
lua.create_userdata(UserData(123)).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_userdata_index(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
fn userdata_call_index(c: &mut Criterion) {
|
||||
struct UserData(#[allow(unused)] i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, index: String| Ok(index));
|
||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
let ud = lua.create_userdata(UserData(123)).unwrap();
|
||||
let index = lua
|
||||
.load("function(ud) return ud.test end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("call [userdata index] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do local v = userdata.test end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
c.bench_function("userdata [call index]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(index.call::<LuaString>(&ud).unwrap(), "test");
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_userdata_method(c: &mut Criterion) {
|
||||
fn userdata_call_method(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |_, this, ()| Ok(this.0));
|
||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("add", |_, this, i: i64| Ok(this.0 + i));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
let ud = lua.create_userdata(UserData(123)).unwrap();
|
||||
let method = lua
|
||||
.load("function(ud, i) return ud:add(i) end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
let i = AtomicUsize::new(0);
|
||||
|
||||
c.bench_function("call [userdata method] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
c.bench_function("userdata [call method]", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do userdata:method() end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
i.fetch_add(1, Ordering::Relaxed)
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
|i| {
|
||||
assert_eq!(method.call::<usize>((&ud, i)).unwrap(), 123 + i);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_async_userdata_method(c: &mut Criterion) {
|
||||
#[derive(Clone, Copy)]
|
||||
fn userdata_async_call_method(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0) });
|
||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_async_method("add", |_, this, i: i64| async move {
|
||||
task::yield_now().await;
|
||||
Ok(this.0 + i)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let options = LuaOptions::new().thread_cache_size(1024);
|
||||
let options = LuaOptions::new().thread_pool_size(1024);
|
||||
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
let ud = lua.create_userdata(UserData(123)).unwrap();
|
||||
let method = lua
|
||||
.load("function(ud, i) return ud:add(i) end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
let i = AtomicUsize::new(0);
|
||||
|
||||
c.bench_function("call async [userdata method] 10", |b| {
|
||||
c.bench_function("userdata [async call method] 10", |b| {
|
||||
let rt = Runtime::new().unwrap();
|
||||
b.to_async(rt).iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do userdata:method() end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
(method.clone(), ud.clone(), i.fetch_add(1, Ordering::Relaxed))
|
||||
},
|
||||
|function| async move {
|
||||
function.call_async::<_, ()>(()).await.unwrap();
|
||||
|(method, ud, i)| async move {
|
||||
assert_eq!(method.call_async::<usize>((ud, i)).await.unwrap(), 123 + i);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -296,23 +388,32 @@ fn call_async_userdata_method(c: &mut Criterion) {
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default()
|
||||
.sample_size(300)
|
||||
.sample_size(500)
|
||||
.measurement_time(Duration::from_secs(10))
|
||||
.noise_threshold(0.02);
|
||||
targets =
|
||||
create_table,
|
||||
create_array,
|
||||
create_string_table,
|
||||
create_function,
|
||||
call_lua_function,
|
||||
call_sum_callback,
|
||||
call_async_sum_callback,
|
||||
call_concat_callback,
|
||||
create_registry_values,
|
||||
create_userdata,
|
||||
call_userdata_index,
|
||||
call_userdata_method,
|
||||
call_async_userdata_method,
|
||||
table_create_empty,
|
||||
table_create_array,
|
||||
table_create_hash,
|
||||
table_get_set,
|
||||
table_traversal_pairs,
|
||||
table_traversal_for_each,
|
||||
table_traversal_sequence,
|
||||
|
||||
function_create,
|
||||
function_call_sum,
|
||||
function_call_lua_sum,
|
||||
function_call_concat,
|
||||
function_call_lua_concat,
|
||||
function_async_call_sum,
|
||||
|
||||
registry_value_create,
|
||||
registry_value_get,
|
||||
|
||||
userdata_create,
|
||||
userdata_call_index,
|
||||
userdata_call_method,
|
||||
userdata_async_call_method,
|
||||
}
|
||||
|
||||
criterion_main!(benches);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
fn collect_gc_twice(lua: &Lua) {
|
||||
lua.gc_collect().unwrap();
|
||||
lua.gc_collect().unwrap();
|
||||
}
|
||||
|
||||
fn encode_json(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let encode = lua
|
||||
.create_function(|_, t: LuaValue| Ok(serde_json::to_string(&t).unwrap()))
|
||||
.unwrap();
|
||||
let table = lua
|
||||
.load(
|
||||
r#"{
|
||||
name = "Clark Kent",
|
||||
address = {
|
||||
city = "Smallville",
|
||||
state = "Kansas",
|
||||
country = "USA",
|
||||
},
|
||||
age = 22,
|
||||
parents = {"Jonathan Kent", "Martha Kent"},
|
||||
superman = true,
|
||||
interests = {"flying", "saving the world", "kryptonite"},
|
||||
}"#,
|
||||
)
|
||||
.eval::<LuaTable>()
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("serialize json", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
encode.call::<LuaString>(&table).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn decode_json(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let decode = lua
|
||||
.create_function(|lua, s: String| {
|
||||
lua.to_value(&serde_json::from_str::<serde_json::Value>(&s).unwrap())
|
||||
})
|
||||
.unwrap();
|
||||
let json = r#"{
|
||||
"name": "Clark Kent",
|
||||
"address": {
|
||||
"city": "Smallville",
|
||||
"state": "Kansas",
|
||||
"country": "USA"
|
||||
},
|
||||
"age": 22,
|
||||
"parents": ["Jonathan Kent", "Martha Kent"],
|
||||
"superman": true,
|
||||
"interests": ["flying", "saving the world", "kryptonite"]
|
||||
}"#;
|
||||
|
||||
c.bench_function("deserialize json", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
decode.call::<LuaTable>(json).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default()
|
||||
.sample_size(500)
|
||||
.measurement_time(Duration::from_secs(10))
|
||||
.noise_threshold(0.02);
|
||||
targets =
|
||||
encode_json,
|
||||
decode_json,
|
||||
}
|
||||
|
||||
criterion_main!(benches);
|
||||
@@ -1,5 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::env;
|
||||
use std::ops::Bound;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn get_env_var(name: &str) -> String {
|
||||
match env::var(name) {
|
||||
Ok(val) => val,
|
||||
Err(env::VarError::NotPresent) => String::new(),
|
||||
Err(err) => panic!("cannot get {}: {}", name, err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
let include_dir = get_env_var("LUA_INC");
|
||||
let lib_dir = get_env_var("LUA_LIB");
|
||||
let lua_lib = get_env_var("LUA_LIB_NAME");
|
||||
|
||||
println!("cargo:rerun-if-env-changed=LUA_INC");
|
||||
println!("cargo:rerun-if-env-changed=LUA_LIB");
|
||||
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
|
||||
println!("cargo:rerun-if-env-changed=LUA_LINK");
|
||||
|
||||
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
|
||||
|
||||
if !include_dir.is_empty() {
|
||||
if need_lua_lib {
|
||||
if lib_dir.is_empty() {
|
||||
panic!("LUA_LIB is not set");
|
||||
}
|
||||
if lua_lib.is_empty() {
|
||||
panic!("LUA_LIB_NAME is not set");
|
||||
}
|
||||
|
||||
let mut link_lib = "";
|
||||
if get_env_var("LUA_LINK") == "static" {
|
||||
link_lib = "static=";
|
||||
};
|
||||
println!("cargo:rustc-link-search=native={}", lib_dir);
|
||||
println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
|
||||
}
|
||||
return Some(PathBuf::from(include_dir));
|
||||
}
|
||||
|
||||
// Find using `pkg-config`
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", "lua5.4", "5.4");
|
||||
#[cfg(feature = "lua53")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", "lua5.3", "5.3");
|
||||
#[cfg(feature = "lua52")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", "lua5.2", "5.2");
|
||||
#[cfg(feature = "lua51")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", "lua5.1", "5.1");
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51"
|
||||
))]
|
||||
{
|
||||
let mut lua = pkg_config::Config::new()
|
||||
.range_version((Bound::Included(incl_bound), Bound::Excluded(excl_bound)))
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("lua");
|
||||
|
||||
if lua.is_err() {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe(alt_probe);
|
||||
}
|
||||
|
||||
lua.unwrap_or_else(|_| panic!("cannot find Lua {} using `pkg-config`", ver))
|
||||
.include_paths
|
||||
.get(0)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
{
|
||||
let lua = pkg_config::Config::new()
|
||||
.range_version((Bound::Included("2.0.4"), Bound::Unbounded))
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("luajit");
|
||||
|
||||
lua.expect("cannot find LuaJIT using `pkg-config`")
|
||||
.include_paths
|
||||
.get(0)
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
#[cfg_attr(
|
||||
any(
|
||||
feature = "luau",
|
||||
all(
|
||||
feature = "vendored",
|
||||
any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit"
|
||||
)
|
||||
)
|
||||
),
|
||||
path = "find_vendored.rs"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
all(
|
||||
not(feature = "vendored"),
|
||||
any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit"
|
||||
)
|
||||
),
|
||||
path = "find_normal.rs"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
)),
|
||||
path = "find_dummy.rs"
|
||||
)]
|
||||
mod find;
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
)))]
|
||||
compile_error!(
|
||||
"You must enable one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(
|
||||
feature = "lua54",
|
||||
any(
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
)
|
||||
))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(
|
||||
feature = "lua53",
|
||||
any(
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
)
|
||||
))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(
|
||||
feature = "lua52",
|
||||
any(feature = "lua51", feature = "luajit", feature = "luau")
|
||||
))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "lua51", any(feature = "luajit", feature = "luau")))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "luajit", feature = "luau"))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
// We don't support "vendored module" mode on windows
|
||||
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
|
||||
compile_error!(
|
||||
"Vendored (static) builds are not supported for modules on Windows.\n"
|
||||
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "module"))]
|
||||
compile_error!("Luau does not support module mode");
|
||||
|
||||
#[cfg(any(not(feature = "module"), target_os = "windows"))]
|
||||
find::probe_lua();
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
## mlua v0.10 release notes
|
||||
|
||||
The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.
|
||||
This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
|
||||
|
||||
### New features
|
||||
|
||||
#### `'static` Lua types
|
||||
|
||||
In previous mlua versions, it was required to have a `'lua` lifetime attached to every Lua value. v0.9 introduced (experimental) owned types that are `'static` without a lifetime attached, but they kept strong references to the Lua instance.
|
||||
In v0.10 all Lua types are `'static` and have only weak reference to the Lua instance. It means they are more flexible and can be used in more places without worrying about memory leaks.
|
||||
|
||||
#### Truly `send` feature
|
||||
|
||||
In this version Lua is `Send + Sync` when the `send` feature flag is enabled (previously was only `Send`). It means Lua instance and their values can be safely shared between threads and used in multi threaded async contexts.
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.globals().set("i", 0)?;
|
||||
let func = lua.load("i = i + ...").into_function()?;
|
||||
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
for i in 0..5 {
|
||||
func.call::<()>(i).unwrap();
|
||||
}
|
||||
});
|
||||
s.spawn(|| {
|
||||
for i in 0..5 {
|
||||
func.call::<()>(i).unwrap();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
assert_eq!(lua.globals().get::<i32>("i")?, 20);
|
||||
```
|
||||
|
||||
Under the hood, to synchronize access to the Lua state, mlua uses [`ReentrantMutex`] which can be recursively locked by a single thread. Only one thread can execute Lua code at a time, but it's possible to share Lua values between threads.
|
||||
|
||||
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and does not supported in module mode.
|
||||
|
||||
[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html
|
||||
|
||||
#### Register Rust functions with variable number of arguments
|
||||
|
||||
The new traits `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` have been introduced to provide a way to register Rust functions with variable number of arguments in Lua, without needing to pass all arguments as a tuple.
|
||||
|
||||
They are used by `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async` methods:
|
||||
|
||||
```rust
|
||||
let add = Function::wrap(|a: i64, b: i64| Ok(a + b));
|
||||
|
||||
lua.globals().set("add", add).unwrap();
|
||||
|
||||
// Prints 50
|
||||
lua.load(r#"print(add(5, 45))"#).exec().unwrap();
|
||||
```
|
||||
|
||||
To wrap functions that return direct value (non-`Result`) you can use `Function::wrap_raw` method.
|
||||
|
||||
#### Setting metatable for Lua builtin types
|
||||
|
||||
For Lua builtin types (like `string`, `function`, `number`, etc.) that have a shared metatable for all instances, it's now possible to set a custom metatable for them.
|
||||
|
||||
```rust
|
||||
let mt = lua.create_table()?;
|
||||
mt.set("__tostring", lua.create_function(|_, b: bool| Ok(if b { "2" } else { "0" }))?)?;
|
||||
lua.set_type_metatable::<bool>(Some(mt));
|
||||
lua.load("assert(tostring(true) == '2')").exec().unwrap();
|
||||
```
|
||||
|
||||
### Improvements
|
||||
|
||||
#### New `ObjectLike` trait
|
||||
|
||||
The `ObjectLike` trait is a combination of the `AnyUserDataExt` and `TableExt` traits used in previous versions. It provides a unified interface for working with Lua tables and userdata.
|
||||
|
||||
#### `Either<L, R>` enum
|
||||
|
||||
The `Either<L, R>` enum is a simple enum that can hold either `L` or `R` value. It's useful when you need to return or receive one of two types in a function.
|
||||
This type implements `IntoLua` and `FromLua` traits and can generate a meaningful error message when conversion fails.
|
||||
|
||||
```rust
|
||||
let func = Function::wrap(|x: Either<i32, String>| Ok(format!("received: {x}")));
|
||||
|
||||
lua.globals().set("func", func).unwrap();
|
||||
|
||||
// Prints: received: 123
|
||||
lua.load(r#"print(func(123))"#).exec().unwrap();
|
||||
|
||||
// Prints: bad argument #1: error converting Lua table to Either<i32, String>
|
||||
lua.load(r#"print(pcall(func, {}))"#).exec().unwrap();
|
||||
```
|
||||
|
||||
#### `Lua::exec_raw` helper to execute low-level Lua C API code
|
||||
|
||||
For advanced users, it's now possible to execute low-level Lua C API code using the `Lua::exec_raw` method.
|
||||
|
||||
```rust
|
||||
let t = lua.create_sequence_from([1, 2, 3, 4, 5])?;
|
||||
let sum: i64 = unsafe {
|
||||
lua.exec_raw(&t, |state| {
|
||||
// top of the stack: table `t`
|
||||
let mut sum = 0;
|
||||
// push nil as the first key
|
||||
mlua::ffi::lua_pushnil(state);
|
||||
while mlua::ffi::lua_next(state, -2) != 0 {
|
||||
sum += mlua::ffi::lua_tointeger(state, -1);
|
||||
// Remove the value, keep the key for the next iteration
|
||||
mlua::ffi::lua_pop(state, 1);
|
||||
}
|
||||
mlua::ffi::lua_pop(state, 1);
|
||||
mlua::ffi::lua_pushinteger(state, sum);
|
||||
// top of the stack: sum
|
||||
})
|
||||
}?;
|
||||
assert_eq!(sum, 15);
|
||||
```
|
||||
|
||||
The `exec_raw` method is longjmp-safe. It's not recommended to move `Drop` types into the closure to avoid possible memory leaks.
|
||||
|
||||
#### `anyhow` feature flag
|
||||
|
||||
The new `anyhow` feature flag adds `IntoLua` and `Into<mlua::Error>` implementation for the `anyhow::Error` type.
|
||||
|
||||
```rust
|
||||
let f = lua.create_function(|_, ()| {
|
||||
Err(anyhow!("error message"))?;
|
||||
Ok(())
|
||||
})?;
|
||||
```
|
||||
|
||||
### Breaking changes
|
||||
|
||||
#### Scope changes
|
||||
|
||||
The following `Scope` methods were changed:
|
||||
- Removed `Scope::create_any_userdata`
|
||||
- `Scope::create_nonstatic_userdata` is renamed to `Scope::create_userdata`
|
||||
|
||||
Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.
|
||||
|
||||
`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
|
||||
In mlua v0.9 this can cause read-after-free bug in some edge cases.
|
||||
|
||||
To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:
|
||||
|
||||
```rust
|
||||
let data = "hello".to_string();
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata_ref(&data)?;
|
||||
|
||||
// We can only borrow scoped userdata using this method
|
||||
ud.borrow_scoped::<String, ()>(|s| {
|
||||
assert_eq!(s, "hello");
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
```
|
||||
|
||||
Those methods work for scoped and regular userdata objects (but still require `T: 'static`).
|
||||
|
||||
#### String changes
|
||||
|
||||
Since `mlua::String` holds a weak reference to Lua without any guarantees about the lifetime of the underlying data, getting a `&str` or `&[u8]` from it is no longer safe.
|
||||
Lua instance can be destroyed while reference to the data is still alive:
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
let s: mlua::String = lua.create_string("hello, world")?; // only weak reference to Lua!
|
||||
let s_ref: &str = s.to_str()?; // this is not safe!
|
||||
drop(lua);
|
||||
println!("{s_ref}"); // use after free!
|
||||
```
|
||||
|
||||
To solve this issue, return types of `mlua::String::to_str` and `mlua::String::as_bytes` methods changed to `BorrowedStr` and `BorrowedBytes` respectively.
|
||||
|
||||
These new types hold a strong reference to the Lua instance and can be safely converted to `&str` or `&[u8]`:
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
let s: mlua::String = lua.create_string("hello, world")?;
|
||||
let s_ref: mlua::BorrowedStr = s.to_str()?; // The strong reference to Lua is held here
|
||||
drop(lua);
|
||||
println!("{s_ref}"); // ok
|
||||
```
|
||||
|
||||
The good news is that `BorrowedStr` implements `Deref<Target = str>`/`AsRef<str>` as well as `Display`, `Debug`, `Eq`, `PartialEq` and other traits for easy usage.
|
||||
The same applies to `BorrowedBytes`.
|
||||
|
||||
Unfortunately, `mlua::String::to_string_lossy` cannot return `Cow<'a, str>` anymore, because it requires a strong reference to Lua. It now returns Rust `String` instead.
|
||||
@@ -0,0 +1,361 @@
|
||||
## mlua v0.9 release notes
|
||||
|
||||
The v0.9 version of mlua is a major release that includes a number of API changes and improvements. This release is a stepping stone towards the v1.0.
|
||||
This document highlights the most important changes. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
|
||||
|
||||
### New features
|
||||
|
||||
#### 1. New Any UserData API
|
||||
|
||||
This is a long awaited feature that allows to register in Lua foreign types that cannot implement `UserData` trait because of the Rust orphan rules.
|
||||
|
||||
Now you can register any type that implements [`Any`] trait as a userdata type.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```rust
|
||||
lua.register_userdata_type::<std::string::String>(|reg| {
|
||||
reg.add_method("len", |_, this, ()| Ok(this.len()));
|
||||
|
||||
reg.add_method_mut("push", |_, this, s: String| {
|
||||
this.push_str(&s);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
|
||||
})?;
|
||||
|
||||
let s = lua.create_any_userdata("hello".to_string())?;
|
||||
lua.load(chunk! {
|
||||
print("s:len() is " .. $s:len())
|
||||
$s:push(" world")
|
||||
// Prints: hello, world
|
||||
print($s)
|
||||
})
|
||||
.exec()?;
|
||||
```
|
||||
|
||||
In this example we registered [`std::string::String`] as a userdata type with a set of methods and then created an instance of this type in Lua.
|
||||
|
||||
It's _not_ required to register a type before using the `Lua::create_any_userdata()` method, instead an empty metatable will be created for you.
|
||||
You can also register the same type multiple times with different methods. Any previously created instances will share the old metatable, while new instances will have the new one.
|
||||
|
||||
The new set of API is called `any_userdata` because it allows to register types that implements [`Any`] trait.
|
||||
|
||||
[`std::string::String`]: https://doc.rust-lang.org/stable/std/string/struct.String.html
|
||||
[`Any`]: https://doc.rust-lang.org/stable/std/any/trait.Any.html
|
||||
|
||||
#### 2. Scope support for the new any userdata types
|
||||
|
||||
When you need to create non-static userdata instances in Lua, the usual way is use `Lua::scope()` helper to make them scoped. When out of scope, any scoped objects will be automatically
|
||||
dropped. The only downside of this approach is that every new instance will have a new metatable. This is not very fast if you need to create a lot of instances.
|
||||
|
||||
With the new Any UserData API, you can place non-static references `&T` where `T: 'static` into a scope and they will share a single static metatable.
|
||||
|
||||
```rust
|
||||
lua.register_userdata_type::<std::string::String>(|reg| {
|
||||
reg.add_method_mut("replace", |_, this, (pat, to): (String, String)| {
|
||||
*this = this.replace(&pat, &to);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
reg.add_meta_method(MetaMethod::ToString, |lua, this, ()| lua.create_string(this));
|
||||
})?;
|
||||
|
||||
let mut s = "hello, world".to_string();
|
||||
|
||||
lua.scope(|scope| {
|
||||
// This userdata instance holds only a mutable reference to our string
|
||||
let ud = scope.create_any_userdata_ref_mut(&mut s)?;
|
||||
lua.load(chunk! {
|
||||
$ud:replace("world", "user")
|
||||
})
|
||||
.exec()
|
||||
})?;
|
||||
|
||||
// Prints: hello, user!
|
||||
println!("{s}!");
|
||||
```
|
||||
|
||||
#### 3. Owned types (`unstable`)
|
||||
|
||||
One of the common questions was how to embed a Lua type into Rust struct to use it later. It was non-trivial to do because of the `'lua` lifetime attached to every Lua value.
|
||||
|
||||
In v0.9 mlua introduces "owned" types `OwnedTable`/`OwnedFunction`/`OwnedString`/`OwnedAnyUserData`/ `OwnedThread`that are `'static` (no lifetime attached).
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyStruct {
|
||||
table: OwnedTable,
|
||||
func: OwnedFunction,
|
||||
}
|
||||
|
||||
let my_struct = MyStruct {
|
||||
table: lua.globals().into_owned(),
|
||||
func: lua
|
||||
.create_function(|_, t: Table| Ok(format!("{t:#?}")))?
|
||||
.into_owned(),
|
||||
};
|
||||
|
||||
// It's safe to drop Lua!
|
||||
drop(lua);
|
||||
|
||||
let result = my_struct.func.call::<_, String>(my_struct.table)?;
|
||||
println!("{result}");
|
||||
```
|
||||
|
||||
Prior to v0.9, it was possible to do by creating a reference to the Lua value in registry using `Lua::create_registry_value()`
|
||||
and retrieving value later using `Lua::registry_value()` method.
|
||||
|
||||
All owned handles hold a *strong* reference to the current Lua instance.
|
||||
Be warned, if you place them into a Lua type (eg. `UserData` or a Rust callback), it is *very easy*
|
||||
to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
|
||||
Please note this functionality is available under the `unstable` feature flag and not available when the `send` feature is enabled.
|
||||
|
||||
#### New ffi module
|
||||
|
||||
In v0.9 release the internal `ffi` module has been moved into the new [`mlua-sys`] crate and became available for public use.
|
||||
This crate provides unified Lua FFI API (targeting Lua 5.4) using a (limited) compatibility layer for older versions.
|
||||
|
||||
mlua re-exports the `ffi` module aliasing the `mlua-sys` crate and provides (unsafe) functionality to work with raw Lua state:
|
||||
|
||||
```rust
|
||||
unsafe {
|
||||
unsafe extern "C-unwind" fn lua_add(state: *mut mlua::lua_State) -> i32 {
|
||||
let a = mlua::ffi::luaL_checkinteger(state, 1);
|
||||
let b = mlua::ffi::luaL_checkinteger(state, 2);
|
||||
mlua::ffi::lua_pushinteger(state, a + b);
|
||||
1
|
||||
}
|
||||
|
||||
let add = lua.create_c_function(lua_add)?;
|
||||
assert_eq!(add.call::<_, i32>((2, 3))?, 5);
|
||||
}
|
||||
```
|
||||
|
||||
[`mlua-sys`]: https://crates.io/crates/mlua-sys
|
||||
|
||||
#### Luau JIT support
|
||||
|
||||
mlua brings support for the new [Luau] JIT backend under the `luau-jit` feature flag.
|
||||
|
||||
It will automatically trigger JIT compilation for new Lua chunks. To disable it, just call `lua.enable_jit(false)` before loading Lua code
|
||||
(but any previously compiled chunks will remain JIT-compiled).
|
||||
|
||||
[Luau]: https://luau-lang.org
|
||||
|
||||
### Improvements
|
||||
|
||||
#### 1. Better error reporting
|
||||
|
||||
When calling a Rust function from Lua and passing wrong arguments, previous mlua versions reported a error message without any context or reference to the particular argument.
|
||||
|
||||
In v0.9 it reports a error message with the argument index and expected type:
|
||||
|
||||
```rust
|
||||
let func = lua.create_function(|_, _a: i32| Ok(()))?;
|
||||
lua.load(chunk! {
|
||||
local ok, err = pcall($func, "not a number")
|
||||
// Prints: bad argument #1: error converting Lua string to i32 (expected number or string coercible to number)
|
||||
print(err)
|
||||
})
|
||||
.exec()?;
|
||||
```
|
||||
|
||||
Similar changes have been made for userdata functions and methods:
|
||||
|
||||
```rust
|
||||
lua.register_userdata_type::<&'static str>(|reg| {
|
||||
reg.add_method("len", |_, this, ()| Ok(this.len()));
|
||||
})?;
|
||||
|
||||
let s = lua.create_any_userdata("hello")?;
|
||||
lua.load(chunk! {
|
||||
local ok, err = pcall($s.len, 123)
|
||||
// Prints: bad argument `self` to `&str.len`: error converting Lua integer to userdata
|
||||
print(err)
|
||||
})
|
||||
.exec()?;
|
||||
```
|
||||
|
||||
#### 2. Error context
|
||||
|
||||
Similar to the [`anyhow`] Error type, now it's possible to attach context to Lua errors:
|
||||
|
||||
```rust
|
||||
let read = lua.create_function(|lua, path: String| {
|
||||
let bytes = std::fs::read(&path)
|
||||
.into_lua_err()
|
||||
.context(format!("Failed to open `{path}`"))?;
|
||||
Ok(lua.create_string(bytes))
|
||||
})?;
|
||||
|
||||
lua.load(chunk! {
|
||||
local ok, err = pcall($read, "/nonexistent")
|
||||
/// Prints:
|
||||
/// Failed to open /nonexistent
|
||||
/// No such file or directory (os error 2)
|
||||
/// stack traceback:
|
||||
/// ...
|
||||
print(err)
|
||||
})
|
||||
.exec()?;
|
||||
```
|
||||
|
||||
[`anyhow`]: https://crates.io/crates/anyhow
|
||||
|
||||
#### 4. New methods `Function::wrap`/`AnyUserData::wrap`
|
||||
|
||||
Sometimes it's useful to have `IntoLua` trait implementation for a Rust function or type `T: Any` without needing to call `Lua::create_function()`/`Lua::create_any_userdata()` methods.
|
||||
Since v0.9 you can call the new methods `Function::wrap()`/`AnyUserData::wrap()` that allows to do this. They return an abstract type that `impl IntoLua`:
|
||||
|
||||
```rust
|
||||
lua.globals().set("print_rust", Function::wrap(|_, s: String| Ok(println!("{}", s))))?;
|
||||
lua.globals().set("rust_ud", AnyUserData::wrap("hello"))?;
|
||||
```
|
||||
|
||||
In addition there are also `Function::wrap_mut()`/`Function::wrap_async()` methods that allow to wrap mutable and async functions respectively.
|
||||
|
||||
For a `T: 'UserData + 'static` the `IntoLua` trait is still always implemented.
|
||||
|
||||
#### `UserDataRef` and `UserDataRefMut` type wrappers
|
||||
|
||||
The new wrappers `UserDataRef` and `UserDataRefMut` are receivers for userdata type `T` and borrow underlying instance for the lifetime of the wrapper.
|
||||
|
||||
```rust
|
||||
lua.globals()
|
||||
.set("ud", AnyUserData::wrap("hello".to_string()))?;
|
||||
|
||||
let mut ud_mut: UserDataRefMut<String> = lua.globals().get("ud")?;
|
||||
ud_mut.push_str(", Rust");
|
||||
drop(ud_mut);
|
||||
|
||||
let ud_ref: UserDataRef<String> = lua.globals().get("ud")?;
|
||||
// Prints: hello, Rust
|
||||
println!("{}", *ud_ref);
|
||||
```
|
||||
|
||||
In the previous mlua versions the same functionality can be achieved by receiving `AnyUserData` and calling `AnyUserData::borrow()`/`AnyUserData::borrow_mut()` methods.
|
||||
|
||||
The new wrappers are identical to Rust [`Ref`]/[`RefMut`] types.
|
||||
|
||||
[`Ref`]: https://doc.rust-lang.org/std/cell/struct.Ref.html
|
||||
[`RefMut`]: https://doc.rust-lang.org/std/cell/struct.RefMut.html
|
||||
|
||||
#### New `AnyUserDataExt` trait
|
||||
|
||||
Similar to the `TableExt` trait, the `AnyUserDataExt` provides a set of extra methods for the `AnyUserData` type.
|
||||
|
||||
1) `AnyUserDataExt::get()/set()` to get/set a value by key from the userdata, assuming it has `__index` metamethod.
|
||||
|
||||
2) `AnyUserDataExt::call()` to call the userdata as a function assuming it has `__call` metamethod.
|
||||
|
||||
3) `AnyUserData::call_method(name, ...)` to call the userdata method, assuming it has `__index` metamethod and the associated function.
|
||||
|
||||
#### Pretty formatting Lua values
|
||||
|
||||
`mlua::Value` implements a new format `:#?` that allows to (recursively) pretty print Lua values:
|
||||
|
||||
```rust
|
||||
println!("{:#?}", lua.globals());
|
||||
```
|
||||
|
||||
Prints:
|
||||
```
|
||||
{
|
||||
["_G"] = table: 0x7fa2d0706260,
|
||||
["_VERSION"] = "Lua 5.4",
|
||||
["assert"] = function: 0x10451d11d,
|
||||
["collectgarbage"] = function: 0x10451d198,
|
||||
["coroutine"] = {
|
||||
["close"] = function: 0x10451e28f,
|
||||
...
|
||||
},
|
||||
["dofile"] = function: 0x10451d37c,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
In addition a new method `Value::to_string()` has been added to convert `Value` to a string (using `__tostring` metamethod if available).
|
||||
|
||||
#### Environment for Lua functions
|
||||
|
||||
Any Lua functions have an associated environment table that is used to resolve global variables. By default it sets to a Lua globals table.
|
||||
|
||||
In the new release it's possible to get or update a function environment using `Function::environment()` or `Function::set_environment()` methods respectively.
|
||||
|
||||
```rust
|
||||
let f = lua.load("return a").into_function()?;
|
||||
|
||||
assert_eq!(f.environment(), Some(lua.globals()));
|
||||
|
||||
lua.globals().set("a", 1)?;
|
||||
assert_eq!(f.call::<_, i32>(())?, 1);
|
||||
|
||||
f.set_environment(lua.create_table_from([("a", "hello")])?)?;
|
||||
assert_eq!(f.call::<_, mlua::String>(())?, "hello");
|
||||
```
|
||||
|
||||
#### Performance optimizations
|
||||
|
||||
The new mlua version has a number of performance improvements. Please check the [benchmarks results] to see how mlua compares to rlua and rhai.
|
||||
|
||||
[benchmarks results]: https://github.com/mlua-rs/script-bench-rs
|
||||
|
||||
### Changes in `module` mode
|
||||
|
||||
#### New attributes
|
||||
|
||||
The `lua_module` macro now support the following attributes:
|
||||
|
||||
- `name=...` - sets name of the module (defaults to the name of the function).
|
||||
|
||||
Eg.:
|
||||
|
||||
```rust
|
||||
#[mlua::lua_module(name = "alt_module")]
|
||||
fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
lua.create_table()
|
||||
}
|
||||
```
|
||||
|
||||
Under the hood a new function `luaopen_alt_module` will be created for the Lua module loader.
|
||||
|
||||
- `skip_memory_check` - skip memory allocation checks for some operations.
|
||||
|
||||
In module mode, mlua runs in unknown environment and cannot say are there any memory limits or not. As result, some operations that require memory allocation runs in
|
||||
protected mode. Setting this attribute will improve performance of such operations with risk of having uncaught exceptions and memory leaks.
|
||||
|
||||
#### Improved Windows target
|
||||
|
||||
In previous mlua versions, building a Lua module for Windows requires having Lua development libraries installed on the system.
|
||||
In contrast, on Linux and macOS, modules can be built without any external dependencies using the `-undefined=dynamic_lookup` linker flag.
|
||||
|
||||
With Rust 1.71+ it's now possible to lift this restriction for Windows as well. You can build modules normally and they will be linked with
|
||||
`lua54.dll`/`lua53.dll`/`lua52.dll`/`lua51.dll` depending on the enabled Lua version.
|
||||
|
||||
You still need to have the dll although, linked to application where the module will be loaded.
|
||||
|
||||
### Breaking changes
|
||||
|
||||
1) `ToLua`/`ToLuaMulti` traits have been renamed to `IntoLua`/`IntoLuaMulti` respectively (with the methods called `into_lua`/`into_lua_multi`).
|
||||
|
||||
The main reason for this change is following the Rust self [convention](https://rust-lang.github.io/rust-clippy/master/index.html#/wrong_self_convention).
|
||||
|
||||
2) Removed `FromLua` implementation for `T: UserData + Clone`.
|
||||
|
||||
During the usage of mlua, it was found that this implementation is not very useful and prevents custom `FromLua` implementations for `T: UserData`.
|
||||
It should be a developer decision to opt-in `FromLua` for their `T` if needed rather than having enabled it unconditionally.
|
||||
|
||||
To opt-in `FromLua` for `T: Clone` you can use a simple `#[derive(FromLua)]` macro (requires `feature = "macros"`):
|
||||
|
||||
```rust
|
||||
#[derive(Clone, Copy, mlua::FromLua)]
|
||||
struct MyUserData(i32);
|
||||
```
|
||||
|
||||
`T` is not required to implement `UserData` because of the new relaxed restrictions on userdata types.
|
||||
@@ -1,33 +1,36 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use hyper::body::{Body as HyperBody, HttpBody as _};
|
||||
use hyper::Client as HyperClient;
|
||||
use http_body_util::BodyExt as _;
|
||||
use hyper::body::Incoming;
|
||||
use hyper_util::client::legacy::Client as HyperClient;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
|
||||
use mlua::{chunk, AnyUserData, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
|
||||
struct BodyReader(HyperBody);
|
||||
struct BodyReader(Incoming);
|
||||
|
||||
impl UserData for BodyReader {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_function("read", |lua, reader: AnyUserData| async move {
|
||||
let mut reader = reader.borrow_mut::<Self>()?;
|
||||
if let Some(bytes) = reader.0.data().await {
|
||||
let bytes = bytes.to_lua_err()?;
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
// Every call returns a next chunk
|
||||
methods.add_async_method_mut("read", |lua, mut reader, ()| async move {
|
||||
if let Some(bytes) = reader.0.frame().await {
|
||||
if let Some(bytes) = bytes.into_lua_err()?.data_ref() {
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
|
||||
let client = HyperClient::new();
|
||||
let uri = uri.parse().to_lua_err()?;
|
||||
let resp = client.get(uri).await.to_lua_err()?;
|
||||
let client = HyperClient::builder(TokioExecutor::new()).build_http::<String>();
|
||||
let uri = uri.parse().into_lua_err()?;
|
||||
let resp = client.get(uri).await.into_lua_err()?;
|
||||
|
||||
let lua_resp = lua.create_table()?;
|
||||
lua_resp.set("status", resp.status().as_u16())?;
|
||||
@@ -37,7 +40,7 @@ async fn main() -> Result<()> {
|
||||
headers
|
||||
.entry(key.as_str())
|
||||
.or_insert(Vec::new())
|
||||
.push(value.to_str().to_lua_err()?);
|
||||
.push(value.to_str().into_lua_err()?);
|
||||
}
|
||||
|
||||
lua_resp.set("headers", headers)?;
|
||||
@@ -56,11 +59,11 @@ async fn main() -> Result<()> {
|
||||
end
|
||||
end
|
||||
repeat
|
||||
local body = res.body:read()
|
||||
if body then
|
||||
print(body)
|
||||
local chunk = res.body:read()
|
||||
if chunk then
|
||||
print(chunk)
|
||||
end
|
||||
until not body
|
||||
until not chunk
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
|
||||
@@ -1,33 +1,27 @@
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result};
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result, Value};
|
||||
|
||||
#[tokio::main]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let null = lua.null();
|
||||
|
||||
let fetch_json = lua.create_async_function(|lua, uri: String| async move {
|
||||
let resp = reqwest::get(&uri)
|
||||
.await
|
||||
.and_then(|resp| resp.error_for_status())
|
||||
.to_lua_err()?;
|
||||
let json = resp.json::<serde_json::Value>().await.to_lua_err()?;
|
||||
.into_lua_err()?;
|
||||
let json = resp.json::<serde_json::Value>().await.into_lua_err()?;
|
||||
lua.to_value(&json)
|
||||
})?;
|
||||
|
||||
let dbg = lua.create_function(|_, value: Value| {
|
||||
println!("{value:#?}");
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let f = lua
|
||||
.load(chunk! {
|
||||
function print_r(t, indent)
|
||||
local indent = indent or ""
|
||||
for k, v in pairs(t) do
|
||||
io.write(indent, tostring(k))
|
||||
if type(v) == "table" then io.write(":\n") print_r(v, indent.." ")
|
||||
else io.write(": ", v == $null and "null" or tostring(v), "\n") end
|
||||
end
|
||||
end
|
||||
|
||||
local res = $fetch_json(...)
|
||||
print_r(res)
|
||||
$dbg(res)
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
|
||||
@@ -1,60 +1,70 @@
|
||||
use std::convert::Infallible;
|
||||
use std::future::Future;
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::Service;
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use http_body_util::combinators::BoxBody;
|
||||
use http_body_util::{BodyExt as _, Empty, Full};
|
||||
use hyper::body::{Bytes, Incoming};
|
||||
use hyper::server::conn::http1;
|
||||
use hyper::{Request, Response};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use mlua::{
|
||||
chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods,
|
||||
};
|
||||
use mlua::{chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods};
|
||||
|
||||
struct LuaRequest(SocketAddr, Request<Body>);
|
||||
/// Wrapper around incoming request that implements UserData
|
||||
struct LuaRequest(SocketAddr, Request<Incoming>);
|
||||
|
||||
impl UserData for LuaRequest {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("remote_addr", |_lua, req, ()| Ok((req.0).to_string()));
|
||||
methods.add_method("method", |_lua, req, ()| Ok((req.1).method().to_string()));
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("remote_addr", |_, req, ()| Ok((req.0).to_string()));
|
||||
methods.add_method("method", |_, req, ()| Ok((req.1).method().to_string()));
|
||||
methods.add_method("path", |_, req, ()| Ok(req.1.uri().path().to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Svc(Rc<Lua>, SocketAddr);
|
||||
/// Service that handles incoming requests
|
||||
#[derive(Clone)]
|
||||
pub struct Svc {
|
||||
handler: Function,
|
||||
peer_addr: SocketAddr,
|
||||
}
|
||||
|
||||
impl Service<Request<Body>> for Svc {
|
||||
type Response = Response<Body>;
|
||||
type Error = LuaError;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
impl Svc {
|
||||
pub fn new(handler: Function, peer_addr: SocketAddr) -> Self {
|
||||
Self { handler, peer_addr }
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
impl hyper::service::Service<Request<Incoming>> for Svc {
|
||||
type Response = Response<BoxBody<Bytes, Infallible>>;
|
||||
type Error = LuaError;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
|
||||
|
||||
fn call(&self, req: Request<Incoming>) -> Self::Future {
|
||||
// If handler returns an error then generate 5xx response
|
||||
let lua = self.0.clone();
|
||||
let lua_req = LuaRequest(self.1, req);
|
||||
let handler = self.handler.clone();
|
||||
let lua_req = LuaRequest(self.peer_addr, req);
|
||||
Box::pin(async move {
|
||||
let handler: Function = lua.named_registry_value("http_handler")?;
|
||||
match handler.call_async::<_, Table>(lua_req).await {
|
||||
match handler.call_async::<Table>(lua_req).await {
|
||||
Ok(lua_resp) => {
|
||||
let status = lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200);
|
||||
let status = lua_resp.get::<Option<u16>>("status")?.unwrap_or(200);
|
||||
let mut resp = Response::builder().status(status);
|
||||
|
||||
// Set headers
|
||||
if let Some(headers) = lua_resp.get::<_, Option<Table>>("headers")? {
|
||||
if let Some(headers) = lua_resp.get::<Option<Table>>("headers")? {
|
||||
for pair in headers.pairs::<String, LuaString>() {
|
||||
let (h, v) = pair?;
|
||||
resp = resp.header(&h, v.as_bytes());
|
||||
resp = resp.header(&h, &*v.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// Set body
|
||||
let body = lua_resp
|
||||
.get::<_, Option<LuaString>>("body")?
|
||||
.map(|b| Body::from(b.as_bytes().to_vec()))
|
||||
.unwrap_or_else(Body::empty);
|
||||
.get::<Option<LuaString>>("body")?
|
||||
.map(|b| Full::new(Bytes::copy_from_slice(&b.as_bytes())).boxed())
|
||||
.unwrap_or_else(|| Empty::<Bytes>::new().boxed());
|
||||
|
||||
Ok(resp.body(body).unwrap())
|
||||
}
|
||||
@@ -62,7 +72,7 @@ impl Service<Request<Body>> for Svc {
|
||||
eprintln!("{}", err);
|
||||
Ok(Response::builder()
|
||||
.status(500)
|
||||
.body(Body::from("Internal Server Error"))
|
||||
.body(Full::new(Bytes::from("Internal Server Error")).boxed())
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
@@ -72,65 +82,47 @@ impl Service<Request<Body>> for Svc {
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
let lua = Rc::new(Lua::new());
|
||||
let lua = Lua::new();
|
||||
|
||||
// Create Lua handler function
|
||||
let handler: Function = lua
|
||||
let handler = lua
|
||||
.load(chunk! {
|
||||
function(req)
|
||||
return {
|
||||
status = 200,
|
||||
headers = {
|
||||
["X-Req-Method"] = req:method(),
|
||||
["X-Req-Path"] = req:path(),
|
||||
["X-Remote-Addr"] = req:remote_addr(),
|
||||
},
|
||||
body = "Hello from Lua!\n"
|
||||
}
|
||||
end
|
||||
})
|
||||
.eval()
|
||||
.expect("cannot create Lua handler");
|
||||
.eval::<Function>()
|
||||
.expect("Failed to create Lua handler");
|
||||
|
||||
// Store it in the Registry
|
||||
lua.set_named_registry_value("http_handler", handler)
|
||||
.expect("cannot store Lua handler");
|
||||
let listen_addr = "127.0.0.1:3000";
|
||||
let listener = TcpListener::bind(listen_addr).await.unwrap();
|
||||
println!("Listening on http://{listen_addr}");
|
||||
|
||||
let addr = ([127, 0, 0, 1], 3000).into();
|
||||
let server = Server::bind(&addr).executor(LocalExec).serve(MakeSvc(lua));
|
||||
loop {
|
||||
let (stream, peer_addr) = match listener.accept().await {
|
||||
Ok(x) => x,
|
||||
Err(err) => {
|
||||
eprintln!("Failed to accept connection: {err}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
// Create `LocalSet` to spawn !Send futures
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local.run_until(server).await.expect("cannot run server")
|
||||
}
|
||||
|
||||
struct MakeSvc(Rc<Lua>);
|
||||
|
||||
impl Service<&AddrStream> for MakeSvc {
|
||||
type Response = Svc;
|
||||
type Error = hyper::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, stream: &AddrStream) -> Self::Future {
|
||||
let lua = self.0.clone();
|
||||
let remote_addr = stream.remote_addr();
|
||||
Box::pin(async move { Ok(Svc(lua, remote_addr)) })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct LocalExec;
|
||||
|
||||
impl<F> hyper::rt::Executor<F> for LocalExec
|
||||
where
|
||||
F: std::future::Future + 'static, // not requiring `Send`
|
||||
{
|
||||
fn execute(&self, fut: F) {
|
||||
tokio::task::spawn_local(fut);
|
||||
let svc = Svc::new(handler.clone(), peer_addr);
|
||||
tokio::task::spawn(async move {
|
||||
if let Err(err) = http1::Builder::new()
|
||||
.serve_connection(TokioIo::new(stream), svc)
|
||||
.await
|
||||
{
|
||||
eprintln!("Error serving connection: {:?}", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,42 @@
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::task;
|
||||
|
||||
use mlua::{
|
||||
chunk, AnyUserData, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods,
|
||||
};
|
||||
use mlua::{chunk, BString, Function, Lua, UserData, UserDataMethods};
|
||||
|
||||
struct LuaTcpStream(TcpStream);
|
||||
|
||||
impl UserData for LuaTcpStream {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("peer_addr", |_, this, ()| {
|
||||
Ok(this.0.peer_addr()?.to_string())
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("peer_addr", |_, this, ()| Ok(this.0.peer_addr()?.to_string()));
|
||||
|
||||
methods.add_async_method_mut("read", |lua, mut this, size| async move {
|
||||
let mut buf = vec![0; size];
|
||||
let n = this.0.read(&mut buf).await?;
|
||||
buf.truncate(n);
|
||||
lua.create_string(&buf)
|
||||
});
|
||||
|
||||
methods.add_async_function(
|
||||
"read",
|
||||
|lua, (this, size): (AnyUserData, usize)| async move {
|
||||
let mut this = this.borrow_mut::<Self>()?;
|
||||
let mut buf = vec![0; size];
|
||||
let n = this.0.read(&mut buf).await?;
|
||||
buf.truncate(n);
|
||||
lua.create_string(&buf)
|
||||
},
|
||||
);
|
||||
methods.add_async_method_mut("write", |_, mut this, data: BString| async move {
|
||||
let n = this.0.write(&data).await?;
|
||||
Ok(n)
|
||||
});
|
||||
|
||||
methods.add_async_function(
|
||||
"write",
|
||||
|_, (this, data): (AnyUserData, LuaString)| async move {
|
||||
let mut this = this.borrow_mut::<Self>()?;
|
||||
let n = this.0.write(&data.as_bytes()).await?;
|
||||
Ok(n)
|
||||
},
|
||||
);
|
||||
|
||||
methods.add_async_function("close", |_, this: AnyUserData| async move {
|
||||
let mut this = this.borrow_mut::<Self>()?;
|
||||
methods.add_async_method_mut("close", |_, mut this, ()| async move {
|
||||
this.0.shutdown().await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_server(lua: Lua, handler: RegistryKey) -> io::Result<()> {
|
||||
async fn run_server(handler: Function) -> io::Result<()> {
|
||||
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
|
||||
let listener = TcpListener::bind(addr).await.expect("cannot bind addr");
|
||||
|
||||
println!("Listening on {}", addr);
|
||||
|
||||
let lua = Rc::new(lua);
|
||||
let handler = Rc::new(handler);
|
||||
loop {
|
||||
let (stream, _) = match listener.accept().await {
|
||||
Ok(res) => res,
|
||||
@@ -61,15 +44,10 @@ async fn run_server(lua: Lua, handler: RegistryKey) -> io::Result<()> {
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
let lua = lua.clone();
|
||||
let handler = handler.clone();
|
||||
task::spawn_local(async move {
|
||||
let handler: Function = lua
|
||||
.registry_value(&handler)
|
||||
.expect("cannot get Lua handler");
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
let stream = LuaTcpStream(stream);
|
||||
if let Err(err) = handler.call_async::<_, ()>(stream).await {
|
||||
if let Err(err) = handler.call_async::<()>(stream).await {
|
||||
eprintln!("{}", err);
|
||||
}
|
||||
});
|
||||
@@ -81,7 +59,7 @@ async fn main() {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Create Lua handler function
|
||||
let handler_fn = lua
|
||||
let handler = lua
|
||||
.load(chunk! {
|
||||
function(stream)
|
||||
local peer_addr = stream:peer_addr()
|
||||
@@ -103,15 +81,7 @@ async fn main() {
|
||||
.eval::<Function>()
|
||||
.expect("cannot create Lua handler");
|
||||
|
||||
// Store it in the Registry
|
||||
let handler = lua
|
||||
.create_registry_value(handler_fn)
|
||||
.expect("cannot store Lua handler");
|
||||
|
||||
task::LocalSet::new()
|
||||
.run_until(run_server(lua, handler))
|
||||
.await
|
||||
.expect("cannot run server")
|
||||
run_server(handler).await.expect("cannot run server")
|
||||
}
|
||||
|
||||
fn is_transient_error(e: &io::Error) -> bool {
|
||||
|
||||
+26
-19
@@ -1,7 +1,7 @@
|
||||
use std::f32;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use mlua::{chunk, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
|
||||
use mlua::{chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||
@@ -17,12 +17,12 @@ fn main() -> Result<()> {
|
||||
globals.set("string_var", "hello")?;
|
||||
globals.set("int_var", 42)?;
|
||||
|
||||
assert_eq!(globals.get::<_, String>("string_var")?, "hello");
|
||||
assert_eq!(globals.get::<_, i64>("int_var")?, 42);
|
||||
assert_eq!(globals.get::<String>("string_var")?, "hello");
|
||||
assert_eq!(globals.get::<i64>("int_var")?, 42);
|
||||
|
||||
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
|
||||
// that allows you to change settings before running Lua code. Here, we are using it to set
|
||||
// the name of the laoded chunk to "example code", which will be used when Lua error
|
||||
// the name of the loaded chunk to "example code", which will be used when Lua error
|
||||
// messages are printed.
|
||||
|
||||
lua.load(
|
||||
@@ -30,9 +30,9 @@ fn main() -> Result<()> {
|
||||
global = 'foo'..'bar'
|
||||
"#,
|
||||
)
|
||||
.set_name("example code")?
|
||||
.set_name("example code")
|
||||
.exec()?;
|
||||
assert_eq!(globals.get::<_, String>("global")?, "foobar");
|
||||
assert_eq!(globals.get::<String>("global")?, "foobar");
|
||||
|
||||
assert_eq!(lua.load("1 + 1").eval::<i32>()?, 2);
|
||||
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
|
||||
@@ -85,20 +85,20 @@ fn main() -> Result<()> {
|
||||
// You can load Lua functions
|
||||
|
||||
let print: Function = globals.get("print")?;
|
||||
print.call::<_, ()>("hello from rust")?;
|
||||
print.call::<()>("hello from rust")?;
|
||||
|
||||
// This API generally handles variadics using tuples. This is one way to call a function with
|
||||
// This API generally handles variadic using tuples. This is one way to call a function with
|
||||
// multiple parameters:
|
||||
|
||||
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
|
||||
print.call::<()>(("hello", "again", "from", "rust"))?;
|
||||
|
||||
// But, you can also pass variadic arguments with the `Variadic` type.
|
||||
|
||||
print.call::<_, ()>(Variadic::from_iter(
|
||||
print.call::<()>(Variadic::from_iter(
|
||||
["hello", "yet", "again", "from", "rust"].iter().cloned(),
|
||||
))?;
|
||||
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state itself as their
|
||||
// first parameter, and the arguments given to the function as the second parameter. The type
|
||||
// of the arguments can be anything that is convertible from the parameters given by Lua, in
|
||||
// this case, the function expects two string sequences.
|
||||
@@ -151,8 +151,18 @@ fn main() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
struct Vec2(f32, f32);
|
||||
|
||||
// We can implement `FromLua` trait for our `Vec2` to return a copy
|
||||
impl FromLua for Vec2 {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Vec2 {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("magnitude", |_, vec, ()| {
|
||||
let mag_squared = vec.0 * vec.0 + vec.1 * vec.1;
|
||||
Ok(mag_squared.sqrt())
|
||||
@@ -167,19 +177,15 @@ fn main() -> Result<()> {
|
||||
let vec2_constructor = lua.create_function(|_, (x, y): (f32, f32)| Ok(Vec2(x, y)))?;
|
||||
globals.set("vec2", vec2_constructor)?;
|
||||
|
||||
assert!(
|
||||
(lua.load("(vec2(1, 2) + vec2(2, 2)):magnitude()")
|
||||
.eval::<f32>()?
|
||||
- 5.0)
|
||||
.abs()
|
||||
< f32::EPSILON
|
||||
);
|
||||
assert!((lua.load("(vec2(1, 2) + vec2(2, 2)):magnitude()").eval::<f32>()? - 5.0).abs() < f32::EPSILON);
|
||||
|
||||
// Normally, Rust types passed to `Lua` must be `'static`, because there is no way to be
|
||||
// sure of their lifetime inside the Lua state. There is, however, a limited way to lift this
|
||||
// requirement. You can call `Lua::scope` to create userdata and callbacks types that only live
|
||||
// for as long as the call to scope, but do not have to be `'static` (and `Send`).
|
||||
|
||||
// TODO: Re-enable this
|
||||
/*
|
||||
{
|
||||
let mut rust_val = 0;
|
||||
|
||||
@@ -201,6 +207,7 @@ fn main() -> Result<()> {
|
||||
|
||||
assert_eq!(rust_val, 42);
|
||||
}
|
||||
*/
|
||||
|
||||
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
|
||||
// try to run our 'sketchy' function outside of the scope, the function we created will have
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "rust_module"
|
||||
version = "0.0.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
//! This example shows a simple read-evaluate-print-loop (REPL).
|
||||
|
||||
use mlua::{Error, Lua, MultiValue};
|
||||
use rustyline::Editor;
|
||||
use rustyline::DefaultEditor;
|
||||
|
||||
fn main() {
|
||||
let lua = Lua::new();
|
||||
let mut editor = Editor::<()>::new().expect("Failed to make rustyline editor");
|
||||
let mut editor = DefaultEditor::new().expect("Failed to create editor");
|
||||
|
||||
loop {
|
||||
let mut prompt = "> ";
|
||||
@@ -19,12 +19,12 @@ fn main() {
|
||||
|
||||
match lua.load(&line).eval::<MultiValue>() {
|
||||
Ok(values) => {
|
||||
editor.add_history_entry(line);
|
||||
editor.add_history_entry(line).unwrap();
|
||||
println!(
|
||||
"{}",
|
||||
values
|
||||
.iter()
|
||||
.map(|value| format!("{:?}", value))
|
||||
.map(|value| format!("{:#?}", value))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\t")
|
||||
);
|
||||
|
||||
@@ -28,9 +28,14 @@ fn main() -> Result<()> {
|
||||
let globals = lua.globals();
|
||||
|
||||
// Create Car struct from a Lua table
|
||||
let car: Car = lua.from_value(lua.load(r#"
|
||||
let car: Car = lua.from_value(
|
||||
lua.load(
|
||||
r#"
|
||||
{active = true, model = "Volkswagen Golf", transmission = "Automatic", engine = {v = 1499, kw = 90}}
|
||||
"#).eval()?)?;
|
||||
"#,
|
||||
)
|
||||
.eval()?,
|
||||
)?;
|
||||
|
||||
// Set it as (serializable) userdata
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
@@ -7,7 +7,7 @@ struct Rectangle {
|
||||
}
|
||||
|
||||
impl UserData for Rectangle {
|
||||
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("length", |_, this| Ok(this.length));
|
||||
fields.add_field_method_set("length", |_, this, val| {
|
||||
this.length = val;
|
||||
@@ -20,7 +20,7 @@ impl UserData for Rectangle {
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("area", |_, this, ()| Ok(this.length * this.width));
|
||||
methods.add_method("diagonal", |_, this, ()| {
|
||||
Ok((this.length.pow(2) as f64 + this.width.pow(2) as f64).sqrt())
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.6.8"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua-sys"
|
||||
readme = "README.md"
|
||||
categories = ["external-ffi-bindings"]
|
||||
license = "MIT"
|
||||
links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua54", "vendored"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
lua54 = []
|
||||
lua53 = []
|
||||
lua52 = []
|
||||
lua51 = []
|
||||
luajit = []
|
||||
luajit52 = ["luajit"]
|
||||
luau = ["luau0-src"]
|
||||
luau-codegen = ["luau"]
|
||||
luau-vector4 = ["luau"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
module = []
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 547.0.0, < 547.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true }
|
||||
luau0-src = { version = "0.12.0", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
@@ -0,0 +1,8 @@
|
||||
# mlua-sys
|
||||
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and [Luau].
|
||||
|
||||
Intended to be consumed by the [mlua] crate.
|
||||
|
||||
[Luau]: https://github.com/luau-lang/luau
|
||||
[mlua]: https://crates.io/crates/mlua
|
||||
@@ -0,0 +1,68 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::env;
|
||||
use std::ops::Bound;
|
||||
|
||||
pub fn probe_lua() {
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
|
||||
if target_arch == "wasm32" && cfg!(not(feature = "vendored")) {
|
||||
panic!("Please enable `vendored` feature to build for wasm32");
|
||||
}
|
||||
|
||||
let lib_dir = env::var("LUA_LIB").unwrap_or_default();
|
||||
let lua_lib = env::var("LUA_LIB_NAME").unwrap_or_default();
|
||||
|
||||
println!("cargo:rerun-if-env-changed=LUA_LIB");
|
||||
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
|
||||
println!("cargo:rerun-if-env-changed=LUA_LINK");
|
||||
|
||||
if !lua_lib.is_empty() {
|
||||
if !lib_dir.is_empty() {
|
||||
println!("cargo:rustc-link-search=native={lib_dir}");
|
||||
}
|
||||
let mut link_lib = "";
|
||||
if env::var("LUA_LINK").as_deref() == Ok("static") {
|
||||
link_lib = "static=";
|
||||
};
|
||||
println!("cargo:rustc-link-lib={link_lib}{lua_lib}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find using `pkg-config`
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.4", "5.5", ["lua5.4", "lua-5.4", "lua54"], "5.4");
|
||||
#[cfg(feature = "lua53")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.3", "5.4", ["lua5.3", "lua-5.3", "lua53"], "5.3");
|
||||
#[cfg(feature = "lua52")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.2", "5.3", ["lua5.2", "lua-5.2", "lua52"], "5.2");
|
||||
#[cfg(feature = "lua51")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.1", "5.2", ["lua5.1", "lua-5.1", "lua51"], "5.1");
|
||||
#[cfg(feature = "luajit")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", [], "JIT");
|
||||
|
||||
#[rustfmt::skip]
|
||||
let mut lua = pkg_config::Config::new()
|
||||
.range_version((Bound::Included(incl_bound), Bound::Excluded(excl_bound)))
|
||||
.cargo_metadata(true)
|
||||
.probe(if cfg!(feature = "luajit") { "luajit" } else { "lua" });
|
||||
|
||||
if lua.is_err() {
|
||||
for pkg in alt_probe {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(true)
|
||||
.probe(pkg);
|
||||
|
||||
if lua.is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lua.unwrap_or_else(|err| panic!("cannot find Lua{ver} using `pkg-config`: {err}"));
|
||||
}
|
||||
@@ -1,28 +1,29 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
pub fn probe_lua() {
|
||||
#[cfg(feature = "lua54")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
|
||||
|
||||
#[cfg(feature = "lua53")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua53);
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua52);
|
||||
|
||||
#[cfg(feature = "lua51")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua51);
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
let artifacts = {
|
||||
let mut builder = luajit_src::Build::new();
|
||||
if cfg!(feature = "luajit52") {
|
||||
builder.lua52compat(true);
|
||||
}
|
||||
builder.build()
|
||||
};
|
||||
let artifacts = luajit_src::Build::new()
|
||||
.lua52compat(cfg!(feature = "luajit52"))
|
||||
.build();
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
let artifacts = luau0_src::Build::new().build();
|
||||
let artifacts = luau0_src::Build::new()
|
||||
.enable_codegen(cfg!(feature = "luau-codegen"))
|
||||
.set_max_cstack_size(1000000)
|
||||
.set_vector_size(if cfg!(feature = "luau-vector4") { 4 } else { 3 })
|
||||
.build();
|
||||
|
||||
artifacts.print_cargo_metadata();
|
||||
|
||||
Some(artifacts.include_dir().to_owned())
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "lua54", not(any(feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "lua53", not(any(feature = "lua54", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "lua52", not(any(feature = "lua54", feature = "lua53", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "lua51", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "luajit", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "luau", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else {
|
||||
fn main() {
|
||||
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use std::env;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(feature = "luau", feature = "vendored"))] {
|
||||
#[path = "find_vendored.rs"]
|
||||
mod find;
|
||||
} else {
|
||||
#[path = "find_normal.rs"]
|
||||
mod find;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(all(feature = "luau", feature = "module", windows))]
|
||||
compile_error!("Luau does not support `module` mode on Windows");
|
||||
|
||||
#[cfg(all(feature = "module", feature = "vendored"))]
|
||||
compile_error!("`vendored` and `module` features are mutually exclusive");
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
if target_os == "windows" && cfg!(feature = "module") {
|
||||
if !std::env::var("LUA_LIB_NAME").unwrap_or_default().is_empty() {
|
||||
// Don't use raw-dylib linking
|
||||
find::probe_lua();
|
||||
return;
|
||||
}
|
||||
|
||||
println!("cargo:rustc-cfg=raw_dylib");
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "module"))]
|
||||
find::probe_lua();
|
||||
}
|
||||
@@ -1,103 +1,105 @@
|
||||
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 including LuaJIT.
|
||||
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau.
|
||||
|
||||
#![allow(non_camel_case_types, non_snake_case, dead_code)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua54", doc))]
|
||||
pub use lua54::*;
|
||||
|
||||
#[cfg(feature = "lua53")]
|
||||
#[cfg(any(feature = "lua53", doc))]
|
||||
pub use lua53::*;
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[cfg(any(feature = "lua52", doc))]
|
||||
pub use lua52::*;
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
|
||||
pub use lua51::*;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
pub use luau::*;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[doc(hidden)]
|
||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
||||
|
||||
#[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "vendored"))))]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[doc(hidden)]
|
||||
pub const LUA_MAX_UPVALUES: c_int = 60;
|
||||
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
pub const LUA_MAX_UPVALUES: c_int = 120;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(hidden)]
|
||||
pub const LUA_MAX_UPVALUES: c_int = 200;
|
||||
|
||||
// I believe `luaL_traceback` < 5.4 requires this much free stack to not error.
|
||||
// 5.4 uses `luaL_Buffer`
|
||||
#[doc(hidden)]
|
||||
pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/common/alloc.rs
|
||||
// The minimum alignment guaranteed by the architecture. This value is used to
|
||||
// add fast paths for low alignment values.
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
|
||||
#[cfg(all(any(
|
||||
#[cfg(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "m68k",
|
||||
target_arch = "csky",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips32r6",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "sparc",
|
||||
target_arch = "asmjs",
|
||||
target_arch = "wasm32",
|
||||
target_arch = "hexagon",
|
||||
all(target_arch = "riscv32", not(target_os = "espidf")),
|
||||
all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))),
|
||||
all(target_arch = "xtensa", not(target_os = "espidf")),
|
||||
)))]
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(all(any(
|
||||
#[cfg(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm64ec",
|
||||
target_arch = "loongarch64",
|
||||
target_arch = "mips64",
|
||||
target_arch = "mips64r6",
|
||||
target_arch = "s390x",
|
||||
target_arch = "sparc64",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "wasm64",
|
||||
)))]
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 16;
|
||||
// The allocator on the esp-idf platform guarentees 4 byte alignment.
|
||||
#[cfg(all(any(
|
||||
all(target_arch = "riscv32", target_os = "espidf"),
|
||||
// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment.
|
||||
#[cfg(any(
|
||||
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
|
||||
all(target_arch = "xtensa", target_os = "espidf"),
|
||||
)))]
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 4;
|
||||
|
||||
// Hack to avoid stripping a few unused Lua symbols that could be imported
|
||||
// by C modules in unsafe mode
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) fn keep_lua_symbols() {
|
||||
let mut symbols: Vec<*const extern "C" fn()> = Vec::new();
|
||||
symbols.push(lua_atpanic as _);
|
||||
symbols.push(lua_isuserdata as _);
|
||||
symbols.push(lua_tocfunction as _);
|
||||
symbols.push(luaL_loadstring as _);
|
||||
symbols.push(luaL_openlibs as _);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
symbols.push(lua_getglobal as _);
|
||||
symbols.push(lua_setglobal as _);
|
||||
symbols.push(luaL_setfuncs as _);
|
||||
}
|
||||
}
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua54", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
pub mod lua54;
|
||||
|
||||
#[cfg(feature = "lua53")]
|
||||
#[cfg(any(feature = "lua53", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua53")))]
|
||||
pub mod lua53;
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[cfg(any(feature = "lua52", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua52")))]
|
||||
pub mod lua52;
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua51", feature = "luajit"))))]
|
||||
pub mod lua51;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub mod luau;
|
||||
@@ -2,10 +2,8 @@
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use super::lauxlib::*;
|
||||
use super::lua::*;
|
||||
@@ -178,7 +176,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
||||
let abs_to = lua_absindex(L, toidx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_pushvalue(L, fromidx);
|
||||
lua_replace(L, abs_to);
|
||||
}
|
||||
@@ -316,7 +314,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_pushlightuserdata(L, p as *mut c_void);
|
||||
lua_insert(L, -2);
|
||||
lua_rawset(L, abs_i);
|
||||
@@ -329,12 +327,7 @@ pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
data: *mut c_void,
|
||||
_strip: c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
|
||||
lua_dump_(L, writer, data)
|
||||
}
|
||||
|
||||
@@ -366,12 +359,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
_from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
|
||||
let ret = lua_resume_(L, narg);
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
@@ -415,6 +403,25 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
if env != 0 {
|
||||
env = lua_absindex(L, env);
|
||||
}
|
||||
let status = luaL_loadbufferx(L, data, size, name, mode);
|
||||
if status == LUA_OK && env != 0 {
|
||||
lua_pushvalue(L, env);
|
||||
lua_setfenv(L, -2);
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
@@ -437,7 +444,7 @@ pub unsafe fn luaL_loadbufferx(
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
let mut isnum = 0;
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_len(L, idx);
|
||||
let res = lua_tointegerx(L, -1, &mut isnum);
|
||||
lua_pop(L, 1);
|
||||
@@ -447,12 +454,7 @@ pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
res
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_traceback(
|
||||
L: *mut lua_State,
|
||||
L1: *mut lua_State,
|
||||
msg: *const c_char,
|
||||
mut level: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
let top = lua_gettop(L);
|
||||
let numlevels = compat53_countlevels(L1);
|
||||
@@ -486,10 +488,10 @@ pub unsafe fn luaL_traceback(
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||
idx = lua_absindex(L, idx);
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
let t = lua_type(L, idx);
|
||||
match t {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
}
|
||||
@@ -503,7 +505,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_pushliteral(L, "true");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
@@ -512,7 +514,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2);
|
||||
lua_replace(L, -2); // remove '__name'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -524,14 +526,14 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
luaL_getmetatable(L, tname);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
lua_pushstring_(L, fname);
|
||||
if lua_gettable(L, abs_i) == LUA_TTABLE {
|
||||
return 1;
|
||||
@@ -544,14 +546,9 @@ pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_ch
|
||||
0
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, openf);
|
||||
@@ -8,13 +8,17 @@ use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
||||
// Extra error code for 'luaL_load'
|
||||
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
|
||||
|
||||
// Key, in the registry, for table of loaded modules
|
||||
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
pub name: *const c_char,
|
||||
pub func: lua_CFunction,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg);
|
||||
#[link_name = "luaL_getmetafield"]
|
||||
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
@@ -42,7 +46,7 @@ extern "C" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
@@ -56,17 +60,13 @@ extern "C" {
|
||||
pub const LUA_NOREF: c_int = -2;
|
||||
pub const LUA_REFNIL: c_int = -1;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
pub fn luaL_loadfile(L: *mut lua_State, filename: *const c_char) -> c_int;
|
||||
pub fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
sz: usize,
|
||||
name: *const c_char,
|
||||
) -> c_int;
|
||||
pub fn luaL_loadbuffer(L: *mut lua_State, buff: *const c_char, sz: usize, name: *const c_char) -> c_int;
|
||||
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
|
||||
|
||||
pub fn luaL_newstate() -> *mut lua_State;
|
||||
@@ -73,23 +73,23 @@ pub type lua_Integer = i32;
|
||||
pub type lua_Integer = i64;
|
||||
|
||||
/// Type for native C functions that can be passed to Lua.
|
||||
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
|
||||
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
|
||||
|
||||
// Type for functions that read/write blocks when loading/dumping Lua chunks
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Reader =
|
||||
unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Writer =
|
||||
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
|
||||
/// Type for memory-allocation functions
|
||||
pub type lua_Alloc = unsafe extern "C" fn(
|
||||
ud: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
nsize: usize,
|
||||
) -> *mut c_void;
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
@@ -97,9 +97,6 @@ extern "C" {
|
||||
pub fn lua_close(L: *mut lua_State);
|
||||
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
pub fn lua_resetthread(L: *mut lua_State, th: *mut lua_State);
|
||||
|
||||
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
|
||||
|
||||
//
|
||||
@@ -221,21 +218,33 @@ pub const LUA_GCSTEP: c_int = 5;
|
||||
pub const LUA_GCSETPAUSE: c_int = 6;
|
||||
pub const LUA_GCSETSTEPMUL: c_int = 7;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
|
||||
}
|
||||
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
extern "C" {
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
|
||||
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -319,6 +328,14 @@ pub unsafe fn lua_getglobal_(L: *mut lua_State, var: *const c_char) {
|
||||
lua_getfield_(L, LUA_GLOBALSINDEX, var)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
@@ -351,9 +368,10 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
|
||||
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
|
||||
|
||||
/// Type for functions to be called on debug events.
|
||||
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
|
||||
@@ -361,12 +379,7 @@ extern "C" {
|
||||
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
|
||||
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
|
||||
|
||||
pub fn lua_sethook(
|
||||
L: *mut lua_State,
|
||||
func: Option<lua_Hook>,
|
||||
mask: c_int,
|
||||
count: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int) -> c_int;
|
||||
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
|
||||
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
|
||||
@@ -20,7 +20,8 @@ pub const LUA_JITLIBNAME: &str = "jit";
|
||||
#[cfg(feature = "luajit")]
|
||||
pub const LUA_FFILIBNAME: &str = "ffi";
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_io(L: *mut lua_State) -> c_int;
|
||||
@@ -2,7 +2,6 @@
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
|
||||
@@ -158,22 +157,12 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
data: *mut c_void,
|
||||
_strip: c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
|
||||
lua_dump_(L, writer, data)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
|
||||
let ret = lua_resume_(L, from, narg);
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
@@ -205,10 +194,10 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||
idx = lua_absindex(L, idx);
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
let t = lua_type(L, idx);
|
||||
match t {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
}
|
||||
@@ -222,7 +211,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_pushliteral(L, "true");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
@@ -231,7 +220,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2);
|
||||
lua_replace(L, -2); // remove '__name'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -241,14 +230,9 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_tolstring(L, -1, len)
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, openf);
|
||||
@@ -263,3 +247,22 @@ pub unsafe fn luaL_requiref(
|
||||
}
|
||||
lua_replace(L, -2);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
if env != 0 {
|
||||
env = lua_absindex(L, env);
|
||||
}
|
||||
let status = luaL_loadbufferx(L, data, size, name, mode);
|
||||
if status == LUA_OK && env != 0 {
|
||||
lua_pushvalue(L, env);
|
||||
lua_setupvalue(L, -2, 1);
|
||||
}
|
||||
status
|
||||
}
|
||||
@@ -8,13 +8,20 @@ use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Un
|
||||
// Extra error code for 'luaL_load'
|
||||
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
|
||||
|
||||
// Key, in the registry, for table of loaded modules
|
||||
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
|
||||
|
||||
// Key, in the registry, for table of preloaded loaders
|
||||
pub const LUA_PRELOAD_TABLE: *const c_char = cstr!("_PRELOAD");
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
pub name: *const c_char,
|
||||
pub func: lua_CFunction,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number);
|
||||
|
||||
#[link_name = "luaL_getmetafield"]
|
||||
@@ -24,12 +31,8 @@ extern "C" {
|
||||
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||
pub fn luaL_optlstring(
|
||||
L: *mut lua_State,
|
||||
arg: c_int,
|
||||
def: *const c_char,
|
||||
l: *mut usize,
|
||||
) -> *const c_char;
|
||||
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||
-> *const c_char;
|
||||
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||
@@ -48,7 +51,7 @@ extern "C" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
@@ -65,12 +68,12 @@ extern "C" {
|
||||
pub const LUA_NOREF: c_int = -2;
|
||||
pub const LUA_REFNIL: c_int = -1;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
|
||||
-> c_int;
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -78,7 +81,8 @@ pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
|
||||
luaL_loadfilex(L, f, ptr::null())
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
@@ -106,12 +110,7 @@ extern "C" {
|
||||
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
|
||||
|
||||
#[link_name = "luaL_requiref"]
|
||||
pub fn luaL_requiref_(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
);
|
||||
pub fn luaL_requiref_(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -170,12 +169,7 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
s: *const c_char,
|
||||
sz: usize,
|
||||
n: *const c_char,
|
||||
) -> c_int {
|
||||
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||
}
|
||||
|
||||
@@ -78,23 +78,23 @@ pub type lua_Integer = i64;
|
||||
pub type lua_Unsigned = c_uint;
|
||||
|
||||
/// Type for native C functions that can be passed to Lua
|
||||
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
|
||||
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
|
||||
|
||||
// Type for functions that read/write blocks when loading/dumping Lua chunks
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Reader =
|
||||
unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Writer =
|
||||
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
|
||||
/// Type for memory-allocation functions
|
||||
pub type lua_Alloc = unsafe extern "C" fn(
|
||||
ud: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
nsize: usize,
|
||||
) -> *mut c_void;
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
@@ -155,20 +155,19 @@ pub const LUA_OPMOD: c_int = 4;
|
||||
pub const LUA_OPPOW: c_int = 5;
|
||||
pub const LUA_OPUNM: c_int = 6;
|
||||
|
||||
extern "C" {
|
||||
pub fn lua_arith(L: *mut lua_State, op: c_int);
|
||||
}
|
||||
|
||||
pub const LUA_OPEQ: c_int = 0;
|
||||
pub const LUA_OPLT: c_int = 1;
|
||||
pub const LUA_OPLE: c_int = 2;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_arith(L: *mut lua_State, op: c_int);
|
||||
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
|
||||
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// Push functions (C -> stack)
|
||||
//
|
||||
@@ -223,13 +222,7 @@ extern "C" {
|
||||
//
|
||||
// 'load' and 'call' functions (load and run Lua code)
|
||||
//
|
||||
pub fn lua_callk(
|
||||
L: *mut lua_State,
|
||||
nargs: c_int,
|
||||
nresults: c_int,
|
||||
ctx: c_int,
|
||||
k: Option<lua_CFunction>,
|
||||
);
|
||||
pub fn lua_callk(L: *mut lua_State, nargs: c_int, nresults: c_int, ctx: c_int, k: Option<lua_CFunction>);
|
||||
pub fn lua_pcallk(
|
||||
L: *mut lua_State,
|
||||
nargs: c_int,
|
||||
@@ -262,16 +255,12 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
|
||||
lua_pcallk(L, n, r, f, 0, None)
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// Coroutine functions
|
||||
//
|
||||
pub fn lua_yieldk(
|
||||
L: *mut lua_State,
|
||||
nresults: c_int,
|
||||
ctx: c_int,
|
||||
k: Option<lua_CFunction>,
|
||||
) -> c_int;
|
||||
pub fn lua_yieldk(L: *mut lua_State, nresults: c_int, ctx: c_int, k: Option<lua_CFunction>) -> c_int;
|
||||
#[link_name = "lua_resume"]
|
||||
pub fn lua_resume_(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int;
|
||||
pub fn lua_status(L: *mut lua_State) -> c_int;
|
||||
@@ -298,15 +287,18 @@ pub const LUA_GCISRUNNING: c_int = 9;
|
||||
pub const LUA_GCGEN: c_int = 10;
|
||||
pub const LUA_GCINC: c_int = 11;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||
@@ -314,6 +306,15 @@ extern "C" {
|
||||
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -405,6 +406,14 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
||||
lua_rawgeti_(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS as _)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
@@ -437,9 +446,10 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
|
||||
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
|
||||
|
||||
/// Type for functions to be called on debug events.
|
||||
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
|
||||
@@ -450,7 +460,7 @@ extern "C" {
|
||||
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
|
||||
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
|
||||
|
||||
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int);
|
||||
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int) -> c_int;
|
||||
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
|
||||
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
|
||||
@@ -14,7 +14,8 @@ pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_LOADLIBNAME: &str = "package";
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
@@ -0,0 +1,34 @@
|
||||
//! MLua compatibility layer for Lua 5.3
|
||||
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use super::lauxlib::*;
|
||||
use super::lua::*;
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
|
||||
let ret = lua_resume_(L, from, narg);
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
if env != 0 {
|
||||
env = lua_absindex(L, env);
|
||||
}
|
||||
let status = luaL_loadbufferx(L, data, size, name, mode);
|
||||
if status == LUA_OK && env != 0 {
|
||||
lua_pushvalue(L, env);
|
||||
lua_setupvalue(L, -2, 1);
|
||||
}
|
||||
status
|
||||
}
|
||||
@@ -20,20 +20,18 @@ pub struct luaL_Reg {
|
||||
pub func: lua_CFunction,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
|
||||
|
||||
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
#[link_name = "luaL_tolstring"]
|
||||
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||
pub fn luaL_optlstring(
|
||||
L: *mut lua_State,
|
||||
arg: c_int,
|
||||
def: *const c_char,
|
||||
l: *mut usize,
|
||||
) -> *const c_char;
|
||||
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||
-> *const c_char;
|
||||
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||
@@ -49,7 +47,7 @@ extern "C" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
@@ -66,12 +64,12 @@ extern "C" {
|
||||
pub const LUA_NOREF: c_int = -2;
|
||||
pub const LUA_REFNIL: c_int = -1;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
|
||||
-> c_int;
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -79,7 +77,8 @@ pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
|
||||
luaL_loadfilex(L, f, ptr::null())
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
@@ -93,8 +92,6 @@ extern "C" {
|
||||
|
||||
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
|
||||
|
||||
// TODO: luaL_addgsub
|
||||
|
||||
pub fn luaL_gsub(
|
||||
L: *mut lua_State,
|
||||
s: *const c_char,
|
||||
@@ -108,12 +105,7 @@ extern "C" {
|
||||
|
||||
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
|
||||
|
||||
pub fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
);
|
||||
pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -169,15 +161,15 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
||||
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
luaL_tolstring_(L, lua::lua_absindex(L, idx), len)
|
||||
}
|
||||
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
s: *const c_char,
|
||||
sz: usize,
|
||||
n: *const c_char,
|
||||
) -> c_int {
|
||||
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
// Mark for precompiled code (`<esc>Lua`)
|
||||
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
|
||||
@@ -82,27 +81,27 @@ pub type lua_Unsigned = u64;
|
||||
pub type lua_KContext = isize;
|
||||
|
||||
/// Type for native C functions that can be passed to Lua
|
||||
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
|
||||
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
|
||||
|
||||
/// Type for continuation functions
|
||||
pub type lua_KFunction =
|
||||
unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
|
||||
|
||||
// Type for functions that read/write blocks when loading/dumping Lua chunks
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Reader =
|
||||
unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Writer =
|
||||
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
|
||||
/// Type for memory-allocation functions
|
||||
pub type lua_Alloc = unsafe extern "C" fn(
|
||||
ud: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
nsize: usize,
|
||||
) -> *mut c_void;
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
@@ -167,20 +166,19 @@ pub const LUA_OPSHR: c_int = 11;
|
||||
pub const LUA_OPUNM: c_int = 12;
|
||||
pub const LUA_OPBNOT: c_int = 13;
|
||||
|
||||
extern "C" {
|
||||
pub fn lua_arith(L: *mut lua_State, op: c_int);
|
||||
}
|
||||
|
||||
pub const LUA_OPEQ: c_int = 0;
|
||||
pub const LUA_OPLT: c_int = 1;
|
||||
pub const LUA_OPLE: c_int = 2;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_arith(L: *mut lua_State, op: c_int);
|
||||
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
|
||||
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// Push functions (C -> stack)
|
||||
//
|
||||
@@ -252,12 +250,7 @@ extern "C" {
|
||||
mode: *const c_char,
|
||||
) -> c_int;
|
||||
|
||||
pub fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
data: *mut c_void,
|
||||
strip: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, strip: c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -270,7 +263,8 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
|
||||
lua_pcallk(L, n, r, f, 0, None)
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// Coroutine functions
|
||||
//
|
||||
@@ -304,15 +298,18 @@ pub const LUA_GCSETPAUSE: c_int = 6;
|
||||
pub const LUA_GCSETSTEPMUL: c_int = 7;
|
||||
pub const LUA_GCISRUNNING: c_int = 9;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||
@@ -321,6 +318,15 @@ extern "C" {
|
||||
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -412,6 +418,14 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
@@ -461,9 +475,10 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
|
||||
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
|
||||
|
||||
/// Type for functions to be called on debug events.
|
||||
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
|
||||
@@ -15,7 +15,8 @@ pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_LOADLIBNAME: &str = "package";
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
@@ -20,7 +20,8 @@ pub struct luaL_Reg {
|
||||
pub func: lua_CFunction,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
|
||||
|
||||
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
@@ -28,12 +29,8 @@ extern "C" {
|
||||
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||
pub fn luaL_optlstring(
|
||||
L: *mut lua_State,
|
||||
arg: c_int,
|
||||
def: *const c_char,
|
||||
l: *mut usize,
|
||||
) -> *const c_char;
|
||||
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||
-> *const c_char;
|
||||
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||
@@ -49,7 +46,7 @@ extern "C" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
@@ -66,12 +63,12 @@ extern "C" {
|
||||
pub const LUA_NOREF: c_int = -2;
|
||||
pub const LUA_REFNIL: c_int = -1;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
|
||||
-> c_int;
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -79,7 +76,8 @@ pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
|
||||
luaL_loadfilex(L, f, ptr::null())
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
@@ -93,6 +91,8 @@ extern "C" {
|
||||
|
||||
pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;
|
||||
|
||||
// TODO: luaL_addgsub
|
||||
|
||||
pub fn luaL_gsub(
|
||||
L: *mut lua_State,
|
||||
s: *const c_char,
|
||||
@@ -106,12 +106,7 @@ extern "C" {
|
||||
|
||||
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
|
||||
|
||||
pub fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
);
|
||||
pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -170,15 +165,29 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
s: *const c_char,
|
||||
sz: usize,
|
||||
n: *const c_char,
|
||||
) -> c_int {
|
||||
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
if env != 0 {
|
||||
env = lua::lua_absindex(L, env);
|
||||
}
|
||||
let status = luaL_loadbufferx(L, data, size, name, mode);
|
||||
if status == lua::LUA_OK && env != 0 {
|
||||
lua::lua_pushvalue(L, env);
|
||||
lua::lua_setupvalue(L, -2, 1);
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Generic Buffer Manipulation
|
||||
//
|
||||
@@ -1,9 +1,8 @@
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_ushort, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
// Mark for precompiled code (`<esc>Lua`)
|
||||
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
|
||||
@@ -81,38 +80,40 @@ pub type lua_Unsigned = u64;
|
||||
pub type lua_KContext = isize;
|
||||
|
||||
/// Type for native C functions that can be passed to Lua
|
||||
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
|
||||
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
|
||||
|
||||
/// Type for continuation functions
|
||||
pub type lua_KFunction =
|
||||
unsafe extern "C" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int;
|
||||
|
||||
// Type for functions that read/write blocks when loading/dumping Lua chunks
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Reader =
|
||||
unsafe extern "C" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Writer =
|
||||
unsafe extern "C" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
|
||||
/// Type for memory-allocation functions
|
||||
pub type lua_Alloc = unsafe extern "C" fn(
|
||||
ud: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
nsize: usize,
|
||||
) -> *mut c_void;
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
/// Type for warning functions
|
||||
pub type lua_WarnFunction =
|
||||
unsafe extern "C" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
|
||||
pub type lua_WarnFunction = unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
|
||||
pub fn lua_close(L: *mut lua_State);
|
||||
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||
// Deprecated in Lua 5.4.6
|
||||
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
|
||||
#[cfg(feature = "vendored")]
|
||||
pub fn lua_closethread(L: *mut lua_State, from: *mut lua_State) -> c_int;
|
||||
|
||||
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
|
||||
|
||||
@@ -146,13 +147,21 @@ extern "C" {
|
||||
pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
|
||||
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize;
|
||||
#[link_name = "lua_rawlen"]
|
||||
fn lua_rawlen_(L: *mut lua_State, idx: c_int) -> lua_Unsigned;
|
||||
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
||||
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
|
||||
// lua_rawlen's return type changed from size_t to lua_Unsigned int in Lua 5.4.
|
||||
// This adapts the crate API to the new Lua ABI.
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
|
||||
lua_rawlen_(L, idx) as usize
|
||||
}
|
||||
|
||||
//
|
||||
// Comparison and arithmetic functions
|
||||
//
|
||||
@@ -171,20 +180,19 @@ pub const LUA_OPSHR: c_int = 11;
|
||||
pub const LUA_OPUNM: c_int = 12;
|
||||
pub const LUA_OPBNOT: c_int = 13;
|
||||
|
||||
extern "C" {
|
||||
pub fn lua_arith(L: *mut lua_State, op: c_int);
|
||||
}
|
||||
|
||||
pub const LUA_OPEQ: c_int = 0;
|
||||
pub const LUA_OPLT: c_int = 1;
|
||||
pub const LUA_OPLE: c_int = 2;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_arith(L: *mut lua_State, op: c_int);
|
||||
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
|
||||
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// Push functions (C -> stack)
|
||||
//
|
||||
@@ -256,12 +264,7 @@ extern "C" {
|
||||
mode: *const c_char,
|
||||
) -> c_int;
|
||||
|
||||
pub fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
data: *mut c_void,
|
||||
strip: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, strip: c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -274,7 +277,8 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
|
||||
lua_pcallk(L, n, r, f, 0, None)
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// Coroutine functions
|
||||
//
|
||||
@@ -284,12 +288,7 @@ extern "C" {
|
||||
ctx: lua_KContext,
|
||||
k: Option<lua_KFunction>,
|
||||
) -> c_int;
|
||||
pub fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int;
|
||||
pub fn lua_status(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
|
||||
}
|
||||
@@ -302,7 +301,8 @@ pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
|
||||
//
|
||||
// Warning-related functions
|
||||
//
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_setwarnf(L: *mut lua_State, f: Option<lua_WarnFunction>, ud: *mut c_void);
|
||||
pub fn lua_warning(L: *mut lua_State, msg: *const c_char, tocont: c_int);
|
||||
}
|
||||
@@ -322,15 +322,18 @@ pub const LUA_GCISRUNNING: c_int = 9;
|
||||
pub const LUA_GCGEN: c_int = 10;
|
||||
pub const LUA_GCINC: c_int = 11;
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, ...) -> c_int;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||
@@ -342,6 +345,15 @@ extern "C" {
|
||||
pub fn lua_closeslot(L: *mut lua_State, idx: c_int);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -433,6 +445,14 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
@@ -497,9 +517,10 @@ pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
|
||||
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
|
||||
|
||||
/// Type for functions to be called on debug events.
|
||||
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
|
||||
@@ -14,7 +14,8 @@ pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_LOADLIBNAME: &str = "package";
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
@@ -1,11 +1,10 @@
|
||||
//! MLua compatibility layer for Roblox Luau.
|
||||
//! MLua compatibility layer for Luau.
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use super::lauxlib::*;
|
||||
use super::lua::*;
|
||||
@@ -53,11 +52,7 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
||||
0 // not found
|
||||
}
|
||||
|
||||
unsafe fn compat53_pushglobalfuncname(
|
||||
L: *mut lua_State,
|
||||
level: c_int,
|
||||
ar: *mut lua_Debug,
|
||||
) -> c_int {
|
||||
unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int {
|
||||
let top = lua_gettop(L);
|
||||
// push function
|
||||
lua_getinfo(L, level, cstr!("f"), ar);
|
||||
@@ -113,7 +108,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
||||
let abs_to = lua_absindex(L, toidx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_pushvalue(L, fromidx);
|
||||
lua_replace(L, abs_to);
|
||||
}
|
||||
@@ -222,7 +217,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_pushlightuserdata(L, p as *mut c_void);
|
||||
lua_insert(L, -2);
|
||||
lua_rawset(L, abs_i);
|
||||
@@ -281,12 +276,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
|
||||
let ret = lua_resume_(L, from, narg);
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
@@ -323,57 +313,72 @@ pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char)
|
||||
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
|
||||
if luaL_newmetatable_(L, tname) != 0 {
|
||||
lua_pushstring(L, tname);
|
||||
lua_setfield(L, -2, cstr!("__name"));
|
||||
lua_setfield(L, -2, cstr!("__type"));
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferx(
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
mut size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
extern "C" {
|
||||
fn free(p: *mut c_void);
|
||||
}
|
||||
|
||||
let chunk_is_text = size == 0 || (*data as u8) >= b'\n';
|
||||
unsafe extern "C-unwind" fn data_dtor(data: *mut c_void) {
|
||||
free(*(data as *mut *mut c_char) as *mut c_void);
|
||||
}
|
||||
|
||||
let chunk_is_text = size == 0 || (*data as u8) >= b'\t';
|
||||
if !mode.is_null() {
|
||||
let modeb = CStr::from_ptr(mode).to_bytes();
|
||||
if !chunk_is_text && !modeb.contains(&b'b') {
|
||||
lua_pushfstring(
|
||||
L,
|
||||
cstr!("attempt to load a binary chunk (mode is '%s')"),
|
||||
mode,
|
||||
);
|
||||
lua_pushfstring(L, cstr!("attempt to load a binary chunk (mode is '%s')"), mode);
|
||||
return LUA_ERRSYNTAX;
|
||||
} else if chunk_is_text && !modeb.contains(&b't') {
|
||||
lua_pushfstring(
|
||||
L,
|
||||
cstr!("attempt to load a text chunk (mode is '%s')"),
|
||||
mode,
|
||||
);
|
||||
lua_pushfstring(L, cstr!("attempt to load a text chunk (mode is '%s')"), mode);
|
||||
return LUA_ERRSYNTAX;
|
||||
}
|
||||
}
|
||||
|
||||
if chunk_is_text {
|
||||
if env < 0 {
|
||||
env -= 1;
|
||||
}
|
||||
let data_ud = lua_newuserdatadtor(L, mem::size_of::<*mut c_char>(), data_dtor) as *mut *mut c_char;
|
||||
let data = luau_compile_(data, size, ptr::null_mut(), &mut size);
|
||||
let ok = luau_load(L, name, data, size, 0) == 0;
|
||||
free(data as *mut c_void);
|
||||
ptr::write(data_ud, data);
|
||||
// By deferring the `free(data)` to the userdata destructor, we ensure that
|
||||
// even if `luau_load` throws an error, the `data` is still released.
|
||||
let ok = luau_load(L, name, data, size, env) == 0;
|
||||
lua_replace(L, -2); // replace data with the result
|
||||
if !ok {
|
||||
return LUA_ERRSYNTAX;
|
||||
}
|
||||
} else if luau_load(L, name, data, size, 0) != 0 {
|
||||
} else if luau_load(L, name, data, size, env) != 0 {
|
||||
return LUA_ERRSYNTAX;
|
||||
}
|
||||
LUA_OK
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
) -> c_int {
|
||||
luaL_loadbufferenv(L, data, size, name, mode, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
@@ -381,13 +386,13 @@ pub unsafe fn luaL_loadbuffer(
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
) -> c_int {
|
||||
luaL_loadbufferx(L, data, size, name, ptr::null())
|
||||
luaL_loadbufferenv(L, data, size, name, ptr::null(), 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
let mut isnum = 0;
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_len(L, idx);
|
||||
let res = lua_tointegerx(L, -1, &mut isnum);
|
||||
lua_pop(L, 1);
|
||||
@@ -397,12 +402,7 @@ pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
res
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_traceback(
|
||||
L: *mut lua_State,
|
||||
L1: *mut lua_State,
|
||||
msg: *const c_char,
|
||||
mut level: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
let top = lua_gettop(L);
|
||||
let numlevels = lua_stackdepth(L);
|
||||
@@ -436,10 +436,10 @@ pub unsafe fn luaL_traceback(
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||
idx = lua_absindex(L, idx);
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
let t = lua_type(L, idx);
|
||||
match t {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
}
|
||||
@@ -453,8 +453,8 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_pushliteral(L, "true");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__type"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
} else {
|
||||
@@ -462,7 +462,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2);
|
||||
lua_replace(L, -2); // remove '__type'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -474,14 +474,14 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
luaL_getmetatable(L, tname);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
lua_pushstring_(L, fname);
|
||||
if lua_gettable(L, abs_i) == LUA_TTABLE {
|
||||
return 1;
|
||||
@@ -494,12 +494,7 @@ pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_ch
|
||||
0
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
@@ -3,9 +3,7 @@
|
||||
use std::os::raw::{c_char, c_float, c_int, c_void};
|
||||
use std::ptr;
|
||||
|
||||
use super::lua::{
|
||||
self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX,
|
||||
};
|
||||
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
@@ -13,7 +11,7 @@ pub struct luaL_Reg {
|
||||
pub func: lua_CFunction,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg);
|
||||
#[link_name = "luaL_getmetafield"]
|
||||
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
@@ -52,6 +50,8 @@ extern "C" {
|
||||
pub fn luaL_newmetatable_(L: *mut lua_State, tname: *const c_char) -> c_int;
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_checkbuffer(L: *mut lua_State, narg: c_int, len: *mut usize) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
|
||||
#[link_name = "luaL_errorL"]
|
||||
@@ -142,9 +142,12 @@ pub unsafe fn luaL_sandbox(L: *mut lua_State, enabled: c_int) {
|
||||
|
||||
// set all builtin metatables to read-only
|
||||
lua_pushliteral(L, "");
|
||||
lua_getmetatable(L, -1);
|
||||
lua_setreadonly(L, -1, enabled);
|
||||
lua_pop(L, 2);
|
||||
if lua_getmetatable(L, -1) != 0 {
|
||||
lua_setreadonly(L, -1, enabled);
|
||||
lua_pop(L, 2);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
// set globals to readonly and activate safeenv since the env is immutable
|
||||
lua_setreadonly(L, LUA_GLOBALSINDEX, enabled);
|
||||
@@ -152,5 +155,48 @@ pub unsafe fn luaL_sandbox(L: *mut lua_State, enabled: c_int) {
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Generic Buffer Manipulation
|
||||
// Generic Buffer Manipulation
|
||||
//
|
||||
|
||||
/// Buffer size used for on-stack string operations. This limit depends on native stack size.
|
||||
pub const LUA_BUFFERSIZE: usize = 512;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Strbuf {
|
||||
p: *mut c_char, // current position in buffer
|
||||
end: *mut c_char, // end of the current buffer
|
||||
L: *mut lua_State,
|
||||
storage: *mut c_void, // TString
|
||||
buffer: [c_char; LUA_BUFFERSIZE],
|
||||
}
|
||||
|
||||
// For compatibility
|
||||
pub type luaL_Buffer = luaL_Strbuf;
|
||||
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Strbuf);
|
||||
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
|
||||
pub fn luaL_prepbuffsize(B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
|
||||
pub fn luaL_addlstring(B: *mut luaL_Strbuf, s: *const c_char, l: usize);
|
||||
pub fn luaL_addvalue(B: *mut luaL_Strbuf);
|
||||
pub fn luaL_addvalueany(B: *mut luaL_Strbuf, idx: c_int);
|
||||
pub fn luaL_pushresult(B: *mut luaL_Strbuf);
|
||||
pub fn luaL_pushresultsize(B: *mut luaL_Strbuf, size: usize);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_addchar(B: *mut luaL_Strbuf, c: c_char) {
|
||||
if (*B).p >= (*B).end {
|
||||
luaL_prepbuffsize(B, 1);
|
||||
}
|
||||
*(*B).p = c;
|
||||
(*B).p = (*B).p.add(1);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_addstring(B: *mut luaL_Strbuf, s: *const c_char) {
|
||||
// Calculate length of s
|
||||
let mut len = 0;
|
||||
while *s.add(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
luaL_addlstring(B, s, len);
|
||||
}
|
||||
@@ -2,17 +2,26 @@
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_double, c_float, c_int, c_uint, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
// Option for multiple returns in 'lua_pcall' and 'lua_call'
|
||||
pub const LUA_MULTRET: c_int = -1;
|
||||
|
||||
// Max number of Lua stack slots
|
||||
const LUAI_MAXCSTACK: c_int = 1000000;
|
||||
|
||||
// Number of valid Lua userdata tags
|
||||
const LUA_UTAG_LIMIT: c_int = 128;
|
||||
|
||||
// Number of valid Lua lightuserdata tags
|
||||
const LUA_LUTAG_LIMIT: c_int = 128;
|
||||
|
||||
//
|
||||
// Pseudo-indices
|
||||
//
|
||||
pub const LUA_REGISTRYINDEX: c_int = -10000;
|
||||
pub const LUA_ENVIRONINDEX: c_int = -10001;
|
||||
pub const LUA_GLOBALSINDEX: c_int = -10002;
|
||||
pub const LUA_REGISTRYINDEX: c_int = -LUAI_MAXCSTACK - 2000;
|
||||
pub const LUA_ENVIRONINDEX: c_int = -LUAI_MAXCSTACK - 2001;
|
||||
pub const LUA_GLOBALSINDEX: c_int = -LUAI_MAXCSTACK - 2002;
|
||||
|
||||
pub const fn lua_upvalueindex(i: c_int) -> c_int {
|
||||
LUA_GLOBALSINDEX - i
|
||||
@@ -52,6 +61,7 @@ pub const LUA_TTABLE: c_int = 6;
|
||||
pub const LUA_TFUNCTION: c_int = 7;
|
||||
pub const LUA_TUSERDATA: c_int = 8;
|
||||
pub const LUA_TTHREAD: c_int = 9;
|
||||
pub const LUA_TBUFFER: c_int = 10;
|
||||
|
||||
/// Guaranteed number of Lua stack slots available to a C function.
|
||||
pub const LUA_MINSTACK: c_int = 20;
|
||||
@@ -66,21 +76,23 @@ pub type lua_Integer = c_int;
|
||||
pub type lua_Unsigned = c_uint;
|
||||
|
||||
/// Type for native C functions that can be passed to Lua.
|
||||
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
|
||||
pub type lua_Continuation = unsafe extern "C" fn(L: *mut lua_State, status: c_int) -> c_int;
|
||||
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
|
||||
pub type lua_Continuation = unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int) -> c_int;
|
||||
|
||||
/// Type for userdata destructor functions.
|
||||
pub type lua_Udestructor = unsafe extern "C" fn(*mut c_void);
|
||||
pub type lua_Udestructor = unsafe extern "C-unwind" fn(*mut c_void);
|
||||
pub type lua_Destructor = unsafe extern "C-unwind" fn(L: *mut lua_State, *mut c_void);
|
||||
|
||||
/// Type for memory-allocation functions.
|
||||
pub type lua_Alloc = unsafe extern "C" fn(
|
||||
ud: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
nsize: usize,
|
||||
) -> *mut c_void;
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
extern "C" {
|
||||
/// Returns Luau release version (eg. `0.xxx`).
|
||||
pub const fn luau_version() -> Option<&'static str> {
|
||||
option_env!("LUAU_VERSION")
|
||||
}
|
||||
|
||||
extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
@@ -134,10 +146,13 @@ extern "C" {
|
||||
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
|
||||
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
||||
pub fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_tolightuserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_touserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_userdatatag(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_lightuserdatatag(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
|
||||
pub fn lua_tobuffer(L: *mut lua_State, idx: c_int, len: *mut usize) -> *mut c_void;
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
|
||||
//
|
||||
@@ -147,7 +162,10 @@ extern "C" {
|
||||
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
|
||||
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
|
||||
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float, w: c_float);
|
||||
#[link_name = "lua_pushlstring"]
|
||||
pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize);
|
||||
#[link_name = "lua_pushstring"]
|
||||
@@ -165,10 +183,13 @@ extern "C" {
|
||||
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
|
||||
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
|
||||
|
||||
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
|
||||
pub fn lua_pushlightuserdatatagged(L: *mut lua_State, p: *mut c_void, tag: c_int);
|
||||
pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_newuserdatataggedwithmetatable(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Udestructor) -> *mut c_void;
|
||||
|
||||
pub fn lua_newbuffer(L: *mut lua_State, sz: usize) -> *mut c_void;
|
||||
|
||||
//
|
||||
// Get functions (Lua -> stack)
|
||||
//
|
||||
@@ -239,14 +260,14 @@ pub const LUA_GCSETGOAL: c_int = 7;
|
||||
pub const LUA_GCSETSTEPMUL: c_int = 8;
|
||||
pub const LUA_GCSETSTEPSIZE: c_int = 9;
|
||||
|
||||
extern "C" {
|
||||
extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
|
||||
}
|
||||
|
||||
//
|
||||
// Memory statistics
|
||||
//
|
||||
extern "C" {
|
||||
extern "C-unwind" {
|
||||
pub fn lua_setmemcat(L: *mut lua_State, category: c_int);
|
||||
pub fn lua_totalbytes(L: *mut lua_State, category: c_int) -> usize;
|
||||
}
|
||||
@@ -254,7 +275,7 @@ extern "C" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
extern "C" {
|
||||
extern "C-unwind" {
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_rawiter(L: *mut lua_State, idx: c_int, iter: c_int) -> c_int;
|
||||
@@ -262,13 +283,15 @@ extern "C" {
|
||||
// TODO: lua_encodepointer
|
||||
pub fn lua_clock() -> c_double;
|
||||
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
|
||||
pub fn lua_setuserdatadtor(
|
||||
L: *mut lua_State,
|
||||
tag: c_int,
|
||||
dtor: Option<unsafe extern "C" fn(*mut lua_State, *mut c_void)>,
|
||||
);
|
||||
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Destructor>);
|
||||
pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option<lua_Destructor>;
|
||||
pub fn lua_setuserdatametatable(L: *mut lua_State, tag: c_int, idx: c_int);
|
||||
pub fn lua_getuserdatametatable(L: *mut lua_State, tag: c_int);
|
||||
pub fn lua_setlightuserdataname(L: *mut lua_State, tag: c_int, name: *const c_char);
|
||||
pub fn lua_getlightuserdataname(L: *mut lua_State, tag: c_int) -> *const c_char;
|
||||
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
|
||||
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
|
||||
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -277,7 +300,7 @@ extern "C" {
|
||||
pub const LUA_NOREF: c_int = -1;
|
||||
pub const LUA_REFNIL: c_int = 0;
|
||||
|
||||
extern "C" {
|
||||
extern "C-unwind" {
|
||||
pub fn lua_ref(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_unref(L: *mut lua_State, r#ref: c_int);
|
||||
}
|
||||
@@ -316,6 +339,15 @@ pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
|
||||
lua_newuserdatatagged(L, sz, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_newuserdata_t<T>(L: *mut lua_State) -> *mut T {
|
||||
unsafe extern "C-unwind" fn destructor<T>(ud: *mut c_void) {
|
||||
ptr::drop_in_place(ud as *mut T);
|
||||
}
|
||||
|
||||
lua_newuserdatadtor(L, mem::size_of::<T>(), destructor::<T>) as *mut T
|
||||
}
|
||||
|
||||
// TODO: lua_strlen
|
||||
|
||||
#[inline(always)]
|
||||
@@ -353,6 +385,11 @@ pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TTHREAD) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isbuffer(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TBUFFER) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TNONE) as c_int
|
||||
@@ -370,27 +407,31 @@ pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) {
|
||||
lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
|
||||
lua_pushcclosurek(L, f, ptr::null(), 0, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcfunctiond(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char) {
|
||||
lua_pushcclosurek(L, f, debugname, 0, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, nup: c_int) {
|
||||
lua_pushcclosurek(L, f, ptr::null(), nup, None)
|
||||
}
|
||||
|
||||
pub unsafe fn lua_pushcclosured(
|
||||
L: *mut lua_State,
|
||||
f: lua_CFunction,
|
||||
debugname: *const c_char,
|
||||
nup: c_int,
|
||||
) {
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcclosured(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char, nup: c_int) {
|
||||
lua_pushcclosurek(L, f, debugname, nup, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void) {
|
||||
lua_pushlightuserdatatagged(L, p, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_setglobal(L: *mut lua_State, var: *const c_char) {
|
||||
lua_setfield(L, LUA_GLOBALSINDEX, var)
|
||||
@@ -414,9 +455,9 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
const LUA_IDSIZE: usize = 256;
|
||||
|
||||
/// Type for functions to be called on debug events.
|
||||
pub type lua_Hook = unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
pub type lua_Coverage = unsafe extern "C" fn(
|
||||
pub type lua_Coverage = unsafe extern "C-unwind" fn(
|
||||
context: *mut c_void,
|
||||
function: *const c_char,
|
||||
linedefined: c_int,
|
||||
@@ -425,14 +466,9 @@ pub type lua_Coverage = unsafe extern "C" fn(
|
||||
size: usize,
|
||||
);
|
||||
|
||||
extern "C" {
|
||||
extern "C-unwind" {
|
||||
pub fn lua_stackdepth(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_getinfo(
|
||||
L: *mut lua_State,
|
||||
level: c_int,
|
||||
what: *const c_char,
|
||||
ar: *mut lua_Debug,
|
||||
) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, level: c_int, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getargument(L: *mut lua_State, level: c_int, n: c_int) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, level: c_int, n: c_int) -> *const c_char;
|
||||
pub fn lua_setlocal(L: *mut lua_State, level: c_int, n: c_int) -> *const c_char;
|
||||
@@ -440,19 +476,9 @@ extern "C" {
|
||||
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
|
||||
|
||||
pub fn lua_singlestep(L: *mut lua_State, enabled: c_int);
|
||||
pub fn lua_breakpoint(
|
||||
L: *mut lua_State,
|
||||
funcindex: c_int,
|
||||
line: c_int,
|
||||
enabled: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_breakpoint(L: *mut lua_State, funcindex: c_int, line: c_int, enabled: c_int) -> c_int;
|
||||
|
||||
pub fn lua_getcoverage(
|
||||
L: *mut lua_State,
|
||||
funcindex: c_int,
|
||||
context: *mut c_void,
|
||||
callback: lua_Coverage,
|
||||
);
|
||||
pub fn lua_getcoverage(L: *mut lua_State, funcindex: c_int, context: *mut c_void, callback: lua_Coverage);
|
||||
|
||||
pub fn lua_debugtrace(L: *mut lua_State) -> *const c_char;
|
||||
}
|
||||
@@ -478,30 +504,40 @@ pub struct lua_Debug {
|
||||
//
|
||||
|
||||
#[repr(C)]
|
||||
#[non_exhaustive]
|
||||
pub struct lua_Callbacks {
|
||||
/// arbitrary userdata pointer that is never overwritten by Luau
|
||||
pub userdata: *mut c_void,
|
||||
|
||||
/// gets called at safepoints (loop back edges, call/ret, gc) if set
|
||||
pub interrupt: Option<unsafe extern "C" fn(L: *mut lua_State, gc: c_int)>,
|
||||
pub interrupt: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, gc: c_int)>,
|
||||
/// gets called when an unprotected error is raised (if longjmp is used)
|
||||
pub panic: Option<unsafe extern "C" fn(L: *mut lua_State, errcode: c_int)>,
|
||||
pub panic: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, errcode: c_int)>,
|
||||
|
||||
/// gets called when L is created (LP == parent) or destroyed (LP == NULL)
|
||||
pub userthread: Option<unsafe extern "C" fn(LP: *mut lua_State, L: *mut lua_State)>,
|
||||
pub userthread: Option<unsafe extern "C-unwind" fn(LP: *mut lua_State, L: *mut lua_State)>,
|
||||
/// gets called when a string is created; returned atom can be retrieved via tostringatom
|
||||
pub useratom: Option<unsafe extern "C" fn(s: *const c_char, l: usize) -> i16>,
|
||||
pub useratom: Option<unsafe extern "C-unwind" fn(s: *const c_char, l: usize) -> i16>,
|
||||
|
||||
/// gets called when BREAK instruction is encountered
|
||||
pub debugbreak: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
|
||||
pub debugbreak: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
|
||||
/// gets called after each instruction in single step mode
|
||||
pub debugstep: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
|
||||
pub debugstep: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
|
||||
/// gets called when thread execution is interrupted by break in another thread
|
||||
pub debuginterrupt: Option<unsafe extern "C" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
|
||||
pub debuginterrupt: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
|
||||
/// gets called when protected call results in an error
|
||||
pub debugprotectederror: Option<unsafe extern "C" fn(L: *mut lua_State)>,
|
||||
pub debugprotectederror: Option<unsafe extern "C-unwind" fn(L: *mut lua_State)>,
|
||||
|
||||
/// gets called when memory is allocated
|
||||
pub onallocate: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, osize: usize, nsize: usize)>,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn lua_callbacks(L: *mut lua_State) -> *mut lua_Callbacks;
|
||||
}
|
||||
|
||||
// Functions from customization lib
|
||||
extern "C" {
|
||||
pub fn luau_setfflag(name: *const c_char, value: c_int) -> c_int;
|
||||
pub fn lua_getmetatablepointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//! Contains definitions from `luacode.h`.
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::{ptr, slice};
|
||||
|
||||
#[repr(C)]
|
||||
#[non_exhaustive]
|
||||
pub struct lua_CompileOptions {
|
||||
pub optimizationLevel: c_int,
|
||||
pub debugLevel: c_int,
|
||||
pub typeInfoLevel: c_int,
|
||||
pub coverageLevel: c_int,
|
||||
pub vectorLib: *const c_char,
|
||||
pub vectorCtor: *const c_char,
|
||||
pub vectorType: *const c_char,
|
||||
pub mutableGlobals: *const *const c_char,
|
||||
pub userdataTypes: *const *const c_char,
|
||||
pub librariesWithKnownMembers: *const *const c_char,
|
||||
pub libraryMemberTypeCallback: Option<lua_LibraryMemberTypeCallback>,
|
||||
pub libraryMemberConstantCallback: Option<lua_LibraryMemberConstantCallback>,
|
||||
pub disabledBuiltins: *const *const c_char,
|
||||
}
|
||||
|
||||
impl Default for lua_CompileOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
optimizationLevel: 1,
|
||||
debugLevel: 1,
|
||||
typeInfoLevel: 0,
|
||||
coverageLevel: 0,
|
||||
vectorLib: ptr::null(),
|
||||
vectorCtor: ptr::null(),
|
||||
vectorType: ptr::null(),
|
||||
mutableGlobals: ptr::null(),
|
||||
userdataTypes: ptr::null(),
|
||||
librariesWithKnownMembers: ptr::null(),
|
||||
libraryMemberTypeCallback: None,
|
||||
libraryMemberConstantCallback: None,
|
||||
disabledBuiltins: ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct lua_CompileConstant {
|
||||
_data: [u8; 0],
|
||||
_marker: PhantomData<(*mut u8, PhantomPinned)>,
|
||||
}
|
||||
|
||||
/// Type table tags
|
||||
#[doc(hidden)]
|
||||
#[repr(i32)]
|
||||
#[non_exhaustive]
|
||||
pub enum luau_BytecodeType {
|
||||
Nil = 0,
|
||||
Boolean,
|
||||
Number,
|
||||
String,
|
||||
Table,
|
||||
Function,
|
||||
Thread,
|
||||
UserData,
|
||||
Vector,
|
||||
Buffer,
|
||||
|
||||
Any = 15,
|
||||
}
|
||||
|
||||
pub type lua_LibraryMemberTypeCallback =
|
||||
unsafe extern "C-unwind" fn(library: *const c_char, member: *const c_char) -> c_int;
|
||||
|
||||
pub type lua_LibraryMemberConstantCallback = unsafe extern "C-unwind" fn(
|
||||
library: *const c_char,
|
||||
member: *const c_char,
|
||||
constant: *mut lua_CompileConstant,
|
||||
);
|
||||
|
||||
extern "C" {
|
||||
pub fn luau_set_compile_constant_nil(cons: *mut lua_CompileConstant);
|
||||
pub fn luau_set_compile_constant_boolean(cons: *mut lua_CompileConstant, b: c_int);
|
||||
pub fn luau_set_compile_constant_number(cons: *mut lua_CompileConstant, n: f64);
|
||||
pub fn luau_set_compile_constant_vector(cons: *mut lua_CompileConstant, x: f32, y: f32, z: f32, w: f32);
|
||||
pub fn luau_set_compile_constant_string(cons: *mut lua_CompileConstant, s: *const c_char, l: usize);
|
||||
}
|
||||
|
||||
extern "C-unwind" {
|
||||
#[link_name = "luau_compile"]
|
||||
pub fn luau_compile_(
|
||||
source: *const c_char,
|
||||
size: usize,
|
||||
options: *mut lua_CompileOptions,
|
||||
outsize: *mut usize,
|
||||
) -> *mut c_char;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn free(p: *mut c_void);
|
||||
}
|
||||
|
||||
pub unsafe fn luau_compile(source: &[u8], mut options: lua_CompileOptions) -> Vec<u8> {
|
||||
let mut outsize = 0;
|
||||
let data_ptr = luau_compile_(
|
||||
source.as_ptr() as *const c_char,
|
||||
source.len(),
|
||||
&mut options,
|
||||
&mut outsize,
|
||||
);
|
||||
assert!(!data_ptr.is_null(), "luau_compile failed");
|
||||
let data = slice::from_raw_parts(data_ptr as *mut u8, outsize).to_vec();
|
||||
free(data_ptr as *mut c_void);
|
||||
data
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
//! Contains definitions from `luacodegen.h`.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
extern "C-unwind" {
|
||||
pub fn luau_codegen_supported() -> c_int;
|
||||
pub fn luau_codegen_create(state: *mut lua_State);
|
||||
pub fn luau_codegen_compile(state: *mut lua_State, idx: c_int);
|
||||
}
|
||||
@@ -9,20 +9,24 @@ pub const LUA_TABLIBNAME: &str = "table";
|
||||
pub const LUA_OSLIBNAME: &str = "os";
|
||||
pub const LUA_STRLIBNAME: &str = "string";
|
||||
pub const LUA_BITLIBNAME: &str = "bit32";
|
||||
pub const LUA_BUFFERLIBNAME: &str = "buffer";
|
||||
pub const LUA_UTF8LIBNAME: &str = "utf8";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_VECLIBNAME: &str = "vector";
|
||||
|
||||
extern "C" {
|
||||
extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_os(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_string(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_buffer(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_vector(L: *mut lua_State) -> c_int;
|
||||
|
||||
// open all builtin libraries
|
||||
pub fn luaL_openlibs(L: *mut lua_State);
|
||||
@@ -4,10 +4,12 @@ pub use compat::*;
|
||||
pub use lauxlib::*;
|
||||
pub use lua::*;
|
||||
pub use luacode::*;
|
||||
pub use luacodegen::*;
|
||||
pub use lualib::*;
|
||||
|
||||
pub mod compat;
|
||||
pub mod lauxlib;
|
||||
pub mod lua;
|
||||
pub mod luacode;
|
||||
pub mod luacodegen;
|
||||
pub mod lualib;
|
||||
@@ -0,0 +1,6 @@
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! cstr {
|
||||
($s:expr) => {
|
||||
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] as *const ::std::os::raw::c_char
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.8.0"
|
||||
version = "0.10.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
keywords = ["lua", "mlua"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -12,13 +12,13 @@ license = "MIT"
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
|
||||
macros = ["proc-macro-error2", "itertools", "regex", "once_cell"]
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
proc-macro-error2 = { version = "2.0.1", optional = true }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
itertools = { version = "0.14", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.0", optional = true }
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let ident_str = ident.to_string();
|
||||
let (impl_generics, ty_generics, _) = generics.split_for_impl();
|
||||
let where_clause = match &generics.where_clause {
|
||||
Some(where_clause) => quote! { #where_clause, Self: 'static + Clone },
|
||||
None => quote! { where Self: 'static + Clone },
|
||||
};
|
||||
|
||||
quote! {
|
||||
impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause {
|
||||
#[inline]
|
||||
fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
|
||||
match value {
|
||||
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
_ => Err(::mlua::Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: #ident_str.to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
+75
-37
@@ -1,38 +1,73 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, AttributeArgs, Error, ItemFn};
|
||||
use syn::meta::ParseNestedMeta;
|
||||
use syn::{parse_macro_input, ItemFn, LitStr, Result};
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
|
||||
proc_macro_error::proc_macro_error,
|
||||
proc_macro_error2::proc_macro_error,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct ModuleAttributes {
|
||||
name: Option<Ident>,
|
||||
skip_memory_check: bool,
|
||||
}
|
||||
|
||||
impl ModuleAttributes {
|
||||
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
|
||||
if meta.path.is_ident("name") {
|
||||
match meta.value() {
|
||||
Ok(value) => {
|
||||
self.name = Some(value.parse::<LitStr>()?.parse()?);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(meta.error("`name` attribute must have a value"));
|
||||
}
|
||||
}
|
||||
} else if meta.path.is_ident("skip_memory_check") {
|
||||
if meta.value().is_ok() {
|
||||
return Err(meta.error("`skip_memory_check` attribute have no values"));
|
||||
}
|
||||
self.skip_memory_check = true;
|
||||
} else {
|
||||
return Err(meta.error("unsupported module attribute"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(attr as AttributeArgs);
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
|
||||
if !args.is_empty() {
|
||||
let err = Error::new(Span::call_site(), "the macro does not support arguments")
|
||||
.to_compile_error();
|
||||
return err.into();
|
||||
let mut args = ModuleAttributes::default();
|
||||
if !attr.is_empty() {
|
||||
let args_parser = syn::meta::parser(|meta| args.parse(meta));
|
||||
parse_macro_input!(attr with args_parser);
|
||||
}
|
||||
|
||||
let func_name = func.sig.ident.clone();
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", func_name), Span::call_site());
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
let func_name = &func.sig.ident;
|
||||
let module_name = args.name.unwrap_or_else(|| func_name.clone());
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
|
||||
let skip_memory_check = if args.skip_memory_check {
|
||||
quote! { lua.skip_memory_check(true); }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let wrapped = quote! {
|
||||
::mlua::require_module_feature!();
|
||||
mlua::require_module_feature!();
|
||||
|
||||
#func
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
::mlua::Lua::init_from_ptr(state)
|
||||
.entrypoint1(#func_name)
|
||||
.expect("cannot initialize module")
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
mlua::Lua::entrypoint1(state, move |lua| {
|
||||
#skip_memory_check
|
||||
#func_name(lua)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
@@ -61,30 +96,21 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
});
|
||||
|
||||
let wrapped_code = quote! {{
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
|
||||
use mlua::{AsChunk, ChunkMode, Lua, Result, Table};
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::cell::Cell;
|
||||
use ::std::io::Result as IoResult;
|
||||
use ::std::marker::PhantomData;
|
||||
use ::std::sync::Mutex;
|
||||
|
||||
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
|
||||
struct InnerChunk<F: FnOnce(&Lua) -> Result<Table>>(Cell<Option<F>>);
|
||||
|
||||
struct InnerChunk<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>, PhantomData<&'a ()>);
|
||||
|
||||
impl<'lua, F> AsChunk<'lua> for InnerChunk<'lua, F>
|
||||
impl<F> AsChunk<'static> for InnerChunk<F>
|
||||
where
|
||||
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
|
||||
F: FnOnce(&Lua) -> Result<Table>,
|
||||
{
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||
}
|
||||
|
||||
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
|
||||
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
|
||||
if #caps_len > 0 {
|
||||
if let Ok(mut make_env) = self.0.lock() {
|
||||
if let Some(make_env) = make_env.take() {
|
||||
return make_env(lua).map(Some);
|
||||
}
|
||||
if let Some(make_env) = self.0.take() {
|
||||
return make_env(lua).map(Some);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
@@ -93,9 +119,13 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
|
||||
let make_env = move |lua: &Lua| -> Result<Table> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
@@ -106,16 +136,24 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
Ok(Value::Table(env))
|
||||
});
|
||||
Ok(env)
|
||||
};
|
||||
|
||||
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
|
||||
InnerChunk(Cell::new(Some(make_env)))
|
||||
}};
|
||||
|
||||
wrapped_code.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
#[proc_macro_derive(FromLua)]
|
||||
pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
from_lua::from_lua(input)
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
mod chunk;
|
||||
#[cfg(feature = "macros")]
|
||||
mod from_lua;
|
||||
#[cfg(feature = "macros")]
|
||||
mod token;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::{
|
||||
cmp::{Eq, PartialEq},
|
||||
fmt::{self, Display, Formatter},
|
||||
iter::IntoIterator,
|
||||
vec::IntoIter,
|
||||
};
|
||||
use std::cmp::{Eq, PartialEq};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::vec::IntoIter;
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
@@ -48,10 +45,7 @@ fn span_pos(span: &Span) -> (Pos, Pos) {
|
||||
return fallback_span_pos(span);
|
||||
}
|
||||
|
||||
(
|
||||
Pos::new(start.line, start.column),
|
||||
Pos::new(end.line, end.column),
|
||||
)
|
||||
(Pos::new(start.line, start.column), Pos::new(end.line, end.column))
|
||||
}
|
||||
|
||||
fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
@@ -59,7 +53,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
|
||||
|
||||
match RE.captures(&format!("{:?}", span)) {
|
||||
match RE.captures(&format!("{span:?}")) {
|
||||
Some(caps) => match (caps.get(1), caps.get(2)) {
|
||||
(Some(start), Some(end)) => Some((
|
||||
match start.as_str().parse() {
|
||||
@@ -80,9 +74,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let (start, end) = match parse_pos(span) {
|
||||
Some(v) => v,
|
||||
None => proc_macro_error::abort_call_site!(
|
||||
"Cannot retrieve span information; please use nightly"
|
||||
),
|
||||
None => proc_macro_error2::abort_call_site!("Cannot retrieve span information; please use nightly"),
|
||||
};
|
||||
(Pos::new(1, start), Pos::new(1, end))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
imports_granularity = "Module"
|
||||
max_width = 110
|
||||
comment_width = 100
|
||||
wrap_comments = true
|
||||
@@ -0,0 +1,86 @@
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::types::ValueRef;
|
||||
|
||||
/// A Luau buffer type.
|
||||
///
|
||||
/// See the buffer [documentation] for more information.
|
||||
///
|
||||
/// [documentation]: https://luau.org/library#buffer-library
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Buffer(pub(crate) ValueRef);
|
||||
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused))]
|
||||
impl Buffer {
|
||||
/// Copies the buffer data into a new `Vec<u8>`.
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
unsafe { self.as_slice().to_vec() }
|
||||
}
|
||||
|
||||
/// Returns the length of the buffer.
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { self.as_slice().len() }
|
||||
}
|
||||
|
||||
/// Returns `true` if the buffer is empty.
|
||||
#[doc(hidden)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Reads given number of bytes from the buffer at the given offset.
|
||||
///
|
||||
/// Offset is 0-based.
|
||||
#[track_caller]
|
||||
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
|
||||
let data = unsafe { self.as_slice() };
|
||||
let mut bytes = [0u8; N];
|
||||
bytes.copy_from_slice(&data[offset..offset + N]);
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Writes given bytes to the buffer at the given offset.
|
||||
///
|
||||
/// Offset is 0-based.
|
||||
#[track_caller]
|
||||
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
|
||||
let data = unsafe {
|
||||
let (buf, size) = self.as_raw_parts();
|
||||
std::slice::from_raw_parts_mut(buf, size)
|
||||
};
|
||||
data[offset..offset + bytes.len()].copy_from_slice(bytes);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn as_slice(&self) -> &[u8] {
|
||||
let (buf, size) = self.as_raw_parts();
|
||||
std::slice::from_raw_parts(buf, size)
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
|
||||
let lua = self.0.lua.lock();
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
(buf as *mut u8, size)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for Buffer {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
serializer.serialize_bytes(unsafe { self.as_slice() })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl crate::types::LuaType for Buffer {
|
||||
const TYPE_ID: std::os::raw::c_int = ffi::LUA_TBUFFER;
|
||||
}
|
||||
+261
-153
@@ -2,27 +2,25 @@ use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::io::Result as IoResult;
|
||||
use std::marker::PhantomData;
|
||||
use std::panic::Location;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::value::{FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
use crate::state::{Lua, WeakLua};
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
|
||||
///
|
||||
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
|
||||
/// [`Chunk`]: crate::Chunk
|
||||
pub trait AsChunk<'lua> {
|
||||
/// Returns chunk data (can be text or binary)
|
||||
fn source(&self) -> IoResult<Cow<[u8]>>;
|
||||
|
||||
pub trait AsChunk<'a> {
|
||||
/// Returns optional chunk name
|
||||
///
|
||||
/// See [`Chunk::set_name`] for possible name prefixes.
|
||||
fn name(&self) -> Option<StdString> {
|
||||
None
|
||||
}
|
||||
@@ -30,7 +28,8 @@ pub trait AsChunk<'lua> {
|
||||
/// Returns optional chunk [environment]
|
||||
///
|
||||
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
|
||||
fn env(&self, _lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
|
||||
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
|
||||
let _lua = lua; // suppress warning
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@@ -38,62 +37,75 @@ pub trait AsChunk<'lua> {
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns chunk data (can be text or binary)
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>>;
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for str {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
impl<'a> AsChunk<'a> for &'a str {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for StdString {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
impl AsChunk<'static> for StdString {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Owned(self.into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a StdString {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a [u8] {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for Vec<u8> {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Owned(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a Vec<u8> {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for [u8] {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
Ok(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for Vec<u8> {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
Ok(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for Path {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for &Path {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for PathBuf {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for PathBuf {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||
///
|
||||
/// [`Lua::load`]: crate::Lua::load
|
||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||
pub struct Chunk<'lua, 'a> {
|
||||
pub(crate) lua: &'lua Lua,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
pub(crate) name: Option<StdString>,
|
||||
pub(crate) env: Result<Option<Value<'lua>>>,
|
||||
pub struct Chunk<'a> {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) name: StdString,
|
||||
pub(crate) env: Result<Option<Table>>,
|
||||
pub(crate) mode: Option<ChunkMode>,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) compiler: Option<Compiler>,
|
||||
}
|
||||
@@ -112,32 +124,38 @@ pub enum ChunkMode {
|
||||
pub struct Compiler {
|
||||
optimization_level: u8,
|
||||
debug_level: u8,
|
||||
type_info_level: u8,
|
||||
coverage_level: u8,
|
||||
vector_lib: Option<String>,
|
||||
vector_ctor: Option<String>,
|
||||
vector_type: Option<String>,
|
||||
mutable_globals: Vec<String>,
|
||||
userdata_types: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl Default for Compiler {
|
||||
fn default() -> Self {
|
||||
// Defaults are taken from luacode.h
|
||||
Compiler {
|
||||
optimization_level: 1,
|
||||
debug_level: 1,
|
||||
coverage_level: 0,
|
||||
vector_lib: None,
|
||||
vector_ctor: None,
|
||||
mutable_globals: Vec::new(),
|
||||
}
|
||||
const { Self::new() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl Compiler {
|
||||
/// Creates Luau compiler instance with default options
|
||||
pub fn new() -> Self {
|
||||
Compiler::default()
|
||||
pub const fn new() -> Self {
|
||||
// Defaults are taken from luacode.h
|
||||
Compiler {
|
||||
optimization_level: 1,
|
||||
debug_level: 1,
|
||||
type_info_level: 0,
|
||||
coverage_level: 0,
|
||||
vector_lib: None,
|
||||
vector_ctor: None,
|
||||
vector_type: None,
|
||||
mutable_globals: Vec::new(),
|
||||
userdata_types: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets Luau compiler optimization level.
|
||||
@@ -146,7 +164,8 @@ impl Compiler {
|
||||
/// * 0 - no optimization
|
||||
/// * 1 - baseline optimization level that doesn't prevent debuggability (default)
|
||||
/// * 2 - includes optimizations that harm debuggability such as inlining
|
||||
pub fn set_optimization_level(mut self, level: u8) -> Self {
|
||||
#[must_use]
|
||||
pub const fn set_optimization_level(mut self, level: u8) -> Self {
|
||||
self.optimization_level = level;
|
||||
self
|
||||
}
|
||||
@@ -157,44 +176,75 @@ impl Compiler {
|
||||
/// * 0 - no debugging support
|
||||
/// * 1 - line info & function names only; sufficient for backtraces (default)
|
||||
/// * 2 - full debug info with local & upvalue names; necessary for debugger
|
||||
pub fn set_debug_level(mut self, level: u8) -> Self {
|
||||
#[must_use]
|
||||
pub const fn set_debug_level(mut self, level: u8) -> Self {
|
||||
self.debug_level = level;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets Luau type information level used to guide native code generation decisions.
|
||||
///
|
||||
/// Possible values:
|
||||
/// * 0 - generate for native modules (default)
|
||||
/// * 1 - generate for all modules
|
||||
pub const fn set_type_info_level(mut self, level: u8) -> Self {
|
||||
self.type_info_level = level;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets Luau compiler code coverage level.
|
||||
///
|
||||
/// Possible values:
|
||||
/// * 0 - no code coverage support (default)
|
||||
/// * 1 - statement coverage
|
||||
/// * 2 - statement and expression coverage (verbose)
|
||||
pub fn set_coverage_level(mut self, level: u8) -> Self {
|
||||
#[must_use]
|
||||
pub const fn set_coverage_level(mut self, level: u8) -> Self {
|
||||
self.coverage_level = level;
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn set_vector_lib(mut self, lib: Option<String>) -> Self {
|
||||
self.vector_lib = lib;
|
||||
#[must_use]
|
||||
pub fn set_vector_lib(mut self, lib: impl Into<String>) -> Self {
|
||||
self.vector_lib = Some(lib.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn set_vector_ctor(mut self, ctor: Option<String>) -> Self {
|
||||
self.vector_ctor = ctor;
|
||||
#[must_use]
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
|
||||
self.vector_ctor = Some(ctor.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<String>) -> Self {
|
||||
self.vector_type = Some(r#type.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of globals that are mutable.
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through these.
|
||||
#[must_use]
|
||||
pub fn set_mutable_globals(mut self, globals: Vec<String>) -> Self {
|
||||
self.mutable_globals = globals;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of userdata types that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn set_userdata_types(mut self, types: Vec<String>) -> Self {
|
||||
self.userdata_types = types;
|
||||
self
|
||||
}
|
||||
|
||||
/// Compiles the `source` into bytecode.
|
||||
pub fn compile(&self, source: impl AsRef<[u8]>) -> Vec<u8> {
|
||||
///
|
||||
/// Returns [`Error::SyntaxError`] if the source code is invalid.
|
||||
pub fn compile(&self, source: impl AsRef<[u8]>) -> Result<Vec<u8>> {
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
@@ -204,50 +254,84 @@ impl Compiler {
|
||||
let vector_ctor = self.vector_ctor.clone();
|
||||
let vector_ctor = vector_ctor.and_then(|ctor| CString::new(ctor).ok());
|
||||
let vector_ctor = vector_ctor.as_ref();
|
||||
let vector_type = self.vector_type.clone();
|
||||
let vector_type = vector_type.and_then(|t| CString::new(t).ok());
|
||||
let vector_type = vector_type.as_ref();
|
||||
|
||||
let mutable_globals = self
|
||||
.mutable_globals
|
||||
.iter()
|
||||
.map(|name| CString::new(name.clone()).ok())
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
let mut mutable_globals = mutable_globals
|
||||
.iter()
|
||||
.map(|s| s.as_ptr())
|
||||
.collect::<Vec<_>>();
|
||||
let mut mutable_globals_ptr = ptr::null_mut();
|
||||
if !mutable_globals.is_empty() {
|
||||
mutable_globals.push(ptr::null());
|
||||
mutable_globals_ptr = mutable_globals.as_mut_ptr();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let options = ffi::lua_CompileOptions {
|
||||
optimizationLevel: self.optimization_level as c_int,
|
||||
debugLevel: self.debug_level as c_int,
|
||||
coverageLevel: self.coverage_level as c_int,
|
||||
vectorLib: vector_lib.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
vectorCtor: vector_ctor.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
mutableGlobals: mutable_globals_ptr,
|
||||
macro_rules! vec2cstring_ptr {
|
||||
($name:ident, $name_ptr:ident) => {
|
||||
let $name = self
|
||||
.$name
|
||||
.iter()
|
||||
.map(|name| CString::new(name.clone()).ok())
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
let mut $name = $name.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
|
||||
let mut $name_ptr = ptr::null();
|
||||
if !$name.is_empty() {
|
||||
$name.push(ptr::null());
|
||||
$name_ptr = $name.as_ptr();
|
||||
}
|
||||
};
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
}
|
||||
|
||||
vec2cstring_ptr!(mutable_globals, mutable_globals_ptr);
|
||||
vec2cstring_ptr!(userdata_types, userdata_types_ptr);
|
||||
|
||||
let bytecode = unsafe {
|
||||
let mut options = ffi::lua_CompileOptions::default();
|
||||
options.optimizationLevel = self.optimization_level as c_int;
|
||||
options.debugLevel = self.debug_level as c_int;
|
||||
options.typeInfoLevel = self.type_info_level as c_int;
|
||||
options.coverageLevel = self.coverage_level as c_int;
|
||||
options.vectorLib = vector_lib.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.vectorCtor = vector_ctor.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.vectorType = vector_type.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.mutableGlobals = mutable_globals_ptr;
|
||||
options.userdataTypes = userdata_types_ptr;
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
};
|
||||
|
||||
if bytecode.first() == Some(&0) {
|
||||
// The rest of the bytecode is the error message starting with `:`
|
||||
// See https://github.com/luau-lang/luau/blob/0.640/Compiler/src/Compiler.cpp#L4336
|
||||
let message = String::from_utf8_lossy(&bytecode[2..]).to_string();
|
||||
return Err(Error::SyntaxError {
|
||||
incomplete_input: message.ends_with("<eof>"),
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(bytecode)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
pub fn set_name(mut self, name: impl AsRef<str>) -> Result<Self> {
|
||||
self.name = Some(name.as_ref().to_string());
|
||||
// Do extra validation
|
||||
let _ = self.convert_name()?;
|
||||
Ok(self)
|
||||
impl Chunk<'_> {
|
||||
/// Returns the name of this chunk.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
///
|
||||
/// Lua main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
|
||||
/// variable inside the chunk. By default this value is set to the global environment.
|
||||
/// Possible name prefixes:
|
||||
/// - `@` - file path (when truncation is needed, the end of the file path is kept, as this is
|
||||
/// more useful for identifying the file)
|
||||
/// - `=` - custom chunk name (when truncation is needed, the beginning of the name is kept)
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.name = name.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the environment of this chunk.
|
||||
pub fn environment(&self) -> Option<&Table> {
|
||||
self.env.as_ref().ok()?.as_ref()
|
||||
}
|
||||
|
||||
/// Sets the environment of the loaded chunk to the given value.
|
||||
///
|
||||
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the
|
||||
/// `_ENV` variable inside the chunk. By default this value is set to the global environment.
|
||||
///
|
||||
/// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside
|
||||
/// the chunk will refer to the given environment rather than the global one.
|
||||
@@ -255,10 +339,14 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// All global variables (including the standard library!) are looked up in `_ENV`, so it may be
|
||||
/// necessary to populate the environment in order for scripts using custom environments to be
|
||||
/// useful.
|
||||
pub fn set_environment<V: ToLua<'lua>>(mut self, env: V) -> Result<Self> {
|
||||
// Prefer to propagate errors here and wrap to `Ok`
|
||||
self.env = Ok(Some(env.to_lua(self.lua)?));
|
||||
Ok(self)
|
||||
pub fn set_environment(mut self, env: Table) -> Self {
|
||||
self.env = Ok(Some(env));
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the mode (auto-detected by default) of this chunk.
|
||||
pub fn mode(&self) -> ChunkMode {
|
||||
self.detect_mode()
|
||||
}
|
||||
|
||||
/// Sets whether the chunk is text or binary (autodetected by default).
|
||||
@@ -286,8 +374,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// This is equivalent to calling the chunk function with no arguments and no return values.
|
||||
pub fn exec(self) -> Result<()> {
|
||||
self.call(())?;
|
||||
Ok(())
|
||||
self.call(())
|
||||
}
|
||||
|
||||
/// Asynchronously execute this chunk of code.
|
||||
@@ -296,14 +383,11 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`exec`]: #method.exec
|
||||
/// [`exec`]: Chunk::exec
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn exec_async<'fut>(self) -> LocalBoxFuture<'fut, Result<()>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
{
|
||||
self.call_async(())
|
||||
pub async fn exec_async(self) -> Result<()> {
|
||||
self.call_async(()).await
|
||||
}
|
||||
|
||||
/// Evaluate the chunk as either an expression or block.
|
||||
@@ -311,7 +395,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
|
||||
/// the value that it evaluates to. Otherwise, the chunk is interpreted as a block as normal,
|
||||
/// and this is equivalent to calling `exec`.
|
||||
pub fn eval<R: FromLuaMulti<'lua>>(self) -> Result<R> {
|
||||
pub fn eval<R: FromLuaMulti>(self) -> Result<R> {
|
||||
// Bytecode is always interpreted as a statement.
|
||||
// For source code, first try interpreting the lua as an expression by adding
|
||||
// "return", then as a statement. This is the same thing the
|
||||
@@ -331,27 +415,26 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`eval`]: #method.eval
|
||||
/// [`eval`]: Chunk::eval
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn eval_async<'fut, R>(self) -> LocalBoxFuture<'fut, Result<R>>
|
||||
pub async fn eval_async<R>(self) -> Result<R>
|
||||
where
|
||||
'lua: 'fut,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
self.call_async(())
|
||||
self.call_async(()).await
|
||||
} else if let Ok(function) = self.to_expression() {
|
||||
function.call_async(())
|
||||
function.call_async(()).await
|
||||
} else {
|
||||
self.call_async(())
|
||||
self.call_async(()).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Load the chunk function and call it with the given arguments.
|
||||
///
|
||||
/// This is equivalent to `into_function` and calling the resulting function.
|
||||
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
|
||||
pub fn call<R: FromLuaMulti>(self, args: impl IntoLuaMulti) -> Result<R> {
|
||||
self.into_function()?.call(args)
|
||||
}
|
||||
|
||||
@@ -361,54 +444,46 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`call`]: #method.call
|
||||
/// [`call`]: Chunk::call
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn call_async<'fut, A, R>(self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
pub async fn call_async<R>(self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.into_function() {
|
||||
Ok(func) => func.call_async(args),
|
||||
Err(e) => Box::pin(future::err(e)),
|
||||
}
|
||||
self.into_function()?.call_async(args).await
|
||||
}
|
||||
|
||||
/// Load this chunk into a regular `Function`.
|
||||
/// Load this chunk into a regular [`Function`].
|
||||
///
|
||||
/// This simply compiles the chunk without actually executing it.
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
|
||||
pub fn into_function(mut self) -> Result<Function<'lua>> {
|
||||
pub fn into_function(mut self) -> Result<Function> {
|
||||
#[cfg(feature = "luau")]
|
||||
if self.compiler.is_some() {
|
||||
// We don't need to compile source if no compiler set
|
||||
self.compile();
|
||||
}
|
||||
|
||||
let name = self.convert_name()?;
|
||||
let name = Self::convert_name(self.name)?;
|
||||
self.lua
|
||||
.load_chunk(self.source?.as_ref(), name.as_deref(), self.env?, self.mode)
|
||||
.lock()
|
||||
.load_chunk(Some(&name), self.env?.as_ref(), self.mode, self.source?.as_ref())
|
||||
}
|
||||
|
||||
/// Compiles the chunk and changes mode to binary.
|
||||
///
|
||||
/// It does nothing if the chunk is already binary.
|
||||
/// It does nothing if the chunk is already binary or invalid.
|
||||
fn compile(&mut self) {
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
let data = self
|
||||
.compiler
|
||||
.get_or_insert_with(Default::default)
|
||||
.compile(source);
|
||||
if let Ok(data) = self.compiler.get_or_insert_with(Default::default).compile(source) {
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.load_chunk(source.as_ref(), None, None, None) {
|
||||
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
@@ -427,7 +502,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
let mut text_source = None;
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
if let Some(cache) = self.lua.app_data_ref::<ChunksCache>() {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.app_data_ref_unguarded::<ChunksCache>() {
|
||||
if let Some(data) = cache.0.get(source.as_ref()) {
|
||||
self.source = Ok(Cow::Owned(data.clone()));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
@@ -443,13 +519,14 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self.compile();
|
||||
if let Ok(ref binary_source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
if let Some(mut cache) = self.lua.app_data_mut::<ChunksCache>() {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.app_data_mut_unguarded::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
self.lua.set_app_data(cache);
|
||||
}
|
||||
let _ = lua.try_set_app_data(cache);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -457,10 +534,10 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_expression(&self) -> Result<Function<'lua>> {
|
||||
fn to_expression(&self) -> Result<Function> {
|
||||
// We assume that mode is Text
|
||||
let source = self.source.as_ref();
|
||||
let source = source.map_err(|err| Error::RuntimeError(err.to_string()))?;
|
||||
let source = source.map_err(Error::runtime)?;
|
||||
let source = Self::expression_source(source);
|
||||
// We don't need to compile source if no compiler options set
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -468,11 +545,16 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
.compiler
|
||||
.as_ref()
|
||||
.map(|c| c.compile(&source))
|
||||
.transpose()?
|
||||
.unwrap_or(source);
|
||||
|
||||
let name = self.convert_name()?;
|
||||
self.lua
|
||||
.load_chunk(&source, name.as_deref(), self.env.clone()?, None)
|
||||
let name = Self::convert_name(self.name.clone())?;
|
||||
let env = match &self.env {
|
||||
Ok(Some(env)) => Some(env),
|
||||
Ok(None) => None,
|
||||
Err(err) => return Err(err.clone()),
|
||||
};
|
||||
self.lua.lock().load_chunk(Some(&name), env, None, &source)
|
||||
}
|
||||
|
||||
fn detect_mode(&self) -> ChunkMode {
|
||||
@@ -493,12 +575,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_name(&self) -> Result<Option<CString>> {
|
||||
self.name
|
||||
.clone()
|
||||
.map(CString::new)
|
||||
.transpose()
|
||||
.map_err(|err| Error::RuntimeError(format!("invalid name: {err}")))
|
||||
fn convert_name(name: String) -> Result<CString> {
|
||||
CString::new(name).map_err(|err| Error::runtime(format!("invalid name: {err}")))
|
||||
}
|
||||
|
||||
fn expression_source(source: &[u8]) -> Vec<u8> {
|
||||
@@ -508,3 +586,33 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
struct WrappedChunk<'a, T: AsChunk<'a>> {
|
||||
chunk: T,
|
||||
caller: &'static Location<'static>,
|
||||
_marker: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a> Chunk<'a> {
|
||||
/// Wraps a chunk of Lua code, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// The resulted `IntoLua` implementation will convert the chunk into a Lua function without
|
||||
/// executing it.
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn wrap(chunk: impl AsChunk<'a> + 'a) -> impl IntoLua + 'a {
|
||||
WrappedChunk {
|
||||
chunk,
|
||||
caller: Location::caller(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsChunk<'a>> IntoLua for WrappedChunk<'a, T> {
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.load_with_location(self.chunk, self.caller)
|
||||
.into_function()
|
||||
.map(Value::Function)
|
||||
}
|
||||
}
|
||||
|
||||
+781
-231
File diff suppressed because it is too large
Load Diff
+279
-72
@@ -1,5 +1,3 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
@@ -9,6 +7,14 @@ use std::str::Utf8Error;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::private::Sealed;
|
||||
|
||||
#[cfg(feature = "error-send")]
|
||||
type DynStdError = dyn StdError + Send + Sync;
|
||||
|
||||
#[cfg(not(feature = "error-send"))]
|
||||
type DynStdError = dyn StdError;
|
||||
|
||||
/// Error type returned by `mlua` methods.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
@@ -42,16 +48,11 @@ pub enum Error {
|
||||
GarbageCollectorError(StdString),
|
||||
/// Potentially unsafe action in safe mode.
|
||||
SafetyError(StdString),
|
||||
/// Setting memory limit is not available.
|
||||
/// Memory control is not available.
|
||||
///
|
||||
/// This error can only happen when Lua state was not created by us and does not have the
|
||||
/// custom allocator attached.
|
||||
MemoryLimitNotAvailable,
|
||||
/// Main thread is not available.
|
||||
///
|
||||
/// This error can only happen in Lua5.1/LuaJIT module mode, when module loaded within a coroutine.
|
||||
/// These Lua versions does not have `LUA_RIDX_MAINTHREAD` registry key.
|
||||
MainThreadNotAvailable,
|
||||
MemoryControlNotAvailable,
|
||||
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
|
||||
///
|
||||
/// This is an error because a mutable callback can only be borrowed mutably once.
|
||||
@@ -66,15 +67,31 @@ pub enum Error {
|
||||
///
|
||||
/// Due to the way `mlua` works, it should not be directly possible to run out of stack space
|
||||
/// during normal use. The only way that this error can be triggered is if a `Function` is
|
||||
/// called with a huge number of arguments, or a rust callback returns a huge number of return
|
||||
/// called with a huge number of arguments, or a Rust callback returns a huge number of return
|
||||
/// values.
|
||||
StackError,
|
||||
/// Too many arguments to `Function::bind`
|
||||
/// Too many arguments to [`Function::bind`].
|
||||
///
|
||||
/// [`Function::bind`]: crate::Function::bind
|
||||
BindError,
|
||||
/// Bad argument received from Lua (usually when calling a function).
|
||||
///
|
||||
/// This error can help to identify the argument that caused the error
|
||||
/// (which is stored in the corresponding field).
|
||||
BadArgument {
|
||||
/// Function that was called.
|
||||
to: Option<StdString>,
|
||||
/// Argument position (usually starts from 1).
|
||||
pos: usize,
|
||||
/// Argument name.
|
||||
name: Option<StdString>,
|
||||
/// Underlying error returned when converting argument to a Lua value.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
/// A Rust value could not be converted to a Lua value.
|
||||
ToLuaConversionError {
|
||||
/// Name of the Rust type that could not be converted.
|
||||
from: &'static str,
|
||||
from: String,
|
||||
/// Name of the Lua type that could not be created.
|
||||
to: &'static str,
|
||||
/// A message indicating why the conversion failed in more detail.
|
||||
@@ -85,21 +102,21 @@ pub enum Error {
|
||||
/// Name of the Lua type that could not be converted.
|
||||
from: &'static str,
|
||||
/// Name of the Rust type that could not be created.
|
||||
to: &'static str,
|
||||
to: String,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
},
|
||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
||||
/// [`Thread::resume`] was called on an unresumable coroutine.
|
||||
///
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
|
||||
/// the coroutine.
|
||||
/// A coroutine is unresumable if its main function has returned or if an error has occurred
|
||||
/// inside the coroutine. Already running coroutines are also marked as unresumable.
|
||||
///
|
||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||
/// error.
|
||||
///
|
||||
/// [`Thread::resume`]: crate::Thread::resume
|
||||
/// [`Thread::status`]: crate::Thread::status
|
||||
CoroutineInactive,
|
||||
CoroutineUnresumable,
|
||||
/// An [`AnyUserData`] is not the expected type in a borrow.
|
||||
///
|
||||
/// This error can only happen when manually using [`AnyUserData`], or when implementing
|
||||
@@ -116,7 +133,7 @@ pub enum Error {
|
||||
///
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
UserDataDestructed,
|
||||
/// An [`AnyUserData`] immutable borrow failed because it is already borrowed mutably.
|
||||
/// An [`AnyUserData`] immutable borrow failed.
|
||||
///
|
||||
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
|
||||
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
|
||||
@@ -125,7 +142,7 @@ pub enum Error {
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`UserData`]: crate::UserData
|
||||
UserDataBorrowError,
|
||||
/// An [`AnyUserData`] mutable borrow failed because it is already borrowed.
|
||||
/// An [`AnyUserData`] mutable borrow failed.
|
||||
///
|
||||
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
|
||||
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
|
||||
@@ -142,8 +159,11 @@ pub enum Error {
|
||||
///
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodTypeError {
|
||||
/// Name of the metamethod.
|
||||
method: StdString,
|
||||
/// Passed value type.
|
||||
type_name: &'static str,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
},
|
||||
/// A [`RegistryKey`] produced from a different Lua state was used.
|
||||
@@ -177,7 +197,14 @@ pub enum Error {
|
||||
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||
/// from which the original error (and a stack traceback) can be recovered.
|
||||
ExternalError(Arc<dyn StdError + Send + Sync>),
|
||||
ExternalError(Arc<DynStdError>),
|
||||
/// An error with additional context.
|
||||
WithContext {
|
||||
/// A string containing additional context.
|
||||
context: StdString,
|
||||
/// Underlying error.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A specialized `Result` type used by `mlua`'s API.
|
||||
@@ -186,24 +213,21 @@ pub type Result<T> = StdResult<T, Error>;
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message),
|
||||
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg),
|
||||
Error::MemoryError(ref msg) => {
|
||||
write!(fmt, "memory error: {}", msg)
|
||||
match self {
|
||||
Error::SyntaxError { message, .. } => write!(fmt, "syntax error: {message}"),
|
||||
Error::RuntimeError(msg) => write!(fmt, "runtime error: {msg}"),
|
||||
Error::MemoryError(msg) => {
|
||||
write!(fmt, "memory error: {msg}")
|
||||
}
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
Error::GarbageCollectorError(ref msg) => {
|
||||
write!(fmt, "garbage collector error: {}", msg)
|
||||
Error::GarbageCollectorError(msg) => {
|
||||
write!(fmt, "garbage collector error: {msg}")
|
||||
}
|
||||
Error::SafetyError(ref msg) => {
|
||||
write!(fmt, "safety error: {}", msg)
|
||||
Error::SafetyError(msg) => {
|
||||
write!(fmt, "safety error: {msg}")
|
||||
},
|
||||
Error::MemoryLimitNotAvailable => {
|
||||
write!(fmt, "setting memory limit is not available")
|
||||
}
|
||||
Error::MainThreadNotAvailable => {
|
||||
write!(fmt, "main thread is not available in Lua 5.1")
|
||||
Error::MemoryControlNotAvailable => {
|
||||
write!(fmt, "memory control is not available")
|
||||
}
|
||||
Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
|
||||
Error::CallbackDestructed => write!(
|
||||
@@ -218,44 +242,55 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"too many arguments to Function::bind"
|
||||
),
|
||||
Error::ToLuaConversionError { from, to, ref message } => {
|
||||
write!(fmt, "error converting {} to Lua {}", from, to)?;
|
||||
match *message {
|
||||
Error::BadArgument { to, pos, name, cause } => {
|
||||
if let Some(name) = name {
|
||||
write!(fmt, "bad argument `{name}`")?;
|
||||
} else {
|
||||
write!(fmt, "bad argument #{pos}")?;
|
||||
}
|
||||
if let Some(to) = to {
|
||||
write!(fmt, " to `{to}`")?;
|
||||
}
|
||||
write!(fmt, ": {cause}")
|
||||
},
|
||||
Error::ToLuaConversionError { from, to, message } => {
|
||||
write!(fmt, "error converting {from} to Lua {to}")?;
|
||||
match message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
Some(message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::FromLuaConversionError { from, to, ref message } => {
|
||||
write!(fmt, "error converting Lua {} to {}", from, to)?;
|
||||
match *message {
|
||||
Error::FromLuaConversionError { from, to, message } => {
|
||||
write!(fmt, "error converting Lua {from} to {to}")?;
|
||||
match message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
Some(message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
|
||||
Error::CoroutineUnresumable => write!(fmt, "coroutine is non-resumable"),
|
||||
Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
|
||||
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
|
||||
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
|
||||
Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"),
|
||||
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {} is restricted", method),
|
||||
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
|
||||
write!(fmt, "metamethod {} has unsupported type {}", method, type_name)?;
|
||||
match *message {
|
||||
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
|
||||
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
|
||||
Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
|
||||
Error::MetaMethodTypeError { method, type_name, message } => {
|
||||
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
|
||||
match message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
Some(message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::MismatchedRegistryKey => {
|
||||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref cause, ref traceback } => {
|
||||
writeln!(fmt, "callback error")?;
|
||||
Error::CallbackError { cause, traceback } => {
|
||||
// Trace errors down to the root
|
||||
let (mut cause, mut full_traceback) = (cause, None);
|
||||
while let Error::CallbackError { cause: ref cause2, traceback: ref traceback2 } = **cause {
|
||||
while let Error::CallbackError { cause: cause2, traceback: traceback2 } = &**cause {
|
||||
cause = cause2;
|
||||
full_traceback = Some(traceback2);
|
||||
}
|
||||
writeln!(fmt, "{cause}")?;
|
||||
if let Some(full_traceback) = full_traceback {
|
||||
let traceback = traceback.trim_start_matches("stack traceback:");
|
||||
let traceback = traceback.trim_start().trim_end();
|
||||
@@ -269,80 +304,191 @@ impl fmt::Display for Error {
|
||||
} else {
|
||||
writeln!(fmt, "{}", traceback.trim_end())?;
|
||||
}
|
||||
write!(fmt, "caused by: {}", cause)
|
||||
Ok(())
|
||||
}
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::SerializeError(ref err) => {
|
||||
write!(fmt, "serialize error: {}", err)
|
||||
Error::SerializeError(err) => {
|
||||
write!(fmt, "serialize error: {err}")
|
||||
},
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::DeserializeError(ref err) => {
|
||||
write!(fmt, "deserialize error: {}", err)
|
||||
Error::DeserializeError(err) => {
|
||||
write!(fmt, "deserialize error: {err}")
|
||||
},
|
||||
Error::ExternalError(ref err) => write!(fmt, "{}", err),
|
||||
Error::ExternalError(err) => err.fmt(fmt),
|
||||
Error::WithContext { context, cause } => {
|
||||
writeln!(fmt, "{context}")?;
|
||||
write!(fmt, "{cause}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match *self {
|
||||
match self {
|
||||
// An error type with a source error should either return that error via source or
|
||||
// include that source's error message in its own Display output, but never both.
|
||||
// https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
|
||||
// Given that we include source to fmt::Display implementation for `CallbackError`, this call returns nothing.
|
||||
// Given that we include source to fmt::Display implementation for `CallbackError`, this call
|
||||
// returns nothing.
|
||||
Error::CallbackError { .. } => None,
|
||||
Error::ExternalError(ref err) => err.source(),
|
||||
Error::ExternalError(err) => err.source(),
|
||||
Error::WithContext { cause, .. } => Self::source(cause),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
|
||||
/// Creates a new `RuntimeError` with the given message.
|
||||
#[inline]
|
||||
pub fn runtime<S: fmt::Display>(message: S) -> Self {
|
||||
Error::RuntimeError(message.to_string())
|
||||
}
|
||||
|
||||
/// Wraps an external error object.
|
||||
#[inline]
|
||||
pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
|
||||
Error::ExternalError(err.into().into())
|
||||
}
|
||||
|
||||
/// Attempts to downcast the external error object to a concrete type by reference.
|
||||
pub fn downcast_ref<T>(&self) -> Option<&T>
|
||||
where
|
||||
T: StdError + 'static,
|
||||
{
|
||||
match self {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
Error::WithContext { cause, .. } => Self::downcast_ref(cause),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the chain of nested errors wrapped by this Error.
|
||||
pub fn chain(&self) -> impl Iterator<Item = &(dyn StdError + 'static)> {
|
||||
Chain {
|
||||
root: self,
|
||||
current: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the parent of this error.
|
||||
#[doc(hidden)]
|
||||
pub fn parent(&self) -> Option<&Error> {
|
||||
match self {
|
||||
Error::CallbackError { cause, .. } => Some(cause.as_ref()),
|
||||
Error::WithContext { cause, .. } => Some(cause.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn bad_self_argument(to: &str, cause: Error) -> Self {
|
||||
Error::BadArgument {
|
||||
to: Some(to.to_string()),
|
||||
pos: 1,
|
||||
name: Some("self".to_string()),
|
||||
cause: Arc::new(cause),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_lua_conversion(
|
||||
from: &'static str,
|
||||
to: impl ToString,
|
||||
message: impl Into<Option<String>>,
|
||||
) -> Self {
|
||||
Error::FromLuaConversionError {
|
||||
from,
|
||||
to: to.to_string(),
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for converting [`std::error::Error`] into Lua [`Error`].
|
||||
pub trait ExternalError {
|
||||
fn to_lua_err(self) -> Error;
|
||||
fn into_lua_err(self) -> Error;
|
||||
}
|
||||
|
||||
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
|
||||
fn to_lua_err(self) -> Error {
|
||||
impl<E: Into<Box<DynStdError>>> ExternalError for E {
|
||||
fn into_lua_err(self) -> Error {
|
||||
Error::external(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for converting [`std::result::Result`] into Lua [`Result`].
|
||||
pub trait ExternalResult<T> {
|
||||
fn to_lua_err(self) -> Result<T>;
|
||||
fn into_lua_err(self) -> Result<T>;
|
||||
}
|
||||
|
||||
impl<T, E> ExternalResult<T> for StdResult<T, E>
|
||||
where
|
||||
E: ExternalError,
|
||||
{
|
||||
fn to_lua_err(self) -> Result<T> {
|
||||
self.map_err(|e| e.to_lua_err())
|
||||
fn into_lua_err(self) -> Result<T> {
|
||||
self.map_err(|e| e.into_lua_err())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<AddrParseError> for Error {
|
||||
/// Provides the `context` method for [`Error`] and `Result<T, Error>`.
|
||||
pub trait ErrorContext: Sealed {
|
||||
/// Wraps the error value with additional context.
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self;
|
||||
|
||||
/// Wrap the error value with additional context that is evaluated lazily
|
||||
/// only once an error does occur.
|
||||
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self;
|
||||
}
|
||||
|
||||
impl ErrorContext for Error {
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self {
|
||||
let context = context.to_string();
|
||||
match self {
|
||||
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
|
||||
_ => Error::WithContext {
|
||||
context,
|
||||
cause: Arc::new(self),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
|
||||
let context = f(&self).to_string();
|
||||
match self {
|
||||
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
|
||||
_ => Error::WithContext {
|
||||
context,
|
||||
cause: Arc::new(self),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ErrorContext for Result<T> {
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self {
|
||||
self.map_err(|err| err.context(context))
|
||||
}
|
||||
|
||||
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
|
||||
self.map_err(|err| err.with_context(f))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AddrParseError> for Error {
|
||||
fn from(err: AddrParseError) -> Self {
|
||||
Error::external(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<IoError> for Error {
|
||||
impl From<IoError> for Error {
|
||||
fn from(err: IoError) -> Self {
|
||||
Error::external(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<Utf8Error> for Error {
|
||||
impl From<Utf8Error> for Error {
|
||||
fn from(err: Utf8Error) -> Self {
|
||||
Error::external(err)
|
||||
}
|
||||
@@ -361,3 +507,64 @@ impl serde::de::Error for Error {
|
||||
Self::DeserializeError(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "anyhow")]
|
||||
impl From<anyhow::Error> for Error {
|
||||
fn from(err: anyhow::Error) -> Self {
|
||||
match err.downcast::<Self>() {
|
||||
Ok(err) => err,
|
||||
Err(err) => Error::external(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Chain<'a> {
|
||||
root: &'a Error,
|
||||
current: Option<&'a (dyn StdError + 'static)>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Chain<'a> {
|
||||
type Item = &'a (dyn StdError + 'static);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
let error: Option<&dyn StdError> = match self.current {
|
||||
None => {
|
||||
self.current = Some(self.root);
|
||||
self.current
|
||||
}
|
||||
Some(current) => match current.downcast_ref::<Error>()? {
|
||||
Error::BadArgument { cause, .. }
|
||||
| Error::CallbackError { cause, .. }
|
||||
| Error::WithContext { cause, .. } => {
|
||||
self.current = Some(&**cause);
|
||||
self.current
|
||||
}
|
||||
Error::ExternalError(err) => {
|
||||
self.current = Some(&**err);
|
||||
self.current
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
};
|
||||
|
||||
// Skip `ExternalError` as it only wraps the underlying error
|
||||
// without meaningful context
|
||||
if let Some(Error::ExternalError(_)) = error?.downcast_ref::<Error>() {
|
||||
continue;
|
||||
}
|
||||
|
||||
return self.current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "error-send"))]
|
||||
static_assertions::assert_not_impl_any!(Error: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Error: Send, Sync);
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//! MLua compatibility layer for Lua 5.2
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use super::lua::*;
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int {
|
||||
let ret = lua_resume_(L, from, narg);
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
}
|
||||
ret
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//! Contains definitions from `luacode.h`.
|
||||
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::slice;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct lua_CompileOptions {
|
||||
pub optimizationLevel: c_int,
|
||||
pub debugLevel: c_int,
|
||||
pub coverageLevel: c_int,
|
||||
pub vectorLib: *const c_char,
|
||||
pub vectorCtor: *const c_char,
|
||||
pub mutableGlobals: *mut *const c_char,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "luau_compile"]
|
||||
pub fn luau_compile_(
|
||||
source: *const c_char,
|
||||
size: usize,
|
||||
options: *mut lua_CompileOptions,
|
||||
outsize: *mut usize,
|
||||
) -> *mut c_char;
|
||||
|
||||
fn free(p: *mut c_void);
|
||||
}
|
||||
|
||||
pub unsafe fn luau_compile(source: &[u8], mut options: lua_CompileOptions) -> Vec<u8> {
|
||||
let mut outsize = 0;
|
||||
let data_ptr = luau_compile_(
|
||||
source.as_ptr() as *const c_char,
|
||||
source.len(),
|
||||
&mut options,
|
||||
&mut outsize,
|
||||
);
|
||||
let data = slice::from_raw_parts(data_ptr as *mut u8, outsize).to_vec();
|
||||
free(data_ptr as *mut c_void);
|
||||
data
|
||||
}
|
||||
+382
-109
@@ -1,33 +1,52 @@
|
||||
use std::mem;
|
||||
use std::cell::RefCell;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
|
||||
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
|
||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
use {
|
||||
crate::traits::LuaNativeAsyncFn,
|
||||
crate::types::AsyncCallback,
|
||||
std::future::{self, Future},
|
||||
};
|
||||
|
||||
/// Handle to an internal Lua function.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Function(pub(crate) ValueRef);
|
||||
|
||||
/// Contains information about a function.
|
||||
///
|
||||
/// Please refer to the [`Lua Debug Interface`] for more information.
|
||||
///
|
||||
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionInfo {
|
||||
pub name: Option<Vec<u8>>,
|
||||
pub name_what: Option<Vec<u8>>,
|
||||
pub what: Option<Vec<u8>>,
|
||||
pub source: Option<Vec<u8>>,
|
||||
pub short_src: Option<Vec<u8>>,
|
||||
pub line_defined: i32,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub last_line_defined: i32,
|
||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||
pub name: Option<String>,
|
||||
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
|
||||
///
|
||||
/// Always `None` for Luau.
|
||||
pub name_what: Option<&'static str>,
|
||||
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is
|
||||
/// the main part of a chunk.
|
||||
pub what: &'static str,
|
||||
/// Source of the chunk that created the function.
|
||||
pub source: Option<String>,
|
||||
/// A "printable" version of `source`, to be used in error messages.
|
||||
pub short_src: Option<String>,
|
||||
/// The line number where the definition of the function starts.
|
||||
pub line_defined: Option<usize>,
|
||||
/// The line number where the definition of the function ends (not set by Luau).
|
||||
pub last_line_defined: Option<usize>,
|
||||
}
|
||||
|
||||
/// Luau function coverage snapshot.
|
||||
@@ -35,13 +54,13 @@ pub struct FunctionInfo {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CoverageInfo {
|
||||
pub function: Option<std::string::String>,
|
||||
pub function: Option<String>,
|
||||
pub line_defined: i32,
|
||||
pub depth: i32,
|
||||
pub hits: Vec<i32>,
|
||||
}
|
||||
|
||||
impl<'lua> Function<'lua> {
|
||||
impl Function {
|
||||
/// Calls the function, passing `args` as function arguments.
|
||||
///
|
||||
/// The function's return values are converted to the generic type `R`.
|
||||
@@ -58,7 +77,7 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// let tostring: Function = globals.get("tostring")?;
|
||||
///
|
||||
/// assert_eq!(tostring.call::<_, String>(123)?, "123");
|
||||
/// assert_eq!(tostring.call::<String>(123)?, "123");
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -77,41 +96,33 @@ impl<'lua> Function<'lua> {
|
||||
/// end
|
||||
/// "#).eval()?;
|
||||
///
|
||||
/// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4);
|
||||
/// assert_eq!(sum.call::<u32>((3, 4))?, 3 + 4);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
|
||||
let lua = self.0.lua;
|
||||
pub fn call<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
let mut args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
let results = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, nargs + 3)?;
|
||||
|
||||
ffi::lua_pushcfunction(lua.state, error_traceback);
|
||||
let stack_start = ffi::lua_gettop(lua.state);
|
||||
// Push error handler
|
||||
lua.push_error_traceback();
|
||||
let stack_start = ffi::lua_gettop(state);
|
||||
// Push function and the arguments
|
||||
lua.push_ref(&self.0);
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
|
||||
let nargs = args.push_into_stack_multi(&lua)?;
|
||||
// Call the function
|
||||
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
|
||||
if ret != ffi::LUA_OK {
|
||||
return Err(pop_error(lua.state, ret));
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
let nresults = ffi::lua_gettop(lua.state) - stack_start;
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
assert_stack(lua.state, 2);
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
}
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
results
|
||||
};
|
||||
R::from_lua_multi(results, lua)
|
||||
// Get the results
|
||||
let nresults = ffi::lua_gettop(state) - stack_start;
|
||||
R::from_stack_multi(nresults, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
|
||||
@@ -125,18 +136,17 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
/// use futures_timer::Delay;
|
||||
/// # use mlua::{Lua, Result};
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
///
|
||||
/// let sleep = lua.create_async_function(move |_lua, n: u64| async move {
|
||||
/// Delay::new(Duration::from_millis(n)).await;
|
||||
/// tokio::time::sleep(Duration::from_millis(n)).await;
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
///
|
||||
/// sleep.call_async(10).await?;
|
||||
/// sleep.call_async::<()>(10).await?;
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -145,21 +155,19 @@ impl<'lua> Function<'lua> {
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
match lua.create_recycled_thread(self.clone()) {
|
||||
Ok(t) => {
|
||||
let mut t = t.into_async(args);
|
||||
t.set_recyclable(true);
|
||||
Box::pin(t)
|
||||
}
|
||||
Err(e) => Box::pin(future::err(e)),
|
||||
}
|
||||
let lua = self.0.lua.lock();
|
||||
let thread_res = unsafe {
|
||||
lua.create_recycled_thread(self).map(|th| {
|
||||
let mut th = th.into_async(args);
|
||||
th.set_recyclable(true);
|
||||
th
|
||||
})
|
||||
};
|
||||
async move { thread_res?.await }
|
||||
}
|
||||
|
||||
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
|
||||
@@ -181,16 +189,16 @@ impl<'lua> Function<'lua> {
|
||||
/// "#).eval()?;
|
||||
///
|
||||
/// let bound_a = sum.bind(1)?;
|
||||
/// assert_eq!(bound_a.call::<_, u32>(2)?, 1 + 2);
|
||||
/// assert_eq!(bound_a.call::<u32>(2)?, 1 + 2);
|
||||
///
|
||||
/// let bound_a_and_b = sum.bind(13)?.bind(57)?;
|
||||
/// assert_eq!(bound_a_and_b.call::<_, u32>(())?, 13 + 57);
|
||||
/// assert_eq!(bound_a_and_b.call::<u32>(())?, 13 + 57);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
|
||||
unsafe extern "C" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
pub fn bind(&self, args: impl IntoLuaMulti) -> Result<Function> {
|
||||
unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
|
||||
ffi::luaL_checkstack(state, nbinds, ptr::null());
|
||||
@@ -205,9 +213,10 @@ impl<'lua> Function<'lua> {
|
||||
nargs + nbinds
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let args = args.into_lua_multi(lua.lua())?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
if nargs == 0 {
|
||||
@@ -219,20 +228,21 @@ impl<'lua> Function<'lua> {
|
||||
}
|
||||
|
||||
let args_wrapper = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, nargs + 3)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, nargs + 3)?;
|
||||
|
||||
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
||||
for arg in args {
|
||||
ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
|
||||
for arg in &args {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
protect_lua!(lua.state, nargs + 1, 1, fn(state) {
|
||||
protect_lua!(state, nargs + 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
|
||||
})?;
|
||||
|
||||
Function(lua.pop_ref())
|
||||
};
|
||||
|
||||
let lua = lua.lua();
|
||||
lua.load(
|
||||
r#"
|
||||
local func, args_wrapper = ...
|
||||
@@ -242,8 +252,92 @@ impl<'lua> Function<'lua> {
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("_mlua_bind")?
|
||||
.call((self.clone(), args_wrapper))
|
||||
.set_name("__mlua_bind")
|
||||
.call((self, args_wrapper))
|
||||
}
|
||||
|
||||
/// Returns the environment of the Lua function.
|
||||
///
|
||||
/// By default Lua functions shares a global environment.
|
||||
///
|
||||
/// This function always returns `None` for Rust/C functions.
|
||||
pub fn environment(&self) -> Option<Table> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_iscfunction(state, -1) != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_getfenv(state, -1);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
for i in 1..=255 {
|
||||
// Traverse upvalues until we find the _ENV one
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
s if s.is_null() => break,
|
||||
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => break,
|
||||
_ => ffi::lua_pop(state, 1),
|
||||
}
|
||||
}
|
||||
|
||||
if ffi::lua_type(state, -1) != ffi::LUA_TTABLE {
|
||||
return None;
|
||||
}
|
||||
Some(Table(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the environment of the Lua function.
|
||||
///
|
||||
/// The environment is a table that is used as the global environment for the function.
|
||||
/// Returns `true` if environment successfully changed, `false` otherwise.
|
||||
///
|
||||
/// This function does nothing for Rust/C functions.
|
||||
pub fn set_environment(&self, env: Table) -> Result<bool> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_iscfunction(state, -1) != 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
{
|
||||
lua.push_ref(&env.0);
|
||||
ffi::lua_setfenv(state, -2);
|
||||
}
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
for i in 1..=255 {
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
s if s.is_null() => return Ok(false),
|
||||
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => {
|
||||
ffi::lua_pop(state, 1);
|
||||
// Create an anonymous function with the new environment
|
||||
let f_with_env = lua
|
||||
.lua()
|
||||
.load("return _ENV")
|
||||
.set_environment(env)
|
||||
.try_cache()
|
||||
.into_function()?;
|
||||
lua.push_ref(&f_with_env.0);
|
||||
ffi::lua_upvaluejoin(state, -2, i, -1, 1);
|
||||
break;
|
||||
}
|
||||
_ => ffi::lua_pop(state, 1),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns information about the function.
|
||||
@@ -252,34 +346,40 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo
|
||||
pub fn info(&self) -> FunctionInfo {
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
lua.push_ref(&self.0);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let res = ffi::lua_getinfo(lua.state, cstr!(">Sn"), &mut ar);
|
||||
let res = ffi::lua_getinfo(state, cstr!(">Sn"), &mut ar);
|
||||
#[cfg(feature = "luau")]
|
||||
let res = ffi::lua_getinfo(lua.state, -1, cstr!("sn"), &mut ar);
|
||||
let res = ffi::lua_getinfo(state, -1, cstr!("sn"), &mut ar);
|
||||
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
|
||||
|
||||
FunctionInfo {
|
||||
name: ptr_to_cstr_bytes(ar.name).map(|s| s.to_vec()),
|
||||
name: ptr_to_lossy_str(ar.name).map(|s| s.into_owned()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
name_what: ptr_to_cstr_bytes(ar.namewhat).map(|s| s.to_vec()),
|
||||
name_what: match ptr_to_str(ar.namewhat) {
|
||||
Some("") => None,
|
||||
val => val,
|
||||
},
|
||||
#[cfg(feature = "luau")]
|
||||
name_what: None,
|
||||
what: ptr_to_cstr_bytes(ar.what).map(|s| s.to_vec()),
|
||||
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
|
||||
what: ptr_to_str(ar.what).unwrap_or("main"),
|
||||
source: ptr_to_lossy_str(ar.source).map(|s| s.into_owned()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src.as_ptr()).map(|s| s.to_vec()),
|
||||
short_src: ptr_to_lossy_str(ar.short_src.as_ptr()).map(|s| s.into_owned()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src).map(|s| s.to_vec()),
|
||||
line_defined: ar.linedefined,
|
||||
short_src: ptr_to_lossy_str(ar.short_src).map(|s| s.into_owned()),
|
||||
line_defined: linenumber_to_usize(ar.linedefined),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: ar.lastlinedefined,
|
||||
last_line_defined: linenumber_to_usize(ar.lastlinedefined),
|
||||
#[cfg(feature = "luau")]
|
||||
last_line_defined: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,13 +389,13 @@ impl<'lua> Function<'lua> {
|
||||
/// If `strip` is true, the binary representation may not include all debug information
|
||||
/// about the function, to save space.
|
||||
///
|
||||
/// For Luau a [Compiler] can be used to compile Lua chunks to bytecode.
|
||||
/// For Luau a [`Compiler`] can be used to compile Lua chunks to bytecode.
|
||||
///
|
||||
/// [Compiler]: crate::chunk::Compiler
|
||||
/// [`Compiler`]: crate::chunk::Compiler
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn dump(&self, strip: bool) -> Vec<u8> {
|
||||
unsafe extern "C" fn writer(
|
||||
unsafe extern "C-unwind" fn writer(
|
||||
_state: *mut ffi::lua_State,
|
||||
buf: *const c_void,
|
||||
buf_len: usize,
|
||||
@@ -307,16 +407,17 @@ impl<'lua> Function<'lua> {
|
||||
0
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
|
||||
ffi::lua_dump(lua.state, writer, data_ptr, strip as i32);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
ffi::lua_dump(state, writer, data_ptr, strip as i32);
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
data
|
||||
@@ -324,15 +425,15 @@ impl<'lua> Function<'lua> {
|
||||
|
||||
/// Retrieves recorded coverage information about this Lua function including inner calls.
|
||||
///
|
||||
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`] snapshot
|
||||
/// per each executed inner function.
|
||||
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`]
|
||||
/// snapshot per each executed inner function.
|
||||
///
|
||||
/// Recording of coverage information is controlled by [`Compiler::set_coverage_level`] option.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
///
|
||||
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
|
||||
#[cfg(any(feature = "luau", docsrs))]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn coverage<F>(&self, mut func: F)
|
||||
where
|
||||
@@ -341,7 +442,7 @@ impl<'lua> Function<'lua> {
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
unsafe extern "C" fn callback<F: FnMut(CoverageInfo)>(
|
||||
unsafe extern "C-unwind" fn callback<F: FnMut(CoverageInfo)>(
|
||||
data: *mut c_void,
|
||||
function: *const c_char,
|
||||
line_defined: c_int,
|
||||
@@ -363,20 +464,192 @@ impl<'lua> Function<'lua> {
|
||||
});
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let func_ptr = &mut func as *mut F as *mut c_void;
|
||||
ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::<F>);
|
||||
ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this function to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Creates a deep clone of the Lua function.
|
||||
///
|
||||
/// Copies the function prototype and all its upvalues to the
|
||||
/// newly created function.
|
||||
/// This function returns shallow clone (same handle) for Rust/C functions.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn deep_clone(&self) -> Self {
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
if ffi::lua_iscfunction(ref_thread, self.0.index) != 0 {
|
||||
return self.clone();
|
||||
}
|
||||
|
||||
ffi::lua_clonefunction(ref_thread, self.0.index);
|
||||
Function(lua.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Function<'lua> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
struct WrappedFunction(pub(crate) Callback);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
struct WrappedAsyncFunction(pub(crate) AsyncCallback);
|
||||
|
||||
impl Function {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
#[inline]
|
||||
pub fn wrap<F, A, R>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
///
|
||||
/// This function is similar to [`Function::wrap`] but any returned `Result` will be converted
|
||||
/// to a `ok, err` tuple without throwing an exception.
|
||||
#[inline]
|
||||
pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args).push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function is similar to [`Function::wrap_mut`] but any returned `Result` will be
|
||||
/// converted to a `ok, err` tuple without throwing an exception.
|
||||
#[inline]
|
||||
pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args).push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let lua = rawlua.lua();
|
||||
let fut = func.call(args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
///
|
||||
/// This function is similar to [`Function::wrap_async`] but any returned `Result` will be
|
||||
/// converted to a `ok, err` tuple without throwing an exception.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let lua = rawlua.lua();
|
||||
let fut = func.call(args);
|
||||
Box::pin(async move { fut.await.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for WrappedFunction {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.lock().create_callback(self.0).map(Value::Function)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl IntoLua for WrappedAsyncFunction {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.lock().create_async_callback(self.0).map(Value::Function)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Function {
|
||||
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(Function: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Function: Send, Sync);
|
||||
}
|
||||
|
||||
+132
-86
@@ -1,40 +1,63 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ops::Deref;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::ffi::{self, lua_Debug};
|
||||
use crate::lua::Lua;
|
||||
use crate::util::ptr_to_cstr_bytes;
|
||||
use ffi::lua_Debug;
|
||||
|
||||
use crate::state::RawLua;
|
||||
use crate::types::ReentrantMutexGuard;
|
||||
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the Lua [documentation][lua_doc].
|
||||
/// found in the Lua [documentation].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
||||
pub struct Debug<'lua> {
|
||||
lua: &'lua Lua,
|
||||
pub struct Debug<'a> {
|
||||
lua: EitherLua<'a>,
|
||||
ar: ActivationRecord,
|
||||
#[cfg(feature = "luau")]
|
||||
level: c_int,
|
||||
}
|
||||
|
||||
impl<'lua> Debug<'lua> {
|
||||
enum EitherLua<'a> {
|
||||
Owned(ReentrantMutexGuard<'a, RawLua>),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) fn new(lua: &'lua Lua, ar: *mut lua_Debug) -> Self {
|
||||
Borrowed(&'a RawLua),
|
||||
}
|
||||
|
||||
impl Deref for EitherLua<'_> {
|
||||
type Target = RawLua;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
EitherLua::Owned(guard) => guard,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
EitherLua::Borrowed(lua) => lua,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Debug<'a> {
|
||||
// We assume the lock is held when this function is called.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) fn new(lua: &'a RawLua, ar: *mut lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua,
|
||||
lua: EitherLua::Borrowed(lua),
|
||||
ar: ActivationRecord::Borrowed(ar),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_owned(lua: &'lua Lua, _level: c_int, ar: lua_Debug) -> Self {
|
||||
pub(crate) fn new_owned(guard: ReentrantMutexGuard<'a, RawLua>, _level: c_int, ar: lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua,
|
||||
lua: EitherLua::Owned(guard),
|
||||
ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
|
||||
#[cfg(feature = "luau")]
|
||||
level: _level,
|
||||
@@ -43,7 +66,7 @@ impl<'lua> Debug<'lua> {
|
||||
|
||||
/// Returns the specific event that triggered the hook.
|
||||
///
|
||||
/// For [Lua 5.1] `DebugEvent::TailCall` is used for return events to indicate a return
|
||||
/// For [Lua 5.1] [`DebugEvent::TailCall`] is used for return events to indicate a return
|
||||
/// from a function that did a tail call.
|
||||
///
|
||||
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
|
||||
@@ -63,23 +86,26 @@ impl<'lua> Debug<'lua> {
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
pub fn names(&self) -> DebugNames<'lua> {
|
||||
pub fn names(&self) -> DebugNames {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("n"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `n`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, self.level, cstr!("n"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `n`"
|
||||
);
|
||||
|
||||
DebugNames {
|
||||
name: ptr_to_cstr_bytes((*self.ar.get()).name),
|
||||
name: ptr_to_lossy_str((*self.ar.get()).name),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
name_what: ptr_to_cstr_bytes((*self.ar.get()).namewhat),
|
||||
name_what: match ptr_to_str((*self.ar.get()).namewhat) {
|
||||
Some("") => None,
|
||||
val => val,
|
||||
},
|
||||
#[cfg(feature = "luau")]
|
||||
name_what: None,
|
||||
}
|
||||
@@ -87,29 +113,31 @@ impl<'lua> Debug<'lua> {
|
||||
}
|
||||
|
||||
/// Corresponds to the `S` what mask.
|
||||
pub fn source(&self) -> DebugSource<'lua> {
|
||||
pub fn source(&self) -> DebugSource {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("S"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `S`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, self.level, cstr!("s"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `s`"
|
||||
);
|
||||
|
||||
DebugSource {
|
||||
source: ptr_to_cstr_bytes((*self.ar.get()).source),
|
||||
source: ptr_to_lossy_str((*self.ar.get()).source),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
|
||||
short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src),
|
||||
line_defined: (*self.ar.get()).linedefined,
|
||||
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
|
||||
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: (*self.ar.get()).lastlinedefined,
|
||||
what: ptr_to_cstr_bytes((*self.ar.get()).what),
|
||||
last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
|
||||
#[cfg(feature = "luau")]
|
||||
last_line_defined: None,
|
||||
what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,12 +147,12 @@ impl<'lua> Debug<'lua> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("l"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, self.level, cstr!("l"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
|
||||
@@ -139,7 +167,7 @@ impl<'lua> Debug<'lua> {
|
||||
pub fn is_tail_call(&self) -> bool {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("t"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `t`"
|
||||
);
|
||||
(*self.ar.get()).currentline != 0
|
||||
@@ -151,27 +179,27 @@ impl<'lua> Debug<'lua> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("u"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `u`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, self.level, cstr!("a"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `a`"
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("au"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `au`"
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar.get()).nups as i32,
|
||||
num_ups: (*self.ar.get()).nups as _,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
num_params: (*self.ar.get()).nparams as i32,
|
||||
num_params: (*self.ar.get()).nparams as _,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar.get()).nupvals as i32,
|
||||
num_params: (*self.ar.get()).nparams as i32,
|
||||
num_ups: (*self.ar.get()).nupvals,
|
||||
num_params: (*self.ar.get()).nparams,
|
||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
||||
};
|
||||
stack
|
||||
@@ -209,38 +237,42 @@ pub enum DebugEvent {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugNames<'a> {
|
||||
pub name: Option<&'a [u8]>,
|
||||
pub name_what: Option<&'a [u8]>,
|
||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||
pub name: Option<Cow<'a, str>>,
|
||||
/// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
|
||||
///
|
||||
/// Always `None` for Luau.
|
||||
pub name_what: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugSource<'a> {
|
||||
pub source: Option<&'a [u8]>,
|
||||
pub short_src: Option<&'a [u8]>,
|
||||
pub line_defined: i32,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub last_line_defined: i32,
|
||||
pub what: Option<&'a [u8]>,
|
||||
/// Source of the chunk that created the function.
|
||||
pub source: Option<Cow<'a, str>>,
|
||||
/// A "printable" version of `source`, to be used in error messages.
|
||||
pub short_src: Option<Cow<'a, str>>,
|
||||
/// The line number where the definition of the function starts.
|
||||
pub line_defined: Option<usize>,
|
||||
/// The line number where the definition of the function ends (not set by Luau).
|
||||
pub last_line_defined: Option<usize>,
|
||||
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is
|
||||
/// the main part of a chunk.
|
||||
pub what: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct DebugStack {
|
||||
pub num_ups: i32,
|
||||
/// Number of upvalues.
|
||||
pub num_ups: u8,
|
||||
/// Number of parameters.
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
pub num_params: i32,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub num_params: u8,
|
||||
/// Whether the function is a vararg function.
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub is_vararg: bool,
|
||||
}
|
||||
|
||||
@@ -266,48 +298,59 @@ pub struct HookTriggers {
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
impl HookTriggers {
|
||||
/// Returns a new instance of `HookTriggers` with [`on_calls`] trigger set.
|
||||
/// An instance of `HookTriggers` with `on_calls` trigger set.
|
||||
pub const ON_CALLS: Self = HookTriggers::new().on_calls();
|
||||
|
||||
/// An instance of `HookTriggers` with `on_returns` trigger set.
|
||||
pub const ON_RETURNS: Self = HookTriggers::new().on_returns();
|
||||
|
||||
/// An instance of `HookTriggers` with `every_line` trigger set.
|
||||
pub const EVERY_LINE: Self = HookTriggers::new().every_line();
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with all triggers disabled.
|
||||
pub const fn new() -> Self {
|
||||
HookTriggers {
|
||||
on_calls: false,
|
||||
on_returns: false,
|
||||
every_line: false,
|
||||
every_nth_instruction: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an instance of `HookTriggers` with [`on_calls`] trigger set.
|
||||
///
|
||||
/// [`on_calls`]: #structfield.on_calls
|
||||
pub fn on_calls() -> Self {
|
||||
HookTriggers {
|
||||
on_calls: true,
|
||||
..Default::default()
|
||||
}
|
||||
pub const fn on_calls(mut self) -> Self {
|
||||
self.on_calls = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`on_returns`] trigger set.
|
||||
/// Returns an instance of `HookTriggers` with [`on_returns`] trigger set.
|
||||
///
|
||||
/// [`on_returns`]: #structfield.on_returns
|
||||
pub fn on_returns() -> Self {
|
||||
HookTriggers {
|
||||
on_returns: true,
|
||||
..Default::default()
|
||||
}
|
||||
pub const fn on_returns(mut self) -> Self {
|
||||
self.on_returns = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_line`] trigger set.
|
||||
/// Returns an instance of `HookTriggers` with [`every_line`] trigger set.
|
||||
///
|
||||
/// [`every_line`]: #structfield.every_line
|
||||
pub fn every_line() -> Self {
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
}
|
||||
pub const fn every_line(mut self) -> Self {
|
||||
self.every_line = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
|
||||
/// Returns an instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
|
||||
///
|
||||
/// [`every_nth_instruction`]: #structfield.every_nth_instruction
|
||||
pub fn every_nth_instruction(n: u32) -> Self {
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(n),
|
||||
..Default::default()
|
||||
}
|
||||
pub const fn every_nth_instruction(mut self, n: u32) -> Self {
|
||||
self.every_nth_instruction = Some(n);
|
||||
self
|
||||
}
|
||||
|
||||
// Compute the mask to pass to `lua_sethook`.
|
||||
pub(crate) fn mask(&self) -> c_int {
|
||||
pub(crate) const fn mask(&self) -> c_int {
|
||||
let mut mask: c_int = 0;
|
||||
if self.on_calls {
|
||||
mask |= ffi::LUA_MASKCALL
|
||||
@@ -326,8 +369,11 @@ impl HookTriggers {
|
||||
|
||||
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
||||
// returned.
|
||||
pub(crate) fn count(&self) -> c_int {
|
||||
self.every_nth_instruction.unwrap_or(0) as c_int
|
||||
pub(crate) const fn count(&self) -> c_int {
|
||||
match self.every_nth_instruction {
|
||||
Some(n) => n as c_int,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+100
-59
@@ -10,10 +10,10 @@
|
||||
//!
|
||||
//! # Converting data
|
||||
//!
|
||||
//! The [`ToLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
|
||||
//! The [`IntoLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
|
||||
//! versa. They are implemented for many data structures found in Rust's standard library.
|
||||
//!
|
||||
//! For more general conversions, the [`ToLuaMulti`] and [`FromLuaMulti`] traits allow converting
|
||||
//! For more general conversions, the [`IntoLuaMulti`] and [`FromLuaMulti`] traits allow converting
|
||||
//! between Rust types and *any number* of Lua values.
|
||||
//!
|
||||
//! Most code in `mlua` is generic over implementors of those traits, so in most places the normal
|
||||
@@ -27,123 +27,121 @@
|
||||
//!
|
||||
//! # Serde support
|
||||
//!
|
||||
//! The [`LuaSerdeExt`] trait implemented for [`Lua`] allows conversion from Rust types to Lua values
|
||||
//! and vice versa using serde. Any user defined data type that implements [`serde::Serialize`] or
|
||||
//! [`serde::Deserialize`] can be converted.
|
||||
//! The [`LuaSerdeExt`] trait implemented for [`Lua`] allows conversion from Rust types to Lua
|
||||
//! values and vice versa using serde. Any user defined data type that implements
|
||||
//! [`serde::Serialize`] or [`serde::Deserialize`] can be converted.
|
||||
//! For convenience, additional functionality to handle `NULL` values and arrays is provided.
|
||||
//!
|
||||
//! The [`Value`] enum implements [`serde::Serialize`] trait to support serializing Lua values
|
||||
//! (including [`UserData`]) into Rust values.
|
||||
//! The [`Value`] enum and other types implement [`serde::Serialize`] trait to support serializing
|
||||
//! Lua values into Rust values.
|
||||
//!
|
||||
//! Requires `feature = "serialize"`.
|
||||
//!
|
||||
//! # Async/await support
|
||||
//!
|
||||
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
|
||||
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or polling
|
||||
//! [`AsyncThread`] using any runtime (eg. Tokio).
|
||||
//! The [`Lua::create_async_function`] allows creating non-blocking functions that returns
|
||||
//! [`Future`]. Lua code with async capabilities can be executed by [`Function::call_async`] family
|
||||
//! of functions or polling [`AsyncThread`] using any runtime (eg. Tokio).
|
||||
//!
|
||||
//! Requires `feature = "async"`.
|
||||
//!
|
||||
//! # `Send` requirement
|
||||
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds `Send` requirement
|
||||
//! to [`Function`]s and [`UserData`].
|
||||
//! # `Send` and `Sync` support
|
||||
//!
|
||||
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds
|
||||
//! `Send` requirement to Rust functions and [`UserData`] types.
|
||||
//!
|
||||
//! In this case [`Lua`] object and their types can be send or used from other threads. Internally
|
||||
//! access to Lua VM is synchronized using a reentrant mutex that can be locked many times within
|
||||
//! the same thread.
|
||||
//!
|
||||
//! [Lua programming language]: https://www.lua.org/
|
||||
//! [`Lua`]: crate::Lua
|
||||
//! [executing]: crate::Chunk::exec
|
||||
//! [evaluating]: crate::Chunk::eval
|
||||
//! [globals]: crate::Lua::globals
|
||||
//! [`ToLua`]: crate::ToLua
|
||||
//! [`FromLua`]: crate::FromLua
|
||||
//! [`ToLuaMulti`]: crate::ToLuaMulti
|
||||
//! [`FromLuaMulti`]: crate::FromLuaMulti
|
||||
//! [`Function`]: crate::Function
|
||||
//! [`UserData`]: crate::UserData
|
||||
//! [`UserDataFields`]: crate::UserDataFields
|
||||
//! [`UserDataMethods`]: crate::UserDataMethods
|
||||
//! [`LuaSerdeExt`]: crate::LuaSerdeExt
|
||||
//! [`Value`]: crate::Value
|
||||
//! [`create_async_function`]: crate::Lua::create_async_function
|
||||
//! [`call_async`]: crate::Function::call_async
|
||||
//! [`AsyncThread`]: crate::AsyncThread
|
||||
//! [`Future`]: std::future::Future
|
||||
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.8.5")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
||||
#![allow(clippy::ptr_eq)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod buffer;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod error;
|
||||
mod ffi;
|
||||
mod function;
|
||||
mod hook;
|
||||
mod lua;
|
||||
#[cfg(feature = "luau")]
|
||||
mod luau;
|
||||
mod memory;
|
||||
mod multi;
|
||||
mod scope;
|
||||
mod state;
|
||||
mod stdlib;
|
||||
mod string;
|
||||
mod table;
|
||||
mod thread;
|
||||
mod traits;
|
||||
mod types;
|
||||
mod userdata;
|
||||
mod userdata_impl;
|
||||
mod util;
|
||||
mod value;
|
||||
mod vector;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
|
||||
pub use bstr::BString;
|
||||
pub use ffi::{self, lua_CFunction, lua_State};
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::{Function, FunctionInfo};
|
||||
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::lua::{GCMode, Lua, LuaOptions};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::multi::{MultiValue, Variadic};
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
|
||||
pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::String;
|
||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
pub use crate::traits::{
|
||||
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
|
||||
};
|
||||
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
pub use crate::types::{
|
||||
AppDataRef, AppDataRefMut, Either, Integer, LightUserData, MaybeSend, Number, RegistryKey, VmState,
|
||||
};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef,
|
||||
UserDataRefMut, UserDataRegistry,
|
||||
};
|
||||
pub use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub use crate::hook::HookTriggers;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::{chunk::Compiler, function::CoverageInfo, types::VmState};
|
||||
pub use crate::{buffer::Buffer, chunk::Compiler, function::CoverageInfo, vector::Vector};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{
|
||||
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
|
||||
};
|
||||
pub use crate::serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub mod serde;
|
||||
|
||||
#[cfg(any(feature = "mlua_derive"))]
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
@@ -153,7 +151,7 @@ extern crate mlua_derive;
|
||||
/// This macro allows to write Lua code directly in Rust code.
|
||||
///
|
||||
/// Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
|
||||
/// User's Rust types needs to implement [`UserData`] or [`ToLua`] traits.
|
||||
/// User's Rust types needs to implement [`UserData`] or [`IntoLua`] traits.
|
||||
///
|
||||
/// Captured variables are **moved** into the chunk.
|
||||
///
|
||||
@@ -188,27 +186,31 @@ extern crate mlua_derive;
|
||||
///
|
||||
/// Other minor limitations:
|
||||
///
|
||||
/// - Certain escape codes in string literals don't work.
|
||||
/// (Specifically: `\a`, `\b`, `\f`, `\v`, `\123` (octal escape codes), `\u`, and `\U`).
|
||||
/// - Certain escape codes in string literals don't work. (Specifically: `\a`, `\b`, `\f`, `\v`,
|
||||
/// `\123` (octal escape codes), `\u`, and `\U`).
|
||||
///
|
||||
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`.
|
||||
///
|
||||
/// - The `//` (floor division) operator is unusable, as its start a comment.
|
||||
///
|
||||
/// Everything else should work.
|
||||
///
|
||||
/// [`AsChunk`]: crate::AsChunk
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`ToLua`]: crate::ToLua
|
||||
#[cfg(any(feature = "macros"))]
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
|
||||
/// Derive [`FromLua`] for a Rust type.
|
||||
///
|
||||
/// Current implementation generate code that takes [`UserData`] value, borrow it (of the Rust type)
|
||||
/// and clone.
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::FromLua;
|
||||
|
||||
/// Registers Lua module entrypoint.
|
||||
///
|
||||
/// You can register multiple entrypoints as required.
|
||||
///
|
||||
/// ```
|
||||
/// ```ignore
|
||||
/// use mlua::{Lua, Result, Table};
|
||||
///
|
||||
/// #[mlua::lua_module]
|
||||
@@ -221,6 +223,45 @@ pub use mlua_derive::chunk;
|
||||
///
|
||||
/// Internally in the code above the compiler defines C function `luaopen_my_module`.
|
||||
///
|
||||
#[cfg(any(feature = "module", docsrs))]
|
||||
/// You can also pass options to the attribute:
|
||||
///
|
||||
/// * name - name of the module, defaults to the name of the function
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[mlua::lua_module(name = "alt_module")]
|
||||
/// fn my_module(lua: &Lua) -> Result<Table> {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// * skip_memory_check - skip memory allocation checks for some operations.
|
||||
///
|
||||
/// In module mode, mlua runs in unknown environment and cannot say are there any memory
|
||||
/// limits or not. As result, some operations that require memory allocation runs in
|
||||
/// protected mode. Setting this attribute will improve performance of such operations
|
||||
/// with risk of having uncaught exceptions and memory leaks.
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[mlua::lua_module(skip_memory_check)]
|
||||
/// fn my_module(lua: &Lua) -> Result<Table> {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(all(feature = "mlua_derive", any(feature = "module", doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
#[cfg(all(feature = "module", feature = "send"))]
|
||||
compile_error!("`send` feature is not supported in module mode");
|
||||
|
||||
pub(crate) mod private {
|
||||
use super::*;
|
||||
|
||||
pub trait Sealed {}
|
||||
|
||||
impl Sealed for Error {}
|
||||
impl<T> Sealed for std::result::Result<T, Error> {}
|
||||
impl Sealed for Lua {}
|
||||
impl Sealed for Table {}
|
||||
impl Sealed for AnyUserData {}
|
||||
}
|
||||
|
||||
-3307
File diff suppressed because it is too large
Load Diff
-124
@@ -1,124 +0,0 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_float, c_int};
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::value::Value;
|
||||
|
||||
// Since Luau has some missing standard function, we re-implement them here
|
||||
|
||||
impl Lua {
|
||||
pub(crate) unsafe fn prepare_luau_state(&self) -> Result<()> {
|
||||
let globals = self.globals();
|
||||
|
||||
globals.raw_set(
|
||||
"collectgarbage",
|
||||
self.create_c_function(lua_collectgarbage)?,
|
||||
)?;
|
||||
globals.raw_set("require", self.create_function(lua_require)?)?;
|
||||
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
|
||||
let option = ffi::luaL_optstring(state, 1, cstr!("collect"));
|
||||
let option = CStr::from_ptr(option);
|
||||
let arg = ffi::luaL_optinteger(state, 2, 0);
|
||||
match option.to_str() {
|
||||
Ok("collect") => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
|
||||
0
|
||||
}
|
||||
Ok("stop") => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
|
||||
0
|
||||
}
|
||||
Ok("restart") => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
|
||||
0
|
||||
}
|
||||
Ok("count") => {
|
||||
let kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0) as ffi::lua_Number;
|
||||
let kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0) as ffi::lua_Number;
|
||||
ffi::lua_pushnumber(state, kbytes + kbytes_rem / 1024.0);
|
||||
1
|
||||
}
|
||||
Ok("step") => {
|
||||
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg);
|
||||
ffi::lua_pushboolean(state, res);
|
||||
1
|
||||
}
|
||||
Ok("isrunning") => {
|
||||
let res = ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0);
|
||||
ffi::lua_pushboolean(state, res);
|
||||
1
|
||||
}
|
||||
_ => ffi::luaL_error(state, cstr!("collectgarbage called with invalid option")),
|
||||
}
|
||||
}
|
||||
|
||||
fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
|
||||
let name = name.ok_or_else(|| Error::RuntimeError("invalid module name".into()))?;
|
||||
|
||||
// Find module in the cache
|
||||
let loaded = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
protect_lua!(lua.state, 0, 1, fn(state) {
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
})?;
|
||||
Table(lua.pop_ref())
|
||||
};
|
||||
if let Some(v) = loaded.raw_get(name.clone())? {
|
||||
return Ok(v);
|
||||
}
|
||||
|
||||
// Load file from filesystem
|
||||
let mut search_path = std::env::var("LUAU_PATH").unwrap_or_default();
|
||||
if search_path.is_empty() {
|
||||
search_path = "?.luau;?.lua".into();
|
||||
}
|
||||
|
||||
let (mut source, mut source_name) = (None, String::new());
|
||||
for path in search_path.split(';') {
|
||||
let file_path = path.replacen('?', &name, 1);
|
||||
if let Ok(buf) = std::fs::read(&file_path) {
|
||||
source = Some(buf);
|
||||
source_name = file_path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let source = source.ok_or_else(|| Error::RuntimeError(format!("cannot find '{}'", name)))?;
|
||||
|
||||
let value = lua
|
||||
.load(&source)
|
||||
.set_name(&format!("={}", source_name))?
|
||||
.set_mode(ChunkMode::Text)
|
||||
.call::<_, Value>(())?;
|
||||
|
||||
// Save in the cache
|
||||
loaded.raw_set(
|
||||
name,
|
||||
match value.clone() {
|
||||
Value::Nil => Value::Boolean(true),
|
||||
v => v,
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
// Luau vector datatype constructor
|
||||
unsafe extern "C" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
|
||||
let x = ffi::luaL_checknumber(state, 1) as c_float;
|
||||
let y = ffi::luaL_checknumber(state, 2) as c_float;
|
||||
let z = ffi::luaL_checknumber(state, 3) as c_float;
|
||||
ffi::lua_pushvector(state, x, y, z);
|
||||
1
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::state::Lua;
|
||||
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
|
||||
impl Lua {
|
||||
pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
|
||||
let globals = self.globals();
|
||||
|
||||
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
// The environment variable `LUAU_VERSION` set by the build script
|
||||
if let Some(version) = ffi::luau_version() {
|
||||
globals.raw_set("_VERSION", format!("Luau {version}"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn disable_c_modules(&self) -> Result<()> {
|
||||
package::disable_dylibs(self);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
|
||||
let option = ffi::luaL_optstring(state, 1, cstr!("collect"));
|
||||
let option = CStr::from_ptr(option);
|
||||
let arg = ffi::luaL_optinteger(state, 2, 0);
|
||||
match option.to_str() {
|
||||
Ok("collect") => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
|
||||
0
|
||||
}
|
||||
Ok("stop") => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
|
||||
0
|
||||
}
|
||||
Ok("restart") => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
|
||||
0
|
||||
}
|
||||
Ok("count") => {
|
||||
let kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0) as ffi::lua_Number;
|
||||
let kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0) as ffi::lua_Number;
|
||||
ffi::lua_pushnumber(state, kbytes + kbytes_rem / 1024.0);
|
||||
1
|
||||
}
|
||||
Ok("step") => {
|
||||
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg);
|
||||
ffi::lua_pushboolean(state, res);
|
||||
1
|
||||
}
|
||||
Ok("isrunning") => {
|
||||
let res = ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0);
|
||||
ffi::lua_pushboolean(state, res);
|
||||
1
|
||||
}
|
||||
_ => ffi::luaL_error(state, cstr!("collectgarbage called with invalid option")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use package::register_package_module;
|
||||
|
||||
mod package;
|
||||
@@ -0,0 +1,271 @@
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Write;
|
||||
use std::os::raw::c_int;
|
||||
use std::path::{PathBuf, MAIN_SEPARATOR_STR};
|
||||
use std::string::String as StdString;
|
||||
use std::{env, fs};
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::Result;
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::traits::IntoLua;
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(unix)]
|
||||
use {libloading::Library, rustc_hash::FxHashMap};
|
||||
|
||||
//
|
||||
// Luau package module
|
||||
//
|
||||
|
||||
#[cfg(unix)]
|
||||
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 3;
|
||||
|
||||
#[cfg(all(unix, feature = "module"))]
|
||||
#[no_mangle]
|
||||
#[used]
|
||||
pub static MLUA_LUAU_ABI_VERSION: u32 = TARGET_MLUA_LUAU_ABI_VERSION;
|
||||
|
||||
// We keep reference to the loaded dylibs in application data
|
||||
#[cfg(unix)]
|
||||
struct LoadedDylibs(FxHashMap<PathBuf, Library>);
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::ops::Deref for LoadedDylibs {
|
||||
type Target = FxHashMap<PathBuf, Library>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::ops::DerefMut for LoadedDylibs {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
|
||||
// Create the package table
|
||||
let package = lua.create_table()?;
|
||||
|
||||
// Set `package.path`
|
||||
let mut search_path = env::var("LUAU_PATH")
|
||||
.or_else(|_| env::var("LUA_PATH"))
|
||||
.unwrap_or_default();
|
||||
if search_path.is_empty() {
|
||||
search_path = "?.luau;?.lua".to_string();
|
||||
}
|
||||
package.raw_set("path", search_path)?;
|
||||
|
||||
// Set `package.cpath`
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let mut search_cpath = env::var("LUAU_CPATH")
|
||||
.or_else(|_| env::var("LUA_CPATH"))
|
||||
.unwrap_or_default();
|
||||
if search_cpath.is_empty() {
|
||||
if cfg!(any(target_os = "macos", target_os = "ios")) {
|
||||
search_cpath = "?.dylib".to_string();
|
||||
} else {
|
||||
search_cpath = "?.so".to_string();
|
||||
}
|
||||
}
|
||||
package.raw_set("cpath", search_cpath)?;
|
||||
}
|
||||
|
||||
// Set `package.loaded` (table with a list of loaded modules)
|
||||
let loaded = if let Ok(Some(loaded)) = lua.named_registry_value::<Option<Table>>("_LOADED") {
|
||||
package.raw_set("loaded", &loaded)?;
|
||||
loaded
|
||||
} else {
|
||||
let loaded = lua.create_table()?;
|
||||
package.raw_set("loaded", &loaded)?;
|
||||
lua.set_named_registry_value("_LOADED", &loaded)?;
|
||||
loaded
|
||||
};
|
||||
|
||||
// Set `package.loaders`
|
||||
let loaders = lua.create_sequence_from([lua.create_function(lua_loader)?])?;
|
||||
package.raw_set("loaders", &loaders)?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
loaders.push(lua.create_function(dylib_loader)?)?;
|
||||
lua.set_app_data(LoadedDylibs(FxHashMap::default()));
|
||||
}
|
||||
lua.set_named_registry_value("_LOADERS", loaders)?;
|
||||
|
||||
// Register the module and `require` function in globals
|
||||
let globals = lua.globals();
|
||||
globals.raw_set("package", &package)?;
|
||||
loaded.raw_set("package", package)?;
|
||||
globals.raw_set("require", unsafe { lua.create_c_function(lua_require)? })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn disable_dylibs(lua: &Lua) {
|
||||
// Presence of `LoadedDylibs` in app data is used as a flag
|
||||
// to check whether binary modules are enabled
|
||||
#[cfg(unix)]
|
||||
lua.remove_app_data::<LoadedDylibs>();
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_require(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_settop(state, 1);
|
||||
let name = ffi::luaL_checkstring(state, 1);
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED")); // _LOADED is at index 2
|
||||
if ffi::lua_rawgetfield(state, 2, name) != ffi::LUA_TNIL {
|
||||
return 1; // module is already loaded
|
||||
}
|
||||
ffi::lua_pop(state, 1); // remove nil
|
||||
|
||||
// load the module
|
||||
let err_buf = ffi::lua_newuserdata_t::<StdString>(state);
|
||||
err_buf.write(StdString::new());
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADERS")); // _LOADERS is at index 3
|
||||
for i in 1.. {
|
||||
if ffi::lua_rawgeti(state, -1, i) == ffi::LUA_TNIL {
|
||||
// no more loaders?
|
||||
if (&*err_buf).is_empty() {
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found"), name);
|
||||
} else {
|
||||
let bytes = (&*err_buf).as_bytes();
|
||||
let extra = ffi::lua_pushlstring(state, bytes.as_ptr() as *const _, bytes.len());
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found:%s"), name, extra);
|
||||
}
|
||||
}
|
||||
ffi::lua_pushvalue(state, 1); // name arg
|
||||
ffi::lua_call(state, 1, 2); // call loader
|
||||
match ffi::lua_type(state, -2) {
|
||||
ffi::LUA_TFUNCTION => break, // loader found
|
||||
ffi::LUA_TSTRING => {
|
||||
// error message
|
||||
let msg = ffi::lua_tostring(state, -2);
|
||||
let msg = CStr::from_ptr(msg).to_string_lossy();
|
||||
_ = write!(&mut *err_buf, "\n\t{msg}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ffi::lua_pop(state, 2); // remove both results
|
||||
}
|
||||
ffi::lua_pushvalue(state, 1); // name is 1st argument to module loader
|
||||
ffi::lua_rotate(state, -2, 1); // loader data <-> name
|
||||
|
||||
// stack: ...; loader function; module name; loader data
|
||||
ffi::lua_call(state, 2, 1);
|
||||
// stack: ...; result from loader function
|
||||
if ffi::lua_isnil(state, -1) != 0 {
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_pushboolean(state, 1); // use true as result
|
||||
}
|
||||
ffi::lua_pushvalue(state, -1); // make copy of entrypoint result
|
||||
ffi::lua_setfield(state, 2, name); /* _LOADED[name] = returned value */
|
||||
1
|
||||
}
|
||||
|
||||
/// Searches for the given `name` in the given `path`.
|
||||
///
|
||||
/// `path` is a string containing a sequence of templates separated by semicolons.
|
||||
fn package_searchpath(name: &str, search_path: &str, try_prefix: bool) -> Option<PathBuf> {
|
||||
let mut names = vec![name.replace('.', MAIN_SEPARATOR_STR)];
|
||||
if try_prefix && name.contains('.') {
|
||||
let prefix = name.split_once('.').map(|(prefix, _)| prefix).unwrap();
|
||||
names.push(prefix.to_string());
|
||||
}
|
||||
for path in search_path.split(';') {
|
||||
for name in &names {
|
||||
let file_path = PathBuf::from(path.replace('?', name));
|
||||
if let Ok(true) = fs::metadata(&file_path).map(|m| m.is_file()) {
|
||||
return Some(file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
//
|
||||
// Module loaders
|
||||
//
|
||||
|
||||
/// Tries to load a lua (text) file
|
||||
fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
|
||||
loaded.raw_get::<Table>("package")
|
||||
}?;
|
||||
let search_path = package.get::<StdString>("path").unwrap_or_default();
|
||||
|
||||
if let Some(file_path) = package_searchpath(&modname, &search_path, false) {
|
||||
match fs::read(&file_path) {
|
||||
Ok(buf) => {
|
||||
return lua
|
||||
.load(buf)
|
||||
.set_name(format!("={}", file_path.display()))
|
||||
.set_mode(ChunkMode::Text)
|
||||
.into_function()
|
||||
.map(Value::Function);
|
||||
}
|
||||
Err(err) => {
|
||||
return format!("cannot open '{}': {err}", file_path.display()).into_lua(lua);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
/// Tries to load a dynamic library
|
||||
#[cfg(unix)]
|
||||
fn dylib_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
|
||||
loaded.raw_get::<Table>("package")
|
||||
}?;
|
||||
let search_cpath = package.get::<StdString>("cpath").unwrap_or_default();
|
||||
|
||||
let find_symbol = |lib: &Library| unsafe {
|
||||
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes()) {
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
// Try all in one mode
|
||||
if let Ok(entry) =
|
||||
lib.get::<ffi::lua_CFunction>(format!("luaopen_{}\0", modname.replace('.', "_")).as_bytes())
|
||||
{
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
"cannot find module entrypoint".into_lua(lua)
|
||||
};
|
||||
|
||||
if let Some(file_path) = package_searchpath(&modname, &search_cpath, true) {
|
||||
let file_path = file_path.canonicalize()?;
|
||||
// Load the library and check for symbol
|
||||
unsafe {
|
||||
let mut loaded_dylibs = match lua.app_data_mut::<LoadedDylibs>() {
|
||||
Some(loaded_dylibs) => loaded_dylibs,
|
||||
None => return "dynamic libraries are disabled in safe mode".into_lua(lua),
|
||||
};
|
||||
// Check if it's already loaded
|
||||
if let Some(lib) = loaded_dylibs.get(&file_path) {
|
||||
return find_symbol(lib);
|
||||
}
|
||||
if let Ok(lib) = Library::new(&file_path) {
|
||||
// Check version
|
||||
let mod_version = lib.get::<*const u32>(b"MLUA_LUAU_ABI_VERSION");
|
||||
let mod_version = mod_version.map(|v| **v).unwrap_or_default();
|
||||
if mod_version != TARGET_MLUA_LUAU_ABI_VERSION {
|
||||
let err = format!("wrong module ABI version (expected {TARGET_MLUA_LUAU_ABI_VERSION}, got {mod_version})");
|
||||
return err.into_lua(lua);
|
||||
}
|
||||
let symbol = find_symbol(&lib);
|
||||
loaded_dylibs.insert(file_path, lib);
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
+9
-4
@@ -10,8 +10,7 @@ macro_rules! bug_msg {
|
||||
|
||||
macro_rules! cstr {
|
||||
($s:expr) => {
|
||||
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
|
||||
as *const ::std::os::raw::c_char
|
||||
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] as *const ::std::os::raw::c_char
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,9 +100,15 @@ macro_rules! protect_lua {
|
||||
};
|
||||
|
||||
($state:expr, $nargs:expr, $nresults:expr, fn($state_inner:ident) $code:expr) => {{
|
||||
unsafe extern "C" fn do_call($state_inner: *mut ffi::lua_State) -> ::std::os::raw::c_int {
|
||||
use ::std::os::raw::c_int;
|
||||
unsafe extern "C-unwind" fn do_call($state_inner: *mut ffi::lua_State) -> c_int {
|
||||
$code;
|
||||
$nresults
|
||||
let nresults = $nresults;
|
||||
if nresults == ::ffi::LUA_MULTRET {
|
||||
ffi::lua_gettop($state_inner)
|
||||
} else {
|
||||
nresults
|
||||
}
|
||||
}
|
||||
|
||||
crate::util::protect_lua_call($state, $nargs, do_call)
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
use std::alloc::{self, Layout};
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
pub(crate) static ALLOCATOR: ffi::lua_Alloc = allocator;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
pub(crate) struct MemoryState {
|
||||
used_memory: isize,
|
||||
memory_limit: isize,
|
||||
// Can be set to temporary ignore the memory limit.
|
||||
// This is used when calling `lua_pushcfunction` for lua5.1/jit/luau.
|
||||
ignore_limit: bool,
|
||||
// Indicates that the memory limit was reached on the last allocation.
|
||||
#[cfg(feature = "luau")]
|
||||
limit_reached: bool,
|
||||
}
|
||||
|
||||
impl MemoryState {
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
ffi::lua_getallocf(state, &mut mem_state);
|
||||
mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata");
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::since(1.85)]
|
||||
#[inline]
|
||||
#[allow(clippy::incompatible_msrv)]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::before(1.85)]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn used_memory(&self) -> usize {
|
||||
self.used_memory as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn memory_limit(&self) -> usize {
|
||||
self.memory_limit as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_memory_limit(&mut self, limit: usize) -> usize {
|
||||
let prev_limit = self.memory_limit;
|
||||
self.memory_limit = limit as isize;
|
||||
prev_limit as usize
|
||||
}
|
||||
|
||||
// This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit/luau
|
||||
// to bypass the memory limit (if set).
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
let mem_state = Self::get(state);
|
||||
if !mem_state.is_null() {
|
||||
(*mem_state).ignore_limit = true;
|
||||
f();
|
||||
(*mem_state).ignore_limit = false;
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
// Does nothing apart from calling `f()`, we don't need to bypass any limits
|
||||
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(_state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
f();
|
||||
}
|
||||
|
||||
// Returns `true` if the memory limit was reached on the last memory operation
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn limit_reached(state: *mut ffi::lua_State) -> bool {
|
||||
(*Self::get(state)).limit_reached
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn allocator(
|
||||
extra: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
nsize: usize,
|
||||
) -> *mut c_void {
|
||||
let mem_state = &mut *(extra as *mut MemoryState);
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
// Reset the flag
|
||||
mem_state.limit_reached = false;
|
||||
}
|
||||
|
||||
if nsize == 0 {
|
||||
// Free memory
|
||||
if !ptr.is_null() {
|
||||
let layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
|
||||
alloc::dealloc(ptr as *mut u8, layout);
|
||||
mem_state.used_memory -= osize as isize;
|
||||
}
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
// Do not allocate more than isize::MAX
|
||||
if nsize > isize::MAX as usize {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
// Are we fit to the memory limits?
|
||||
let mut mem_diff = nsize as isize;
|
||||
if !ptr.is_null() {
|
||||
mem_diff -= osize as isize;
|
||||
}
|
||||
let mem_limit = mem_state.memory_limit;
|
||||
let new_used_memory = mem_state.used_memory + mem_diff;
|
||||
if mem_limit > 0 && new_used_memory > mem_limit && !mem_state.ignore_limit {
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
mem_state.limit_reached = true;
|
||||
}
|
||||
return ptr::null_mut();
|
||||
}
|
||||
mem_state.used_memory += mem_diff;
|
||||
|
||||
if ptr.is_null() {
|
||||
// Allocate new memory
|
||||
let new_layout = match Layout::from_size_align(nsize, ffi::SYS_MIN_ALIGN) {
|
||||
Ok(layout) => layout,
|
||||
Err(_) => return ptr::null_mut(),
|
||||
};
|
||||
let new_ptr = alloc::alloc(new_layout) as *mut c_void;
|
||||
if new_ptr.is_null() {
|
||||
alloc::handle_alloc_error(new_layout);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
// Reallocate memory
|
||||
let old_layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
|
||||
let new_ptr = alloc::realloc(ptr as *mut u8, old_layout, nsize) as *mut c_void;
|
||||
if new_ptr.is_null() {
|
||||
alloc::handle_alloc_error(old_layout);
|
||||
}
|
||||
new_ptr
|
||||
}
|
||||
+320
-84
@@ -1,58 +1,212 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::collections::{vec_deque, VecDeque};
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::util::check_stack;
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
||||
/// Result is convertible to [`MultiValue`] following the common Lua idiom of returning the result
|
||||
/// on success, or in the case of an error, returning `nil` and an error message.
|
||||
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
|
||||
impl<T: IntoLua, E: IntoLua> IntoLuaMulti for StdResult<T, E> {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut result = MultiValue::new_or_cached(lua);
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
match self {
|
||||
Ok(v) => result.push_front(v.to_lua(lua)?),
|
||||
Err(e) => {
|
||||
result.push_front(e.to_lua(lua)?);
|
||||
result.push_front(Nil);
|
||||
}
|
||||
Ok(val) => (val,).into_lua_multi(lua),
|
||||
Err(err) => (Nil, err).into_lua_multi(lua),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
match self {
|
||||
Ok(val) => (val,).push_into_stack_multi(lua),
|
||||
Err(err) => (Nil, err).push_into_stack_multi(lua),
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
||||
impl<E: IntoLua> IntoLuaMulti for StdResult<(), E> {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut v = MultiValue::new_or_cached(lua);
|
||||
v.push_front(self.to_lua(lua)?);
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
match self {
|
||||
Ok(_) => const { Ok(MultiValue::new()) },
|
||||
Err(err) => (Nil, err).into_lua_multi(lua),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
match self {
|
||||
Ok(_) => Ok(0),
|
||||
Err(err) => (Nil, err).push_into_stack_multi(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoLua> IntoLuaMulti for T {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
let mut v = MultiValue::with_capacity(1);
|
||||
v.push_back(self.into_lua(lua)?);
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
|
||||
lua.cache_multivalue(values);
|
||||
res
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
self.push_into_stack(lua)?;
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLuaMulti<'lua> for MultiValue<'lua> {
|
||||
impl<T: FromLua> FromLuaMulti for T {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
|
||||
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_lua_args(mut args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
T::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
if nvals == 0 {
|
||||
return T::from_lua(Nil, lua.lua());
|
||||
}
|
||||
T::from_stack(-nvals, lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
if nargs == 0 {
|
||||
return T::from_lua_arg(Nil, i, to, lua.lua());
|
||||
}
|
||||
T::from_stack_arg(-nargs, i, to, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiple Lua values used for both argument passing and also for multiple return values.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct MultiValue(VecDeque<Value>);
|
||||
|
||||
impl Deref for MultiValue {
|
||||
type Target = VecDeque<Value>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MultiValue {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiValue {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
#[inline]
|
||||
pub const fn new() -> MultiValue {
|
||||
MultiValue(VecDeque::new())
|
||||
}
|
||||
|
||||
/// Creates an empty `MultiValue` container with space for at least `capacity` elements.
|
||||
pub fn with_capacity(capacity: usize) -> MultiValue {
|
||||
MultiValue(VecDeque::with_capacity(capacity))
|
||||
}
|
||||
|
||||
/// Creates a `MultiValue` container from vector of values.
|
||||
///
|
||||
/// This method works in *O*(1) time and does not allocate any additional memory.
|
||||
#[inline]
|
||||
pub fn from_vec(vec: Vec<Value>) -> MultiValue {
|
||||
vec.into()
|
||||
}
|
||||
|
||||
/// Consumes the `MultiValue` and returns a vector of values.
|
||||
///
|
||||
/// This method needs *O*(*n*) data movement if the circular buffer doesn't happen to be at the
|
||||
/// beginning of the allocation.
|
||||
#[inline]
|
||||
pub fn into_vec(self) -> Vec<Value> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
|
||||
let iter = iter.into_iter();
|
||||
let mut multi_value = MultiValue::with_capacity(iter.size_hint().0);
|
||||
for value in iter {
|
||||
multi_value.push_back(value.into_lua(lua)?);
|
||||
}
|
||||
Ok(multi_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Value>> for MultiValue {
|
||||
#[inline]
|
||||
fn from(value: Vec<Value>) -> Self {
|
||||
MultiValue(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiValue> for Vec<Value> {
|
||||
#[inline]
|
||||
fn from(value: MultiValue) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Value> for MultiValue {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
|
||||
let mut multi_value = MultiValue::new();
|
||||
multi_value.extend(iter);
|
||||
multi_value
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for MultiValue {
|
||||
type Item = Value;
|
||||
type IntoIter = vec_deque::IntoIter<Value>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(mut self) -> Self::IntoIter {
|
||||
let deque = mem::take(&mut self.0);
|
||||
mem::forget(self);
|
||||
deque.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a MultiValue {
|
||||
type Item = &'a Value;
|
||||
type IntoIter = vec_deque::Iter<'a, Value>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLuaMulti for MultiValue {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
|
||||
impl FromLuaMulti for MultiValue {
|
||||
#[inline]
|
||||
fn from_lua_multi(values: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_multi(values: MultiValue, _: &Lua) -> Result<Self> {
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
@@ -80,10 +234,7 @@ impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`FromLua`]: crate::FromLua
|
||||
/// [`MultiValue`]: crate::MultiValue
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Variadic<T>(Vec<T>);
|
||||
|
||||
impl<T> Variadic<T> {
|
||||
@@ -91,11 +242,38 @@ impl<T> Variadic<T> {
|
||||
pub const fn new() -> Variadic<T> {
|
||||
Variadic(Vec::new())
|
||||
}
|
||||
|
||||
/// Creates an empty `Variadic` container with space for at least `capacity` elements.
|
||||
pub fn with_capacity(capacity: usize) -> Variadic<T> {
|
||||
Variadic(Vec::with_capacity(capacity))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Variadic<T> {
|
||||
fn default() -> Variadic<T> {
|
||||
Variadic::new()
|
||||
impl<T> Deref for Variadic<T> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Variadic<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for Variadic<T> {
|
||||
#[inline]
|
||||
fn from(vec: Vec<T>) -> Self {
|
||||
Variadic(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Variadic<T>> for Vec<T> {
|
||||
#[inline]
|
||||
fn from(value: Variadic<T>) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,88 +292,136 @@ impl<T> IntoIterator for Variadic<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Variadic<T> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Variadic<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
|
||||
impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut values = MultiValue::new_or_cached(lua);
|
||||
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
|
||||
Ok(values)
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
MultiValue::from_lua_iter(lua, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
impl<T: FromLua> FromLuaMulti for Variadic<T> {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = values
|
||||
.drain_all()
|
||||
.map(|e| T::from_lua(e, lua))
|
||||
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
|
||||
values
|
||||
.drain(..)
|
||||
.map(|val| T::from_lua(val, lua))
|
||||
.collect::<Result<Vec<T>>>()
|
||||
.map(Variadic);
|
||||
lua.cache_multivalue(values);
|
||||
res
|
||||
.map(Variadic)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_tuple {
|
||||
() => (
|
||||
impl<'lua> ToLuaMulti<'lua> for () {
|
||||
impl IntoLuaMulti for () {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new_or_cached(lua))
|
||||
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
|
||||
const { Ok(MultiValue::new()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, _lua: &RawLua) -> Result<c_int> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for () {
|
||||
impl FromLuaMulti for () {
|
||||
#[inline]
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
lua.cache_multivalue(values);
|
||||
fn from_lua_multi(_values: MultiValue, _lua: &Lua) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(_nvals: c_int, _lua: &RawLua) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($last:ident $($name:ident)*) => (
|
||||
impl<'lua, $($name,)* $last> ToLuaMulti<'lua> for ($($name,)* $last,)
|
||||
where $($name: ToLua<'lua>,)*
|
||||
$last: ToLuaMulti<'lua>
|
||||
impl<$($name,)* $last> IntoLuaMulti for ($($name,)* $last,)
|
||||
where $($name: IntoLua,)*
|
||||
$last: IntoLuaMulti
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
let ($($name,)* $last,) = self;
|
||||
|
||||
let mut results = $last.to_lua_multi(lua)?;
|
||||
push_reverse!(results, $($name.to_lua(lua)?,)*);
|
||||
let mut results = $last.into_lua_multi(lua)?;
|
||||
push_reverse!(results, $($name.into_lua(lua)?,)*);
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
let ($($name,)* $last,) = self;
|
||||
let mut nresults = 0;
|
||||
$(
|
||||
_ = $name;
|
||||
nresults += 1;
|
||||
)*
|
||||
check_stack(lua.state(), nresults + 1)?;
|
||||
$(
|
||||
$name.push_into_stack(lua)?;
|
||||
)*
|
||||
nresults += $last.push_into_stack_multi(lua)?;
|
||||
Ok(nresults)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,)
|
||||
where $($name: FromLua<'lua>,)*
|
||||
$last: FromLuaMulti<'lua>
|
||||
impl<$($name,)* $last> FromLuaMulti for ($($name,)* $last,)
|
||||
where $($name: FromLua,)*
|
||||
$last: FromLuaMulti
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(let $name = values.pop_front().unwrap_or(Nil);)*
|
||||
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
|
||||
$(let $name = FromLua::from_lua(values.pop_front().unwrap_or(Nil), lua)?;)*
|
||||
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
|
||||
Ok(($(FromLua::from_lua($name, lua)?,)* $last,))
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_args(mut args: MultiValue, mut i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
$(
|
||||
let $name = FromLua::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)?;
|
||||
i += 1;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_lua_args(args, i, to, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(mut nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
$(
|
||||
let $name = if nvals > 0 {
|
||||
nvals -= 1;
|
||||
FromLua::from_stack(-(nvals + 1), lua)
|
||||
} else {
|
||||
FromLua::from_lua(Nil, lua.lua())
|
||||
}?;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_stack_multi(nvals, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(mut nargs: c_int, mut i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
$(
|
||||
let $name = if nargs > 0 {
|
||||
nargs -= 1;
|
||||
FromLua::from_stack_arg(-(nargs + 1), i, to, lua)
|
||||
} else {
|
||||
FromLua::from_lua_arg(Nil, i, to, lua.lua())
|
||||
}?;
|
||||
i += 1;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_stack_args(nargs, i, to, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -231,3 +457,13 @@ impl_tuple!(A B C D E F G H I J K L M);
|
||||
impl_tuple!(A B C D E F G H I J K L M N);
|
||||
impl_tuple!(A B C D E F G H I J K L M N O);
|
||||
impl_tuple!(A B C D E F G H I J K L M N O P);
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(MultiValue: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
|
||||
}
|
||||
|
||||
+15
-15
@@ -2,17 +2,18 @@
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
|
||||
Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, LuaOptions,
|
||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, String as LuaString,
|
||||
Table as LuaTable, TableExt as LuaTableExt, TablePairs as LuaTablePairs,
|
||||
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua,
|
||||
ToLuaMulti, UserData as LuaUserData, UserDataFields as LuaUserDataFields,
|
||||
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
|
||||
Value as LuaValue,
|
||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Either as LuaEither, Error as LuaError,
|
||||
ErrorContext as LuaErrorContext, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
|
||||
FromLua, FromLuaMulti, Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
|
||||
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn,
|
||||
LuaNativeFnMut, LuaOptions, MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil,
|
||||
Number as LuaNumber, ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult,
|
||||
StdLib as LuaStdLib, String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs,
|
||||
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus,
|
||||
UserData as LuaUserData, UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
|
||||
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
|
||||
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -21,15 +22,14 @@ pub use crate::HookTriggers as LuaHookTriggers;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{CoverageInfo as LuaCoverageInfo, VmState as LuaVmState};
|
||||
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::AsyncThread as LuaAsyncThread;
|
||||
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
|
||||
SerializeOptions as LuaSerializeOptions,
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt, SerializeOptions as LuaSerializeOptions,
|
||||
};
|
||||
|
||||
+236
-805
File diff suppressed because it is too large
Load Diff
+231
-93
@@ -1,7 +1,9 @@
|
||||
//! Deserialize Lua values to a Rust data structure.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result as StdResult;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
@@ -9,12 +11,13 @@ use serde::de::{self, IntoDeserializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::table::{Table, TablePairs, TableSequence};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::value::Value;
|
||||
|
||||
/// A struct for deserializing Lua values into Rust values.
|
||||
#[derive(Debug)]
|
||||
pub struct Deserializer<'lua> {
|
||||
value: Value<'lua>,
|
||||
pub struct Deserializer {
|
||||
value: Value,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
@@ -23,14 +26,14 @@ pub struct Deserializer<'lua> {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
/// If true, an attempt to serialize types such as [`Thread`], [`UserData`], [`LightUserData`]
|
||||
/// If true, an attempt to serialize types such as [`Function`], [`Thread`], [`LightUserData`]
|
||||
/// and [`Error`] will cause an error.
|
||||
/// Otherwise these types skipped when iterating or serialized as unit type.
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`Function`]: crate::Function
|
||||
/// [`Thread`]: crate::Thread
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`LightUserData`]: crate::LightUserData
|
||||
/// [`Error`]: crate::Error
|
||||
pub deny_unsupported_types: bool,
|
||||
@@ -41,11 +44,21 @@ pub struct Options {
|
||||
///
|
||||
/// Default: **true**
|
||||
pub deny_recursive_tables: bool,
|
||||
|
||||
/// If true, keys in tables will be iterated in sorted order.
|
||||
///
|
||||
/// Default: **false**
|
||||
pub sort_keys: bool,
|
||||
|
||||
/// If true, empty Lua tables will be encoded as array, instead of map.
|
||||
///
|
||||
/// Default: **false**
|
||||
pub encode_empty_tables_as_array: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
const { Self::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +68,8 @@ impl Options {
|
||||
Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
sort_keys: false,
|
||||
encode_empty_tables_as_array: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,16 +90,34 @@ impl Options {
|
||||
self.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`sort_keys`] option.
|
||||
///
|
||||
/// [`sort_keys`]: #structfield.sort_keys
|
||||
#[must_use]
|
||||
pub const fn sort_keys(mut self, enabled: bool) -> Self {
|
||||
self.sort_keys = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`encode_empty_tables_as_array`] option.
|
||||
///
|
||||
/// [`encode_empty_tables_as_array`]: #structfield.encode_empty_tables_as_array
|
||||
#[must_use]
|
||||
pub const fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
|
||||
self.encode_empty_tables_as_array = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Deserializer<'lua> {
|
||||
/// Creates a new Lua Deserializer for the `Value`.
|
||||
pub fn new(value: Value<'lua>) -> Self {
|
||||
impl Deserializer {
|
||||
/// Creates a new Lua Deserializer for the [`Value`].
|
||||
pub fn new(value: Value) -> Self {
|
||||
Self::new_with_options(value, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Deserializer for the `Value` with custom options.
|
||||
pub fn new_with_options(value: Value<'lua>, options: Options) -> Self {
|
||||
/// Creates a new Lua Deserializer for the [`Value`] with custom options.
|
||||
pub fn new_with_options(value: Value, options: Options) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
@@ -92,11 +125,7 @@ impl<'lua> Deserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_parts(
|
||||
value: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
) -> Self {
|
||||
fn from_parts(value: Value, options: Options, visited: Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
@@ -105,7 +134,7 @@ impl<'lua> Deserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
@@ -117,30 +146,35 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
Value::Nil => visitor.visit_unit(),
|
||||
Value::Boolean(b) => visitor.visit_bool(b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => {
|
||||
visitor.visit_i64(i.try_into().expect("cannot convert lua_Integer to i64"))
|
||||
}
|
||||
Value::Integer(i) => visitor.visit_i64(i.into()),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => visitor.visit_f64(n.into()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(_, _, _) => self.deserialize_seq(visitor),
|
||||
Value::Vector(_) => self.deserialize_seq(visitor),
|
||||
Value::String(s) => match s.to_str() {
|
||||
Ok(s) => visitor.visit_str(s),
|
||||
Err(_) => visitor.visit_bytes(s.as_bytes()),
|
||||
Ok(s) => visitor.visit_str(&s),
|
||||
Err(_) => visitor.visit_bytes(&s.as_bytes()),
|
||||
},
|
||||
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
|
||||
Value::Table(ref t) if self.options.encode_empty_tables_as_array && t.is_empty() => {
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
Value::Table(_) => self.deserialize_map(visitor),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_any(visitor))
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(buf) => visitor.visit_bytes(unsafe { buf.as_slice() }),
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => {
|
||||
| Value::Error(_)
|
||||
| Value::Other(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
Err(de::Error::custom(format!(
|
||||
"unsupported value type `{}`",
|
||||
self.value.type_name()
|
||||
)))
|
||||
let msg = format!("unsupported value type `{}`", self.value.type_name());
|
||||
Err(de::Error::custom(msg))
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
@@ -163,8 +197,8 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
#[inline]
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &str,
|
||||
_variants: &'static [&'static str],
|
||||
name: &'static str,
|
||||
variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
@@ -191,13 +225,18 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
&"map with a single key",
|
||||
));
|
||||
}
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
let skip = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
if skip {
|
||||
return Err(de::Error::custom("bad enum value"));
|
||||
}
|
||||
|
||||
(variant, Some(value), Some(_guard))
|
||||
}
|
||||
Value::String(variant) => (variant.to_str()?.to_owned(), None, None),
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
return serde_userdata(ud, |value| value.deserialize_enum(name, variants, visitor));
|
||||
}
|
||||
_ => return Err(de::Error::custom("bad enum value")),
|
||||
};
|
||||
|
||||
@@ -216,9 +255,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
{
|
||||
match self.value {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => {
|
||||
Value::Vector(vec) => {
|
||||
let mut deserializer = VecDeserializer {
|
||||
vec: [x, y, z],
|
||||
vec,
|
||||
next: 0,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
@@ -228,9 +267,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
Value::Table(t) => {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let len = t.raw_len() as usize;
|
||||
let len = t.raw_len();
|
||||
let mut deserializer = SeqDeserializer {
|
||||
seq: t.raw_sequence_values(),
|
||||
seq: t.sequence_values(),
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
};
|
||||
@@ -238,12 +277,12 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
if deserializer.seq.count() == 0 {
|
||||
Ok(seq)
|
||||
} else {
|
||||
Err(de::Error::invalid_length(
|
||||
len,
|
||||
&"fewer elements in the table",
|
||||
))
|
||||
Err(de::Error::invalid_length(len, &"fewer elements in the table"))
|
||||
}
|
||||
}
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_seq(visitor))
|
||||
}
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
@@ -260,12 +299,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
fn deserialize_tuple_struct<V>(self, _name: &'static str, _len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
@@ -282,7 +316,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let mut deserializer = MapDeserializer {
|
||||
pairs: t.pairs(),
|
||||
pairs: MapPairs::new(&t, self.options.sort_keys)?,
|
||||
value: None,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
@@ -299,6 +333,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
))
|
||||
}
|
||||
}
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_map(visitor))
|
||||
}
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
@@ -320,26 +357,53 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
|
||||
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_newtype_struct(self)
|
||||
match self.value {
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_newtype_struct(name, visitor))
|
||||
}
|
||||
_ => visitor.visit_newtype_struct(self),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
|
||||
_ => self.deserialize_any(visitor),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
|
||||
_ => self.deserialize_any(visitor),
|
||||
}
|
||||
}
|
||||
|
||||
serde::forward_to_deserialize_any! {
|
||||
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
|
||||
byte_buf unit unit_struct identifier ignored_any
|
||||
byte_buf identifier ignored_any
|
||||
}
|
||||
}
|
||||
|
||||
struct SeqDeserializer<'lua> {
|
||||
seq: TableSequence<'lua, Value<'lua>>,
|
||||
struct SeqDeserializer<'a> {
|
||||
seq: TableSequence<'a, Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
impl<'de> de::SeqAccess<'de> for SeqDeserializer<'_> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
@@ -350,7 +414,9 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
match self.seq.next() {
|
||||
Some(value) => {
|
||||
let value = value?;
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
let skip = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
if skip {
|
||||
continue;
|
||||
}
|
||||
let visited = Rc::clone(&self.visited);
|
||||
@@ -372,7 +438,7 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
struct VecDeserializer {
|
||||
vec: [f32; 3],
|
||||
vec: crate::Vector,
|
||||
next: usize,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
@@ -386,12 +452,11 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.vec.get(self.next) {
|
||||
match self.vec.0.get(self.next) {
|
||||
Some(&n) => {
|
||||
self.next += 1;
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let deserializer =
|
||||
Deserializer::from_parts(Value::Number(n as _), self.options, visited);
|
||||
let deserializer = Deserializer::from_parts(Value::Number(n as _), self.options, visited);
|
||||
seed.deserialize(deserializer).map(Some)
|
||||
}
|
||||
None => Ok(None),
|
||||
@@ -399,55 +464,116 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
Some(3)
|
||||
Some(crate::Vector::SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
value: Option<Value<'lua>>,
|
||||
pub(crate) enum MapPairs<'a> {
|
||||
Iter(TablePairs<'a, Value, Value>),
|
||||
Vec(Vec<(Value, Value)>),
|
||||
}
|
||||
|
||||
impl<'a> MapPairs<'a> {
|
||||
pub(crate) fn new(t: &'a Table, sort_keys: bool) -> Result<Self> {
|
||||
if sort_keys {
|
||||
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
|
||||
pairs.sort_by(|(a, _), (b, _)| b.sort_cmp(a)); // reverse order as we pop values from the end
|
||||
Ok(MapPairs::Vec(pairs))
|
||||
} else {
|
||||
Ok(MapPairs::Iter(t.pairs::<Value, Value>()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn count(self) -> usize {
|
||||
match self {
|
||||
MapPairs::Iter(iter) => iter.count(),
|
||||
MapPairs::Vec(vec) => vec.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self {
|
||||
MapPairs::Iter(iter) => iter.size_hint(),
|
||||
MapPairs::Vec(vec) => (vec.len(), Some(vec.len())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for MapPairs<'_> {
|
||||
type Item = Result<(Value, Value)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
MapPairs::Iter(iter) => iter.next(),
|
||||
MapPairs::Vec(vec) => vec.pop().map(Ok),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MapDeserializer<'a> {
|
||||
pairs: MapPairs<'a>,
|
||||
value: Option<Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
impl MapDeserializer<'_> {
|
||||
fn next_key_deserializer(&mut self) -> Result<Option<Deserializer>> {
|
||||
loop {
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
if check_value_if_skip(&key, self.options, &self.visited)?
|
||||
|| check_value_if_skip(&value, self.options, &self.visited)?
|
||||
{
|
||||
let skip_key = check_value_for_skip(&key, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
let skip_value = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
if skip_key || skip_value {
|
||||
continue;
|
||||
}
|
||||
self.processed += 1;
|
||||
self.value = Some(value);
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let key_de = Deserializer::from_parts(key, self.options, visited);
|
||||
return seed.deserialize(key_de).map(Some);
|
||||
return Ok(Some(key_de));
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_value_deserializer(&mut self) -> Result<Deserializer> {
|
||||
match self.value.take() {
|
||||
Some(value) => {
|
||||
let visited = Rc::clone(&self.visited);
|
||||
Ok(Deserializer::from_parts(value, self.options, visited))
|
||||
}
|
||||
None => Err(de::Error::custom("value is missing")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.next_key_deserializer() {
|
||||
Ok(Some(key_de)) => seed.deserialize(key_de).map(Some),
|
||||
Ok(None) => Ok(None),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value.take() {
|
||||
Some(value) => {
|
||||
let visited = Rc::clone(&self.visited);
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, visited))
|
||||
}
|
||||
None => Err(de::Error::custom("value is missing")),
|
||||
match self.next_value_deserializer() {
|
||||
Ok(value_de) => seed.deserialize(value_de),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,16 +585,16 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
struct EnumDeserializer<'lua> {
|
||||
struct EnumDeserializer {
|
||||
variant: StdString,
|
||||
value: Option<Value<'lua>>,
|
||||
value: Option<Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
impl<'de> de::EnumAccess<'de> for EnumDeserializer {
|
||||
type Error = Error;
|
||||
type Variant = VariantDeserializer<'lua>;
|
||||
type Variant = VariantDeserializer;
|
||||
|
||||
fn variant_seed<T>(self, seed: T) -> Result<(T::Value, Self::Variant)>
|
||||
where
|
||||
@@ -484,13 +610,13 @@ impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantDeserializer<'lua> {
|
||||
value: Option<Value<'lua>>,
|
||||
struct VariantDeserializer {
|
||||
value: Option<Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
impl<'de> de::VariantAccess<'de> for VariantDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn unit_variant(self) -> Result<()> {
|
||||
@@ -508,9 +634,7 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => {
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, self.visited))
|
||||
}
|
||||
Some(value) => seed.deserialize(Deserializer::from_parts(value, self.options, self.visited)),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"newtype variant",
|
||||
@@ -553,14 +677,14 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
|
||||
// Adds `ptr` to the `visited` map and removes on drop
|
||||
// Used to track recursive tables but allow to traverse same tables multiple times
|
||||
struct RecursionGuard {
|
||||
pub(crate) struct RecursionGuard {
|
||||
ptr: *const c_void,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl RecursionGuard {
|
||||
#[inline]
|
||||
fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
pub(crate) fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
let visited = Rc::clone(visited);
|
||||
let ptr = table.to_pointer();
|
||||
visited.borrow_mut().insert(ptr);
|
||||
@@ -575,21 +699,22 @@ impl Drop for RecursionGuard {
|
||||
}
|
||||
|
||||
// Checks `options` and decides should we emit an error or skip next element
|
||||
fn check_value_if_skip(
|
||||
pub(crate) fn check_value_for_skip(
|
||||
value: &Value,
|
||||
options: Options,
|
||||
visited: &RefCell<FxHashSet<*const c_void>>,
|
||||
) -> Result<bool> {
|
||||
) -> StdResult<bool, &'static str> {
|
||||
match value {
|
||||
Value::Table(table) => {
|
||||
let ptr = table.to_pointer();
|
||||
if visited.borrow().contains(&ptr) {
|
||||
if options.deny_recursive_tables {
|
||||
return Err(de::Error::custom("recursive table detected"));
|
||||
return Err("recursive table detected");
|
||||
}
|
||||
return Ok(true); // skip
|
||||
}
|
||||
}
|
||||
Value::UserData(ud) if ud.is_serializable() => {}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
@@ -603,3 +728,16 @@ fn check_value_if_skip(
|
||||
}
|
||||
Ok(false) // do not skip
|
||||
}
|
||||
|
||||
fn serde_userdata<V>(
|
||||
ud: AnyUserData,
|
||||
f: impl FnOnce(serde_value::Value) -> std::result::Result<V, serde_value::DeserializerError>,
|
||||
) -> Result<V> {
|
||||
match serde_value::to_value(ud) {
|
||||
Ok(value) => match f(value) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(error) => Err(Error::DeserializeError(error.to_string())),
|
||||
},
|
||||
Err(error) => Err(Error::SerializeError(error.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
+24
-34
@@ -1,21 +1,20 @@
|
||||
//! (De)Serialization support using serde.
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::private::Sealed;
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::LightUserData;
|
||||
use crate::util::check_stack;
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub trait LuaSerdeExt<'lua> {
|
||||
pub trait LuaSerdeExt: Sealed {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
@@ -37,7 +36,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn null(&'lua self) -> Value<'lua>;
|
||||
fn null(&self) -> Value;
|
||||
|
||||
/// A metatable attachable to a Lua table to systematically encode it as Array (instead of Map).
|
||||
/// As result, encoded Array will contain only sequence part of the table, with the same length
|
||||
@@ -68,7 +67,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn array_metatable(&'lua self) -> Table<'lua>;
|
||||
fn array_metatable(&self) -> Table;
|
||||
|
||||
/// Converts `T` into a [`Value`] instance.
|
||||
///
|
||||
@@ -101,14 +100,12 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
fn to_value<T: Serialize + ?Sized>(&self, t: &T) -> Result<Value>;
|
||||
|
||||
/// Converts `T` into a [`Value`] instance with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@@ -126,7 +123,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized;
|
||||
|
||||
@@ -134,8 +131,6 @@ pub trait LuaSerdeExt<'lua> {
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@@ -159,14 +154,12 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
|
||||
fn from_value<T: DeserializeOwned>(&self, value: Value) -> Result<T>;
|
||||
|
||||
/// Deserializes a [`Value`] into any serde deserializable object with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@@ -191,49 +184,46 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_value_with<T: Deserialize<'lua>>(
|
||||
&'lua self,
|
||||
value: Value<'lua>,
|
||||
options: de::Options,
|
||||
) -> Result<T>;
|
||||
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options) -> Result<T>;
|
||||
}
|
||||
|
||||
impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
fn null(&'lua self) -> Value<'lua> {
|
||||
Value::LightUserData(LightUserData(ptr::null_mut()))
|
||||
impl LuaSerdeExt for Lua {
|
||||
fn null(&self) -> Value {
|
||||
Value::NULL
|
||||
}
|
||||
|
||||
fn array_metatable(&'lua self) -> Table<'lua> {
|
||||
fn array_metatable(&self) -> Table {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
push_array_metatable(self.ref_thread());
|
||||
Table(self.pop_ref_thread())
|
||||
push_array_metatable(lua.ref_thread());
|
||||
Table(lua.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value<T>(&'lua self, t: &T) -> Result<Value<'lua>>
|
||||
fn to_value<T>(&self, t: &T) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer::new(self))
|
||||
}
|
||||
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer::new_with_options(self, options))
|
||||
}
|
||||
|
||||
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
|
||||
fn from_value<T>(&self, value: Value) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
T::deserialize(de::Deserializer::new(value))
|
||||
}
|
||||
|
||||
fn from_value_with<T>(&'lua self, value: Value<'lua>, options: de::Options) -> Result<T>
|
||||
fn from_value_with<T>(&self, value: Value, options: de::Options) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
T::deserialize(de::Deserializer::new_with_options(value, options))
|
||||
}
|
||||
|
||||
+211
-127
@@ -1,21 +1,18 @@
|
||||
use std::os::raw::c_int;
|
||||
//! Serialize a Rust data structure into Lua value.
|
||||
|
||||
use serde::{ser, Serialize};
|
||||
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::Integer;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::value::{ToLua, Value};
|
||||
use crate::traits::IntoLua;
|
||||
use crate::value::Value;
|
||||
|
||||
/// A struct for serializing Rust values into Lua values.
|
||||
#[derive(Debug)]
|
||||
pub struct Serializer<'lua> {
|
||||
lua: &'lua Lua,
|
||||
pub struct Serializer<'a> {
|
||||
lua: &'a Lua,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
@@ -48,11 +45,17 @@ pub struct Options {
|
||||
/// [`null`]: crate::LuaSerdeExt::null
|
||||
/// [`Nil`]: crate::Value::Nil
|
||||
pub serialize_unit_to_null: bool,
|
||||
|
||||
/// If true, serialize `serde_json::Number` with arbitrary_precision to a Lua number.
|
||||
/// Otherwise it will be serialized as an object (what serde does).
|
||||
///
|
||||
/// Default: **false**
|
||||
pub detect_serde_json_arbitrary_precision: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
const { Self::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +66,7 @@ impl Options {
|
||||
set_array_metatable: true,
|
||||
serialize_none_to_null: true,
|
||||
serialize_unit_to_null: true,
|
||||
detect_serde_json_arbitrary_precision: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,16 +96,30 @@ impl Options {
|
||||
self.serialize_unit_to_null = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`detect_serde_json_arbitrary_precision`] option.
|
||||
///
|
||||
/// This option is used to serialize `serde_json::Number` with arbitrary precision to a Lua
|
||||
/// number. Otherwise it will be serialized as an object (what serde does).
|
||||
///
|
||||
/// This option is disabled by default.
|
||||
///
|
||||
/// [`detect_serde_json_arbitrary_precision`]: #structfield.detect_serde_json_arbitrary_precision
|
||||
#[must_use]
|
||||
pub const fn detect_serde_json_arbitrary_precision(mut self, enabled: bool) -> Self {
|
||||
self.detect_serde_json_arbitrary_precision = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Serializer<'lua> {
|
||||
impl<'a> Serializer<'a> {
|
||||
/// Creates a new Lua Serializer with default options.
|
||||
pub fn new(lua: &'lua Lua) -> Self {
|
||||
pub fn new(lua: &'a Lua) -> Self {
|
||||
Self::new_with_options(lua, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Serializer with custom options.
|
||||
pub fn new_with_options(lua: &'lua Lua, options: Options) -> Self {
|
||||
pub fn new_with_options(lua: &'a Lua, options: Options) -> Self {
|
||||
Serializer { lua, options }
|
||||
}
|
||||
}
|
||||
@@ -109,28 +127,28 @@ impl<'lua> Serializer<'lua> {
|
||||
macro_rules! lua_serialize_number {
|
||||
($name:ident, $t:ty) => {
|
||||
#[inline]
|
||||
fn $name(self, value: $t) -> Result<Value<'lua>> {
|
||||
value.to_lua(self.lua)
|
||||
fn $name(self, value: $t) -> Result<Value> {
|
||||
value.into_lua(self.lua)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl<'a> ser::Serializer for Serializer<'a> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
// Associated types for keeping track of additional state while serializing
|
||||
// compound data structures like sequences and maps.
|
||||
type SerializeSeq = SerializeVec<'lua>;
|
||||
type SerializeTuple = SerializeVec<'lua>;
|
||||
type SerializeTupleStruct = SerializeVec<'lua>;
|
||||
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
|
||||
type SerializeMap = SerializeMap<'lua>;
|
||||
type SerializeStruct = SerializeMap<'lua>;
|
||||
type SerializeStructVariant = SerializeStructVariant<'lua>;
|
||||
type SerializeSeq = SerializeSeq<'a>;
|
||||
type SerializeTuple = SerializeSeq<'a>;
|
||||
type SerializeTupleStruct = SerializeSeq<'a>;
|
||||
type SerializeTupleVariant = SerializeTupleVariant<'a>;
|
||||
type SerializeMap = SerializeMap<'a>;
|
||||
type SerializeStruct = SerializeStruct<'a>;
|
||||
type SerializeStructVariant = SerializeStructVariant<'a>;
|
||||
|
||||
#[inline]
|
||||
fn serialize_bool(self, value: bool) -> Result<Value<'lua>> {
|
||||
fn serialize_bool(self, value: bool) -> Result<Value> {
|
||||
Ok(Value::Boolean(value))
|
||||
}
|
||||
|
||||
@@ -149,22 +167,22 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
lua_serialize_number!(serialize_f64, f64);
|
||||
|
||||
#[inline]
|
||||
fn serialize_char(self, value: char) -> Result<Value<'lua>> {
|
||||
fn serialize_char(self, value: char) -> Result<Value> {
|
||||
self.serialize_str(&value.to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_str(self, value: &str) -> Result<Value<'lua>> {
|
||||
fn serialize_str(self, value: &str) -> Result<Value> {
|
||||
self.lua.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Value<'lua>> {
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Value> {
|
||||
self.lua.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Value<'lua>> {
|
||||
fn serialize_none(self) -> Result<Value> {
|
||||
if self.options.serialize_none_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
@@ -173,7 +191,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
@@ -181,7 +199,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit(self) -> Result<Value<'lua>> {
|
||||
fn serialize_unit(self) -> Result<Value> {
|
||||
if self.options.serialize_unit_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
@@ -190,7 +208,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value<'lua>> {
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value> {
|
||||
if self.options.serialize_unit_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
@@ -204,12 +222,12 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Value<'lua>> {
|
||||
) -> Result<Value> {
|
||||
self.serialize_str(variant)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
|
||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
@@ -223,7 +241,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Value<'lua>>
|
||||
) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
@@ -236,13 +254,11 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
|
||||
#[inline]
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
let len = len.unwrap_or(0) as c_int;
|
||||
let table = self.lua.create_table_with_capacity(len, 0)?;
|
||||
let table = self.lua.create_table_with_capacity(len.unwrap_or(0), 0)?;
|
||||
if self.options.set_array_metatable {
|
||||
table.set_metatable(Some(self.lua.array_metatable()));
|
||||
}
|
||||
let options = self.options;
|
||||
Ok(SerializeVec { table, options })
|
||||
Ok(SerializeSeq::new(self.lua, table, self.options))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -251,11 +267,12 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeTupleStruct> {
|
||||
#[cfg(feature = "luau")]
|
||||
if name == "Vector" && len == crate::Vector::SIZE {
|
||||
return Ok(SerializeSeq::new_vector(self.lua, self.options));
|
||||
}
|
||||
_ = name;
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
@@ -268,7 +285,8 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
Ok(SerializeTupleVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
lua: self.lua,
|
||||
variant,
|
||||
table: self.lua.create_table()?,
|
||||
options: self.options,
|
||||
})
|
||||
@@ -276,17 +294,32 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
|
||||
#[inline]
|
||||
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
|
||||
let len = len.unwrap_or(0) as c_int;
|
||||
Ok(SerializeMap {
|
||||
lua: self.lua,
|
||||
key: None,
|
||||
table: self.lua.create_table_with_capacity(0, len)?,
|
||||
table: self.lua.create_table_with_capacity(0, len.unwrap_or(0))?,
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
|
||||
self.serialize_map(Some(len))
|
||||
fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
|
||||
if self.options.detect_serde_json_arbitrary_precision
|
||||
&& name == "$serde_json::private::Number"
|
||||
&& len == 1
|
||||
{
|
||||
return Ok(SerializeStruct {
|
||||
lua: self.lua,
|
||||
inner: None,
|
||||
options: self.options,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(SerializeStruct {
|
||||
lua: self.lua,
|
||||
inner: Some(Value::Table(self.lua.create_table_with_capacity(0, len)?)),
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -298,56 +331,70 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
Ok(SerializeStructVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
table: self.lua.create_table_with_capacity(0, len as c_int)?,
|
||||
lua: self.lua,
|
||||
variant,
|
||||
table: self.lua.create_table_with_capacity(0, len)?,
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeVec<'lua> {
|
||||
table: Table<'lua>,
|
||||
pub struct SerializeSeq<'a> {
|
||||
lua: &'a Lua,
|
||||
#[cfg(feature = "luau")]
|
||||
vector: Option<crate::Vector>,
|
||||
table: Option<Table>,
|
||||
next: usize,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl<'a> SerializeSeq<'a> {
|
||||
fn new(lua: &'a Lua, table: Table, options: Options) -> Self {
|
||||
Self {
|
||||
lua,
|
||||
#[cfg(feature = "luau")]
|
||||
vector: None,
|
||||
table: Some(table),
|
||||
next: 0,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
const fn new_vector(lua: &'a Lua, options: Options) -> Self {
|
||||
Self {
|
||||
lua,
|
||||
vector: Some(crate::Vector::zero()),
|
||||
table: None,
|
||||
next: 0,
|
||||
options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::SerializeSeq for SerializeSeq<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.table.0);
|
||||
lua.push_value(value)?;
|
||||
if lua.unlikely_memory_error() {
|
||||
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
|
||||
ffi::lua_rawseti(lua.state, -2, len + 1);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
Ok(())
|
||||
} else {
|
||||
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
})
|
||||
}
|
||||
}
|
||||
let value = self.lua.to_value_with(value, self.options)?;
|
||||
let table = self.table.as_ref().unwrap();
|
||||
table.raw_seti(self.next + 1, value)?;
|
||||
self.next += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self.table))
|
||||
fn end(self) -> Result<Value> {
|
||||
Ok(Value::Table(self.table.unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeTuple for SerializeSeq<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
@@ -357,73 +404,82 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
fn end(self) -> Result<Value> {
|
||||
ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeTupleStruct for SerializeSeq<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
#[cfg(feature = "luau")]
|
||||
if let Some(vector) = self.vector.as_mut() {
|
||||
let value = self.lua.to_value_with(value, self.options)?;
|
||||
let value = self.lua.unpack(value)?;
|
||||
vector.0[self.next] = value;
|
||||
self.next += 1;
|
||||
return Ok(());
|
||||
}
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
fn end(self) -> Result<Value> {
|
||||
#[cfg(feature = "luau")]
|
||||
if let Some(vector) = self.vector {
|
||||
return Ok(Value::Vector(vector));
|
||||
}
|
||||
ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeTupleVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
table: Table<'lua>,
|
||||
pub struct SerializeTupleVariant<'a> {
|
||||
lua: &'a Lua,
|
||||
variant: &'static str,
|
||||
table: Table,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeTupleVariant for SerializeTupleVariant<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let idx = self.table.raw_len() + 1;
|
||||
self.table
|
||||
.raw_insert(idx, lua.to_value_with(value, self.options)?)
|
||||
self.table.raw_push(self.lua.to_value_with(value, self.options)?)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table()?;
|
||||
table.raw_set(self.name, self.table)?;
|
||||
fn end(self) -> Result<Value> {
|
||||
let table = self.lua.create_table()?;
|
||||
table.raw_set(self.variant, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeMap<'lua> {
|
||||
table: Table<'lua>,
|
||||
key: Option<Value<'lua>>,
|
||||
pub struct SerializeMap<'a> {
|
||||
lua: &'a Lua,
|
||||
table: Table,
|
||||
key: Option<Value>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeMap for SerializeMap<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.key = Some(lua.to_value_with(key, self.options)?);
|
||||
self.key = Some(self.lua.to_value_with(key, self.options)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -431,62 +487,90 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let key = mlua_expect!(
|
||||
self.key.take(),
|
||||
"serialize_value called before serialize_key"
|
||||
);
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
let key = mlua_expect!(self.key.take(), "serialize_value called before serialize_key");
|
||||
let value = self.lua.to_value_with(value, self.options)?;
|
||||
self.table.raw_set(key, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
fn end(self) -> Result<Value> {
|
||||
Ok(Value::Table(self.table))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStruct<'a> {
|
||||
lua: &'a Lua,
|
||||
inner: Option<Value>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl ser::SerializeStruct for SerializeStruct<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
ser::SerializeMap::serialize_key(self, key)?;
|
||||
ser::SerializeMap::serialize_value(self, value)
|
||||
match self.inner {
|
||||
Some(Value::Table(ref table)) => {
|
||||
table.raw_set(key, self.lua.to_value_with(value, self.options)?)?;
|
||||
}
|
||||
None if self.options.detect_serde_json_arbitrary_precision => {
|
||||
// A special case for `serde_json::Number` with arbitrary precision.
|
||||
assert_eq!(key, "$serde_json::private::Number");
|
||||
self.inner = Some(self.lua.to_value_with(value, self.options)?);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
ser::SerializeMap::end(self)
|
||||
fn end(self) -> Result<Value> {
|
||||
match self.inner {
|
||||
Some(table @ Value::Table(_)) => Ok(table),
|
||||
Some(value) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
let number_s = value.as_str().expect("not an arbitrary precision number");
|
||||
if number_s.contains(['.', 'e', 'E']) {
|
||||
if let Ok(number) = number_s.parse().map(Value::Number) {
|
||||
return Ok(number);
|
||||
}
|
||||
}
|
||||
Ok(number_s
|
||||
.parse()
|
||||
.map(Value::Integer)
|
||||
.or_else(|_| number_s.parse().map(Value::Number))
|
||||
.unwrap_or(value))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStructVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
table: Table<'lua>,
|
||||
pub struct SerializeStructVariant<'a> {
|
||||
lua: &'a Lua,
|
||||
variant: &'static str,
|
||||
table: Table,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeStructVariant for SerializeStructVariant<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.table
|
||||
.raw_set(key, lua.to_value_with(value, self.options)?)?;
|
||||
.raw_set(key, self.lua.to_value_with(value, self.options)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table()?;
|
||||
table.raw_set(self.name, self.table)?;
|
||||
fn end(self) -> Result<Value> {
|
||||
let table = self.lua.create_table_with_capacity(0, 1)?;
|
||||
table.raw_set(self.variant, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
+2066
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::state::RawLua;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||
use crate::userdata::RawUserDataRegistry;
|
||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
use crate::chunk::Compiler;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_util::task::noop_waker_ref, std::ptr::NonNull, std::task::Waker};
|
||||
|
||||
use super::{Lua, WeakLua};
|
||||
|
||||
// Unique key to store `ExtraData` in the registry
|
||||
static EXTRA_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
const WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY: usize = 64;
|
||||
const REF_STACK_RESERVE: c_int = 2;
|
||||
|
||||
/// Data associated with the Lua state.
|
||||
pub(crate) struct ExtraData {
|
||||
pub(super) lua: MaybeUninit<Lua>,
|
||||
pub(super) weak: MaybeUninit<WeakLua>,
|
||||
pub(super) owned: bool,
|
||||
|
||||
pub(super) pending_userdata_reg: FxHashMap<TypeId, RawUserDataRegistry>,
|
||||
pub(super) registered_userdata_t: FxHashMap<TypeId, c_int>,
|
||||
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
|
||||
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
|
||||
|
||||
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
|
||||
pub(super) registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
// Container to store arbitrary data (extensions)
|
||||
pub(super) app_data: AppData,
|
||||
|
||||
pub(super) safe: bool,
|
||||
pub(super) libs: StdLib,
|
||||
// Used in module mode
|
||||
pub(super) skip_memory_check: bool,
|
||||
|
||||
// Auxiliary thread to store references
|
||||
pub(super) ref_thread: *mut ffi::lua_State,
|
||||
pub(super) ref_stack_size: c_int,
|
||||
pub(super) ref_stack_top: c_int,
|
||||
pub(super) ref_free: Vec<c_int>,
|
||||
|
||||
// Pool of `WrappedFailure` enums in the ref thread (as userdata)
|
||||
pub(super) wrapped_failure_pool: Vec<c_int>,
|
||||
pub(super) wrapped_failure_top: usize,
|
||||
// Pool of `Thread`s (coroutines) for async execution
|
||||
#[cfg(feature = "async")]
|
||||
pub(super) thread_pool: Vec<c_int>,
|
||||
|
||||
// Address of `WrappedFailure` metatable
|
||||
pub(super) wrapped_failure_mt_ptr: *const c_void,
|
||||
|
||||
// Waker for polling futures
|
||||
#[cfg(feature = "async")]
|
||||
pub(super) waker: NonNull<Waker>,
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(super) hook_callback: Option<crate::types::HookCallback>,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(super) hook_thread: *mut ffi::lua_State,
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(super) warn_callback: Option<crate::types::WarnCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) sandboxed: bool,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) compiler: Option<Compiler>,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
pub(super) enable_jit: bool,
|
||||
}
|
||||
|
||||
impl Drop for ExtraData {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.owned {
|
||||
self.lua.assume_init_drop();
|
||||
}
|
||||
|
||||
self.weak.assume_init_drop();
|
||||
}
|
||||
*self.registry_unref_list.lock() = None;
|
||||
}
|
||||
}
|
||||
|
||||
static EXTRA_TYPE_KEY: u8 = 0;
|
||||
|
||||
impl TypeKey for XRc<UnsafeCell<ExtraData>> {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&EXTRA_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtraData {
|
||||
// Index of `error_traceback` function in auxiliary thread stack
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
pub(super) const ERROR_TRACEBACK_IDX: c_int = 1;
|
||||
|
||||
pub(super) unsafe fn init(state: *mut ffi::lua_State, owned: bool) -> XRc<UnsafeCell<Self>> {
|
||||
// Create ref stack thread and place it in the registry to prevent it
|
||||
// from being garbage collected.
|
||||
let ref_thread = mlua_expect!(
|
||||
protect_lua!(state, 0, 0, |state| {
|
||||
let thread = ffi::lua_newthread(state);
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
|
||||
thread
|
||||
}),
|
||||
"Error while creating ref thread",
|
||||
);
|
||||
|
||||
let wrapped_failure_mt_ptr = {
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
let ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ptr
|
||||
};
|
||||
|
||||
// Store `error_traceback` function on the ref stack
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushcfunction(ref_thread, crate::util::error_traceback);
|
||||
assert_eq!(ffi::lua_gettop(ref_thread), Self::ERROR_TRACEBACK_IDX);
|
||||
}
|
||||
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
let extra = XRc::new(UnsafeCell::new(ExtraData {
|
||||
lua: MaybeUninit::uninit(),
|
||||
weak: MaybeUninit::uninit(),
|
||||
owned,
|
||||
pending_userdata_reg: FxHashMap::default(),
|
||||
registered_userdata_t: FxHashMap::default(),
|
||||
registered_userdata_mt: FxHashMap::default(),
|
||||
last_checked_userdata_mt: (ptr::null(), None),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
app_data: AppData::default(),
|
||||
safe: false,
|
||||
libs: StdLib::NONE,
|
||||
skip_memory_check: false,
|
||||
ref_thread,
|
||||
// We need some reserved stack space to move values in and out of the ref stack.
|
||||
ref_stack_size: ffi::LUA_MINSTACK - REF_STACK_RESERVE,
|
||||
ref_stack_top: ffi::lua_gettop(ref_thread),
|
||||
ref_free: Vec::new(),
|
||||
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY),
|
||||
wrapped_failure_top: 0,
|
||||
#[cfg(feature = "async")]
|
||||
thread_pool: Vec::new(),
|
||||
wrapped_failure_mt_ptr,
|
||||
#[cfg(feature = "async")]
|
||||
waker: NonNull::from(noop_waker_ref()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
hook_callback: None,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
hook_thread: ptr::null_mut(),
|
||||
#[cfg(feature = "lua54")]
|
||||
warn_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
interrupt_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
sandboxed: false,
|
||||
#[cfg(feature = "luau")]
|
||||
compiler: None,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
enable_jit: true,
|
||||
}));
|
||||
|
||||
// Store it in the registry
|
||||
mlua_expect!(Self::store(&extra, state), "Error while storing extra data");
|
||||
|
||||
extra
|
||||
}
|
||||
|
||||
pub(super) unsafe fn set_lua(&mut self, raw: &XRc<ReentrantMutex<RawLua>>) {
|
||||
self.lua.write(Lua {
|
||||
raw: XRc::clone(raw),
|
||||
collect_garbage: false,
|
||||
});
|
||||
if self.owned {
|
||||
XRc::decrement_strong_count(XRc::as_ptr(raw));
|
||||
}
|
||||
self.weak.write(WeakLua(XRc::downgrade(raw)));
|
||||
}
|
||||
|
||||
pub(super) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
#[cfg(feature = "luau")]
|
||||
if cfg!(not(feature = "module")) {
|
||||
// In the main app we can use `lua_callbacks` to access ExtraData
|
||||
return (*ffi::lua_callbacks(state)).userdata as *mut _;
|
||||
}
|
||||
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
|
||||
// `ExtraData` can be null only when Lua state is foreign.
|
||||
// This case in used in `Lua::try_from_ptr()`.
|
||||
ffi::lua_pop(state, 1);
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let extra_ptr = ffi::lua_touserdata(state, -1) as *mut Rc<UnsafeCell<ExtraData>>;
|
||||
ffi::lua_pop(state, 1);
|
||||
(*extra_ptr).get()
|
||||
}
|
||||
|
||||
unsafe fn store(extra: &XRc<UnsafeCell<Self>>, state: *mut ffi::lua_State) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
if cfg!(not(feature = "module")) {
|
||||
(*ffi::lua_callbacks(state)).userdata = extra.get() as *mut _;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
push_internal_userdata(state, XRc::clone(extra), true)?;
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn lua(&self) -> &Lua {
|
||||
self.lua.assume_init_ref()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn raw_lua(&self) -> &RawLua {
|
||||
&*self.lua.assume_init_ref().raw.data_ptr()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn weak(&self) -> &WeakLua {
|
||||
self.weak.assume_init_ref()
|
||||
}
|
||||
}
|
||||
+1423
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,172 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{ExtraData, RawLua};
|
||||
use crate::util::{self, get_internal_metatable, WrappedFailure};
|
||||
|
||||
struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||
|
||||
impl<'a> StateGuard<'a> {
|
||||
fn new(inner: &'a RawLua, mut state: *mut ffi::lua_State) -> Self {
|
||||
state = inner.state.replace(state);
|
||||
Self(inner, state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StateGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.0.state.set(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
|
||||
// and instead reuses unused values from previous calls (or allocates new).
|
||||
pub(super) unsafe fn callback_error_ext<F, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
mut extra: *mut ExtraData,
|
||||
f: F,
|
||||
) -> R
|
||||
where
|
||||
F: FnOnce(*mut ExtraData, c_int) -> Result<R>,
|
||||
{
|
||||
if extra.is_null() {
|
||||
extra = ExtraData::get(state);
|
||||
}
|
||||
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
|
||||
enum PreallocatedFailure {
|
||||
New(*mut WrappedFailure),
|
||||
Reserved,
|
||||
}
|
||||
|
||||
impl PreallocatedFailure {
|
||||
unsafe fn reserve(state: *mut ffi::lua_State, extra: *mut ExtraData) -> Self {
|
||||
if (*extra).wrapped_failure_top > 0 {
|
||||
(*extra).wrapped_failure_top -= 1;
|
||||
return PreallocatedFailure::Reserved;
|
||||
}
|
||||
|
||||
// We need to check stack for Luau in case when callback is called from interrupt
|
||||
// See https://github.com/luau-lang/luau/issues/446 and mlua #142 and #153
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
// Place it to the beginning of the stack
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_insert(state, 1);
|
||||
PreallocatedFailure::New(ud)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
unsafe fn r#use(&self, state: *mut ffi::lua_State, extra: *mut ExtraData) -> *mut WrappedFailure {
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match *self {
|
||||
PreallocatedFailure::New(ud) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ud
|
||||
}
|
||||
PreallocatedFailure::Reserved => {
|
||||
let index = (*extra).wrapped_failure_pool.pop().unwrap();
|
||||
ffi::lua_settop(state, 0);
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
ffi::lua_xpush(ref_thread, state, index);
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, index);
|
||||
(*extra).ref_free.push(index);
|
||||
ffi::lua_touserdata(state, -1) as *mut WrappedFailure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn release(self, state: *mut ffi::lua_State, extra: *mut ExtraData) {
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match self {
|
||||
PreallocatedFailure::New(_) => {
|
||||
ffi::lua_rotate(state, 1, -1);
|
||||
ffi::lua_xmove(state, ref_thread, 1);
|
||||
let index = ref_stack_pop(extra);
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
(*extra).wrapped_failure_top += 1;
|
||||
}
|
||||
PreallocatedFailure::Reserved => (*extra).wrapped_failure_top += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot shadow Rust errors with Lua ones, so we need to reserve pre-allocated memory
|
||||
// to store a wrapped failure (error or panic) *before* we proceed.
|
||||
let prealloc_failure = PreallocatedFailure::reserve(state, extra);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
f(extra, nargs)
|
||||
})) {
|
||||
Ok(Ok(r)) => {
|
||||
// Return unused `WrappedFailure` to the pool
|
||||
prealloc_failure.release(state, extra);
|
||||
r
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
let wrapped_error = prealloc_failure.r#use(state, extra);
|
||||
|
||||
// Build `CallbackError` with traceback
|
||||
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||
let traceback = util::to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
traceback
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_string()
|
||||
};
|
||||
let cause = Arc::new(err);
|
||||
ptr::write(
|
||||
wrapped_error,
|
||||
WrappedFailure::Error(Error::CallbackError { traceback, cause }),
|
||||
);
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
let wrapped_panic = prealloc_failure.r#use(state, extra);
|
||||
ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
|
||||
let extra = &mut *extra;
|
||||
if let Some(free) = extra.ref_free.pop() {
|
||||
ffi::lua_replace(extra.ref_thread, free);
|
||||
return free;
|
||||
}
|
||||
|
||||
// Try to grow max stack size
|
||||
if extra.ref_stack_top >= extra.ref_stack_size {
|
||||
let mut inc = extra.ref_stack_size; // Try to double stack size
|
||||
while inc > 0 && ffi::lua_checkstack(extra.ref_thread, inc) == 0 {
|
||||
inc /= 2;
|
||||
}
|
||||
if inc == 0 {
|
||||
// Pop item on top of the stack to avoid stack leaking and successfully run destructors
|
||||
// during unwinding.
|
||||
ffi::lua_pop(extra.ref_thread, 1);
|
||||
let top = extra.ref_stack_top;
|
||||
// It is a user error to create enough references to exhaust the Lua max stack size for
|
||||
// the ref thread.
|
||||
panic!("cannot create a Lua reference, out of auxiliary stack space (used {top} slots)");
|
||||
}
|
||||
extra.ref_stack_size += inc;
|
||||
}
|
||||
extra.ref_stack_top += 1;
|
||||
extra.ref_stack_top
|
||||
}
|
||||
+22
-10
@@ -1,5 +1,4 @@
|
||||
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
|
||||
use std::u32;
|
||||
|
||||
/// Flags describing the set of lua standard libraries to load.
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
@@ -9,45 +8,57 @@ impl StdLib {
|
||||
/// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub const COROUTINE: StdLib = StdLib(1);
|
||||
|
||||
/// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library
|
||||
pub const TABLE: StdLib = StdLib(1 << 1);
|
||||
|
||||
/// [`io`](https://www.lua.org/manual/5.4/manual.html#6.8) library
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub const IO: StdLib = StdLib(1 << 2);
|
||||
|
||||
/// [`os`](https://www.lua.org/manual/5.4/manual.html#6.9) library
|
||||
pub const OS: StdLib = StdLib(1 << 3);
|
||||
|
||||
/// [`string`](https://www.lua.org/manual/5.4/manual.html#6.4) library
|
||||
pub const STRING: StdLib = StdLib(1 << 4);
|
||||
|
||||
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
pub const UTF8: StdLib = StdLib(1 << 5);
|
||||
|
||||
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
|
||||
///
|
||||
/// Requires `feature = "lua52/luajit/luau"`
|
||||
#[cfg(any(feature = "lua52", feature = "luajit", feature = "luau", doc))]
|
||||
pub const BIT: StdLib = StdLib(1 << 6);
|
||||
|
||||
/// [`math`](https://www.lua.org/manual/5.4/manual.html#6.7) library
|
||||
pub const MATH: StdLib = StdLib(1 << 7);
|
||||
|
||||
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub const PACKAGE: StdLib = StdLib(1 << 8);
|
||||
|
||||
/// [`buffer`](https://luau.org/library#buffer-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const BUFFER: StdLib = StdLib(1 << 9);
|
||||
|
||||
/// [`vector`](https://luau.org/library#vector-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const VECTOR: StdLib = StdLib(1 << 10);
|
||||
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
///
|
||||
/// Requires `feature = "luajit"`
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const JIT: StdLib = StdLib(1 << 9);
|
||||
pub const JIT: StdLib = StdLib(1 << 11);
|
||||
|
||||
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
///
|
||||
@@ -55,6 +66,7 @@ impl StdLib {
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const FFI: StdLib = StdLib(1 << 30);
|
||||
|
||||
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10) library
|
||||
pub const DEBUG: StdLib = StdLib(1 << 31);
|
||||
|
||||
|
||||
+310
-51
@@ -1,8 +1,15 @@
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::c_void;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::{slice, str};
|
||||
use std::{cmp, fmt, slice, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::traits::IntoLua;
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -10,18 +17,14 @@ use {
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
|
||||
/// Handle to an internal Lua string.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct String<'lua>(pub(crate) LuaRef<'lua>);
|
||||
#[derive(Clone)]
|
||||
pub struct String(pub(crate) ValueRef);
|
||||
|
||||
impl<'lua> String<'lua> {
|
||||
/// Get a `&str` slice if the Lua string is valid UTF-8.
|
||||
impl String {
|
||||
/// Get a [`BorrowedStr`] if the Lua string is valid UTF-8.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -40,19 +43,19 @@ impl<'lua> String<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<&str> {
|
||||
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str",
|
||||
message: Some(e.to_string()),
|
||||
})
|
||||
pub fn to_str(&self) -> Result<BorrowedStr> {
|
||||
BorrowedStr::try_from(self)
|
||||
}
|
||||
|
||||
/// Converts this string to a [`Cow<str>`].
|
||||
/// Converts this string to a [`StdString`].
|
||||
///
|
||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
||||
///
|
||||
/// This method returns [`StdString`] instead of [`Cow<'_, str>`] because lifetime cannot be
|
||||
/// bound to a weak Lua object.
|
||||
///
|
||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||
/// [`Cow<'_, str>`]: std::borrow::Cow
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -67,8 +70,18 @@ impl<'lua> String<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
StdString::from_utf8_lossy(self.as_bytes())
|
||||
pub fn to_string_lossy(&self) -> StdString {
|
||||
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
}
|
||||
|
||||
/// Returns an object that implements [`Display`] for safely printing a Lua [`String`] that may
|
||||
/// contain non-Unicode data.
|
||||
///
|
||||
/// This may perform lossy conversion.
|
||||
///
|
||||
/// [`Display`]: fmt::Display
|
||||
pub fn display(&self) -> impl fmt::Display + '_ {
|
||||
Display(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
@@ -89,87 +102,333 @@ impl<'lua> String<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
let nulled = self.as_bytes_with_nul();
|
||||
&nulled[..nulled.len() - 1]
|
||||
pub fn as_bytes(&self) -> BorrowedBytes {
|
||||
BorrowedBytes::from(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string, including the trailing nul byte.
|
||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(self);
|
||||
// Include the trailing nul byte (it's always present but excluded by default)
|
||||
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
|
||||
BorrowedBytes { buf, borrow, _lua }
|
||||
}
|
||||
|
||||
// Does not return the terminating nul byte
|
||||
unsafe fn to_slice(&self) -> (&[u8], Lua) {
|
||||
let lua = self.0.lua.upgrade();
|
||||
let slice = {
|
||||
let rawlua = lua.lock();
|
||||
let ref_thread = rawlua.ref_thread();
|
||||
|
||||
mlua_debug_assert!(
|
||||
ffi::lua_type(ref_thread, self.0.index) == ffi::LUA_TSTRING,
|
||||
"string ref is not string type"
|
||||
);
|
||||
|
||||
let mut size = 0;
|
||||
// This will not trigger a 'm' error, because the reference is guaranteed to be of
|
||||
// string type
|
||||
let mut size = 0;
|
||||
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
|
||||
|
||||
slice::from_raw_parts(data as *const u8, size + 1)
|
||||
}
|
||||
slice::from_raw_parts(data as *const u8, size)
|
||||
};
|
||||
(slice, lua)
|
||||
}
|
||||
|
||||
/// Converts the string to a generic C pointer.
|
||||
/// Converts this string to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
|
||||
self.0.to_pointer()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsRef<[u8]> for String<'lua> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
impl fmt::Debug for String {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.as_bytes();
|
||||
// Check if the string is valid utf8
|
||||
if let Ok(s) = str::from_utf8(&bytes) {
|
||||
return s.fmt(f);
|
||||
}
|
||||
|
||||
// Format as bytes
|
||||
write!(f, "b")?;
|
||||
<bstr::BStr as fmt::Debug>::fmt(bstr::BStr::new(&bytes), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Borrow<[u8]> for String<'lua> {
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
// Lua strings are basically &[u8] slices, so implement PartialEq for anything resembling that.
|
||||
// Lua strings are basically `&[u8]` slices, so implement `PartialEq` for anything resembling that.
|
||||
//
|
||||
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str`, `String` and `mlua::String`
|
||||
// itself.
|
||||
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
|
||||
//
|
||||
// The only downside is that this disallows a comparison with `Cow<str>`, as that only implements
|
||||
// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but limit us
|
||||
// in other ways.
|
||||
impl<'lua, T> PartialEq<T> for String<'lua>
|
||||
impl<T> PartialEq<T> for String
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.as_bytes() == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Eq for String<'lua> {}
|
||||
impl PartialEq for String {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_bytes() == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Hash for String<'lua> {
|
||||
impl Eq for String {}
|
||||
|
||||
impl<T> PartialOrd<T> for String
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(&other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for String {
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for String {
|
||||
fn cmp(&self, other: &String) -> cmp::Ordering {
|
||||
self.as_bytes().cmp(&other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for String {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for String<'lua> {
|
||||
impl Serialize for String {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self.to_str() {
|
||||
Ok(s) => serializer.serialize_str(s),
|
||||
Err(_) => serializer.serialize_bytes(self.as_bytes()),
|
||||
Ok(s) => serializer.serialize_str(&s),
|
||||
Err(_) => serializer.serialize_bytes(&self.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Display<'a>(&'a String);
|
||||
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.0.as_bytes();
|
||||
<bstr::BStr as fmt::Display>::fmt(bstr::BStr::new(&bytes), f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedStr<'a> {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a str,
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedStr<'_> {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &str {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for BorrowedStr<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for BorrowedStr<'_> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BorrowedStr<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedStr<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for BorrowedStr<'_>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.buf == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BorrowedStr<'_> {}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedStr<'_>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.buf.partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BorrowedStr<'_> {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: &'a String) -> Result<Self> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
|
||||
let buf = str::from_utf8(buf).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str".to_string(),
|
||||
message: Some(e.to_string()),
|
||||
})?;
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedBytes<'a> {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a [u8],
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedBytes<'_> {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &[u8] {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8]> for BorrowedBytes<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BorrowedBytes<'_> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedBytes<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for BorrowedBytes<'_>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.buf == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BorrowedBytes<'_> {}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedBytes<'_>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.buf.partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BorrowedBytes<'_> {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
|
||||
type Item = &'a u8;
|
||||
type IntoIter = slice::Iter<'a, u8>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a String> for BorrowedBytes<'a> {
|
||||
#[inline]
|
||||
fn from(value: &'a String) -> Self {
|
||||
let (buf, _lua) = unsafe { value.to_slice() };
|
||||
let borrow = Cow::Borrowed(value);
|
||||
Self { buf, borrow, _lua }
|
||||
}
|
||||
}
|
||||
|
||||
struct WrappedString<T: AsRef<[u8]>>(T);
|
||||
|
||||
impl String {
|
||||
/// Wraps bytes, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_string`] under the hood.
|
||||
pub fn wrap(data: impl AsRef<[u8]>) -> impl IntoLua {
|
||||
WrappedString(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> IntoLua for WrappedString<T> {
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.create_string(self.0).map(Value::String)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for String {
|
||||
const TYPE_ID: c_int = ffi::LUA_TSTRING;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(String: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(String: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(BorrowedBytes: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(BorrowedStr: Send, Sync);
|
||||
}
|
||||
|
||||
+681
-476
File diff suppressed because it is too large
Load Diff
+332
-242
@@ -1,83 +1,110 @@
|
||||
use std::cmp;
|
||||
use std::os::raw::c_int;
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
all(feature = "luajit", feature = "vendored"),
|
||||
feature = "luau",
|
||||
))]
|
||||
use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::{
|
||||
hook::{Debug, HookTriggers},
|
||||
types::MaybeSend,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{
|
||||
lua::{Lua, ASYNC_POLL_PENDING},
|
||||
value::{MultiValue, Value},
|
||||
},
|
||||
futures_core::{future::Future, stream::Stream},
|
||||
futures_util::stream::Stream,
|
||||
std::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
ptr::NonNull,
|
||||
task::{Context, Poll, Waker},
|
||||
},
|
||||
};
|
||||
|
||||
/// Status of a Lua thread (or coroutine).
|
||||
/// Status of a Lua thread (coroutine).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ThreadStatus {
|
||||
/// The thread was just created, or is suspended because it has called `coroutine.yield`.
|
||||
/// The thread was just created or is suspended (yielded).
|
||||
///
|
||||
/// If a thread is in this state, it can be resumed by calling [`Thread::resume`].
|
||||
///
|
||||
/// [`Thread::resume`]: crate::Thread::resume
|
||||
Resumable,
|
||||
/// Either the thread has finished executing, or the thread is currently running.
|
||||
Unresumable,
|
||||
/// The thread is currently running.
|
||||
Running,
|
||||
/// The thread has finished executing.
|
||||
Finished,
|
||||
/// The thread has raised a Lua error during execution.
|
||||
Error,
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua thread (or coroutine).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
||||
/// Internal representation of a Lua thread status.
|
||||
///
|
||||
/// The number in `New` and `Yielded` variants is the number of arguments pushed
|
||||
/// to the thread stack.
|
||||
#[derive(Clone, Copy)]
|
||||
enum ThreadStatusInner {
|
||||
New,
|
||||
Running,
|
||||
Yielded,
|
||||
Finished,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl ThreadStatusInner {
|
||||
#[inline(always)]
|
||||
fn is_resumable(self) -> bool {
|
||||
matches!(self, ThreadStatusInner::New | ThreadStatusInner::Yielded)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua thread (coroutine).
|
||||
#[derive(Clone)]
|
||||
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Send for Thread {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Sync for Thread {}
|
||||
|
||||
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`Future`]: futures_core::future::Future
|
||||
/// [`Stream`]: futures_core::stream::Stream
|
||||
/// [`Future`]: std::future::Future
|
||||
/// [`Stream`]: futures_util::stream::Stream
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[derive(Debug)]
|
||||
pub struct AsyncThread<'lua, R> {
|
||||
thread: Thread<'lua>,
|
||||
args0: RefCell<Option<Result<MultiValue<'lua>>>>,
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncThread<A, R> {
|
||||
thread: Thread,
|
||||
init_args: Option<A>,
|
||||
ret: PhantomData<R>,
|
||||
recycle: bool,
|
||||
}
|
||||
|
||||
impl<'lua> Thread<'lua> {
|
||||
impl Thread {
|
||||
#[inline(always)]
|
||||
fn state(&self) -> *mut ffi::lua_State {
|
||||
self.1
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// Equivalent to `coroutine.resume`.
|
||||
/// Equivalent to [`coroutine.resume`].
|
||||
///
|
||||
/// Passes `args` as arguments to the thread. If the coroutine has called `coroutine.yield`, it
|
||||
/// will return these arguments. Otherwise, the coroutine wasn't yet started, so the arguments
|
||||
/// are passed to its main function.
|
||||
/// Passes `args` as arguments to the thread. If the coroutine has called [`coroutine.yield`],
|
||||
/// it will return these arguments. Otherwise, the coroutine wasn't yet started, so the
|
||||
/// arguments are passed to its main function.
|
||||
///
|
||||
/// If the thread is no longer in `Active` state (meaning it has finished execution or
|
||||
/// encountered an error), this will return `Err(CoroutineInactive)`, otherwise will return `Ok`
|
||||
/// as follows:
|
||||
/// If the thread is no longer resumable (meaning it has finished execution or encountered an
|
||||
/// error), this will return [`Error::CoroutineUnresumable`], otherwise will return `Ok` as
|
||||
/// follows:
|
||||
///
|
||||
/// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread
|
||||
/// If the thread calls [`coroutine.yield`], returns the values passed to `yield`. If the thread
|
||||
/// `return`s values from its main function, returns those.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -95,80 +122,110 @@ impl<'lua> Thread<'lua> {
|
||||
/// end)
|
||||
/// "#).eval()?;
|
||||
///
|
||||
/// assert_eq!(thread.resume::<_, u32>(42)?, 123);
|
||||
/// assert_eq!(thread.resume::<_, u32>(43)?, 987);
|
||||
/// assert_eq!(thread.resume::<u32>(42)?, 123);
|
||||
/// assert_eq!(thread.resume::<u32>(43)?, 987);
|
||||
///
|
||||
/// // The coroutine has now returned, so `resume` will fail
|
||||
/// match thread.resume::<_, u32>(()) {
|
||||
/// Err(Error::CoroutineInactive) => {},
|
||||
/// match thread.resume::<u32>(()) {
|
||||
/// Err(Error::CoroutineUnresumable) => {},
|
||||
/// unexpected => panic!("unexpected result {:?}", unexpected),
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn resume<A, R>(&self, args: A) -> Result<R>
|
||||
///
|
||||
/// [`coroutine.resume`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.resume
|
||||
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
|
||||
pub fn resume<R>(&self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let mut args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
let results = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, cmp::max(nargs + 1, 3))?;
|
||||
let lua = self.0.lua.lock();
|
||||
if !self.status_inner(&lua).is_resumable() {
|
||||
return Err(Error::CoroutineUnresumable);
|
||||
}
|
||||
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
||||
return Err(Error::CoroutineInactive);
|
||||
}
|
||||
let nresults = self.resume_inner(&lua, args)?;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
R::from_stack_multi(nresults, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
|
||||
unsafe fn resume_inner(&self, lua: &RawLua, args: impl IntoLuaMulti) -> Result<c_int> {
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
|
||||
let nargs = args.push_into_stack_multi(lua)?;
|
||||
if nargs > 0 {
|
||||
check_stack(thread_state, nargs)?;
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
}
|
||||
|
||||
let mut nresults = 0;
|
||||
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
if ret == ffi::LUA_ERRMEM {
|
||||
// Don't call error handler for memory errors
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
ffi::lua_xmove(lua.state, thread_state, nargs);
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(state, thread_state))?;
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
|
||||
let mut nresults = 0;
|
||||
|
||||
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
check_stack(lua.state, 3)?;
|
||||
protect_lua!(lua.state, 0, 1, |state| error_traceback_thread(
|
||||
state,
|
||||
thread_state
|
||||
))?;
|
||||
return Err(pop_error(lua.state, ret));
|
||||
}
|
||||
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
|
||||
ffi::lua_xmove(thread_state, lua.state, nresults);
|
||||
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
}
|
||||
results
|
||||
};
|
||||
R::from_lua_multi(results, lua)
|
||||
Ok(nresults)
|
||||
}
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
match self.status_inner(&self.0.lua.lock()) {
|
||||
ThreadStatusInner::New | ThreadStatusInner::Yielded => ThreadStatus::Resumable,
|
||||
ThreadStatusInner::Running => ThreadStatus::Running,
|
||||
ThreadStatusInner::Finished => ThreadStatus::Finished,
|
||||
ThreadStatusInner::Error => ThreadStatus::Error,
|
||||
}
|
||||
}
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
ThreadStatus::Error
|
||||
} else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
|
||||
ThreadStatus::Resumable
|
||||
} else {
|
||||
ThreadStatus::Unresumable
|
||||
}
|
||||
/// Gets the status of the thread (internal implementation).
|
||||
fn status_inner(&self, lua: &RawLua) -> ThreadStatusInner {
|
||||
let thread_state = self.state();
|
||||
if thread_state == lua.state() {
|
||||
// The thread is currently running
|
||||
return ThreadStatusInner::Running;
|
||||
}
|
||||
let status = unsafe { ffi::lua_status(thread_state) };
|
||||
match status {
|
||||
ffi::LUA_YIELD => ThreadStatusInner::Yielded,
|
||||
ffi::LUA_OK if unsafe { ffi::lua_gettop(thread_state) } > 0 => ThreadStatusInner::New,
|
||||
ffi::LUA_OK => ThreadStatusInner::Finished,
|
||||
_ => ThreadStatusInner::Error,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a hook function that will periodically be called as Lua code executes.
|
||||
///
|
||||
/// This function is similar or [`Lua::set_hook`] except that it sets for the thread.
|
||||
/// To remove a hook call [`Lua::remove_hook`].
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
|
||||
where
|
||||
F: Fn(&crate::Lua, Debug) -> Result<crate::VmState> + MaybeSend + 'static,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
lua.set_thread_hook(self.state(), triggers, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,47 +235,47 @@ impl<'lua> Thread<'lua> {
|
||||
/// Returns a error in case of either the original error that stopped the thread or errors
|
||||
/// in closing methods.
|
||||
///
|
||||
/// In [LuaJIT] and Luau: resets to the initial state of a newly created Lua thread.
|
||||
/// In Luau: resets to the initial state of a newly created Lua thread.
|
||||
/// Lua threads in arbitrary states (like yielded or errored) can be reset properly.
|
||||
///
|
||||
/// Other Lua versions can reset only new or finished threads.
|
||||
///
|
||||
/// Sets a Lua function for the thread afterwards.
|
||||
///
|
||||
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"` OR `feature = "luau"`
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_resetthread
|
||||
/// [LuaJIT]: https://github.com/openresty/luajit2#lua_resetthread
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
all(feature = "luajit", feature = "vendored"),
|
||||
feature = "luau",
|
||||
))]
|
||||
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_closethread
|
||||
pub fn reset(&self, func: Function) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let thread_state = self.state();
|
||||
match self.status_inner(&lua) {
|
||||
ThreadStatusInner::Running => return Err(Error::runtime("cannot reset a running thread")),
|
||||
// Any Lua can reuse new or finished thread
|
||||
ThreadStatusInner::New => unsafe { ffi::lua_settop(thread_state, 0) },
|
||||
ThreadStatusInner::Finished => {}
|
||||
#[cfg(not(any(feature = "lua54", feature = "luau")))]
|
||||
_ => return Err(Error::runtime("cannot reset non-finished thread")),
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
_ => unsafe {
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, lua.state());
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
},
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
ffi::lua_resetthread(lua.state, thread_state);
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
|
||||
lua.push_ref(&func.0);
|
||||
ffi::lua_xmove(lua.state, thread_state, 1);
|
||||
// Push function to the top of the thread stack
|
||||
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
ffi::lua_xpush(lua.state, thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
// Inherit `LUA_GLOBALSINDEX` from the main thread
|
||||
ffi::lua_xpush(lua.main_state(), thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
}
|
||||
|
||||
@@ -226,27 +283,28 @@ impl<'lua> Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Thread to an AsyncThread which implements [`Future`] and [`Stream`] traits.
|
||||
/// Converts [`Thread`] to an [`AsyncThread`] which implements [`Future`] and [`Stream`] traits.
|
||||
///
|
||||
/// `args` are passed as arguments to the thread function for first call.
|
||||
/// The object calls [`resume()`] while polling and also allows to run rust futures
|
||||
/// The object calls [`resume`] while polling and also allow to run Rust futures
|
||||
/// to completion using an executor.
|
||||
///
|
||||
/// Using AsyncThread as a Stream allows to iterate through `coroutine.yield()`
|
||||
/// values whereas Future version discards that values and poll until the final
|
||||
/// Using [`AsyncThread`] as a [`Stream`] allow to iterate through [`coroutine.yield`]
|
||||
/// values whereas [`Future`] version discards that values and poll until the final
|
||||
/// one (returned from the thread function).
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`Future`]: futures_core::future::Future
|
||||
/// [`Stream`]: futures_core::stream::Stream
|
||||
/// [`resume()`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
|
||||
/// [`Future`]: std::future::Future
|
||||
/// [`Stream`]: futures_util::stream::Stream
|
||||
/// [`resume`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
|
||||
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, Thread};
|
||||
/// use futures::stream::TryStreamExt;
|
||||
/// use futures_util::stream::TryStreamExt;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
@@ -260,7 +318,7 @@ impl<'lua> Thread<'lua> {
|
||||
/// end)
|
||||
/// "#).eval()?;
|
||||
///
|
||||
/// let mut stream = thread.into_async::<_, i64>(1);
|
||||
/// let mut stream = thread.into_async::<i64>(1);
|
||||
/// let mut sum = 0;
|
||||
/// while let Some(n) = stream.try_next().await? {
|
||||
/// sum += n;
|
||||
@@ -273,15 +331,13 @@ impl<'lua> Thread<'lua> {
|
||||
/// ```
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
|
||||
pub fn into_async<R>(self, args: impl IntoLuaMulti) -> AsyncThread<impl IntoLuaMulti, R>
|
||||
where
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let args = args.to_lua_multi(self.0.lua);
|
||||
AsyncThread {
|
||||
thread: self,
|
||||
args0: RefCell::new(Some(args)),
|
||||
init_args: Some(args),
|
||||
ret: PhantomData,
|
||||
recycle: false,
|
||||
}
|
||||
@@ -292,7 +348,7 @@ impl<'lua> Thread<'lua> {
|
||||
/// Under the hood replaces the global environment table with a new table,
|
||||
/// that performs writes locally and proxies reads to caller's global environment.
|
||||
///
|
||||
/// This mode ideally should be used together with the global sandbox mode [`Lua::sandbox()`].
|
||||
/// This mode ideally should be used together with the global sandbox mode [`Lua::sandbox`].
|
||||
///
|
||||
/// Please note that Luau links environment table with chunk when loading it into Lua state.
|
||||
/// Therefore you need to load chunks into a thread to link with the thread environment.
|
||||
@@ -301,178 +357,212 @@ impl<'lua> Thread<'lua> {
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result};
|
||||
/// # #[cfg(feature = "luau")]
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let thread = lua.create_thread(lua.create_function(|lua2, ()| {
|
||||
/// lua2.load("var = 123").exec()?;
|
||||
/// assert_eq!(lua2.globals().get::<_, u32>("var")?, 123);
|
||||
/// assert_eq!(lua2.globals().get::<u32>("var")?, 123);
|
||||
/// Ok(())
|
||||
/// })?)?;
|
||||
/// thread.sandbox()?;
|
||||
/// thread.resume(())?;
|
||||
/// thread.resume::<()>(())?;
|
||||
///
|
||||
/// // The global environment should be unchanged
|
||||
/// assert_eq!(lua.globals().get::<_, Option<u32>>("var")?, None);
|
||||
/// assert_eq!(lua.globals().get::<Option<u32>>("var")?, None);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
///
|
||||
/// # #[cfg(not(feature = "luau"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", docsrs))]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[doc(hidden)]
|
||||
pub fn sandbox(&self) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
check_stack(thread, 1)?;
|
||||
check_stack(lua.state, 3)?;
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
ffi::lua_xpush(lua.state, thread, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(thread, ffi::LUA_GLOBALSINDEX);
|
||||
protect_lua!(lua.state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
|
||||
check_stack(thread_state, 3)?;
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread_state))
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this thread to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Thread<'lua> {
|
||||
impl fmt::Debug for Thread {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_tuple("Thread").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Thread {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Thread {
|
||||
const TYPE_ID: c_int = ffi::LUA_TTHREAD;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> AsyncThread<'lua, R> {
|
||||
#[inline]
|
||||
impl<A, R> AsyncThread<A, R> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
|
||||
self.recycle = recyclable;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
all(feature = "luajit", feature = "vendored"),
|
||||
feature = "luau",
|
||||
))]
|
||||
impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
||||
impl<A, R> Drop for AsyncThread<A, R> {
|
||||
fn drop(&mut self) {
|
||||
if self.recycle {
|
||||
unsafe {
|
||||
let lua = self.thread.0.lua;
|
||||
if let Some(lua) = self.thread.0.lua.try_lock() {
|
||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
||||
if !lua.recycle_thread(&mut self.thread) {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status() == ThreadStatus::Error {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.thread.0.index);
|
||||
ffi::lua_resetthread(thread_state);
|
||||
}
|
||||
}
|
||||
unsafe { lua.recycle_thread(&mut self.thread) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> Stream for AsyncThread<'lua, R>
|
||||
where
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
impl<A: IntoLuaMulti, R: FromLuaMulti> Stream for AsyncThread<A, R> {
|
||||
type Item = Result<R>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let lua = self.thread.0.lua;
|
||||
|
||||
match self.thread.status() {
|
||||
ThreadStatus::Resumable => {}
|
||||
_ => return Poll::Ready(None),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua, cx.waker().clone());
|
||||
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
|
||||
self.thread.resume(args?)?
|
||||
} else {
|
||||
self.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if !self.thread.status_inner(&lua).is_resumable() {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Ready(Some(R::from_lua_multi(ret, lua)))
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(&lua, args)?
|
||||
} else {
|
||||
this.thread.resume_inner(&lua, ())?
|
||||
};
|
||||
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Ready(Some(R::from_stack_multi(nresults, &lua)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> Future for AsyncThread<'lua, R>
|
||||
where
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
impl<A: IntoLuaMulti, R: FromLuaMulti> Future for AsyncThread<A, R> {
|
||||
type Output = Result<R>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let lua = self.thread.0.lua;
|
||||
|
||||
match self.thread.status() {
|
||||
ThreadStatus::Resumable => {}
|
||||
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua, cx.waker().clone());
|
||||
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
|
||||
self.thread.resume(args?)?
|
||||
} else {
|
||||
self.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if !self.thread.status_inner(&lua).is_resumable() {
|
||||
return Poll::Ready(Err(Error::CoroutineUnresumable));
|
||||
}
|
||||
|
||||
if let ThreadStatus::Resumable = self.thread.status() {
|
||||
// Ignore value returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
return Poll::Pending;
|
||||
}
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
|
||||
Poll::Ready(R::from_lua_multi(ret, lua))
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(&lua, args)?
|
||||
} else {
|
||||
this.thread.resume_inner(&lua, ())?
|
||||
};
|
||||
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
if ffi::lua_status(thread_state) == ffi::LUA_YIELD {
|
||||
// Ignore value returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
Poll::Ready(R::from_stack_multi(nresults, &lua))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
fn is_poll_pending(val: &MultiValue) -> bool {
|
||||
match val.iter().enumerate().last() {
|
||||
Some((0, Value::LightUserData(ud))) => {
|
||||
std::ptr::eq(ud.0 as *const u8, &ASYNC_POLL_PENDING as *const u8)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
|
||||
ffi::lua_tolightuserdata(state, -1) == crate::Lua::poll_pending().0
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
struct WakerGuard<'lua> {
|
||||
lua: &'lua Lua,
|
||||
prev: Option<Waker>,
|
||||
struct WakerGuard<'lua, 'a> {
|
||||
lua: &'lua RawLua,
|
||||
prev: NonNull<Waker>,
|
||||
_phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua> WakerGuard<'lua> {
|
||||
impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||
#[inline]
|
||||
pub fn new(lua: &Lua, waker: Waker) -> Result<WakerGuard> {
|
||||
unsafe {
|
||||
let prev = lua.set_waker(Some(waker));
|
||||
Ok(WakerGuard { lua, prev })
|
||||
}
|
||||
pub fn new(lua: &'lua RawLua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
|
||||
let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
|
||||
Ok(WakerGuard {
|
||||
lua,
|
||||
prev,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua> Drop for WakerGuard<'lua> {
|
||||
impl Drop for WakerGuard<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.lua.set_waker(self.prev.take());
|
||||
}
|
||||
unsafe { self.lua.set_waker(self.prev) };
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(Thread: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Thread: Send, Sync);
|
||||
#[cfg(all(feature = "async", not(feature = "send")))]
|
||||
static_assertions::assert_not_impl_any!(AsyncThread<(), ()>: Send);
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
static_assertions::assert_impl_all!(AsyncThread<(), ()>: Send, Sync);
|
||||
}
|
||||
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::multi::MultiValue;
|
||||
use crate::private::Sealed;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::types::MaybeSend;
|
||||
use crate::util::{check_stack, short_type_name};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
|
||||
/// Trait for types convertible to [`Value`].
|
||||
pub trait IntoLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value>;
|
||||
|
||||
/// Pushes the value into the Lua stack.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method does not check Lua stack space.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_value(&self.into_lua(lua.lua())?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible from [`Value`].
|
||||
pub trait FromLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
|
||||
|
||||
/// Performs the conversion for an argument (eg. function argument).
|
||||
///
|
||||
/// `i` is the argument index (position),
|
||||
/// `to` is a function name that received the argument.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
|
||||
/// Performs the conversion for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
Self::from_lua(lua.stack_value(idx, None), lua.lua())
|
||||
}
|
||||
|
||||
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to any number of Lua values.
|
||||
///
|
||||
/// This is a generalization of [`IntoLua`], allowing any number of resulting Lua values instead of
|
||||
/// just one. Any type that implements [`IntoLua`] will automatically implement this trait.
|
||||
pub trait IntoLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
|
||||
|
||||
/// Pushes the values into the Lua stack.
|
||||
///
|
||||
/// Returns number of pushed values.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
let values = self.into_lua_multi(lua.lua())?;
|
||||
let len: c_int = values.len().try_into().unwrap();
|
||||
unsafe {
|
||||
check_stack(lua.state(), len + 1)?;
|
||||
for val in &values {
|
||||
lua.push_value(val)?;
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types that can be created from an arbitrary number of Lua values.
|
||||
///
|
||||
/// This is a generalization of [`FromLua`], allowing an arbitrary number of Lua values to
|
||||
/// participate in the conversion. Any type that implements [`FromLua`] will automatically
|
||||
/// implement this trait.
|
||||
pub trait FromLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
///
|
||||
/// In case `values` contains more values than needed to perform the conversion, the excess
|
||||
/// values should be ignored. This reflects the semantics of Lua when calling a function or
|
||||
/// assigning values. Similarly, if not enough values are given, conversions should assume that
|
||||
/// any missing values are nil.
|
||||
fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
|
||||
|
||||
/// Performs the conversion for a list of arguments.
|
||||
///
|
||||
/// `i` is an index (position) of the first argument,
|
||||
/// `to` is a function name that received the arguments.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_lua_multi(args, lua)
|
||||
}
|
||||
|
||||
/// Performs the conversion for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let mut values = MultiValue::with_capacity(nvals as usize);
|
||||
for idx in 0..nvals {
|
||||
values.push_back(lua.stack_value(-nvals + idx, None));
|
||||
}
|
||||
Self::from_lua_multi(values, lua.lua())
|
||||
}
|
||||
|
||||
/// Same as `from_lua_args` but for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_stack_multi(nargs, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types that can be used as Lua objects (usually table and userdata).
|
||||
pub trait ObjectLike: Sealed {
|
||||
/// Gets the value associated to `key` from the object, assuming it has `__index` metamethod.
|
||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V>;
|
||||
|
||||
/// Sets the value associated to `key` in the object, assuming it has `__newindex` metamethod.
|
||||
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()>;
|
||||
|
||||
/// Calls the object as a function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the object as its first argument, followed by the passed
|
||||
/// arguments.
|
||||
fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Asynchronously calls the object as a function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the object as its first argument, followed by the passed
|
||||
/// arguments.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Gets the function associated to key `name` from the object and calls it,
|
||||
/// passing the object itself along with `args` as function arguments.
|
||||
fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Gets the function associated to key `name` from the object and asynchronously calls it,
|
||||
/// passing the object itself along with `args` as function arguments.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Gets the function associated to key `name` from the object and calls it,
|
||||
/// passing `args` as function arguments.
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
fn call_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Gets the function associated to key `name` from the object and asynchronously calls it,
|
||||
/// passing `args` as function arguments.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Converts the object to a string in a human-readable format.
|
||||
///
|
||||
/// This might invoke the `__tostring` metamethod.
|
||||
fn to_string(&self) -> Result<StdString>;
|
||||
}
|
||||
|
||||
/// A trait for types that can be used as Lua functions.
|
||||
pub trait LuaNativeFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types with mutable state that can be used as Lua functions.
|
||||
pub trait LuaNativeFnMut<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&mut self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types that returns a future and can be used as Lua functions.
|
||||
#[cfg(feature = "async")]
|
||||
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
|
||||
}
|
||||
|
||||
macro_rules! impl_lua_native_fn {
|
||||
($($A:ident),*) => {
|
||||
impl<FN, $($A,)* R> LuaNativeFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FN, $($A,)* R> LuaNativeFnMut<($($A,)*)> for FN
|
||||
where
|
||||
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&mut self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<FN, $($A,)* Fut, R> LuaNativeAsyncFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
Fut: Future<Output = R> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> impl Future<Output = Self::Output> + MaybeSend + 'static {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_lua_native_fn!();
|
||||
impl_lua_native_fn!(A);
|
||||
impl_lua_native_fn!(A, B);
|
||||
impl_lua_native_fn!(A, B, C);
|
||||
impl_lua_native_fn!(A, B, C, D);
|
||||
impl_lua_native_fn!(A, B, C, D, E);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
|
||||
pub(crate) trait ShortTypeName {
|
||||
#[inline(always)]
|
||||
fn type_name() -> StdString {
|
||||
short_type_name::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ShortTypeName for T {}
|
||||
+72
-123
@@ -1,22 +1,25 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::ffi;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::hook::Debug;
|
||||
use crate::lua::{ExtraData, Lua};
|
||||
use crate::util::{assert_stack, StackGuard};
|
||||
use crate::value::MultiValue;
|
||||
use crate::state::{ExtraData, Lua, RawLua};
|
||||
|
||||
// Re-export mutex wrappers
|
||||
pub(crate) use sync::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
|
||||
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
pub(crate) type BoxFuture<'a, T> = futures_util::future::BoxFuture<'a, T>;
|
||||
|
||||
#[cfg(all(feature = "async", not(feature = "send")))]
|
||||
pub(crate) type BoxFuture<'a, T> = futures_util::future::LocalBoxFuture<'a, T>;
|
||||
|
||||
pub use app_data::{AppData, AppDataRef, AppDataRefMut};
|
||||
pub use either::Either;
|
||||
pub use registry_key::RegistryKey;
|
||||
pub(crate) use value_ref::ValueRef;
|
||||
|
||||
/// Type of Lua integer numbers.
|
||||
pub type Integer = ffi::lua_Integer;
|
||||
@@ -27,52 +30,68 @@ pub type Number = ffi::lua_Number;
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct LightUserData(pub *mut c_void);
|
||||
|
||||
pub(crate) type Callback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Send for LightUserData {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Sync for LightUserData {}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + Send + 'static>;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 'static>;
|
||||
|
||||
pub(crate) type ScopedCallback<'s> = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 's>;
|
||||
|
||||
pub(crate) struct Upvalue<T> {
|
||||
pub(crate) data: T,
|
||||
pub(crate) extra: Arc<UnsafeCell<ExtraData>>,
|
||||
pub(crate) extra: XRc<UnsafeCell<ExtraData>>,
|
||||
}
|
||||
|
||||
pub(crate) type CallbackUpvalue = Upvalue<Callback<'static, 'static>>;
|
||||
pub(crate) type CallbackUpvalue = Upvalue<Option<Callback>>;
|
||||
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
pub(crate) type AsyncCallback =
|
||||
Box<dyn for<'a> Fn(&'a RawLua, c_int) -> BoxFuture<'a, Result<c_int>> + Send + 'static>;
|
||||
|
||||
#[cfg(all(feature = "async", not(feature = "send")))]
|
||||
pub(crate) type AsyncCallback =
|
||||
Box<dyn for<'a> Fn(&'a RawLua, c_int) -> BoxFuture<'a, Result<c_int>> + 'static>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static, 'static>>;
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<BoxFuture<'static, Result<c_int>>>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<MultiValue<'static>>>>;
|
||||
|
||||
/// Type to set next Luau VM action after executing interrupt function.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
/// Type to set next Lua VM action after executing interrupt or hook function.
|
||||
pub enum VmState {
|
||||
Continue,
|
||||
/// Yield the current thread.
|
||||
///
|
||||
/// Supported by Lua 5.3+ and Luau.
|
||||
Yield,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "send", not(feature = "luau")))]
|
||||
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
|
||||
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<VmState> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
|
||||
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()>>;
|
||||
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<VmState>>;
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "send"))]
|
||||
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState> + Send>;
|
||||
#[cfg(all(feature = "send", feature = "luau"))]
|
||||
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||
|
||||
#[cfg(all(feature = "luau", not(feature = "send")))]
|
||||
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState>>;
|
||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()> + Send>;
|
||||
pub(crate) type WarnCallback = XRc<dyn Fn(&Lua, &str, bool) -> Result<()> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()>>;
|
||||
pub(crate) type WarnCallback = XRc<dyn Fn(&Lua, &str, bool) -> Result<()>>;
|
||||
|
||||
/// A trait that adds `Send` requirement if `send` feature is enabled.
|
||||
#[cfg(feature = "send")]
|
||||
pub trait MaybeSend: Send {}
|
||||
#[cfg(feature = "send")]
|
||||
@@ -85,103 +104,33 @@ impl<T> MaybeSend for T {}
|
||||
|
||||
pub(crate) struct DestructedUserdata;
|
||||
|
||||
/// An auto generated key into the Lua registry.
|
||||
///
|
||||
/// This is a handle to a value stored inside the Lua registry. It is not automatically
|
||||
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
|
||||
/// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
|
||||
///
|
||||
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
|
||||
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
|
||||
/// Instead of placing a [`RegistryKey`] into a [`UserData`] type, prefer instead to use
|
||||
/// [`AnyUserData::set_user_value`] / [`AnyUserData::get_user_value`].
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
|
||||
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
|
||||
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
|
||||
/// [`AnyUserData::get_user_value`]: crate::AnyUserData::get_user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: c_int,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
pub(crate) trait LuaType {
|
||||
const TYPE_ID: c_int;
|
||||
}
|
||||
|
||||
impl fmt::Debug for RegistryKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "RegistryKey({})", self.registry_id)
|
||||
}
|
||||
impl LuaType for bool {
|
||||
const TYPE_ID: c_int = ffi::LUA_TBOOLEAN;
|
||||
}
|
||||
|
||||
impl Hash for RegistryKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.registry_id.hash(state)
|
||||
}
|
||||
impl LuaType for Number {
|
||||
const TYPE_ID: c_int = ffi::LUA_TNUMBER;
|
||||
}
|
||||
|
||||
impl PartialEq for RegistryKey {
|
||||
fn eq(&self, other: &RegistryKey) -> bool {
|
||||
self.registry_id == other.registry_id && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
}
|
||||
impl LuaType for LightUserData {
|
||||
const TYPE_ID: c_int = ffi::LUA_TLIGHTUSERDATA;
|
||||
}
|
||||
|
||||
impl Eq for RegistryKey {}
|
||||
mod app_data;
|
||||
mod registry_key;
|
||||
mod sync;
|
||||
mod value_ref;
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(self.registry_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
impl RegistryKey {
|
||||
// Destroys the RegistryKey without adding to the drop list
|
||||
pub(crate) fn take(self) -> c_int {
|
||||
let registry_id = self.registry_id;
|
||||
unsafe {
|
||||
ptr::read(&self.unref_list);
|
||||
mem::forget(self);
|
||||
}
|
||||
registry_id
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LuaRef<'lua> {
|
||||
pub(crate) lua: &'lua Lua,
|
||||
pub(crate) index: c_int,
|
||||
}
|
||||
|
||||
impl<'lua> fmt::Debug for LuaRef<'lua> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Ref({})", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Clone for LuaRef<'lua> {
|
||||
fn clone(&self) -> Self {
|
||||
self.lua.clone_ref(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Drop for LuaRef<'lua> {
|
||||
fn drop(&mut self) {
|
||||
if self.index > 0 {
|
||||
self.lua.drop_ref(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for LuaRef<'lua> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let lua = self.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
lua.push_ref(self);
|
||||
lua.push_ref(other);
|
||||
ffi::lua_rawequal(lua.state, -1, -2) == 1
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(ValueRef: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(ValueRef: Send, Sync);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{BorrowError, BorrowMutError, Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::MaybeSend;
|
||||
use crate::state::LuaGuard;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
type Container = UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
type Container = UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>;
|
||||
|
||||
/// A container for arbitrary data associated with the Lua state.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AppData {
|
||||
container: Container,
|
||||
borrow: Cell<usize>,
|
||||
}
|
||||
|
||||
impl AppData {
|
||||
#[track_caller]
|
||||
pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
|
||||
match self.try_insert(data) {
|
||||
Ok(data) => data,
|
||||
Err(_) => panic!("cannot mutably borrow app data container"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
|
||||
if self.borrow.get() != 0 {
|
||||
return Err(data);
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
Ok(unsafe { &mut *self.container.get() }
|
||||
.insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
|
||||
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<T>> {
|
||||
match self.try_borrow(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already mutably borrowed: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_borrow<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRef<T>>, BorrowError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow())
|
||||
.transpose()?
|
||||
.and_then(|data| Ref::filter_map(data, |data| data.downcast_ref()).ok());
|
||||
match data {
|
||||
Some(data) => {
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Ok(Some(AppDataRef {
|
||||
data,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
}))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<T>> {
|
||||
match self.try_borrow_mut(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already borrowed: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_borrow_mut<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRefMut<T>>, BorrowMutError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow_mut())
|
||||
.transpose()?
|
||||
.and_then(|data| RefMut::filter_map(data, |data| data.downcast_mut()).ok());
|
||||
match data {
|
||||
Some(data) => {
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Ok(Some(AppDataRefMut {
|
||||
data,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
}))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
|
||||
if self.borrow.get() != 0 {
|
||||
panic!("cannot mutably borrow app data container");
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
unsafe { &mut *self.container.get() }
|
||||
.remove(&TypeId::of::<T>())?
|
||||
.into_inner()
|
||||
.downcast::<T>()
|
||||
.ok()
|
||||
.map(|data| *data)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for an immutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`Ref`].
|
||||
pub struct AppDataRef<'a, T: ?Sized + 'a> {
|
||||
data: Ref<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
_guard: Option<LuaGuard>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRef<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`RefMut`].
|
||||
pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
|
||||
data: RefMut<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
_guard: Option<LuaGuard>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(AppData: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(AppData: Send);
|
||||
|
||||
// Must be !Send
|
||||
static_assertions::assert_not_impl_any!(AppDataRef<()>: Send);
|
||||
static_assertions::assert_not_impl_any!(AppDataRefMut<()>: Send);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::c_int;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
/// An auto generated key into the Lua registry.
|
||||
///
|
||||
/// This is a handle to a value stored inside the Lua registry. It is not automatically
|
||||
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
|
||||
/// and instances not manually removed can be garbage collected with
|
||||
/// [`Lua::expire_registry_values`].
|
||||
///
|
||||
/// Be warned, If you place this into Lua via a [`UserData`] type or a Rust callback, it is *easy*
|
||||
/// to accidentally cause reference cycles that the Lua garbage collector cannot resolve. Instead of
|
||||
/// placing a [`RegistryKey`] into a [`UserData`] type, consider to use
|
||||
/// [`AnyUserData::set_user_value`].
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
|
||||
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
|
||||
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: i32,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for RegistryKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "RegistryKey({})", self.id())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for RegistryKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.id().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RegistryKey {
|
||||
fn eq(&self, other: &RegistryKey) -> bool {
|
||||
self.id() == other.id() && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for RegistryKey {}
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let registry_id = self.id();
|
||||
// We don't need to collect nil slot
|
||||
if registry_id > ffi::LUA_REFNIL {
|
||||
let mut unref_list = self.unref_list.lock();
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(registry_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistryKey {
|
||||
/// Creates a new instance of `RegistryKey`
|
||||
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
|
||||
RegistryKey {
|
||||
registry_id: id,
|
||||
unref_list,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying Lua reference of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub fn id(&self) -> c_int {
|
||||
self.registry_id
|
||||
}
|
||||
|
||||
/// Sets the unique Lua reference key of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_id(&mut self, id: c_int) {
|
||||
self.registry_id = id;
|
||||
}
|
||||
|
||||
/// Destroys the `RegistryKey` without adding to the unref list
|
||||
pub(crate) fn take(self) -> i32 {
|
||||
let registry_id = self.id();
|
||||
unsafe {
|
||||
ptr::read(&self.unref_list);
|
||||
mem::forget(self);
|
||||
}
|
||||
registry_id
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_impl_all!(RegistryKey: Send, Sync);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#[cfg(feature = "send")]
|
||||
mod inner {
|
||||
use parking_lot::{RawMutex, RawThreadId};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
pub(crate) type XRc<T> = Arc<T>;
|
||||
pub(crate) type XWeak<T> = Weak<T>;
|
||||
|
||||
pub(crate) type ReentrantMutex<T> = parking_lot::ReentrantMutex<T>;
|
||||
|
||||
pub(crate) type ReentrantMutexGuard<'a, T> = parking_lot::ReentrantMutexGuard<'a, T>;
|
||||
|
||||
pub(crate) type ArcReentrantMutexGuard<T> =
|
||||
parking_lot::lock_api::ArcReentrantMutexGuard<RawMutex, RawThreadId, T>;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
mod inner {
|
||||
use std::ops::Deref;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
pub(crate) type XRc<T> = Rc<T>;
|
||||
pub(crate) type XWeak<T> = Weak<T>;
|
||||
|
||||
pub(crate) struct ReentrantMutex<T>(T);
|
||||
|
||||
impl<T> ReentrantMutex<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(val: T) -> Self {
|
||||
ReentrantMutex(val)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<T> {
|
||||
ReentrantMutexGuard(&self.0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn lock_arc(self: &XRc<Self>) -> ArcReentrantMutexGuard<T> {
|
||||
ArcReentrantMutexGuard(Rc::clone(self))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_lock_arc(self: XRc<Self>) -> ArcReentrantMutexGuard<T> {
|
||||
ArcReentrantMutexGuard(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn data_ptr(&self) -> *const T {
|
||||
&self.0 as *const _
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ReentrantMutexGuard<'a, T>(&'a T);
|
||||
|
||||
impl<T> Deref for ReentrantMutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ArcReentrantMutexGuard<T>(XRc<ReentrantMutex<T>>);
|
||||
|
||||
impl<T> Deref for ArcReentrantMutexGuard<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0 .0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use inner::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
|
||||
@@ -0,0 +1,71 @@
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use crate::state::{RawLua, WeakLua};
|
||||
|
||||
/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
|
||||
pub struct ValueRef {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) index: c_int,
|
||||
pub(crate) drop: bool,
|
||||
}
|
||||
|
||||
impl ValueRef {
|
||||
#[inline]
|
||||
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
|
||||
ValueRef {
|
||||
lua: lua.weak().clone(),
|
||||
index,
|
||||
drop: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn to_pointer(&self) -> *const c_void {
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
|
||||
}
|
||||
|
||||
/// Returns a copy of the value, which is valid as long as the original value is held.
|
||||
#[inline]
|
||||
pub(crate) fn copy(&self) -> Self {
|
||||
ValueRef {
|
||||
lua: self.lua.clone(),
|
||||
index: self.index,
|
||||
drop: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ValueRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Ref({:p})", self.to_pointer())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ValueRef {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { self.lua.lock().clone_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ValueRef {
|
||||
fn drop(&mut self) {
|
||||
if self.drop {
|
||||
if let Some(lua) = self.lua.try_lock() {
|
||||
unsafe { lua.drop_ref(self) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ValueRef {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
assert!(
|
||||
self.lua == other.lua,
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_rawequal(lua.ref_thread(), self.index, other.index) == 1 }
|
||||
}
|
||||
}
|
||||
+544
-666
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,298 @@
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::types::XRc;
|
||||
|
||||
use super::lock::{RawLock, UserDataLock};
|
||||
use super::r#ref::{UserDataRef, UserDataRefMut};
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "send")))]
|
||||
type DynSerialize = dyn erased_serde::Serialize;
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "send"))]
|
||||
type DynSerialize = dyn erased_serde::Serialize + Send;
|
||||
|
||||
pub(crate) enum UserDataStorage<T> {
|
||||
Owned(UserDataVariant<T>),
|
||||
Scoped(ScopedUserDataVariant<T>),
|
||||
}
|
||||
|
||||
// A enum for storing userdata values.
|
||||
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
|
||||
pub(crate) enum UserDataVariant<T> {
|
||||
Default(XRc<UserDataCell<T>>),
|
||||
#[cfg(feature = "serialize")]
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>, bool), // bool is `is_sync`
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataVariant<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataVariant<T> {
|
||||
#[inline(always)]
|
||||
pub(super) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
// We don't need to check for `T: Sync` because when this method is used (internally),
|
||||
// Lua mutex is already locked.
|
||||
// If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be
|
||||
// exclusively locked.
|
||||
let _guard = (self.raw_lock().try_lock_shared_guarded()).map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(unsafe { &*self.as_ptr() }))
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value in-place.
|
||||
#[inline(always)]
|
||||
fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
|
||||
let _guard =
|
||||
(self.raw_lock().try_lock_exclusive_guarded()).map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(unsafe { &mut *self.as_ptr() }))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
|
||||
UserDataRef::try_from(self.clone())
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
UserDataRefMut::try_from(self.clone())
|
||||
}
|
||||
|
||||
// Returns the wrapped value.
|
||||
//
|
||||
// This method checks that we have exclusive access to the value.
|
||||
fn into_inner(self) -> Result<T> {
|
||||
if !self.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(match self {
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner, _) => unsafe {
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
||||
*Box::from_raw(raw as *mut T)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn strong_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Default(inner) => XRc::strong_count(inner),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner, _) => XRc::strong_count(inner),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn raw_lock(&self) -> &RawLock {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.raw_lock,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner, _) => &inner.raw_lock,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn as_ptr(&self) -> *mut T {
|
||||
match self {
|
||||
Self::Default(inner) => inner.value.get(),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataStorage<()> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
Self::Owned(variant @ UserDataVariant::Serializable(inner, is_sync)) => unsafe {
|
||||
#[cfg(feature = "send")]
|
||||
if *is_sync {
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
} else {
|
||||
let _guard = (variant.raw_lock().try_lock_exclusive_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let _ = is_sync;
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
},
|
||||
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that provides interior mutability for a userdata value (thread-safe).
|
||||
pub(crate) struct UserDataCell<T> {
|
||||
raw_lock: RawLock,
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Send for UserDataCell<T> {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Sync for UserDataCell<T> {}
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
#[inline(always)]
|
||||
fn new(value: T) -> Self {
|
||||
UserDataCell {
|
||||
raw_lock: RawLock::INIT,
|
||||
value: UnsafeCell::new(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum ScopedUserDataVariant<T> {
|
||||
Ref(*const T),
|
||||
RefMut(RefCell<*mut T>),
|
||||
Boxed(RefCell<*mut T>),
|
||||
}
|
||||
|
||||
impl<T> Drop for ScopedUserDataVariant<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if let Self::Boxed(value) = self {
|
||||
if let Ok(value) = value.try_borrow_mut() {
|
||||
unsafe { drop(Box::from_raw(*value)) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataStorage<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
Self::Owned(UserDataVariant::Default(XRc::new(UserDataCell::new(data))))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ref(data: &T) -> Self {
|
||||
Self::Scoped(ScopedUserDataVariant::Ref(data))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ref_mut(data: &mut T) -> Self {
|
||||
Self::Scoped(ScopedUserDataVariant::RefMut(RefCell::new(data)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: Serialize + crate::types::MaybeSend,
|
||||
{
|
||||
let data = Box::new(data) as Box<DynSerialize>;
|
||||
let is_sync = super::util::is_sync::<T>();
|
||||
let variant = UserDataVariant::Serializable(XRc::new(UserDataCell::new(data)), is_sync);
|
||||
Self::Owned(variant)
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_serializable(&self) -> bool {
|
||||
matches!(self, Self::Owned(UserDataVariant::Serializable(..)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow_owned(),
|
||||
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow_owned_mut(),
|
||||
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_inner(self) -> Result<T> {
|
||||
match self {
|
||||
Self::Owned(data) => data.into_inner(),
|
||||
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataStorage<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_scoped(data: T) -> Self {
|
||||
let data = Box::into_raw(Box::new(data));
|
||||
Self::Scoped(ScopedUserDataVariant::Boxed(RefCell::new(data)))
|
||||
}
|
||||
|
||||
/// Returns `true` if it's safe to destroy the container.
|
||||
///
|
||||
/// It's safe to destroy the container if the reference count is greater than 1 or the lock is
|
||||
/// not acquired.
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_safe_to_destroy(&self) -> bool {
|
||||
match self {
|
||||
Self::Owned(variant) => variant.strong_count() > 1 || !variant.raw_lock().is_locked(),
|
||||
Self::Scoped(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the container has exclusive access to the value.
|
||||
#[inline(always)]
|
||||
pub(crate) fn has_exclusive_access(&self) -> bool {
|
||||
match self {
|
||||
Self::Owned(variant) => !variant.raw_lock().is_locked(),
|
||||
Self::Scoped(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow_scoped(f),
|
||||
Self::Scoped(ScopedUserDataVariant::Ref(value)) => Ok(f(unsafe { &**value })),
|
||||
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
|
||||
let t = value.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(unsafe { &**t }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow_scoped_mut(f),
|
||||
Self::Scoped(ScopedUserDataVariant::Ref(_)) => Err(Error::UserDataBorrowMutError),
|
||||
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
|
||||
let mut t = value
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(unsafe { &mut **t }))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
pub(crate) trait UserDataLock {
|
||||
const INIT: Self;
|
||||
|
||||
fn is_locked(&self) -> bool;
|
||||
fn try_lock_shared(&self) -> bool;
|
||||
fn try_lock_exclusive(&self) -> bool;
|
||||
|
||||
unsafe fn unlock_shared(&self);
|
||||
unsafe fn unlock_exclusive(&self);
|
||||
|
||||
fn try_lock_shared_guarded(&self) -> Result<LockGuard<'_, Self>, ()> {
|
||||
if self.try_lock_shared() {
|
||||
Ok(LockGuard {
|
||||
lock: self,
|
||||
exclusive: false,
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_lock_exclusive_guarded(&self) -> Result<LockGuard<'_, Self>, ()> {
|
||||
if self.try_lock_exclusive() {
|
||||
Ok(LockGuard {
|
||||
lock: self,
|
||||
exclusive: true,
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LockGuard<'a, L: UserDataLock + ?Sized> {
|
||||
lock: &'a L,
|
||||
exclusive: bool,
|
||||
}
|
||||
|
||||
impl<L: UserDataLock + ?Sized> Drop for LockGuard<'_, L> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.exclusive {
|
||||
self.lock.unlock_exclusive();
|
||||
} else {
|
||||
self.lock.unlock_shared();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use lock_impl::RawLock;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
mod lock_impl {
|
||||
use std::cell::Cell;
|
||||
|
||||
// Positive values represent the number of read references.
|
||||
// Negative values represent the number of write references (only one allowed).
|
||||
pub(crate) type RawLock = Cell<isize>;
|
||||
|
||||
const UNUSED: isize = 0;
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = Cell::new(UNUSED);
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
self.get() != UNUSED
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
let flag = self.get().wrapping_add(1);
|
||||
if flag <= UNUSED {
|
||||
return false;
|
||||
}
|
||||
self.set(flag);
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_exclusive(&self) -> bool {
|
||||
let flag = self.get();
|
||||
if flag != UNUSED {
|
||||
return false;
|
||||
}
|
||||
self.set(UNUSED - 1);
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_shared(&self) {
|
||||
let flag = self.get();
|
||||
debug_assert!(flag > UNUSED);
|
||||
self.set(flag - 1);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_exclusive(&self) {
|
||||
let flag = self.get();
|
||||
debug_assert!(flag < UNUSED);
|
||||
self.set(flag + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
mod lock_impl {
|
||||
use parking_lot::lock_api::RawRwLock;
|
||||
|
||||
pub(crate) type RawLock = parking_lot::RawRwLock;
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawRwLock>::INIT;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
RawRwLock::is_locked(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
RawRwLock::try_lock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_exclusive(&self) -> bool {
|
||||
RawRwLock::try_lock_exclusive(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_shared(&self) {
|
||||
RawRwLock::unlock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_exclusive(&self) {
|
||||
RawRwLock::unlock_exclusive(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::value::Value;
|
||||
use crate::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, Either, Future};
|
||||
|
||||
impl ObjectLike for AnyUserData {
|
||||
#[inline]
|
||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
// `lua_gettable` method used under the hood can work with any Lua value
|
||||
// that has `__index` metamethod
|
||||
Table(self.0.copy()).get_protected(key)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
// `lua_settable` method used under the hood can work with any Lua value
|
||||
// that has `__newindex` metamethod
|
||||
Table(self.0.copy()).set_protected(key, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
Function(self.0.copy()).call(args)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
Function(self.0.copy()).call_async(args)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.call_function(name, (self, args))
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.call_async_function(name, (self, args))
|
||||
}
|
||||
|
||||
fn call_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.get(name)? {
|
||||
Value::Function(func) => func.call(args),
|
||||
val => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Err(Error::RuntimeError(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.get(name) {
|
||||
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
|
||||
Ok(val) => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Either::Right(future::ready(Err(Error::RuntimeError(msg))))
|
||||
}
|
||||
Err(err) => Either::Right(future::ready(Err(err))),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
Value::UserData(AnyUserData(self.0.copy())).to_string()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user