mirror of
https://github.com/lua/lua
synced 2026-06-08 15:34:49 +00:00
Compare commits
2550 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c0e44464b | |||
| 6a84c32900 | |||
| 6a10f03ff8 | |||
| 7bd1e53753 | |||
| b98d41db99 | |||
| b2a580bdb1 | |||
| 89f6a85f03 | |||
| 6ef366644f | |||
| 03cde80b58 | |||
| 6b2e202df5 | |||
| 40d8832ee0 | |||
| 91dad09f65 | |||
| 4518e5df24 | |||
| 72a094bda7 | |||
| 46b84580d6 | |||
| df13f25948 | |||
| 643188d6e5 | |||
| 3df5624ff4 | |||
| be78aeae4c | |||
| 5bc47fe830 | |||
| 9405472565 | |||
| 45948e7e55 | |||
| b96b0b5abb | |||
| ca13be9af7 | |||
| a1d8eb2743 | |||
| f64a1b175a | |||
| 09b4e527a0 | |||
| 35a28a58b3 | |||
| 223bb04090 | |||
| fe040633a1 | |||
| f645d31573 | |||
| 35b4efc270 | |||
| 0d52913804 | |||
| b80077b8f3 | |||
| e70f275f32 | |||
| 9a37dc0ce6 | |||
| 0eb6aa4013 | |||
| 7f5c31cdca | |||
| 9e6807c3c9 | |||
| 2f22c6bb79 | |||
| 3c0d3c6fbe | |||
| 440a5ee78c | |||
| dc07719b0d | |||
| 9cdf6b7082 | |||
| 3c1d415bd3 | |||
| d36a31e673 | |||
| 024a6071ca | |||
| 4eefef07ab | |||
| 9c28ed05c9 | |||
| 8082906c05 | |||
| d6af81084d | |||
| 4846f7e3bb | |||
| c220b0a5d0 | |||
| 298f383ffc | |||
| 758c1ef445 | |||
| dd6d8db49a | |||
| 1fb4d53925 | |||
| f6aab3ec1f | |||
| be8445d7e4 | |||
| 3d296304ef | |||
| 54f7b46c1e | |||
| e888976bc6 | |||
| 4d46289331 | |||
| 8eca21c2e8 | |||
| 924bed7297 | |||
| 8b7cfee26b | |||
| c1a63c45f8 | |||
| 4487c28ced | |||
| 05ba288049 | |||
| 6b9490bd72 | |||
| e4b02ca8e4 | |||
| 20a9853e02 | |||
| 1d70708a78 | |||
| be73f72fcc | |||
| 3cd9b56ae6 | |||
| d2a9b4ffb8 | |||
| f39e8c06d6 | |||
| 6aeaeb5656 | |||
| b4d5dff8ec | |||
| 14edd364c3 | |||
| 514d942748 | |||
| 4a3fd8488d | |||
| 2c68e66570 | |||
| 7d0f41df41 | |||
| 2b8b53864c | |||
| b293ae0577 | |||
| d9f40e3f6f | |||
| 347d6961ac | |||
| 0b63d79b36 | |||
| 279c3a6961 | |||
| 49c42f3615 | |||
| d881325c2f | |||
| 3f253f116e | |||
| 389116d8ab | |||
| 01bded3d8c | |||
| 7c5786479c | |||
| b14609032c | |||
| b36e26f51b | |||
| 969b8c1f14 | |||
| c65605151c | |||
| 3da34a5fa7 | |||
| 20b161e285 | |||
| ed2872cd3b | |||
| 2d3f095448 | |||
| b0810c51c3 | |||
| a93e014447 | |||
| 8ba4523ccc | |||
| 979ad95eb1 | |||
| 0f028b9008 | |||
| 65d1aa7a77 | |||
| 8004798b03 | |||
| 5ca1075b71 | |||
| 38425e0692 | |||
| d12262068d | |||
| 0443ad9e28 | |||
| f9b0cf0e2e | |||
| 7ceb2154ed | |||
| 23e6bac8a0 | |||
| 682054920d | |||
| f53eabeed8 | |||
| 39bb3cf242 | |||
| 9b37a4695e | |||
| 1e0c73d5b6 | |||
| 8fa4f1380b | |||
| b56d4e570a | |||
| 9eca305e75 | |||
| c5feac2b5e | |||
| dfebe439db | |||
| cf71a5ddc7 | |||
| 2c32bff609 | |||
| 264659bd53 | |||
| 4ace93ca65 | |||
| c6f7181e91 | |||
| 437a5b07d4 | |||
| ba7da13ec5 | |||
| da37ac9c78 | |||
| 662506476b | |||
| 5d7dec5520 | |||
| af6d9f3116 | |||
| 2258f3133b | |||
| 57f5b81da9 | |||
| fdc25a1ebf | |||
| 3b06f983ae | |||
| 51316f9df7 | |||
| 46beca5bed | |||
| 28d829c867 | |||
| 6d04537ea6 | |||
| 7696c6474f | |||
| 7e63d3da02 | |||
| 84e32ad2eb | |||
| 35296e1fde | |||
| bb0185b196 | |||
| d40cd315f5 | |||
| 8cb84210ab | |||
| 5fda30b4f9 | |||
| 9eafe9c053 | |||
| 7f6f70853c | |||
| b8fed93215 | |||
| 5e76a4fd31 | |||
| e8c779736f | |||
| 2fc6b55dae | |||
| 947a372f58 | |||
| e073cbc2e5 | |||
| 2316ec4c24 | |||
| a006514ea1 | |||
| 6e9b719694 | |||
| 34840301b5 | |||
| 41c800b352 | |||
| 0a9aca56ca | |||
| ea1322ef54 | |||
| 6a4b9bb2b4 | |||
| 7c8146d556 | |||
| c90176f969 | |||
| 3c7dc52909 | |||
| bd96330d03 | |||
| 4cd1f4aac0 | |||
| b114c7d487 | |||
| 9cbf17b0f1 | |||
| 5382a22e0e | |||
| 8c8a91f2ef | |||
| f99509581e | |||
| 3dcd04ad61 | |||
| faaf7e481f | |||
| 3d838f635c | |||
| aa4c5cf190 | |||
| b47f2cd068 | |||
| e885dee5ab | |||
| ccae0f5aad | |||
| 2e297d6ab3 | |||
| fb18346ddd | |||
| 96f9643f33 | |||
| 84058b1506 | |||
| 4d5de1c1fb | |||
| 9a825f6bb9 | |||
| 941b189d98 | |||
| 21f663d29f | |||
| 626cf0581b | |||
| ccf6d098f6 | |||
| de2caf7ee4 | |||
| b08c9079c5 | |||
| 06e08c6d05 | |||
| 7c519dfbd0 | |||
| f59e6a93c0 | |||
| 6683f83b51 | |||
| a314409dba | |||
| 15ce8d0904 | |||
| b43300c14f | |||
| af70905246 | |||
| b95e466218 | |||
| d406d3d05f | |||
| b6780ee41b | |||
| 2c107e13a8 | |||
| 6e600695f8 | |||
| 06127927ff | |||
| aedcfb9414 | |||
| 992b6d2712 | |||
| 588dfa4ce5 | |||
| 6f2b8e21c4 | |||
| c5dc521d65 | |||
| 505fc91222 | |||
| fb8fa66136 | |||
| b397064955 | |||
| 34aa0c5bd7 | |||
| 97e394ba18 | |||
| 950fbcb971 | |||
| b2d4d06428 | |||
| 3b8dba5279 | |||
| 892aff2a07 | |||
| 02ed0b2c30 | |||
| de53c2ec7e | |||
| 80bd4a8940 | |||
| 3e7415e846 | |||
| e64e20ac81 | |||
| deb807837c | |||
| 26eb144541 | |||
| f9c3d6fdbe | |||
| 762baf0548 | |||
| b8a04658b2 | |||
| b44787652b | |||
| 03c6a05ec8 | |||
| 3d0b5edfe4 | |||
| 8d50a998e3 | |||
| bdd10a08b1 | |||
| c5e3b2f814 | |||
| 6486762463 | |||
| 4907444db9 | |||
| c3cb31fa9a | |||
| 0c7738240e | |||
| 7b0b6b3b39 | |||
| 89da4168df | |||
| 6b01b6cf6a | |||
| e3388ebfad | |||
| 9e3db70482 | |||
| dbec41f34c | |||
| 0b3db69e41 | |||
| 80ae1c1c16 | |||
| 40683b4934 | |||
| 6480e73599 | |||
| 4a1612ff9b | |||
| 464658b16a | |||
| bdcde45d05 | |||
| 8b0434e5e6 | |||
| 8b0b675149 | |||
| 8c429311a3 | |||
| 66b7b075a6 | |||
| 62a392ff46 | |||
| 893f382e94 | |||
| 0eb10c6303 | |||
| 00008b8ed0 | |||
| df49384516 | |||
| fbea553ca2 | |||
| 34b00c16e2 | |||
| 12110dec0e | |||
| 4105cafb84 | |||
| 76ff55750e | |||
| b7edf5d2d8 | |||
| ef8263f81f | |||
| 2952bc5fc9 | |||
| 38d3bc8909 | |||
| 75efc6722b | |||
| d766e2ae17 | |||
| f055a9dffd | |||
| 4a7fe61806 | |||
| 9243c414d9 | |||
| 477ca2fe8c | |||
| c72ac048b9 | |||
| b03dddf9e4 | |||
| c80c7a49fd | |||
| 212095a601 | |||
| c67603fafb | |||
| 06865aa01d | |||
| 465b474899 | |||
| 6353d619a5 | |||
| ca6fe7449a | |||
| 1afd5a152d | |||
| 422318f677 | |||
| 49dae52d08 | |||
| 104d249ffb | |||
| c7a8cba745 | |||
| 0682fe8169 | |||
| b1379936cf | |||
| 4e0de3a43c | |||
| 318a9a5859 | |||
| 73d797ce7e | |||
| 51280ef2ad | |||
| fc3eaa2559 | |||
| daff7c3b4d | |||
| a131eae925 | |||
| 022abc301b | |||
| 56e50e8bc5 | |||
| 90569630d6 | |||
| dc0ab1e8ca | |||
| e2b15aa21d | |||
| 89110986d7 | |||
| 53979dfe0d | |||
| 6710a2b0ef | |||
| 5bd8d388de | |||
| 28f215ecf8 | |||
| d2fb34ac88 | |||
| ab0a851db4 | |||
| 728ff94595 | |||
| ad960095bf | |||
| 33e3774f44 | |||
| a9295a2b8e | |||
| c6fedc92f8 | |||
| 68af7cc81a | |||
| 28323aeaa6 | |||
| cf7eff45f3 | |||
| 8691612f01 | |||
| f99f3c42ff | |||
| f360e7778b | |||
| 4676f6599e | |||
| 1d5b885437 | |||
| 4dc0be950a | |||
| 3153a41e33 | |||
| d388c165ef | |||
| ab07005568 | |||
| 7024f49c42 | |||
| 2f6f6abeba | |||
| 3064edead2 | |||
| f8c1c1469a | |||
| 4b6928e7f5 | |||
| b3f924bc69 | |||
| e001d5aea6 | |||
| e752d84ed8 | |||
| 86431a2f1c | |||
| 36cf8f3a3c | |||
| 725c15a4ca | |||
| b077b20206 | |||
| 3cf340f676 | |||
| 7ad20af2cf | |||
| bfb88e99e9 | |||
| c5ebed7399 | |||
| e663a24ab0 | |||
| 40f823ec90 | |||
| 7622373033 | |||
| 46bc7f2bf7 | |||
| cc01d46247 | |||
| 9fa1baf6de | |||
| 49dfaf7447 | |||
| 348fa1ca56 | |||
| ae11e37e53 | |||
| c7ee7fe026 | |||
| 421e459684 | |||
| 9d28b40152 | |||
| e0bece77d6 | |||
| d0356d5f15 | |||
| ac78b914b6 | |||
| 10b8c99bbb | |||
| bdc751ce23 | |||
| 19c6b375c3 | |||
| 745eb41993 | |||
| c766e4103d | |||
| 36aecd4548 | |||
| 1a5e8c1014 | |||
| ff5fe51044 | |||
| dfd188ba12 | |||
| 093c16b67b | |||
| 599f1742c6 | |||
| 73abfde2ef | |||
| 194a4f9710 | |||
| 196c87c9ce | |||
| 39f26b1480 | |||
| 3c230cc825 | |||
| 41f2936d8f | |||
| 14c3aa12b5 | |||
| f3ca52bfa9 | |||
| c47111bd4e | |||
| e4e5aa85a2 | |||
| 4c0e36a46e | |||
| 5440b42f43 | |||
| 7d4828cc9f | |||
| 62f3b7c472 | |||
| fb9be62f79 | |||
| 7c0175bc83 | |||
| 32fef60743 | |||
| 26679ea35b | |||
| c3e5946fb2 | |||
| ad0704e40c | |||
| 5a3f26f855 | |||
| 93fd67b793 | |||
| 6bb3e40a8d | |||
| 7612f7735d | |||
| 472c560705 | |||
| 54eb35a8aa | |||
| ba36180fd7 | |||
| b9e76be8a6 | |||
| c5482468fd | |||
| ad5dcdcf0f | |||
| de9128d09d | |||
| 1d8920dd7f | |||
| 911f1e3e7f | |||
| a1ef58b3a5 | |||
| 283e7455ff | |||
| 8fbe9e3470 | |||
| 9ed9f40f1e | |||
| 940472c75c | |||
| f3b3d9b5c2 | |||
| 7f9a32ad85 | |||
| bc1b0733b8 | |||
| 722bdbe17d | |||
| 1b10033583 | |||
| 00e728af88 | |||
| 13256a4173 | |||
| abb17cf19b | |||
| e0c0e2ee14 | |||
| 022e4427cf | |||
| 80d9b09f35 | |||
| 029d269f4d | |||
| ac65bab25f | |||
| f185c0132e | |||
| b77f792b23 | |||
| 4053eae9eb | |||
| 6d998055c8 | |||
| 11769b203f | |||
| b1daa069ba | |||
| 4dff277255 | |||
| 07db10813c | |||
| f96497397a | |||
| 5a1c8d8ef3 | |||
| 124bfd2081 | |||
| b42430fd3a | |||
| 60a7492d24 | |||
| 73ec04fcf3 | |||
| d13a3fb070 | |||
| b6f87491af | |||
| 4bb30f461b | |||
| dad85e4131 | |||
| b029e7ea20 | |||
| 4bc33d64de | |||
| 8821746841 | |||
| 72d82a296c | |||
| 4804bbd9bb | |||
| be0d951be8 | |||
| a9dbc2d641 | |||
| c25380c28d | |||
| 03094da80c | |||
| 01c96ad12e | |||
| 1bdc328c75 | |||
| e3d52da144 | |||
| e39ee2cc58 | |||
| 84910e04e2 | |||
| de74289049 | |||
| 92b3deaffa | |||
| 49f7aab62a | |||
| 3d879fbc5d | |||
| 6d95de83c6 | |||
| 5c8770f896 | |||
| 7647d5d13d | |||
| 7184f6343a | |||
| b1b7790f7c | |||
| ab5a650029 | |||
| fb9de1b4d7 | |||
| e8757a73e6 | |||
| 4ce8d2047c | |||
| 2376eb6347 | |||
| 8634b2a011 | |||
| 5ecb31003f | |||
| a454e884e0 | |||
| 502a1d1108 | |||
| 173e41b2eb | |||
| a3f9c1a77a | |||
| 6dbae1b5d9 | |||
| cb3d5dce30 | |||
| 2caecf1b3e | |||
| f399e6705f | |||
| 69371c4b84 | |||
| 6a98aa0bb0 | |||
| c354211744 | |||
| f748b4bb40 | |||
| 7ae180f8e8 | |||
| c7bdc0e0e8 | |||
| a45945b6d5 | |||
| 9e1f1b1f62 | |||
| 4679294796 | |||
| f74b87c3c2 | |||
| 16001acb15 | |||
| 0c8a7e071b | |||
| a3d36fe283 | |||
| 9569ad6b0d | |||
| 2331e1beec | |||
| e4287da3a6 | |||
| 1a1b2f3d7f | |||
| f5f3df3bd1 | |||
| e6c1e6005a | |||
| d266d40dea | |||
| e354c6355e | |||
| 94c1b3a8ee | |||
| 08199ade4a | |||
| 2a235312f0 | |||
| 9903dd52a3 | |||
| 24f6e236a3 | |||
| 9f594ca6f5 | |||
| 4df4560bf5 | |||
| b2aa2ba046 | |||
| beec5af201 | |||
| 7b1fba69b7 | |||
| 697593d8d5 | |||
| e2dc807c6e | |||
| 138d00176c | |||
| 8edbf57fb8 | |||
| 7fe1a4cff3 | |||
| dbb6f11e8e | |||
| aeb4c6fff1 | |||
| 3b91b07fd9 | |||
| 349badabc1 | |||
| e4a9e6fcca | |||
| 9de2bb0d62 | |||
| d55fd3455b | |||
| 0d1c6f4188 | |||
| fc6b32bcaa | |||
| de96e26afc | |||
| de3fd8ab83 | |||
| 788109a3de | |||
| 09c9fa36ea | |||
| dcb2998aa6 | |||
| fac00ca023 | |||
| 6487fb11fc | |||
| 644799537f | |||
| aa66ca76ce | |||
| 707b0ba6e2 | |||
| 10b0b09555 | |||
| 4d5ab9baa6 | |||
| b65252b39b | |||
| fbd8614bdb | |||
| f3b52a6061 | |||
| 0232fbffbe | |||
| ed110f66c5 | |||
| 22093f9c6e | |||
| 792ffaccf2 | |||
| 57cdb60429 | |||
| 89c09c8e40 | |||
| 48baa5e89c | |||
| fdd7209688 | |||
| 82a8e06524 | |||
| c258870c59 | |||
| e8e39a277f | |||
| fff7d42a69 | |||
| 783aa8a9da | |||
| 8d4feb504f | |||
| d77a7a8c26 | |||
| e747491b96 | |||
| acff3ad88d | |||
| e7b2e01d43 | |||
| 03ca6385dc | |||
| 7777b412de | |||
| c3e9b14d24 | |||
| 7d6b78ee79 | |||
| 5f3ad5731e | |||
| 494e9ba0f4 | |||
| f79b4568ae | |||
| fd51651684 | |||
| 994374c4df | |||
| 62f6652d53 | |||
| 9945253d57 | |||
| 1f259be52a | |||
| 1a44e82200 | |||
| a272fa66f0 | |||
| b12b635a90 | |||
| 7cd7c2e0a1 | |||
| 07cf8415e3 | |||
| c0836dda74 | |||
| 384d1b47b0 | |||
| 19770b03a9 | |||
| a01eba657e | |||
| b7446ea88d | |||
| a051b3323e | |||
| c4e01c568a | |||
| bda090b961 | |||
| e383941652 | |||
| 656b3cea1b | |||
| 2d1d57bc18 | |||
| 03412af06e | |||
| 542dbd4c65 | |||
| c25d59a6bd | |||
| 5936eb16d8 | |||
| 7dc3ca7b8e | |||
| 71344b5cac | |||
| f230898ad6 | |||
| 3feb702df8 | |||
| c5112f7b15 | |||
| c231a5e2b6 | |||
| dc9ac5b8c2 | |||
| 146508b28e | |||
| d103312661 | |||
| 2e8f8a18e4 | |||
| f5b0459aba | |||
| d682b3c9ec | |||
| fc085aa627 | |||
| 9a5d6aedb7 | |||
| 89e3a84344 | |||
| 04587b6256 | |||
| bde03eeb48 | |||
| 330d426ffd | |||
| 02340375be | |||
| 5100bc8aa1 | |||
| ebb2afa54b | |||
| 7485512384 | |||
| 46de77b219 | |||
| d356183402 | |||
| e61ee8a036 | |||
| ff1289a361 | |||
| cd73f3ccc5 | |||
| 8c1fb91802 | |||
| 33b366ec32 | |||
| c5363a1b58 | |||
| 332a06bbd1 | |||
| ffd0d1232d | |||
| 07a2dcacbf | |||
| c874abac98 | |||
| 789e423b32 | |||
| 96ec8671b1 | |||
| cc3a42b190 | |||
| 6707ce6349 | |||
| 257961c601 | |||
| ae515a346c | |||
| 3cdf1d676b | |||
| 82129b9266 | |||
| 8004ab756f | |||
| ee5ef0f295 | |||
| 5bdee4f810 | |||
| 48098c42ff | |||
| 3ad55386c4 | |||
| caf8278710 | |||
| f7670781cf | |||
| df8b996bcc | |||
| 75d5a8924c | |||
| 880f82d089 | |||
| 1a741157cb | |||
| 0c78de0d6d | |||
| df45f7118b | |||
| 880df57e52 | |||
| 8949904783 | |||
| 9294466234 | |||
| e290bd6760 | |||
| dc4232379d | |||
| 10fffcd80a | |||
| 8264dbc2bb | |||
| 9fae7b6d3f | |||
| 0f1f51be4b | |||
| ee5edb6b68 | |||
| bda83e22c0 | |||
| 364cdbdbdb | |||
| b91bc93fd3 | |||
| 53be1451a8 | |||
| eb0be12ceb | |||
| 1198d82530 | |||
| 6556fcfe5a | |||
| 41964648ee | |||
| 502214f8a5 | |||
| ed19fe766c | |||
| 8f25d08637 | |||
| 3b795541c4 | |||
| 20b9e59441 | |||
| 935d9f7f90 | |||
| e247c3ada3 | |||
| b5dc2f9b0c | |||
| 5273477cb9 | |||
| b9db9bd0ef | |||
| 050e8536bb | |||
| 2b61360d82 | |||
| 5b6ac971f9 | |||
| 403e181d81 | |||
| 4af03c5ae1 | |||
| bde17a419d | |||
| 8950e0c049 | |||
| 8217e0d4fe | |||
| 319ccfefbc | |||
| 6a8400ba4f | |||
| 2de3361c6c | |||
| e89763e964 | |||
| be63d995c1 | |||
| 2b1fc1b38c | |||
| f01c12c891 | |||
| 5aabb7a891 | |||
| cb1e451999 | |||
| 43cff79bf7 | |||
| 95c3144393 | |||
| 19eb6ae580 | |||
| cbe05b48bb | |||
| e7f34ad395 | |||
| a5cbb7c3a7 | |||
| dcad08b76d | |||
| afbf5c5dab | |||
| a1415c0d72 | |||
| 2ecaf18138 | |||
| cbef15f3ea | |||
| 16e8bb360b | |||
| 6628012e9e | |||
| ed1fe28bb8 | |||
| 6645bb2df4 | |||
| 02aed045de | |||
| 72bd2ef6c5 | |||
| d39bb51faa | |||
| 6142e663e4 | |||
| 6dc20ff293 | |||
| 99391e24ea | |||
| 0ec12c1bd1 | |||
| a71c0ab861 | |||
| 04ba6ea83b | |||
| a1935b9cba | |||
| 513c639bf9 | |||
| 69b5f7a410 | |||
| ae76c39712 | |||
| 0d4a1f71db | |||
| 67bf789462 | |||
| 48d0674c2e | |||
| 6ffe006f5c | |||
| 4998e852ec | |||
| 65ac789565 | |||
| cfc84c856d | |||
| 63720a4290 | |||
| 484bf14a6b | |||
| abb2f5511d | |||
| fcc6e1220e | |||
| 3a91274547 | |||
| f2a813ae10 | |||
| e723c75c02 | |||
| b436ed58a3 | |||
| 2b37f2150e | |||
| 331632e8d8 | |||
| 7d930ec694 | |||
| 0cf3b6495a | |||
| b66fedadae | |||
| fd6e680e21 | |||
| 57c0db219b | |||
| 4ba0cb4580 | |||
| a30c66f0fc | |||
| bb4baa73ea | |||
| d7d44b038d | |||
| 297512b34c | |||
| 2ae2e6408e | |||
| a80cada914 | |||
| a00013c8d0 | |||
| b9a1f27250 | |||
| 29aed28802 | |||
| 951b562cf8 | |||
| 6408bc0b7f | |||
| 91efb4b895 | |||
| 27c5b1b237 | |||
| d51bdc166d | |||
| 81245b1ad5 | |||
| 397ce11996 | |||
| 97f2aa5a44 | |||
| 419e2cb01d | |||
| 374f6c4612 | |||
| 4bdf9962bf | |||
| e0306e386f | |||
| 2e6e53c7cc | |||
| 2a57d2346e | |||
| 7e425be975 | |||
| 0705e7b39f | |||
| 4ccc4578b3 | |||
| 5e8c162b6c | |||
| c8d6cb0136 | |||
| 0edcdf49c0 | |||
| b7dfd18612 | |||
| 8efcd411fe | |||
| 90d016980f | |||
| b3996c55f6 | |||
| 438a9fcc78 | |||
| 7e2015a46d | |||
| 5b01cb39b5 | |||
| a0503305bd | |||
| 965f74525b | |||
| 3f2e21f0ea | |||
| 3509914916 | |||
| ae27be40c9 | |||
| cdd26700e8 | |||
| a9a4bf68b0 | |||
| f31cc8e342 | |||
| 3c55790ebe | |||
| 0adfa773b9 | |||
| 3317f5c6d9 | |||
| dbf911c516 | |||
| 3a29087cb7 | |||
| 234fb7f695 | |||
| c077d47465 | |||
| 8e5290d81e | |||
| a1c37f834a | |||
| 1a7868c1d5 | |||
| de3933480e | |||
| 50e2eb8954 | |||
| 94eb37dc73 | |||
| 900bdd5761 | |||
| a51069202a | |||
| fb6796ba06 | |||
| 88ef06f4f3 | |||
| eb45f3a9b6 | |||
| 6321041058 | |||
| 2b83711fba | |||
| 3ae21a352c | |||
| 570143bf88 | |||
| 1465edf341 | |||
| 77e786d436 | |||
| 6d79752e65 | |||
| 75812a517b | |||
| 151dc5cc5f | |||
| 0b6cfea005 | |||
| c8da3fbc35 | |||
| c96cd1c647 | |||
| 57e91b4159 | |||
| ef7d29c666 | |||
| 81ecaf6178 | |||
| 27ab59bc16 | |||
| bd8ce7e331 | |||
| 5c519a69d3 | |||
| 6f54b07663 | |||
| 741ad97e92 | |||
| b4633c586b | |||
| 5fae2a5b05 | |||
| 36efa6a6b9 | |||
| c6e74e41c9 | |||
| 6909b5a2b4 | |||
| 594d7266af | |||
| ab4a890d04 | |||
| 64e25a6186 | |||
| 460968353f | |||
| 32a1f54b9a | |||
| 6df197ec15 | |||
| 177807f21e | |||
| 92f02fff11 | |||
| 125296c83d | |||
| 9c41d9d1df | |||
| ad79eafe86 | |||
| fd179ab4b8 | |||
| bf163ea7f0 | |||
| 075661ffde | |||
| db5ac2fa0d | |||
| 1735c05ac7 | |||
| 5fbd40dbe5 | |||
| 049cf14cf9 | |||
| 5d628519d3 | |||
| 244646bdf7 | |||
| 5bdfefd3a5 | |||
| 5873786e27 | |||
| cb59019f58 | |||
| 52c0f9575b | |||
| 4fde357130 | |||
| e5fd1e5fe2 | |||
| 87b179e71d | |||
| b401d37188 | |||
| 6624ccff26 | |||
| 9a38c08011 | |||
| be87789a6c | |||
| d1d1ddec1e | |||
| bfa0898312 | |||
| 79b0d05480 | |||
| 779381fe9e | |||
| b6911c177d | |||
| 9212175ffb | |||
| ad20689feb | |||
| e320790154 | |||
| fee3aa518d | |||
| b58602d93d | |||
| 74e10b9c3b | |||
| b044b8c182 | |||
| a113e1498d | |||
| 17ca3b1763 | |||
| 50b18f60cb | |||
| ad73e5156e | |||
| 720c68dabb | |||
| 81c39a5244 | |||
| cfabcbfb17 | |||
| bde14c3adc | |||
| 4b839d7c72 | |||
| c49edc4566 | |||
| c5d52a5468 | |||
| e75c0148c3 | |||
| 28fdbcf393 | |||
| c25072a246 | |||
| 1a69a3ecea | |||
| dcfc3c9405 | |||
| 4542fb9f78 | |||
| 80e817719d | |||
| 96f3a31b24 | |||
| ff6c034330 | |||
| 05afee0f50 | |||
| 351a446ec5 | |||
| e9885efc7c | |||
| 59fbbf0a65 | |||
| 61c3c80177 | |||
| 9de384e27a | |||
| 4b526045fa | |||
| 4870194380 | |||
| 41d0e66184 | |||
| aa1bd8876c | |||
| 7314acaa80 | |||
| 4b2521cd9e | |||
| 491103708e | |||
| fa4d5c8689 | |||
| aef2d711b1 | |||
| bdf566a8a3 | |||
| c3c78030f7 | |||
| ff9ca88aa6 | |||
| 463edee2fd | |||
| 88ff582fd8 | |||
| 6b12b82532 | |||
| d7a0179cd6 | |||
| 6d613817d4 | |||
| b8d412aa07 | |||
| 961760c9b6 | |||
| 57559547a3 | |||
| cfa84e1232 | |||
| 0d31efb365 | |||
| f97c64d7bf | |||
| ea3155e380 | |||
| c172a4f7c2 | |||
| f8e0d33b25 | |||
| 745618d941 | |||
| 89602bf747 | |||
| 56d4537879 | |||
| 18014ef2fd | |||
| ffe7401ceb | |||
| 65ec3379b1 | |||
| 85fc9ecd5f | |||
| 2a21f6c894 | |||
| 3670db99b6 | |||
| e14a02c24f | |||
| 15f4150518 | |||
| c5cae9362c | |||
| 4f4e0e49bb | |||
| 2f4d429071 | |||
| 27f7bd2e83 | |||
| 798660c9cd | |||
| 34b6664dcb | |||
| d35fff16d5 | |||
| 56699cd603 | |||
| 8ede2c353c | |||
| 6384475ec4 | |||
| 3a15c7ce43 | |||
| 0a6b58c3aa | |||
| 979a663d2a | |||
| 22952d8973 | |||
| df608f842a | |||
| 001bb46ae1 | |||
| 9b25347a67 | |||
| e401513086 | |||
| 7f1a2ad699 | |||
| a1ab5ab396 | |||
| 01549fb1ed | |||
| 4fba117433 | |||
| 2be88d5084 | |||
| f5c690b684 | |||
| 61fa462535 | |||
| fba9050d3d | |||
| d861706620 | |||
| 34ac039fb8 | |||
| 1aa5262634 | |||
| 07c7fdb9df | |||
| 3ccbae84d2 | |||
| 255d59ed5e | |||
| 925d1b59f9 | |||
| 03bbe1baf1 | |||
| f9037ae8c1 | |||
| 57d8424146 | |||
| baa0e23456 | |||
| 55a710545c | |||
| 753f2c1da4 | |||
| b3b8580606 | |||
| 73ad16b5c6 | |||
| 97e299c395 | |||
| c697aa30bc | |||
| 17ee57f8e0 | |||
| 3c6d0aaa7d | |||
| e43612aaf6 | |||
| 2898e2fd12 | |||
| 8704fca0c9 | |||
| 325e44d87e | |||
| 54ac253343 | |||
| bb12903120 | |||
| ca41b43f53 | |||
| 3511e186cd | |||
| 56137d58ff | |||
| 9aec500a26 | |||
| 5a9cc57a5e | |||
| 1aa4f69b51 | |||
| 8bb272a3e3 | |||
| c229ed597f | |||
| 16b4110521 | |||
| f14662fca6 | |||
| a9af12bbe7 | |||
| 5bbb4a06a6 | |||
| d4fb848be7 | |||
| b9dcf9974d | |||
| a77d263e86 | |||
| 7cc40851e1 | |||
| 08a9c9faa3 | |||
| 84fe52dbeb | |||
| 10200585a7 | |||
| c94f11d783 | |||
| 54ac5960a8 | |||
| 0e6229a953 | |||
| d2ab1aef31 | |||
| 89b56e7d84 | |||
| 14929f5764 | |||
| 7021cc9bc8 | |||
| 04da0a40c0 | |||
| eb67828236 | |||
| 94266dfdbd | |||
| e3871abe95 | |||
| 3fc25ff15b | |||
| fa3113ffbf | |||
| d94bb6c273 | |||
| 5784331768 | |||
| 88a9e51f34 | |||
| 8b5940e7ca | |||
| 1a3656e56e | |||
| 542b6cfc02 | |||
| 6f6fd96e3b | |||
| 35a6aad0d7 | |||
| 23f0ff9517 | |||
| 355037528c | |||
| 9e68c047ae | |||
| e9d86eddf3 | |||
| 11aefa4478 | |||
| 5ebdd326b6 | |||
| 84db17b6fc | |||
| 456806f25c | |||
| c98f195eb9 | |||
| 4d696c45b9 | |||
| 36715ab7c1 | |||
| e966e53791 | |||
| c4eff10322 | |||
| 8a0acf0898 | |||
| e2be310a85 | |||
| 17159b491c | |||
| 0aa32fa0cb | |||
| 8927c14317 | |||
| 3e977f02ac | |||
| a73da6112d | |||
| 6a5d87771f | |||
| 15e8405731 | |||
| fd1672ba98 | |||
| 1ddb251d86 | |||
| f8daddba50 | |||
| e58ef8d0bb | |||
| e3b9569e04 | |||
| 8b66040e34 | |||
| 965993da07 | |||
| 5a5a834975 | |||
| 45c430eac0 | |||
| 27d9219cf3 | |||
| 12bd01c567 | |||
| 626124d2d8 | |||
| 5018104a60 | |||
| ec11d132db | |||
| 06dc893f88 | |||
| a09bd59615 | |||
| 42ff788e53 | |||
| 1bd70a8e40 | |||
| ef83457427 | |||
| 4833fb8273 | |||
| 77e7ebca0a | |||
| 6d5324f92f | |||
| 4fe11ae232 | |||
| c549d4fe64 | |||
| ddff6ecf30 | |||
| 6b630c0fa0 | |||
| 1a6ef098b0 | |||
| 5388aa9fc0 | |||
| b123a88673 | |||
| ffa43df3cd | |||
| 9c4398de8f | |||
| a0d4f0fc8a | |||
| 68616c6669 | |||
| e98ba351ce | |||
| cbe4998bc2 | |||
| 0172c9552c | |||
| 3d4913f703 | |||
| c27ef1b79c | |||
| 4422b97472 | |||
| 2f02d264e9 | |||
| 037a70dfea | |||
| 8f961da3db | |||
| 5c46b7b8cf | |||
| a2c094fe2d | |||
| f767f29d87 | |||
| 675530c4d8 | |||
| ab964ad7c6 | |||
| ef789d4161 | |||
| b390d7c168 | |||
| 218f358461 | |||
| bf06e22b6a | |||
| d99ffa920d | |||
| 2771050dfa | |||
| e1daf10e4c | |||
| 115fa77009 | |||
| 561030c211 | |||
| 5336cc9d6a | |||
| fdd137276b | |||
| a8aede68c7 | |||
| c7859a046d | |||
| 4a24883674 | |||
| 9737832641 | |||
| 8b45d9806a | |||
| be52f784d9 | |||
| 144afa4d47 | |||
| 007f66408d | |||
| 48c745a611 | |||
| 3a044de5a1 | |||
| 7d514af0bc | |||
| f61ceee708 | |||
| 5aeb57f11f | |||
| 493b3fdd65 | |||
| 315c5bf7b7 | |||
| 607be77ec8 | |||
| 0d745ed04c | |||
| 3d1c6730da | |||
| 66b7b9b582 | |||
| 8ef9117924 | |||
| ea69f17d98 | |||
| 420cc62fac | |||
| 1a3ebc203a | |||
| 2d5e2212d4 | |||
| da4811238a | |||
| f96872a534 | |||
| 738b3b64af | |||
| 363b525c23 | |||
| 469daa16ee | |||
| 7707f3e7c3 | |||
| 16f8e44924 | |||
| 2f5f77788f | |||
| ed795d5ff4 | |||
| 282d67d8fc | |||
| 114d10cbc5 | |||
| 9f3f5b6f41 | |||
| a39489194e | |||
| a3addae036 | |||
| ad40bb1181 | |||
| 80fe8504f5 | |||
| e976384213 | |||
| 68f4ccdd00 | |||
| 342a936599 | |||
| 901da539e5 | |||
| 197e249433 | |||
| 2aff75f58e | |||
| cd12410195 | |||
| c5bb3643ab | |||
| 5ff1c18a71 | |||
| 99ac4a260f | |||
| fa7e77fd38 | |||
| f69e0ade19 | |||
| 99a1c06ea3 | |||
| 93e28031de | |||
| de84b3fecb | |||
| 054179c2ff | |||
| 986c11daa6 | |||
| c6c41e85b2 | |||
| 87c930676f | |||
| 11e762dbcd | |||
| 5c9cb5f0b1 | |||
| fed236ca7f | |||
| 295f7ac04e | |||
| 6318de80d3 | |||
| 5715de344f | |||
| c86b9da022 | |||
| a4c6dcf999 | |||
| 3f78de256e | |||
| d764cc5522 | |||
| ffa96d988d | |||
| 6a24bd17a8 | |||
| 1cce3e6842 | |||
| 0b04c561f5 | |||
| 90b0ac6495 | |||
| de3b1c9b53 | |||
| 733c58595b | |||
| 4a1ed6e6e5 | |||
| 6b30774469 | |||
| ba3586cc90 | |||
| 68df7c6279 | |||
| 19ad78d713 | |||
| 6ccf150039 | |||
| 4ea60463f5 | |||
| d438e1379d | |||
| 1721d09ac8 | |||
| 2f3da00e51 | |||
| 5a73e3ad9e | |||
| 86dd8bf3f5 | |||
| 67c5de9283 | |||
| 0a850e641d | |||
| 916bd874ad | |||
| cd848cab6b | |||
| 29256e8960 | |||
| 438c534ff4 | |||
| 1ea2d20f74 | |||
| f5133aa1a5 | |||
| c0edab0f6d | |||
| a948054a19 | |||
| a8f8c7fd80 | |||
| 1a19893d6f | |||
| 88e23f508c | |||
| c8585efc78 | |||
| 16493b777f | |||
| cac1ebd1e0 | |||
| 73c5515e00 | |||
| e34c35abcf | |||
| 7a8eb83b4a | |||
| ab5fa213b3 | |||
| 39929384c6 | |||
| de7cf8e63a | |||
| 124598917f | |||
| c5fcba1a17 | |||
| 4c6dfc342b | |||
| 686e57cf9c | |||
| 06156e7575 | |||
| 2e2c109daf | |||
| 6ca7b63bce | |||
| 4eeb1831be | |||
| dd373a8f66 | |||
| 7061fe1d56 | |||
| d8aa8dd97e | |||
| 1150873447 | |||
| 79ab21be90 | |||
| d3bbb34c24 | |||
| 0ad15fc100 | |||
| aeff4f79fa | |||
| 1bf4faec64 | |||
| 8ef9e8460e | |||
| 4f292d753c | |||
| 26629d0af1 | |||
| b5e75fde4e | |||
| 9a871dd3db | |||
| 90972ff136 | |||
| af35c7f398 | |||
| 742b7377d3 | |||
| 50955e27f5 | |||
| 33c49f7fa0 | |||
| 0df6635711 | |||
| ae800656c9 | |||
| 8c68863960 | |||
| 8f6b80aa1d | |||
| 9eff921f8f | |||
| 8e6b7ef9ab | |||
| caceeab750 | |||
| 3991312b94 | |||
| 439d74e29f | |||
| 3679d33b02 | |||
| 78b941039d | |||
| 623e388bb4 | |||
| 677d90165f | |||
| 4244da96bf | |||
| fcf99bf788 | |||
| 98d76cdcae | |||
| 2f8c51a552 | |||
| b5f5fcd782 | |||
| 2ef9bcfd11 | |||
| 5fa680d47f | |||
| 48735da0d0 | |||
| 2b4bd21585 | |||
| beff5b078a | |||
| 5519c98655 | |||
| cbe164191c | |||
| 66d046833d | |||
| 15fdbd26fe | |||
| 2b4975dca7 | |||
| e39da66c8c | |||
| 483c2ccd0f | |||
| 982aa7058f | |||
| 80cdf39d0e | |||
| 45f3797a5b | |||
| de6e5a5919 | |||
| 81102d3689 | |||
| 6579bd4190 | |||
| f45177f2d3 | |||
| d6f5fb2d2c | |||
| 7c4cc505db | |||
| 453450d687 | |||
| 55f566bd22 | |||
| 130c0e40e0 | |||
| c72fb1cf8e | |||
| 138e69cc55 | |||
| 53210d7e5b | |||
| 0394314c7a | |||
| 0beeb4f6fa | |||
| 788b251157 | |||
| 318575627f | |||
| a14992992a | |||
| 48adb6984c | |||
| c5069528e1 | |||
| 6fb0b11350 | |||
| 932e7fb0e1 | |||
| 188192ce9a | |||
| fc083f1138 | |||
| ee865b9fe7 | |||
| d630daca1a | |||
| 8c883cb4e8 | |||
| c75cca0559 | |||
| 1414b7123c | |||
| 3563b3f519 | |||
| d46ed54646 | |||
| e2c8c756ee | |||
| 5ca5086c19 | |||
| 2d6a0ae149 | |||
| b5ec26101f | |||
| 36e8771076 | |||
| 27f09415e3 | |||
| 4ad9970649 | |||
| bef345a4b8 | |||
| 0ce3018109 | |||
| 0233ce0815 | |||
| 1f2b82bf25 | |||
| f2043b7a58 | |||
| 2b1c2c61b0 | |||
| fa6ea831b3 | |||
| b22a4280c6 | |||
| 29fe3abda2 | |||
| 6347004be9 | |||
| 67532d5a10 | |||
| 6920a7f2e3 | |||
| 88bf2f83c0 | |||
| 8fff05f6d0 | |||
| 9b9becf384 | |||
| 00744a2729 | |||
| b4be507578 | |||
| 360587f319 | |||
| eee51492e2 | |||
| c37b7b3cca | |||
| f79584faca | |||
| 4abe99dc34 | |||
| d4e6b75098 | |||
| a2f5c28a80 | |||
| a80a2b5e56 | |||
| bb1851ce98 | |||
| 5951c79ae1 | |||
| e381c582de | |||
| 9600c60df3 | |||
| 32bf6c9b27 | |||
| 9c43d6a24e | |||
| 01b6fe0cbf | |||
| 944709c77b | |||
| 52cb90ec75 | |||
| fa3b126a23 | |||
| 1294b09d8e | |||
| d4f0c4435d | |||
| 44358e0206 | |||
| 07f861385e | |||
| 7a543cfae6 | |||
| 24689927f2 | |||
| 8f8665fffa | |||
| 49c1607157 | |||
| c8e96d6e91 | |||
| af8efcc762 | |||
| 3f6f78ea0b | |||
| e3a7104b40 | |||
| 76492b4bb2 | |||
| 75250a237c | |||
| 437a49be5e | |||
| 4f0c716c8e | |||
| 70b0ee6310 | |||
| 8830901a9c | |||
| 15a3738b49 | |||
| a83ed55f1e | |||
| b559aed2d3 | |||
| d0c038da07 | |||
| db424053e8 | |||
| ad56721948 | |||
| f4e762f688 | |||
| 1ce57628b2 | |||
| 181a837cac | |||
| 0730a56d38 | |||
| eecc1491f3 | |||
| 7ea68d84c4 | |||
| 0e9167bc72 | |||
| 65e31fb179 | |||
| f993771c70 | |||
| 52f9ccd298 | |||
| a799ed5af9 | |||
| 4082c77ff2 | |||
| bc84180d12 | |||
| ad5da6ba14 | |||
| 86b39206d9 | |||
| fdae4b9453 | |||
| 84b6581287 | |||
| c6b6024a9d | |||
| d58ce8106e | |||
| 31f7d553e4 | |||
| adaba04059 | |||
| b157f3546e | |||
| ab7086e209 | |||
| 7a3de1aba3 | |||
| 8287a0db55 | |||
| cf160abcf9 | |||
| ae1d318822 | |||
| 2038073975 | |||
| 2dc91ca5ee | |||
| a3e1c40d6d | |||
| c1a2a646c8 | |||
| 53480f1163 | |||
| 233eac4d3a | |||
| 6625cbecd1 | |||
| 4ac55997ec | |||
| 763f84c84c | |||
| e4f609d0ee | |||
| 5ac56a94dd | |||
| 4fc15bbfb7 | |||
| f7ec36ab16 | |||
| b926472312 | |||
| 78d9865900 | |||
| cc2a60ecb7 | |||
| 43bfb60ac8 | |||
| c510d94549 | |||
| 4dd0622d0c | |||
| ab2c3d5cde | |||
| 01e5f358bd | |||
| 69c775de21 | |||
| 6fcdfc6d4f | |||
| b77a90681e | |||
| 6efffb9acd | |||
| 51e8f08e60 | |||
| 5adb5a4812 | |||
| e29f3a5751 | |||
| 21ed264a38 | |||
| e131eb0d9a | |||
| 054c20cd5b | |||
| 8e7149f496 | |||
| 086da10dac | |||
| b36b4b521f | |||
| 398811a313 | |||
| 2a66b34f72 | |||
| 8d0e1ed52f | |||
| 3ac595da8a | |||
| ea980fdeef | |||
| 5654763797 | |||
| edfa9e007d | |||
| 31829ad177 | |||
| c2361e0b61 | |||
| 77cbd817d1 | |||
| 4ec7d6de95 | |||
| f6a12948ca | |||
| 3cadc37f47 | |||
| bb1146dc39 | |||
| fc24a3a6c4 | |||
| a94ce68e0a | |||
| ec22fc963a | |||
| c54f5f64c9 | |||
| 4cca1a436d | |||
| 8df0198178 | |||
| 048be9cf27 | |||
| 1485ea2ee7 | |||
| b10dbe5c72 | |||
| 462375ba47 | |||
| cfbe2333a4 | |||
| afc3fcf952 | |||
| 99bd1a000f | |||
| 3e66d3b4be | |||
| 29a28693e5 | |||
| 678c1255c9 | |||
| a4b96ce9a3 | |||
| 291f564485 | |||
| 9f1a8dbdd3 | |||
| d19f1da6ef | |||
| f88806a1fb | |||
| fb6eee2805 | |||
| 0010ec68b1 | |||
| 28f8ed9113 | |||
| fd22ccd6d0 | |||
| 76eab106df | |||
| 81ed85ecfb | |||
| 7dcc02b165 | |||
| e2fc2ce8df | |||
| a241b6cb3c | |||
| f0d143f30f | |||
| c82087b8dd | |||
| 138be5b843 | |||
| e320b5b672 | |||
| ca9c405fef | |||
| e2feb886d6 | |||
| 5999e14ad9 | |||
| 01747f2c24 | |||
| 7377fbdebc | |||
| f1d2ac3a98 | |||
| c5da4f4cd0 | |||
| 7bcb2462e4 | |||
| 0bd99b327b | |||
| 6d8b672094 | |||
| e21b26a964 | |||
| 0f388193b3 | |||
| 7948b8568e | |||
| 61b1d9a466 | |||
| 3617e04e97 | |||
| 8c62bde36f | |||
| 51650eac78 | |||
| aea68e5c54 | |||
| ece042fef0 | |||
| 217b65e6d9 | |||
| 8119374e74 | |||
| 3a997eefb5 | |||
| 5184ff2096 | |||
| b0cbe77816 | |||
| 6e899850b9 | |||
| fded7bef19 | |||
| b5d7255034 | |||
| 6cce5c0601 | |||
| eb70f58279 | |||
| ef900f224c | |||
| 3105febf9a | |||
| 7133e20c94 | |||
| 888d39ea75 | |||
| eef22fa4ce | |||
| eda87f1156 | |||
| e0e406ead3 | |||
| 30cea671a7 | |||
| a716ae1f50 | |||
| ea382b0be1 | |||
| 6d8d282e0f | |||
| 00c8a17a2d | |||
| 166ce15512 | |||
| 87cf2bf711 | |||
| fd8a849621 | |||
| 5a568a3b01 | |||
| 475e6c5352 | |||
| af00a0772c | |||
| 6819c2a98a | |||
| 1dd8af67b6 | |||
| 1350a2bcb5 | |||
| 217e67cb22 | |||
| 9bbfe9f3fd | |||
| 788a0ecd27 | |||
| 6d86b937e4 | |||
| bc90cf4b83 | |||
| d79190e27f | |||
| 9caf5b4f79 | |||
| 50cddefeeb | |||
| e24f1ee9ff | |||
| f3b0eb3ecf | |||
| 162ce8256e | |||
| ad90447eb6 | |||
| 87912786af | |||
| 5645a84368 | |||
| 95ed6c1127 | |||
| 12ab78aca6 | |||
| 45c346645c | |||
| 68ce878292 | |||
| 500336efd0 | |||
| 5ea8f108e1 | |||
| 2275030b86 | |||
| 05de314701 | |||
| d281d23f8d | |||
| ec431ce5db | |||
| c8f47c2934 | |||
| ca458f1f52 | |||
| 121dc8af66 | |||
| a8675966ec | |||
| 3dc5475e23 | |||
| 8a008a2057 | |||
| 89b59eee73 | |||
| 166dd0261a | |||
| 51471ba748 | |||
| 0b65cb74cd | |||
| ea46b750dc | |||
| 92afcf2823 | |||
| b5bf7d9ef4 | |||
| 6a5d89b39f | |||
| ae14adc272 | |||
| ff98f17d27 | |||
| 59bcd137ae | |||
| 5ab6d36d99 | |||
| 43c873895f | |||
| 03a078493e | |||
| bae57ea088 | |||
| 2557107e91 | |||
| 32a12e2f3f | |||
| 559bb554c9 | |||
| fd5e810e08 | |||
| 067f761739 | |||
| 7978a8d8b2 | |||
| 32e09c2a16 | |||
| df19931ddc | |||
| b5084fdafe | |||
| d8200d5f3c | |||
| 79cbc3468c | |||
| 9e89a55a4f | |||
| b707e81c3d | |||
| ee37ee50d6 | |||
| ad1a54b5c0 | |||
| 88c29c0cd5 | |||
| d93df22bf6 | |||
| 5017cc6ba2 | |||
| 2f125ebc33 | |||
| 49a01c0991 | |||
| 17526ed2fe | |||
| f180822fa6 | |||
| 594220c624 | |||
| 3dd68624dc | |||
| f9eea453b7 | |||
| 374773748b | |||
| 29644366fc | |||
| 6eadedbfa1 | |||
| 1978094b3a | |||
| 5acc5b0c1e | |||
| 59547c6cda | |||
| 3d3355b4f3 | |||
| 801f43f09a | |||
| 719c01359f | |||
| 43f13729a2 | |||
| 136f8e148d | |||
| f90a130b07 | |||
| cb4950a6d8 | |||
| fdede85419 | |||
| beab626061 | |||
| 817f8674af | |||
| cf0562e1e7 | |||
| 22e1c5ebad | |||
| 470dd56a89 | |||
| 20d30bcd33 | |||
| 44b6f496b5 | |||
| fd80e63468 | |||
| f62565abea | |||
| c9d1d9f9c6 | |||
| 35391d9b1a | |||
| 99b1b8e918 | |||
| 3b44821334 | |||
| 0214dab989 | |||
| 821bd7025e | |||
| 9b7dddad7d | |||
| 3f04a9f2c0 | |||
| 2a59a999d4 | |||
| ee7d0c2649 | |||
| aa13c591f5 | |||
| 6c8a32217a | |||
| cb09f4fef4 | |||
| 3cf1729a02 | |||
| 74b0bb3a45 | |||
| e4076fe6d9 | |||
| 81952e6884 | |||
| 25b4e35ee8 | |||
| 7e1facaa4e | |||
| ad2531a0ee | |||
| bc1c718cc0 | |||
| c9ce754e38 | |||
| d34271fe42 | |||
| d120ec29ca | |||
| 2aff901c93 | |||
| bad231c481 | |||
| a2b1c279f1 | |||
| 2f128c5130 | |||
| 4758113043 | |||
| a4e644add2 | |||
| 05ca63642b | |||
| f17e3624ef | |||
| 2e9b8476ca | |||
| c31f4946e9 | |||
| c86586e866 | |||
| 19fbdf6cae | |||
| 427ee519db | |||
| a958b711f5 | |||
| 1ffdd0bc7a | |||
| 5286650894 | |||
| 0fb1644c60 | |||
| 119d5e46d5 | |||
| 83abbac946 | |||
| a25416a8d5 | |||
| 682cce3813 | |||
| d806710ab5 | |||
| e049abb69a | |||
| 98816d0ce5 | |||
| 7482e8f914 | |||
| 03b769053a | |||
| 3c710f056b | |||
| c0a865fa54 | |||
| 0009ac1f3a | |||
| 7b968bb514 | |||
| bf8b08295a | |||
| 3f5b56c48b | |||
| e7192dfdbb | |||
| 0539f48661 | |||
| e7a9c45a48 | |||
| fd6c1f4898 | |||
| f079749287 | |||
| f8d677f94c | |||
| 094a7d0290 | |||
| 7cc0e63d8a | |||
| a4a8914c20 | |||
| 06c7efb4a9 | |||
| dd547c55c8 | |||
| f6bd8b1147 | |||
| a7faa557fa | |||
| 026a7e92be | |||
| c4ea0c3b29 | |||
| 7106c491dd | |||
| a10d495b18 | |||
| c2e3cc4c89 | |||
| 67feed49f1 | |||
| b63b0928cf | |||
| 511679313f | |||
| 868ff40339 | |||
| aa6faa6331 | |||
| 0b3f4e254e | |||
| 551b076f1c | |||
| 737f119187 | |||
| 8980c630bf | |||
| 8d579c5bc5 | |||
| d51743b0c7 | |||
| 1a92c2b354 | |||
| fe0d54d37e | |||
| c9af0768fc | |||
| 7e0caa7d61 | |||
| a40768e5ea | |||
| 4a83fe569c | |||
| b83b6ba015 | |||
| af119c8b55 | |||
| 1aead7f553 | |||
| 75d8470f0f | |||
| 81646af13b | |||
| 233b71c092 | |||
| ccc4fc9cf0 | |||
| c79b4a97aa | |||
| ac3afc7cd4 | |||
| 350cc4bcb6 | |||
| 35931bbed4 | |||
| 12779b2b71 | |||
| 7ca1bd639f | |||
| 9b7a12c46d | |||
| 5b33e39855 | |||
| a64cf8fac9 | |||
| 092fa71ddd | |||
| a2eaad5d81 | |||
| 54771c5afa | |||
| bcce769d29 | |||
| 24baa919c1 | |||
| d1c0efdb7d | |||
| 566758de79 | |||
| 50334faad6 | |||
| cee7a8e1ec | |||
| 8c21f463ed | |||
| a1952d9c41 | |||
| 1a46a713d2 | |||
| 23b9ceb0a4 | |||
| 0a6a6b9d9d | |||
| 1b54197491 | |||
| c97aa9485c | |||
| e885b91326 | |||
| fa1f724378 | |||
| 2a966116b1 | |||
| 43ec354722 | |||
| 700b003fb5 | |||
| f722ba6890 | |||
| c7d4da8746 | |||
| f1629217f1 | |||
| fdd5f2f7a3 | |||
| c6ef3e2672 | |||
| ba8dca00ac | |||
| 9be89a1864 | |||
| b9f371a3c2 | |||
| 4834de8427 | |||
| 94043a3a1a | |||
| e642cc4206 | |||
| da57477c3d | |||
| f39034889d | |||
| 6b053a63cb | |||
| 572e5e6b28 | |||
| 6098e06e09 | |||
| c6b64ffe65 | |||
| 4590a89b32 | |||
| 1475cb59bf | |||
| 9e8e60dd5f | |||
| d72ec210c7 | |||
| c77baf752c | |||
| a122688091 | |||
| f3026c36c2 | |||
| cd0ebd0a80 | |||
| 8bc33a088c | |||
| c3eb89544f | |||
| ffff9a49da | |||
| b22356e5c5 | |||
| a289a62717 | |||
| e3eabcf913 | |||
| 0df2238063 | |||
| 6828f6d427 | |||
| daa5fe3e31 | |||
| 8d9ea59d28 | |||
| 5e7dbd0b8b | |||
| cd6c276e41 | |||
| fd4b4a2a68 | |||
| d447945685 | |||
| 78f9635111 | |||
| 8b7cf8c62d | |||
| 73b0a3451d | |||
| 85c1461422 | |||
| 5ba556d27a | |||
| 81dd13f4c6 | |||
| 6a02bbe1e2 | |||
| 7631c29b2f | |||
| a9dc7c8828 | |||
| 7192afafee | |||
| a139e2e003 | |||
| a71c5f6f53 | |||
| d9ea6eca7c | |||
| ca3865cf1b | |||
| bef5980744 | |||
| 409741e900 | |||
| b98de30b8f | |||
| 9f48712c15 | |||
| bd262d591f | |||
| 0d116c3ada | |||
| 79f11fb2bb | |||
| fabe4ec487 | |||
| 575074fd85 | |||
| 545f43065f | |||
| 3819c30e55 | |||
| e94fac8956 | |||
| 2c1a5d678d | |||
| bd619b9311 | |||
| aca84ee1a0 | |||
| 5038e3c58c | |||
| 5a7dee0c5d | |||
| ddf1f2a053 | |||
| e34d3c06b0 | |||
| e8a2c36909 | |||
| 3c39a0e8cb | |||
| 3d45a8eb1c | |||
| 3d80aeab5a | |||
| 3811e23b32 | |||
| 12c764ed1e | |||
| e99e9a9473 | |||
| f35ac38e1d | |||
| 058279d548 | |||
| 69ba1ac441 | |||
| 4a925c1552 | |||
| cf531912f2 | |||
| 0d00729e31 | |||
| b6e9fcbd70 | |||
| 8554baeacf | |||
| f5b7f077ec | |||
| e924a7f9ea | |||
| 4fd76b8148 | |||
| de0f51a02c | |||
| 0567221848 | |||
| 1c1a98e872 | |||
| c006f085d9 | |||
| a48e330f37 | |||
| dcc070683c | |||
| 2d81cfa4e1 | |||
| 0bccf03d7e | |||
| ad2b5decc8 | |||
| 32c1764b5d | |||
| de97177744 | |||
| fa2ddb070a | |||
| 0fac33da9e | |||
| 8e1bdda66a | |||
| 9c1347e647 | |||
| b373a40133 | |||
| 25951e0ea5 | |||
| a4f20e3c8b | |||
| 8c583c61a3 | |||
| d2ea5b00b7 | |||
| d77898597e | |||
| 237f755dca | |||
| 7444b5a1d5 | |||
| e7ce7e1850 | |||
| 3e662cec89 | |||
| fde866e303 | |||
| 2d7a81a204 | |||
| 658ea8752b | |||
| aa73403ba4 | |||
| 0c27de2e7b | |||
| d25f7f9d78 | |||
| 85555646e3 | |||
| 4d871ee973 | |||
| b9e1dec2cb | |||
| dc2b8a0073 | |||
| 9a9fe42deb | |||
| 0062db1e06 | |||
| 23001d8607 | |||
| 5d79c6684b | |||
| 3410dcd375 | |||
| 5a7a0c72d8 | |||
| ee7478e884 | |||
| aaa5d7adab | |||
| 3eb1788bb4 | |||
| 00114a95b2 | |||
| 4aaf0ef75c | |||
| ed720d09f3 | |||
| 376e939ef6 | |||
| 9d28e2b3e5 | |||
| 49ca1f7083 | |||
| 4db6f20770 | |||
| cca71912e6 | |||
| 8003e9544d | |||
| 973d81efb3 | |||
| 26d4a73962 | |||
| 99a39f6f4a | |||
| 1bb407e494 | |||
| 53f9499f7f | |||
| 575befc394 | |||
| e7ffb1b501 | |||
| c066ac10a9 | |||
| 1e6940f291 | |||
| d8d81ba891 | |||
| cf22133b69 | |||
| b7be05ad27 | |||
| afdb19ac82 | |||
| 7dfa4cd655 | |||
| fc6203ee43 | |||
| b2dd246b7a | |||
| d20ff60615 | |||
| d41b467320 | |||
| 4541243355 | |||
| 9100f7479a | |||
| 055104f5b6 | |||
| 28aa733c15 | |||
| 6abde1b05a | |||
| d394d5536a | |||
| a2f98314a3 | |||
| a09c8d7beb | |||
| f80ff7a336 | |||
| a08fc34ee4 | |||
| d00d2eaf51 | |||
| 11126422d9 | |||
| e54668b696 | |||
| 9eb832de54 | |||
| 82a1f5d354 | |||
| a8d3aa14fd | |||
| 064e406f67 | |||
| 5c87f61e6b | |||
| 3aa9598177 | |||
| 64d39ed1b6 | |||
| 4433dbb5f5 | |||
| 5cb128ea54 | |||
| 74123e9686 | |||
| 9c196bebad | |||
| 28b2566c97 | |||
| caf74dd731 | |||
| 62840c5fad | |||
| 5ca7cdd709 | |||
| 1514e49d43 | |||
| 22ef84b6c8 | |||
| 63a2b62468 | |||
| 489253d753 | |||
| 25c557ec63 | |||
| f292760f12 | |||
| 1124cb1247 | |||
| c295147874 | |||
| e560ac4862 | |||
| 915a9a912c | |||
| ae0a5e2142 | |||
| 347f0c33d1 | |||
| 622e448710 | |||
| 0fe2576a39 | |||
| d08d237a49 | |||
| 64a7ec987c | |||
| 4274738e81 | |||
| 0d7d559dcc | |||
| af512ad6da | |||
| d27108ccd5 | |||
| a838b3b496 | |||
| 4c54cd3a10 | |||
| c8ff7de7f0 | |||
| 3bae8e047c | |||
| 4f5f2fe367 | |||
| 01586d539e | |||
| c8ab34ab7a | |||
| 36ab1ee10c | |||
| 193ce46970 | |||
| b678f246a4 | |||
| f270e7f044 | |||
| 070d3743a7 | |||
| 0c7de97d47 | |||
| 900b7d4514 | |||
| 91f0b20a0e | |||
| e4b4623abc | |||
| cef96b73e1 | |||
| 427e01eb63 | |||
| d320c908d0 | |||
| 8bfc696676 | |||
| 97a35e70af | |||
| 03b556b963 | |||
| 572b94bbcb | |||
| 692209fa8d | |||
| 953d499ea2 | |||
| a541ada194 | |||
| 55d3138397 | |||
| e74e56f2b1 | |||
| 1ebf109c53 | |||
| f75be1cb28 | |||
| 16d2ad88fe | |||
| f3a6815000 | |||
| 19f8c87375 | |||
| 5bcfe0c700 | |||
| 8cd395564c | |||
| 389c890ed3 | |||
| e3866afa97 | |||
| 25189b420d | |||
| cec25167d1 | |||
| 1fd4c482a0 | |||
| 35e729fa6d | |||
| 0dc09cb42e | |||
| cc1cbd19a0 | |||
| cb3f95d516 | |||
| f84b575cfa | |||
| 3cb343efd6 | |||
| ec0fc1a13b | |||
| c3a6f3fa1c | |||
| 0bbdddc86b | |||
| b3b8dfaaea | |||
| adc6a4865b | |||
| 2108754e46 | |||
| 46f1429936 | |||
| e0f9d1c8f5 | |||
| de6fc75d63 | |||
| 2af0d3b459 | |||
| 9fbe0690fb | |||
| a25b8ff69c | |||
| 15b823ce4f | |||
| 8da245bfd2 | |||
| a2a2abcba4 | |||
| 2e51792596 | |||
| 3a9ae612a4 | |||
| 9db693a57c | |||
| 1375435e4a | |||
| 2bb19ccf08 | |||
| 64d5df7772 | |||
| 2643da67c6 | |||
| ed117bb42a | |||
| a4472490bc | |||
| 86312e1a7d | |||
| df1dc3f1f5 | |||
| 513d0258d9 | |||
| cfa8bc3ca4 | |||
| a654e82f5c | |||
| 3e41afcec5 | |||
| 3acf5ec5a1 | |||
| 93695db903 | |||
| 9107dd115c | |||
| dc4c459546 | |||
| 19a6c38745 | |||
| 3c4d970a7b | |||
| b0f2b288a6 | |||
| 2b25489b47 | |||
| a6f465f558 | |||
| 9a41506b8f | |||
| 9a6d9ea57a | |||
| 062e809e54 | |||
| b4c1824824 | |||
| 2e5ef6a9fb | |||
| 1dea393058 | |||
| 45dec34597 | |||
| b40a38a8ac | |||
| ac899a6307 | |||
| ce444bff33 | |||
| 2d5931ebc8 | |||
| 35fa276099 | |||
| b51d76ce8d | |||
| 95020afb63 | |||
| c5050b1c41 | |||
| b9063a08f5 | |||
| 7fe405739c | |||
| 1ce819333d | |||
| 88eb901f81 | |||
| 2583bac3d3 | |||
| 63a3b1a1eb | |||
| fbf866a1a5 | |||
| 49a67c6d3f | |||
| a921d81033 | |||
| 155dd01163 | |||
| 599789a907 | |||
| 9756f56354 | |||
| b7d5f18d71 | |||
| 5598b2bc55 | |||
| 77077b39d5 | |||
| 5bc91c6405 | |||
| f5073de0a7 | |||
| f6ed285cf2 | |||
| 1448e736f0 | |||
| 6e415e9387 | |||
| b114c99a60 | |||
| 916587508c | |||
| a5382b763c | |||
| ba21aa8b2b | |||
| d119cf3035 | |||
| 0ac3d07ea6 | |||
| 5938212748 | |||
| 1829911d7c | |||
| bed2cb725a | |||
| ab75bd23ee | |||
| cee94df6e0 | |||
| fcc46467fa | |||
| f8e354e240 | |||
| a650378822 | |||
| 99182c6872 | |||
| 67cae2854c | |||
| 0e45ffb8e4 | |||
| 071e54ae0b | |||
| a68494fb32 | |||
| 4b44793dc5 | |||
| 90e2a716c8 | |||
| d9fbbe1f23 | |||
| 4a714cebd1 | |||
| 5b6be84106 | |||
| b2bb2f7f59 | |||
| f096ab5421 | |||
| 4e1ffc482a | |||
| bc439e21cd | |||
| 1b4480003b | |||
| 42167804b8 | |||
| 3135a6bbab | |||
| 95cbc402dc | |||
| decada8dc2 | |||
| ee5d03b42a | |||
| 323f33d014 | |||
| 55dc7fb240 | |||
| 11d7ba79f2 | |||
| bd5fa9cc8f | |||
| 2658c94925 | |||
| 215885768c | |||
| a5f68589c0 | |||
| 28419c4383 | |||
| 5bff2aaf47 | |||
| f76f4cb79d | |||
| abb85fc059 | |||
| aabe3ddbf4 | |||
| 0c258c8492 | |||
| d57c9cdefc | |||
| afb3f7e754 | |||
| 88564c3aec | |||
| 3abe3da9fb | |||
| eb8499c8e0 | |||
| 47c562435b | |||
| 02504d86d3 | |||
| 4a818f068a | |||
| 1d6ebce296 | |||
| bc3e02a1b7 | |||
| f4eed60ca9 | |||
| c1de1fdac6 | |||
| a36c8e1718 | |||
| d7872dcf91 | |||
| 14115170bc | |||
| ec52149485 | |||
| 6980cb1aa7 | |||
| 59a59fafc6 | |||
| 2bdd194f43 | |||
| 96fa34ec32 | |||
| 6a024df440 | |||
| 77fd93188e | |||
| a21c89ddc8 | |||
| 49b88b1c39 | |||
| 3db5f60547 | |||
| 2258ec6bc9 | |||
| 2598138ece | |||
| 4a67e48611 | |||
| 5cdec7d124 | |||
| 9423e22aa3 | |||
| 57f8414de1 | |||
| 139d562861 | |||
| eea6d10866 | |||
| 019ebcb85f | |||
| 889284ebd0 | |||
| 6956331093 | |||
| 92479f1a8b | |||
| e67dc3a227 | |||
| 0cc3add9f8 | |||
| 71c2eeff98 | |||
| d7bc346d7a | |||
| 673c456cba | |||
| ea44570883 | |||
| e091a254df | |||
| 58c3aa8b5f | |||
| c1f78ff3d3 | |||
| e5249b9fb5 | |||
| d3037d97ec | |||
| c6b442bd36 | |||
| 4f88418170 | |||
| 311e9f3ceb | |||
| 8775f25830 | |||
| 70a63fa5ad | |||
| fffbaede75 | |||
| ae9ad6c694 | |||
| 83d2dbb15d | |||
| 4e43741943 | |||
| a274596ecc | |||
| 7e9bc41fe7 | |||
| b1e1c15ced | |||
| 3dbf305408 | |||
| 6d0ae11c57 | |||
| 3ca739b418 | |||
| 9e613b8583 | |||
| 9428ec42d0 | |||
| 6ffcf21367 | |||
| 6427c61e7c | |||
| ba484b9eb1 | |||
| f9d015523e | |||
| 1817dfc301 | |||
| 7837e34e56 | |||
| 910310d3ba | |||
| facfcd497f | |||
| b403317325 | |||
| 9bf05e7364 | |||
| e39e758a73 | |||
| 5438d77221 | |||
| 6905ae900b | |||
| ada82930fd | |||
| f36e319658 | |||
| 0cea28a785 | |||
| d2ebdc045b | |||
| 81ede6bfce | |||
| 2b579b4b83 | |||
| a1d0e1a11a | |||
| 8975739839 | |||
| d07abcc6c7 | |||
| 189378142a | |||
| 82749b765e | |||
| 2b590aea40 | |||
| 547e47649f | |||
| 41dd32e18e | |||
| 5d3cc5def8 | |||
| 53db607963 | |||
| cbf0c7a103 | |||
| 4c399c5dfb | |||
| b472098493 | |||
| 84b3daebbd | |||
| f6978173e0 | |||
| e02483dbc0 | |||
| 9e58e0df8f | |||
| 690efef3de | |||
| b9e92c8b0d | |||
| 4376c9bc5f | |||
| f94cd2201c | |||
| fdbb243ff9 | |||
| c1565c16ed | |||
| 394646891c | |||
| d69aa51088 | |||
| afe849c9b4 | |||
| ccd678ea3e | |||
| bb92ef23cb | |||
| bb48f456d9 | |||
| df802dc74b | |||
| e23da9edee | |||
| 4db2cddeee | |||
| f9dec5fc84 | |||
| 6b8725b0ee | |||
| 6955666290 | |||
| 5298392c5a | |||
| 1ceec74370 | |||
| 2a84c29bc3 | |||
| 4d8a1103d3 | |||
| 202de59254 | |||
| f858a155c2 | |||
| 9e7de9473c | |||
| 0c8f5fc2fd | |||
| 1527d8f00d | |||
| 8efaf8af81 | |||
| c3525610fe | |||
| 7ba62e2985 | |||
| 8fd28611d5 | |||
| 93869acb2c | |||
| 480a1da34a | |||
| 59e2973335 | |||
| 5d09be4832 | |||
| 860e285f31 | |||
| eb8ac6e2a0 | |||
| b1203e036d | |||
| 92a0d4c67f | |||
| 5fa55df2ca | |||
| a3af42de8e | |||
| 198be23f36 | |||
| 24359ae294 | |||
| 6c84722afa | |||
| facb0519a0 | |||
| ffdca3522e | |||
| 4d7469b610 | |||
| 2b84e36b93 | |||
| 85bda9eef5 | |||
| a4d3080fe3 | |||
| 5627d51e05 | |||
| e64b0cada8 | |||
| 7316d61a66 | |||
| e2b366c760 | |||
| fa19baab7f | |||
| 2f8351fe48 | |||
| 32d42bdcdc | |||
| 843d53aabb | |||
| 5ac3386888 | |||
| 6d182faab6 | |||
| f86f4116c8 | |||
| 7f69f0efb0 | |||
| f6cab9666c | |||
| 3deb38814d | |||
| 3b5b14a085 | |||
| aec671c126 | |||
| dd92af69db | |||
| 015e973899 | |||
| 163f58dac5 | |||
| dcee7d2fe0 | |||
| 5f0a2f6248 | |||
| a73930228a | |||
| a76fa25199 | |||
| dd3519ab89 | |||
| 3a515df086 | |||
| 74897bc52f | |||
| e7c989baf1 | |||
| 7a3c8314ac | |||
| 6f8257ec5f | |||
| 87b0191753 | |||
| 56c17ba4c7 | |||
| daddc57abd | |||
| f9cdd09191 | |||
| 47b4858357 | |||
| b8d23d6e07 | |||
| fdecdd0cec | |||
| fabdaa37b6 | |||
| bc82b4d78a | |||
| dae850e0ee | |||
| abcc124df0 | |||
| b4164a9aa7 | |||
| 9f0d62ad9f | |||
| 64ecf24210 | |||
| 658e7caf92 | |||
| 788506dd58 | |||
| 62790ab15d | |||
| ad60b3ead7 | |||
| 5e8dd55574 | |||
| 0e961ad47a | |||
| c06cc60946 | |||
| 2fa476655f | |||
| 7a78495f31 | |||
| 3138afbe2e | |||
| 4eef0aaad1 | |||
| ab09732986 | |||
| b7fa64565a | |||
| 2622ed2528 | |||
| 9cb0607f6f | |||
| 8998032f66 | |||
| 80b4753539 | |||
| fe0838cd1c | |||
| 8acaa2ce07 | |||
| c676f13e1a | |||
| 9b47cee8b2 | |||
| 47e666c090 | |||
| 3059579253 | |||
| 72c2f75648 | |||
| ec68ed66ed | |||
| f6a81dbe97 | |||
| 3904a66ab0 | |||
| ffc5f78229 | |||
| 3f78748ef3 | |||
| d26bfb5ae4 | |||
| 8d3dd04137 | |||
| 791d8d8585 | |||
| e85515958b | |||
| dc59444cd1 | |||
| 6c042e17b1 | |||
| 18a583306b | |||
| fc8d077612 | |||
| e64fcb9d94 | |||
| 6c0a9a272e | |||
| 01fa1bc114 | |||
| aa003eba8e | |||
| becf19feef | |||
| 81fc3c4f45 | |||
| e7fb0d8a6f | |||
| 4eb49163c6 | |||
| 79cb336d74 | |||
| 38063345ae | |||
| ae00b3a76f | |||
| 619be354c8 | |||
| 94d40f3980 | |||
| 585b81f91e | |||
| 5f4f0d4a63 | |||
| 8e36e1fe49 | |||
| 97505caa64 | |||
| df50c84ee0 | |||
| 1a455acc81 | |||
| 60bc151ed7 | |||
| 3b19bd4d57 | |||
| 08f902cf49 | |||
| d3c304e92e | |||
| 3bf0292cd5 | |||
| a7861d9d66 | |||
| d62a21b9d3 | |||
| cf86576a83 | |||
| 92dc64e121 | |||
| 593bfc9668 | |||
| 850c60e81b | |||
| 3d480b04a5 | |||
| c36e2265d9 | |||
| a68df3898d | |||
| 4d7cfff6df | |||
| be822a5f1c | |||
| 9f5a052c27 | |||
| fc875334fd | |||
| 80a267b130 | |||
| 3182ac69a7 | |||
| dead3858b8 | |||
| afa0d0ac0d | |||
| 742b8be0c1 | |||
| 285f926140 | |||
| 3c8865cf66 | |||
| 569a326931 | |||
| d1ef7e0ec6 | |||
| d513c3c66b | |||
| 93d3c8450c | |||
| 8667f29c3b | |||
| d22526ec30 | |||
| bd869c7b31 | |||
| d5a23dde90 | |||
| a7c9e45c64 | |||
| cedd2092eb | |||
| c473818017 | |||
| 6087e67771 | |||
| 93bf618504 | |||
| dfe2f1eeff | |||
| ca7e5b5cb6 | |||
| 5019b2dd20 | |||
| 33e7bc88f8 | |||
| fa0c03aaa6 | |||
| 3fa47c5dc1 | |||
| 0782416a74 | |||
| 3ca9af51a4 | |||
| c7b89dd280 | |||
| 74832b2a10 | |||
| ee41bc03ab | |||
| 98194db429 | |||
| fad7582c9a | |||
| a62fca1ebb | |||
| 2b5c1f99e5 | |||
| e2dc5f5d15 | |||
| ed64346b9d | |||
| 475b0ecbf1 | |||
| 414359b2f1 | |||
| 0079efc479 | |||
| ad0765b4f0 | |||
| 20f4bbdc3a | |||
| c408158047 | |||
| 55e323190e | |||
| fe8f4c06f1 | |||
| c351392940 | |||
| 18330b6091 | |||
| 8487913697 | |||
| 672bb67ee6 | |||
| 90df6b7a54 | |||
| 0238a0b01e | |||
| 1ae0b6c0bf | |||
| e1dda047b2 | |||
| 2c8206d448 | |||
| ae76307847 | |||
| 3d61c31e5a | |||
| 44a9bd6a8c | |||
| 69ea087dff | |||
| c05aaf3bf0 | |||
| 298ae7e230 | |||
| 6316a866a3 | |||
| ea2cc2bc47 | |||
| 64205e91a3 | |||
| 8173688542 | |||
| 3ef5a6797f | |||
| 9e6e43984d | |||
| baffc37f5c | |||
| 440113bbe8 | |||
| 73ebc5d8f6 | |||
| a666752b1d | |||
| f8b7a5581e | |||
| ffb798e1e2 | |||
| fa936f8fa9 | |||
| dd1221582b | |||
| bfdcbbcd76 | |||
| 30eebb2d1c | |||
| 0fd6466957 | |||
| 6a516878e9 | |||
| a486090a5c | |||
| 0160591998 | |||
| c505f341d6 | |||
| 428325baec | |||
| 0561f71f0f | |||
| 9fbefdf69c | |||
| 43c61fc113 | |||
| ea6b1b42c7 | |||
| 87024e257d | |||
| 6cd461633d | |||
| 16fd4abaf6 | |||
| f26b85c5b7 | |||
| 3592c08a2c | |||
| db724e14e0 | |||
| 1702953293 | |||
| 33b4dec1cb | |||
| 7dd10a1d7e | |||
| 521a452f28 | |||
| 2753134a38 | |||
| cbbde11a8a | |||
| 7b89cf3aeb | |||
| d2811e8097 | |||
| e43e95553f | |||
| 930018e273 | |||
| a160266c3d | |||
| d4c99b2673 | |||
| 88f9843eee | |||
| 9b5ae6faf9 | |||
| caf3cfc6ac | |||
| ff7638b0ed | |||
| 8e48ebb638 | |||
| c2bd68d2c3 | |||
| 201288b4df | |||
| 62367dda44 | |||
| 056b6a8ef4 | |||
| 502d8f9a06 | |||
| 7f376e6ea8 | |||
| 053e873145 | |||
| 9f4211310f | |||
| 515d556832 | |||
| ed7039024d | |||
| 5511bf6b9d | |||
| 86704cffe9 | |||
| fa41fafa6e | |||
| ebf646efb5 | |||
| b3f81f1d1d | |||
| a24eb3ef17 | |||
| 4385e2ada4 | |||
| e9b06c506f | |||
| cd12ab2597 | |||
| e251e84e0f | |||
| e6bfbc38b5 | |||
| d31c04c049 | |||
| 522481a788 | |||
| 37b49aa451 | |||
| 0fa9b16148 | |||
| e41fb9a604 | |||
| 5fc92cce12 | |||
| 6dd0c6ccbc | |||
| a77413acd0 | |||
| 5dbb870815 | |||
| ede19c9138 | |||
| 9e88b336b4 | |||
| 0fae476ed4 | |||
| 361a9adba7 | |||
| d8f1cca16e | |||
| bf96d3fdd9 | |||
| c8c2e06899 | |||
| 8dcc6bc532 | |||
| 38f585d271 | |||
| 6887f91799 |
@@ -0,0 +1,9 @@
|
||||
make -s -j
|
||||
cd testes/libs; make -s
|
||||
cd .. # back to directory 'testes'
|
||||
ulimit -S -s 2000
|
||||
if { ../lua -W all.lua; } then
|
||||
echo -e "\n\n final OK!!!!\n\n"
|
||||
else
|
||||
echo -e "\n\n >>>> BUG!!!!\n\n"
|
||||
fi
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lapi.h,v 2.1 2003/12/10 12:13:36 roberto Exp roberto $
|
||||
** $Id: lapi.h $
|
||||
** Auxiliary functions from Lua API
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -8,9 +8,40 @@
|
||||
#define lapi_h
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "llimits.h"
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o);
|
||||
/* Increments 'L->top', checking for stack overflows */
|
||||
#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \
|
||||
"stack overflow");}
|
||||
|
||||
|
||||
/*
|
||||
** If a call returns too many multiple returns, the callee may not have
|
||||
** stack space to accomodate all results. In this case, this macro
|
||||
** increases its stack space ('L->ci->top').
|
||||
*/
|
||||
#define adjustresults(L,nres) \
|
||||
{ if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
|
||||
|
||||
|
||||
/* Ensure the stack has at least 'n' elements */
|
||||
#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \
|
||||
"not enough elements in the stack")
|
||||
|
||||
|
||||
/*
|
||||
** To reduce the overhead of returning from C functions, the presence of
|
||||
** to-be-closed variables in these functions is coded in the CallInfo's
|
||||
** field 'nresults', in a way that functions with no to-be-closed variables
|
||||
** with zero, one, or "all" wanted results have no overhead. Functions
|
||||
** with other number of wanted results, as well as functions with
|
||||
** variables to be closed, have an extra check.
|
||||
*/
|
||||
|
||||
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
||||
|
||||
#define codeNresults(n) (-(n) - 3)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lauxlib.h,v 1.84 2005/08/26 17:36:32 roberto Exp roberto $
|
||||
** $Id: lauxlib.h $
|
||||
** Auxiliary functions for building Lua libraries
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -15,81 +15,106 @@
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
#if defined(LUA_COMPAT_GETN)
|
||||
LUALIB_API int (luaL_getn) (lua_State *L, int t);
|
||||
LUALIB_API void (luaL_setn) (lua_State *L, int t, int n);
|
||||
#else
|
||||
#define luaL_getn(L,i) ((int)lua_objlen(L, i))
|
||||
#define luaL_setn(L,i,j) ((void)0) /* no op! */
|
||||
#endif
|
||||
|
||||
#if defined(LUA_COMPAT_OPENLIB)
|
||||
#define luaI_openlib luaL_openlib
|
||||
#endif
|
||||
/* global table */
|
||||
#define LUA_GNAME "_G"
|
||||
|
||||
|
||||
/* extra error code for `luaL_load' */
|
||||
typedef struct luaL_Buffer luaL_Buffer;
|
||||
|
||||
|
||||
/* extra error code for 'luaL_loadfilex' */
|
||||
#define LUA_ERRFILE (LUA_ERRERR+1)
|
||||
|
||||
|
||||
/* key, in the registry, for table of loaded modules */
|
||||
#define LUA_LOADED_TABLE "_LOADED"
|
||||
|
||||
|
||||
/* key, in the registry, for table of preloaded loaders */
|
||||
#define LUA_PRELOAD_TABLE "_PRELOAD"
|
||||
|
||||
|
||||
typedef struct luaL_Reg {
|
||||
const char *name;
|
||||
lua_CFunction func;
|
||||
} luaL_Reg;
|
||||
|
||||
|
||||
#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
|
||||
|
||||
LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
|
||||
#define luaL_checkversion(L) \
|
||||
luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
|
||||
|
||||
LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname,
|
||||
const luaL_Reg *l, int nup);
|
||||
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
|
||||
const luaL_Reg *l);
|
||||
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
|
||||
LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
|
||||
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
|
||||
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
|
||||
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
|
||||
LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
|
||||
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
|
||||
size_t *l);
|
||||
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
|
||||
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
|
||||
const char *def, size_t *l);
|
||||
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
|
||||
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
|
||||
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
|
||||
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
|
||||
|
||||
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
|
||||
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
|
||||
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
|
||||
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
|
||||
lua_Integer def);
|
||||
|
||||
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
|
||||
LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
|
||||
LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
|
||||
LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
|
||||
LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
|
||||
|
||||
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
|
||||
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
|
||||
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
|
||||
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
||||
|
||||
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
|
||||
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
|
||||
|
||||
LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
|
||||
LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
|
||||
const char *const lst[]);
|
||||
|
||||
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
|
||||
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
|
||||
|
||||
|
||||
/* predefined references */
|
||||
#define LUA_NOREF (-2)
|
||||
#define LUA_REFNIL (-1)
|
||||
|
||||
LUALIB_API int (luaL_ref) (lua_State *L, int t);
|
||||
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
|
||||
|
||||
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
|
||||
LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
|
||||
const char *name);
|
||||
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
|
||||
const char *mode);
|
||||
|
||||
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
|
||||
|
||||
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
|
||||
const char *name, const char *mode);
|
||||
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
|
||||
|
||||
LUALIB_API lua_State *(luaL_newstate) (void);
|
||||
|
||||
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
|
||||
|
||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
|
||||
const char *r);
|
||||
LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s,
|
||||
const char *p, const char *r);
|
||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
|
||||
const char *p, const char *r);
|
||||
|
||||
LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
|
||||
const char *fname);
|
||||
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
|
||||
|
||||
LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
|
||||
|
||||
LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
|
||||
const char *msg, int level);
|
||||
|
||||
LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
||||
lua_CFunction openf, int glb);
|
||||
|
||||
/*
|
||||
** ===============================================================
|
||||
@@ -97,23 +122,40 @@ LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
|
||||
** ===============================================================
|
||||
*/
|
||||
|
||||
#define luaL_argcheck(L, cond,numarg,extramsg) \
|
||||
((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
|
||||
|
||||
#define luaL_newlibtable(L,l) \
|
||||
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
||||
|
||||
#define luaL_newlib(L,l) \
|
||||
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
||||
|
||||
#define luaL_argcheck(L, cond,arg,extramsg) \
|
||||
((void)((cond) || luaL_argerror(L, (arg), (extramsg))))
|
||||
|
||||
#define luaL_argexpected(L,cond,arg,tname) \
|
||||
((void)((cond) || luaL_typeerror(L, (arg), (tname))))
|
||||
|
||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
||||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
||||
|
||||
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
|
||||
|
||||
#define luaL_dofile(L, fn) (luaL_loadfile(L, fn) || lua_pcall(L, 0, 0, 0))
|
||||
#define luaL_dofile(L, fn) \
|
||||
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_dostring(L, s) (luaL_loadstring(L, s) || lua_pcall(L, 0, 0, 0))
|
||||
#define luaL_dostring(L, s) \
|
||||
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
||||
|
||||
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
|
||||
|
||||
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
||||
|
||||
|
||||
/* push the value used to represent failure/error */
|
||||
#define luaL_pushfail(L) lua_pushnil(L)
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
@@ -121,50 +163,111 @@ LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
|
||||
typedef struct luaL_Buffer {
|
||||
char *p; /* current position in buffer */
|
||||
int lvl; /* number of strings in the stack (level) */
|
||||
struct luaL_Buffer {
|
||||
char *b; /* buffer address */
|
||||
size_t size; /* buffer size */
|
||||
size_t n; /* number of characters in buffer */
|
||||
lua_State *L;
|
||||
char buffer[LUAL_BUFFERSIZE];
|
||||
} luaL_Buffer;
|
||||
union {
|
||||
LUAI_MAXALIGN; /* ensure maximum alignment for buffer */
|
||||
char b[LUAL_BUFFERSIZE]; /* initial buffer */
|
||||
} init;
|
||||
};
|
||||
|
||||
|
||||
#define luaL_bufflen(bf) ((bf)->n)
|
||||
#define luaL_buffaddr(bf) ((bf)->b)
|
||||
|
||||
|
||||
#define luaL_addchar(B,c) \
|
||||
((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
|
||||
(*(B)->p++ = (char)(c)))
|
||||
((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
|
||||
((B)->b[(B)->n++] = (c)))
|
||||
|
||||
/* compatibility only */
|
||||
#define luaL_putchar(B,c) luaL_addchar(B,c)
|
||||
|
||||
#define luaL_addsize(B,n) ((B)->p += (n))
|
||||
#define luaL_addsize(B,s) ((B)->n += (s))
|
||||
|
||||
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
||||
LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
|
||||
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
|
||||
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
||||
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
|
||||
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
|
||||
LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
|
||||
|
||||
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/* compatibility with ref system */
|
||||
|
||||
/* pre-defined references */
|
||||
#define LUA_NOREF (-2)
|
||||
#define LUA_REFNIL (-1)
|
||||
/*
|
||||
** {======================================================
|
||||
** File handles for IO library
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
|
||||
(lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
|
||||
/*
|
||||
** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
|
||||
** initial structure 'luaL_Stream' (it may contain other fields
|
||||
** after that initial structure).
|
||||
*/
|
||||
|
||||
#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
|
||||
|
||||
#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
|
||||
#define LUA_FILEHANDLE "FILE*"
|
||||
|
||||
|
||||
typedef struct luaL_Stream {
|
||||
FILE *f; /* stream (NULL for incompletely created streams) */
|
||||
lua_CFunction closef; /* to close stream (NULL for closed streams) */
|
||||
} luaL_Stream;
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** "Abstraction Layer" for basic report of messages and errors
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* print a string */
|
||||
#if !defined(lua_writestring)
|
||||
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
||||
#endif
|
||||
|
||||
/* print a newline and flush the output */
|
||||
#if !defined(lua_writeline)
|
||||
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
|
||||
#endif
|
||||
|
||||
/* print an error message */
|
||||
#if !defined(lua_writestringerror)
|
||||
#define lua_writestringerror(s,p) \
|
||||
(fprintf(stderr, (s), (p)), fflush(stderr))
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {============================================================
|
||||
** Compatibility with deprecated conversions
|
||||
** =============================================================
|
||||
*/
|
||||
#if defined(LUA_COMPAT_APIINTCASTS)
|
||||
|
||||
#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
|
||||
#define luaL_optunsigned(L,a,d) \
|
||||
((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
|
||||
|
||||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
||||
|
||||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
||||
|
||||
#endif
|
||||
/* }============================================================ */
|
||||
|
||||
|
||||
#define luaL_reg luaL_Reg
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+310
-416
@@ -1,9 +1,13 @@
|
||||
/*
|
||||
** $Id: lbaselib.c,v 1.181 2005/08/15 14:12:32 roberto Exp roberto $
|
||||
** $Id: lbaselib.c $
|
||||
** Basic library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lbaselib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
@@ -11,78 +15,107 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define lbaselib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** If your system does not support `stdout', you can just remove this function.
|
||||
** If you need, you can define your own `print' function, following this
|
||||
** model but changing `fputs' to put the strings at a proper place
|
||||
** (a console window or a log file, for instance).
|
||||
*/
|
||||
static int luaB_print (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int i;
|
||||
lua_getglobal(L, "tostring");
|
||||
for (i=1; i<=n; i++) {
|
||||
const char *s;
|
||||
lua_pushvalue(L, -1); /* function to be called */
|
||||
lua_pushvalue(L, i); /* value to print */
|
||||
lua_call(L, 1, 1);
|
||||
s = lua_tostring(L, -1); /* get result */
|
||||
if (s == NULL)
|
||||
return luaL_error(L, LUA_QL("tostring") " must return a string to "
|
||||
LUA_QL("print"));
|
||||
if (i>1) fputs("\t", stdout);
|
||||
fputs(s, stdout);
|
||||
for (i = 1; i <= n; i++) { /* for each argument */
|
||||
size_t l;
|
||||
const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
|
||||
if (i > 1) /* not the first element? */
|
||||
lua_writestring("\t", 1); /* add a tab before it */
|
||||
lua_writestring(s, l); /* print it */
|
||||
lua_pop(L, 1); /* pop result */
|
||||
}
|
||||
fputs("\n", stdout);
|
||||
lua_writeline();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Creates a warning with all given arguments.
|
||||
** Check first for errors; otherwise an error may interrupt
|
||||
** the composition of a warning, leaving it unfinished.
|
||||
*/
|
||||
static int luaB_warn (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int i;
|
||||
luaL_checkstring(L, 1); /* at least one argument */
|
||||
for (i = 2; i <= n; i++)
|
||||
luaL_checkstring(L, i); /* make sure all arguments are strings */
|
||||
for (i = 1; i < n; i++) /* compose warning */
|
||||
lua_warning(L, lua_tostring(L, i), 1);
|
||||
lua_warning(L, lua_tostring(L, n), 0); /* close warning */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define SPACECHARS " \f\n\r\t\v"
|
||||
|
||||
static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
|
||||
lua_Unsigned n = 0;
|
||||
int neg = 0;
|
||||
s += strspn(s, SPACECHARS); /* skip initial spaces */
|
||||
if (*s == '-') { s++; neg = 1; } /* handle sign */
|
||||
else if (*s == '+') s++;
|
||||
if (!isalnum((unsigned char)*s)) /* no digit? */
|
||||
return NULL;
|
||||
do {
|
||||
int digit = (isdigit((unsigned char)*s)) ? *s - '0'
|
||||
: (toupper((unsigned char)*s) - 'A') + 10;
|
||||
if (digit >= base) return NULL; /* invalid numeral */
|
||||
n = n * base + digit;
|
||||
s++;
|
||||
} while (isalnum((unsigned char)*s));
|
||||
s += strspn(s, SPACECHARS); /* skip trailing spaces */
|
||||
*pn = (lua_Integer)((neg) ? (0u - n) : n);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_tonumber (lua_State *L) {
|
||||
int base = luaL_optint(L, 2, 10);
|
||||
if (base == 10) { /* standard conversion */
|
||||
luaL_checkany(L, 1);
|
||||
if (lua_isnumber(L, 1)) {
|
||||
lua_pushnumber(L, lua_tonumber(L, 1));
|
||||
if (lua_isnoneornil(L, 2)) { /* standard conversion? */
|
||||
if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */
|
||||
lua_settop(L, 1); /* yes; return it */
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
size_t l;
|
||||
const char *s = lua_tolstring(L, 1, &l);
|
||||
if (s != NULL && lua_stringtonumber(L, s) == l + 1)
|
||||
return 1; /* successful conversion to number */
|
||||
/* else not a number */
|
||||
luaL_checkany(L, 1); /* (but there must be some parameter) */
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char *s1 = luaL_checkstring(L, 1);
|
||||
char *s2;
|
||||
unsigned long n;
|
||||
size_t l;
|
||||
const char *s;
|
||||
lua_Integer n = 0; /* to avoid warnings */
|
||||
lua_Integer base = luaL_checkinteger(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */
|
||||
s = lua_tolstring(L, 1, &l);
|
||||
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
|
||||
n = strtoul(s1, &s2, base);
|
||||
if (s1 != s2) { /* at least one valid digit? */
|
||||
while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */
|
||||
if (*s2 == '\0') { /* no invalid trailing characters? */
|
||||
lua_pushnumber(L, (lua_Number)n);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_pushnil(L); /* else not a number */
|
||||
if (b_str2int(s, (int)base, &n) == s + l) {
|
||||
lua_pushinteger(L, n);
|
||||
return 1;
|
||||
} /* else not a number */
|
||||
} /* else not a number */
|
||||
luaL_pushfail(L); /* not a number */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_error (lua_State *L) {
|
||||
int level = luaL_optint(L, 2, 1);
|
||||
int level = (int)luaL_optinteger(L, 2, 1);
|
||||
lua_settop(L, 1);
|
||||
if (lua_isstring(L, 1) && level > 0) { /* add extra information? */
|
||||
luaL_where(L, level);
|
||||
if (lua_type(L, 1) == LUA_TSTRING && level > 0) {
|
||||
luaL_where(L, level); /* add extra information */
|
||||
lua_pushvalue(L, 1);
|
||||
lua_concat(L, 2);
|
||||
}
|
||||
@@ -104,60 +137,15 @@ static int luaB_getmetatable (lua_State *L) {
|
||||
static int luaB_setmetatable (lua_State *L) {
|
||||
int t = lua_type(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
|
||||
"nil or table expected");
|
||||
if (luaL_getmetafield(L, 1, "__metatable"))
|
||||
luaL_error(L, "cannot change a protected metatable");
|
||||
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
||||
if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)
|
||||
return luaL_error(L, "cannot change a protected metatable");
|
||||
lua_settop(L, 2);
|
||||
lua_setmetatable(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void getfunc (lua_State *L) {
|
||||
if (lua_isfunction(L, 1)) lua_pushvalue(L, 1);
|
||||
else {
|
||||
lua_Debug ar;
|
||||
int level = luaL_optint(L, 1, 1);
|
||||
luaL_argcheck(L, level >= 0, 1, "level must be non-negative");
|
||||
if (lua_getstack(L, level, &ar) == 0)
|
||||
luaL_argerror(L, 1, "invalid level");
|
||||
lua_getinfo(L, "f", &ar);
|
||||
if (lua_isnil(L, -1))
|
||||
luaL_error(L, "no function environment for tail call at level %d",
|
||||
level);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_getfenv (lua_State *L) {
|
||||
getfunc(L);
|
||||
if (lua_iscfunction(L, -1)) /* is a C function? */
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */
|
||||
else
|
||||
lua_getfenv(L, -1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_setfenv (lua_State *L) {
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
getfunc(L);
|
||||
lua_pushvalue(L, 2);
|
||||
if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) {
|
||||
/* change environment of current thread */
|
||||
lua_pushthread(L);
|
||||
lua_insert(L, -2);
|
||||
lua_setfenv(L, -2);
|
||||
return 0;
|
||||
}
|
||||
else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0)
|
||||
luaL_error(L,
|
||||
LUA_QL("setfenv") " cannot change environment of given object");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_rawequal (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
luaL_checkany(L, 2);
|
||||
@@ -166,6 +154,15 @@ static int luaB_rawequal (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int luaB_rawlen (lua_State *L) {
|
||||
int t = lua_type(L, 1);
|
||||
luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
|
||||
"table or string");
|
||||
lua_pushinteger(L, lua_rawlen(L, 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_rawget (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_checkany(L, 2);
|
||||
@@ -184,27 +181,69 @@ static int luaB_rawset (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int luaB_gcinfo (lua_State *L) {
|
||||
lua_pushinteger(L, lua_getgccount(L));
|
||||
static int pushmode (lua_State *L, int oldmode) {
|
||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" : "generational");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_collectgarbage (lua_State *L) {
|
||||
static const char *const opts[] = {"stop", "restart", "collect",
|
||||
"count", "step", "setpause", "setstepmul", NULL};
|
||||
"count", "step", "setpause", "setstepmul",
|
||||
"isrunning", "generational", "incremental", NULL};
|
||||
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
||||
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL};
|
||||
int o = luaL_checkoption(L, 1, "collect", opts);
|
||||
int ex = luaL_optinteger(L, 2, 0);
|
||||
lua_pushinteger(L, lua_gc(L, optsnum[o], ex));
|
||||
return 1;
|
||||
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
|
||||
LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC};
|
||||
int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
|
||||
switch (o) {
|
||||
case LUA_GCCOUNT: {
|
||||
int k = lua_gc(L, o);
|
||||
int b = lua_gc(L, LUA_GCCOUNTB);
|
||||
lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCSTEP: {
|
||||
int step = (int)luaL_optinteger(L, 2, 0);
|
||||
int res = lua_gc(L, o, step);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCSETPAUSE:
|
||||
case LUA_GCSETSTEPMUL: {
|
||||
int p = (int)luaL_optinteger(L, 2, 0);
|
||||
int previous = lua_gc(L, o, p);
|
||||
lua_pushinteger(L, previous);
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCISRUNNING: {
|
||||
int res = lua_gc(L, o);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCGEN: {
|
||||
int minormul = (int)luaL_optinteger(L, 2, 0);
|
||||
int majormul = (int)luaL_optinteger(L, 3, 0);
|
||||
return pushmode(L, lua_gc(L, o, minormul, majormul));
|
||||
}
|
||||
case LUA_GCINC: {
|
||||
int pause = (int)luaL_optinteger(L, 2, 0);
|
||||
int stepmul = (int)luaL_optinteger(L, 3, 0);
|
||||
int stepsize = (int)luaL_optinteger(L, 4, 0);
|
||||
return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize));
|
||||
}
|
||||
default: {
|
||||
int res = lua_gc(L, o);
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_type (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushstring(L, luaL_typename(L, 1));
|
||||
int t = lua_type(L, 1);
|
||||
luaL_argcheck(L, t != LUA_TNONE, 1, "value expected");
|
||||
lua_pushstring(L, lua_typename(L, t));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -222,122 +261,155 @@ static int luaB_next (lua_State *L) {
|
||||
|
||||
|
||||
static int luaB_pairs (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
|
||||
lua_pushvalue(L, 1); /* state, */
|
||||
lua_pushnil(L); /* and initial value */
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
static int ipairsaux (lua_State *L) {
|
||||
int i = luaL_checkint(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
i++; /* next value */
|
||||
lua_pushinteger(L, i);
|
||||
lua_rawgeti(L, 1, i);
|
||||
return (lua_isnil(L, -1)) ? 0 : 2;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_ipairs (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
|
||||
lua_pushvalue(L, 1); /* state, */
|
||||
lua_pushinteger(L, 0); /* and initial value */
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
static int load_aux (lua_State *L, int status) {
|
||||
if (status == 0) /* OK? */
|
||||
return 1;
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
lua_insert(L, -2); /* put before error message */
|
||||
return 2; /* return nil plus error message */
|
||||
luaL_checkany(L, 1);
|
||||
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
|
||||
lua_pushcfunction(L, luaB_next); /* will return generator, */
|
||||
lua_pushvalue(L, 1); /* state, */
|
||||
lua_pushnil(L); /* and initial value */
|
||||
}
|
||||
else {
|
||||
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
|
||||
lua_call(L, 1, 3); /* get 3 values from metamethod */
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_loadstring (lua_State *L) {
|
||||
size_t l;
|
||||
const char *s = luaL_checklstring(L, 1, &l);
|
||||
const char *chunkname = luaL_optstring(L, 2, s);
|
||||
return load_aux(L, luaL_loadbuffer(L, s, l, chunkname));
|
||||
/*
|
||||
** Traversal function for 'ipairs'
|
||||
*/
|
||||
static int ipairsaux (lua_State *L) {
|
||||
lua_Integer i = luaL_checkinteger(L, 2) + 1;
|
||||
lua_pushinteger(L, i);
|
||||
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** 'ipairs' function. Returns 'ipairsaux', given "table", 0.
|
||||
** (The given "table" may not be a table.)
|
||||
*/
|
||||
static int luaB_ipairs (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushcfunction(L, ipairsaux); /* iteration function */
|
||||
lua_pushvalue(L, 1); /* state */
|
||||
lua_pushinteger(L, 0); /* initial value */
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
static int load_aux (lua_State *L, int status, int envidx) {
|
||||
if (status == LUA_OK) {
|
||||
if (envidx != 0) { /* 'env' parameter? */
|
||||
lua_pushvalue(L, envidx); /* environment for loaded function */
|
||||
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
|
||||
lua_pop(L, 1); /* remove 'env' if not used by previous call */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else { /* error (message is on top of the stack) */
|
||||
luaL_pushfail(L);
|
||||
lua_insert(L, -2); /* put before error message */
|
||||
return 2; /* return fail plus error message */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_loadfile (lua_State *L) {
|
||||
const char *fname = luaL_optstring(L, 1, NULL);
|
||||
return load_aux(L, luaL_loadfile(L, fname));
|
||||
const char *mode = luaL_optstring(L, 2, NULL);
|
||||
int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
|
||||
int status = luaL_loadfilex(L, fname, mode);
|
||||
return load_aux(L, status, env);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Reader for generic `load' function: `lua_load' uses the
|
||||
** {======================================================
|
||||
** Generic Read function
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** reserved slot, above all arguments, to hold a copy of the returned
|
||||
** string to avoid it being collected while parsed. 'load' has four
|
||||
** optional arguments (chunk, source name, mode, and environment).
|
||||
*/
|
||||
#define RESERVEDSLOT 5
|
||||
|
||||
|
||||
/*
|
||||
** Reader for generic 'load' function: 'lua_load' uses the
|
||||
** stack for internal stuff, so the reader cannot change the
|
||||
** stack top. Instead, it keeps its resulting string in a
|
||||
** reserved slot inside the stack.
|
||||
*/
|
||||
static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
|
||||
(void)ud; /* to avoid warnings */
|
||||
(void)(ud); /* not used */
|
||||
luaL_checkstack(L, 2, "too many nested functions");
|
||||
lua_pushvalue(L, 1); /* get function */
|
||||
lua_call(L, 0, 1); /* call it */
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1); /* pop result */
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
else if (lua_isstring(L, -1)) {
|
||||
lua_replace(L, 3); /* save string in a reserved stack slot */
|
||||
return lua_tolstring(L, 3, size);
|
||||
}
|
||||
else luaL_error(L, "reader function must return a string");
|
||||
return NULL; /* to avoid warnings */
|
||||
else if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "reader function must return a string");
|
||||
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
|
||||
return lua_tolstring(L, RESERVEDSLOT, size);
|
||||
}
|
||||
|
||||
|
||||
static int luaB_load (lua_State *L) {
|
||||
int status;
|
||||
const char *cname = luaL_optstring(L, 2, "=(load)");
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
lua_settop(L, 3); /* function, eventual name, plus one reserved slot */
|
||||
status = lua_load(L, generic_reader, NULL, cname);
|
||||
return load_aux(L, status);
|
||||
size_t l;
|
||||
const char *s = lua_tolstring(L, 1, &l);
|
||||
const char *mode = luaL_optstring(L, 3, "bt");
|
||||
int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
|
||||
if (s != NULL) { /* loading a string? */
|
||||
const char *chunkname = luaL_optstring(L, 2, s);
|
||||
status = luaL_loadbufferx(L, s, l, chunkname, mode);
|
||||
}
|
||||
else { /* loading from a reader function */
|
||||
const char *chunkname = luaL_optstring(L, 2, "=(load)");
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
lua_settop(L, RESERVEDSLOT); /* create reserved slot */
|
||||
status = lua_load(L, generic_reader, NULL, chunkname, mode);
|
||||
}
|
||||
return load_aux(L, status, env);
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
|
||||
(void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */
|
||||
return lua_gettop(L) - 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_dofile (lua_State *L) {
|
||||
const char *fname = luaL_optstring(L, 1, NULL);
|
||||
int n = lua_gettop(L);
|
||||
if (luaL_loadfile(L, fname) != 0) lua_error(L);
|
||||
lua_call(L, 0, LUA_MULTRET);
|
||||
return lua_gettop(L) - n;
|
||||
lua_settop(L, 1);
|
||||
if (luaL_loadfile(L, fname) != LUA_OK)
|
||||
return lua_error(L);
|
||||
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
|
||||
return dofilecont(L, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static int luaB_assert (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
if (!lua_toboolean(L, 1))
|
||||
return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!"));
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
|
||||
static int luaB_unpack (lua_State *L) {
|
||||
int i = luaL_optint(L, 2, 1);
|
||||
int e = luaL_optint(L, 3, -1);
|
||||
int n;
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
if (e == -1)
|
||||
e = luaL_getn(L, 1);
|
||||
n = e - i + 1; /* number of elements */
|
||||
if (n <= 0) return 0; /* empty range */
|
||||
luaL_checkstack(L, n, "table too big to unpack");
|
||||
for (; i<=e; i++) /* push arg[i...e] */
|
||||
lua_rawgeti(L, 1, i);
|
||||
return n;
|
||||
if (lua_toboolean(L, 1)) /* condition is true? */
|
||||
return lua_gettop(L); /* return all arguments */
|
||||
else { /* error */
|
||||
luaL_checkany(L, 1); /* there must be a condition */
|
||||
lua_remove(L, 1); /* remove it */
|
||||
lua_pushliteral(L, "assertion failed!"); /* default message */
|
||||
lua_settop(L, 1); /* leave only message (default if no other one) */
|
||||
return luaB_error(L); /* call 'error' */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -348,83 +420,63 @@ static int luaB_select (lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
int i = luaL_checkint(L, 1);
|
||||
if (i <= 0) i = 1;
|
||||
else if (i >= n) i = n;
|
||||
return n - i;
|
||||
lua_Integer i = luaL_checkinteger(L, 1);
|
||||
if (i < 0) i = n + i;
|
||||
else if (i > n) i = n;
|
||||
luaL_argcheck(L, 1 <= i, 1, "index out of range");
|
||||
return n - (int)i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Continuation function for 'pcall' and 'xpcall'. Both functions
|
||||
** already pushed a 'true' before doing the call, so in case of success
|
||||
** 'finishpcall' only has to return everything in the stack minus
|
||||
** 'extra' values (where 'extra' is exactly the number of items to be
|
||||
** ignored).
|
||||
*/
|
||||
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
|
||||
if (status != LUA_OK && status != LUA_YIELD) { /* error? */
|
||||
lua_pushboolean(L, 0); /* first result (false) */
|
||||
lua_pushvalue(L, -2); /* error message */
|
||||
return 2; /* return false, msg */
|
||||
}
|
||||
else
|
||||
return lua_gettop(L) - (int)extra; /* return all results */
|
||||
}
|
||||
|
||||
|
||||
static int luaB_pcall (lua_State *L) {
|
||||
int status;
|
||||
luaL_checkany(L, 1);
|
||||
status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0);
|
||||
lua_pushboolean(L, (status == 0));
|
||||
lua_insert(L, 1);
|
||||
return lua_gettop(L); /* return status + all results */
|
||||
lua_pushboolean(L, 1); /* first result if no errors */
|
||||
lua_insert(L, 1); /* put it in place */
|
||||
status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall);
|
||||
return finishpcall(L, status, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Do a protected call with error handling. After 'lua_rotate', the
|
||||
** stack will have <f, err, true, f, [args...]>; so, the function passes
|
||||
** 2 to 'finishpcall' to skip the 2 first values when returning results.
|
||||
*/
|
||||
static int luaB_xpcall (lua_State *L) {
|
||||
int status;
|
||||
luaL_checkany(L, 2);
|
||||
lua_settop(L, 2);
|
||||
lua_insert(L, 1); /* put error function under function to be called */
|
||||
status = lua_pcall(L, 0, LUA_MULTRET, 1);
|
||||
lua_pushboolean(L, (status == 0));
|
||||
lua_replace(L, 1);
|
||||
return lua_gettop(L); /* return status + all results */
|
||||
int n = lua_gettop(L);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */
|
||||
lua_pushboolean(L, 1); /* first result */
|
||||
lua_pushvalue(L, 1); /* function */
|
||||
lua_rotate(L, 3, 2); /* move them below function's arguments */
|
||||
status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall);
|
||||
return finishpcall(L, status, 2);
|
||||
}
|
||||
|
||||
|
||||
static int luaB_tostring (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */
|
||||
return 1; /* use its value */
|
||||
switch (lua_type(L, 1)) {
|
||||
case LUA_TNUMBER:
|
||||
lua_pushstring(L, lua_tostring(L, 1));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
lua_pushvalue(L, 1);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false"));
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
lua_pushliteral(L, "nil");
|
||||
break;
|
||||
default:
|
||||
lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1));
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_newproxy (lua_State *L) {
|
||||
lua_settop(L, 1);
|
||||
lua_newuserdata(L, 0); /* create proxy */
|
||||
if (lua_toboolean(L, 1) == 0)
|
||||
return 1; /* no metatable */
|
||||
else if (lua_isboolean(L, 1)) {
|
||||
lua_newtable(L); /* create a new metatable `m' ... */
|
||||
lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */
|
||||
lua_pushboolean(L, 1);
|
||||
lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */
|
||||
}
|
||||
else {
|
||||
int validproxy = 0; /* to check if weaktable[metatable(u)] == true */
|
||||
if (lua_getmetatable(L, 1)) {
|
||||
lua_rawget(L, lua_upvalueindex(1));
|
||||
validproxy = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1); /* remove value */
|
||||
}
|
||||
luaL_argcheck(L, validproxy, 1, "boolean or proxy expected");
|
||||
lua_getmetatable(L, 1); /* metatable is valid; get it */
|
||||
}
|
||||
lua_setmetatable(L, 2);
|
||||
luaL_tolstring(L, 1, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -434,200 +486,42 @@ static const luaL_Reg base_funcs[] = {
|
||||
{"collectgarbage", luaB_collectgarbage},
|
||||
{"dofile", luaB_dofile},
|
||||
{"error", luaB_error},
|
||||
{"gcinfo", luaB_gcinfo},
|
||||
{"getfenv", luaB_getfenv},
|
||||
{"getmetatable", luaB_getmetatable},
|
||||
{"ipairs", luaB_ipairs},
|
||||
{"loadfile", luaB_loadfile},
|
||||
{"load", luaB_load},
|
||||
{"loadstring", luaB_loadstring},
|
||||
{"next", luaB_next},
|
||||
{"pairs", luaB_pairs},
|
||||
{"pcall", luaB_pcall},
|
||||
{"print", luaB_print},
|
||||
{"warn", luaB_warn},
|
||||
{"rawequal", luaB_rawequal},
|
||||
{"rawlen", luaB_rawlen},
|
||||
{"rawget", luaB_rawget},
|
||||
{"rawset", luaB_rawset},
|
||||
{"select", luaB_select},
|
||||
{"setfenv", luaB_setfenv},
|
||||
{"setmetatable", luaB_setmetatable},
|
||||
{"tonumber", luaB_tonumber},
|
||||
{"tostring", luaB_tostring},
|
||||
{"type", luaB_type},
|
||||
{"unpack", luaB_unpack},
|
||||
{"xpcall", luaB_xpcall},
|
||||
/* placeholders */
|
||||
{LUA_GNAME, NULL},
|
||||
{"_VERSION", NULL},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Coroutine library
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static int auxresume (lua_State *L, lua_State *co, int narg) {
|
||||
int status;
|
||||
if (!lua_checkstack(co, narg))
|
||||
luaL_error(L, "too many arguments to resume");
|
||||
if (lua_status(co) == 0 && lua_gettop(co) == 0) {
|
||||
lua_pushliteral(L, "cannot resume dead coroutine");
|
||||
return -1; /* error flag */
|
||||
}
|
||||
lua_xmove(L, co, narg);
|
||||
status = lua_resume(co, narg);
|
||||
if (status == 0 || status == LUA_YIELD) {
|
||||
int nres = lua_gettop(co);
|
||||
if (!lua_checkstack(L, nres))
|
||||
luaL_error(L, "too many results to resume");
|
||||
lua_xmove(co, L, nres); /* move yielded values */
|
||||
return nres;
|
||||
}
|
||||
else {
|
||||
lua_xmove(co, L, 1); /* move error message */
|
||||
return -1; /* error flag */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_coresume (lua_State *L) {
|
||||
lua_State *co = lua_tothread(L, 1);
|
||||
int r;
|
||||
luaL_argcheck(L, co, 1, "coroutine expected");
|
||||
r = auxresume(L, co, lua_gettop(L) - 1);
|
||||
if (r < 0) {
|
||||
lua_pushboolean(L, 0);
|
||||
lua_insert(L, -2);
|
||||
return 2; /* return false + error message */
|
||||
}
|
||||
else {
|
||||
lua_pushboolean(L, 1);
|
||||
lua_insert(L, -(r + 1));
|
||||
return r + 1; /* return true + `resume' returns */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_auxwrap (lua_State *L) {
|
||||
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
||||
int r = auxresume(L, co, lua_gettop(L));
|
||||
if (r < 0) {
|
||||
if (lua_isstring(L, -1)) { /* error object is a string? */
|
||||
luaL_where(L, 1); /* add extra info */
|
||||
lua_insert(L, -2);
|
||||
lua_concat(L, 2);
|
||||
}
|
||||
lua_error(L); /* propagate error */
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_cocreate (lua_State *L) {
|
||||
lua_State *NL = lua_newthread(L);
|
||||
luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
|
||||
"Lua function expected");
|
||||
lua_pushvalue(L, 1); /* move function to top */
|
||||
lua_xmove(L, NL, 1); /* move function from L to NL */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_cowrap (lua_State *L) {
|
||||
luaB_cocreate(L);
|
||||
lua_pushcclosure(L, luaB_auxwrap, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_yield (lua_State *L) {
|
||||
return lua_yield(L, lua_gettop(L));
|
||||
}
|
||||
|
||||
|
||||
static int luaB_costatus (lua_State *L) {
|
||||
lua_State *co = lua_tothread(L, 1);
|
||||
luaL_argcheck(L, co, 1, "coroutine expected");
|
||||
if (L == co) lua_pushliteral(L, "running");
|
||||
else {
|
||||
switch (lua_status(co)) {
|
||||
case LUA_YIELD:
|
||||
lua_pushliteral(L, "suspended");
|
||||
break;
|
||||
case 0: {
|
||||
lua_Debug ar;
|
||||
if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */
|
||||
lua_pushliteral(L, "normal"); /* it is running */
|
||||
else if (lua_gettop(co) == 0)
|
||||
lua_pushliteral(L, "dead");
|
||||
else
|
||||
lua_pushliteral(L, "suspended"); /* initial state */
|
||||
break;
|
||||
}
|
||||
default: /* some error occured */
|
||||
lua_pushliteral(L, "dead");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_corunning (lua_State *L) {
|
||||
if (lua_pushthread(L))
|
||||
return 0; /* main thread is not a coroutine */
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg co_funcs[] = {
|
||||
{"create", luaB_cocreate},
|
||||
{"resume", luaB_coresume},
|
||||
{"running", luaB_corunning},
|
||||
{"status", luaB_costatus},
|
||||
{"wrap", luaB_cowrap},
|
||||
{"yield", luaB_yield},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
static void auxopen (lua_State *L, const char *name,
|
||||
lua_CFunction f, lua_CFunction u) {
|
||||
lua_pushcfunction(L, u);
|
||||
lua_pushcclosure(L, f, 1);
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
|
||||
static void base_open (lua_State *L) {
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
luaL_register(L, NULL, base_funcs); /* open lib into global table */
|
||||
lua_pushliteral(L, LUA_VERSION);
|
||||
lua_setglobal(L, "_VERSION"); /* set global _VERSION */
|
||||
/* `ipairs' and `pairs' need auxiliary functions as upvalues */
|
||||
auxopen(L, "ipairs", luaB_ipairs, ipairsaux);
|
||||
auxopen(L, "pairs", luaB_pairs, luaB_next);
|
||||
/* `newproxy' needs a weaktable as upvalue */
|
||||
lua_newtable(L); /* new table `w' */
|
||||
lua_pushvalue(L, -1); /* `w' will be its own metatable */
|
||||
lua_setmetatable(L, -2);
|
||||
lua_pushliteral(L, "kv");
|
||||
lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */
|
||||
lua_pushcclosure(L, luaB_newproxy, 1);
|
||||
lua_setglobal(L, "newproxy"); /* set global `newproxy' */
|
||||
/* create register._LOADED to track loaded modules */
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "_LOADED");
|
||||
LUAMOD_API int luaopen_base (lua_State *L) {
|
||||
/* open lib into global table */
|
||||
lua_pushglobaltable(L);
|
||||
luaL_setfuncs(L, base_funcs, 0);
|
||||
/* set global _G */
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
lua_setglobal(L, "_G");
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API int luaopen_base (lua_State *L) {
|
||||
base_open(L);
|
||||
luaL_register(L, LUA_COLIBNAME, co_funcs);
|
||||
return 2;
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, LUA_GNAME);
|
||||
/* set global _VERSION */
|
||||
lua_pushliteral(L, LUA_VERSION);
|
||||
lua_setfield(L, -2, "_VERSION");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lcode.h,v 1.44 2005/05/20 15:53:42 roberto Exp $
|
||||
** $Id: lcode.h $
|
||||
** Code generator for Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -21,39 +21,59 @@
|
||||
|
||||
|
||||
/*
|
||||
** grep "ORDER OPR" if you change these enums
|
||||
** grep "ORDER OPR" if you change these enums (ORDER OP)
|
||||
*/
|
||||
typedef enum BinOpr {
|
||||
OPR_ADD, OPR_SUB, OPR_MULT, OPR_DIV, OPR_MOD, OPR_POW,
|
||||
/* arithmetic operators */
|
||||
OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW,
|
||||
OPR_DIV, OPR_IDIV,
|
||||
/* bitwise operators */
|
||||
OPR_BAND, OPR_BOR, OPR_BXOR,
|
||||
OPR_SHL, OPR_SHR,
|
||||
/* string operator */
|
||||
OPR_CONCAT,
|
||||
OPR_NE, OPR_EQ,
|
||||
OPR_LT, OPR_LE, OPR_GT, OPR_GE,
|
||||
/* comparison operators */
|
||||
OPR_EQ, OPR_LT, OPR_LE,
|
||||
OPR_NE, OPR_GT, OPR_GE,
|
||||
/* logical operators */
|
||||
OPR_AND, OPR_OR,
|
||||
OPR_NOBINOPR
|
||||
} BinOpr;
|
||||
|
||||
#define binopistest(op) ((op) >= OPR_NE)
|
||||
|
||||
typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
|
||||
/* true if operation is foldable (that is, it is arithmetic or bitwise) */
|
||||
#define foldbinop(op) ((op) <= OPR_SHR)
|
||||
|
||||
|
||||
#define getcode(fs,e) ((fs)->f->code[(e)->info])
|
||||
#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0)
|
||||
|
||||
|
||||
typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
|
||||
|
||||
|
||||
/* get (pointer to) instruction of given 'expdesc' */
|
||||
#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info])
|
||||
|
||||
#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx)
|
||||
|
||||
#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
|
||||
|
||||
LUAI_FUNC int luaK_code (FuncState *fs, Instruction i, int line);
|
||||
#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
|
||||
|
||||
LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
|
||||
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
|
||||
LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C);
|
||||
LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
|
||||
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
|
||||
int B, int C, int k);
|
||||
LUAI_FUNC int luaK_isKint (expdesc *e);
|
||||
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
|
||||
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
|
||||
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
|
||||
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
|
||||
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
|
||||
LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);
|
||||
LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r);
|
||||
LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
|
||||
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
|
||||
@@ -70,10 +90,15 @@ LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
|
||||
LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
|
||||
LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
|
||||
LUAI_FUNC int luaK_getlabel (FuncState *fs);
|
||||
LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v);
|
||||
LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
|
||||
LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
|
||||
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2);
|
||||
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
|
||||
expdesc *v2, int line);
|
||||
LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
|
||||
int ra, int rb, int rc);
|
||||
LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
|
||||
LUAI_FUNC void luaK_finish (FuncState *fs);
|
||||
LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
** $Id: lcorolib.c $
|
||||
** Coroutine Library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lcorolib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
static lua_State *getco (lua_State *L) {
|
||||
lua_State *co = lua_tothread(L, 1);
|
||||
luaL_argexpected(L, co, 1, "thread");
|
||||
return co;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Resumes a coroutine. Returns the number of results for non-error
|
||||
** cases or -1 for errors.
|
||||
*/
|
||||
static int auxresume (lua_State *L, lua_State *co, int narg) {
|
||||
int status, nres;
|
||||
if (!lua_checkstack(co, narg)) {
|
||||
lua_pushliteral(L, "too many arguments to resume");
|
||||
return -1; /* error flag */
|
||||
}
|
||||
lua_xmove(L, co, narg);
|
||||
status = lua_resume(co, L, narg, &nres);
|
||||
if (status == LUA_OK || status == LUA_YIELD) {
|
||||
if (!lua_checkstack(L, nres + 1)) {
|
||||
lua_pop(co, nres); /* remove results anyway */
|
||||
lua_pushliteral(L, "too many results to resume");
|
||||
return -1; /* error flag */
|
||||
}
|
||||
lua_xmove(co, L, nres); /* move yielded values */
|
||||
return nres;
|
||||
}
|
||||
else {
|
||||
lua_xmove(co, L, 1); /* move error message */
|
||||
return -1; /* error flag */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_coresume (lua_State *L) {
|
||||
lua_State *co = getco(L);
|
||||
int r;
|
||||
r = auxresume(L, co, lua_gettop(L) - 1);
|
||||
if (r < 0) {
|
||||
lua_pushboolean(L, 0);
|
||||
lua_insert(L, -2);
|
||||
return 2; /* return false + error message */
|
||||
}
|
||||
else {
|
||||
lua_pushboolean(L, 1);
|
||||
lua_insert(L, -(r + 1));
|
||||
return r + 1; /* return true + 'resume' returns */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_auxwrap (lua_State *L) {
|
||||
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
||||
int r = auxresume(L, co, lua_gettop(L));
|
||||
if (r < 0) {
|
||||
int stat = lua_status(co);
|
||||
if (stat != LUA_OK && stat != LUA_YIELD)
|
||||
lua_resetthread(co); /* close variables in case of errors */
|
||||
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
|
||||
luaL_where(L, 1); /* add extra info, if available */
|
||||
lua_insert(L, -2);
|
||||
lua_concat(L, 2);
|
||||
}
|
||||
return lua_error(L); /* propagate error */
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_cocreate (lua_State *L) {
|
||||
lua_State *NL;
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
NL = lua_newthread(L);
|
||||
lua_pushvalue(L, 1); /* move function to top */
|
||||
lua_xmove(L, NL, 1); /* move function from L to NL */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_cowrap (lua_State *L) {
|
||||
luaB_cocreate(L);
|
||||
lua_pushcclosure(L, luaB_auxwrap, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_yield (lua_State *L) {
|
||||
return lua_yield(L, lua_gettop(L));
|
||||
}
|
||||
|
||||
|
||||
#define COS_RUN 0
|
||||
#define COS_DEAD 1
|
||||
#define COS_YIELD 2
|
||||
#define COS_NORM 3
|
||||
|
||||
|
||||
static const char *const statname[] =
|
||||
{"running", "dead", "suspended", "normal"};
|
||||
|
||||
|
||||
static int auxstatus (lua_State *L, lua_State *co) {
|
||||
if (L == co) return COS_RUN;
|
||||
else {
|
||||
switch (lua_status(co)) {
|
||||
case LUA_YIELD:
|
||||
return COS_YIELD;
|
||||
case LUA_OK: {
|
||||
lua_Debug ar;
|
||||
if (lua_getstack(co, 0, &ar)) /* does it have frames? */
|
||||
return COS_NORM; /* it is running */
|
||||
else if (lua_gettop(co) == 0)
|
||||
return COS_DEAD;
|
||||
else
|
||||
return COS_YIELD; /* initial state */
|
||||
}
|
||||
default: /* some error occurred */
|
||||
return COS_DEAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_costatus (lua_State *L) {
|
||||
lua_State *co = getco(L);
|
||||
lua_pushstring(L, statname[auxstatus(L, co)]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_yieldable (lua_State *L) {
|
||||
lua_State *co = lua_isnone(L, 1) ? L : getco(L);
|
||||
lua_pushboolean(L, lua_isyieldable(co));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_corunning (lua_State *L) {
|
||||
int ismain = lua_pushthread(L);
|
||||
lua_pushboolean(L, ismain);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_close (lua_State *L) {
|
||||
lua_State *co = getco(L);
|
||||
int status = auxstatus(L, co);
|
||||
switch (status) {
|
||||
case COS_DEAD: case COS_YIELD: {
|
||||
status = lua_resetthread(co);
|
||||
if (status == LUA_OK) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushboolean(L, 0);
|
||||
lua_xmove(co, L, 1); /* copy error message */
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
default: /* normal or running coroutine */
|
||||
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg co_funcs[] = {
|
||||
{"create", luaB_cocreate},
|
||||
{"resume", luaB_coresume},
|
||||
{"running", luaB_corunning},
|
||||
{"status", luaB_costatus},
|
||||
{"wrap", luaB_cowrap},
|
||||
{"yield", luaB_yield},
|
||||
{"isyieldable", luaB_yieldable},
|
||||
{"close", luaB_close},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
|
||||
LUAMOD_API int luaopen_coroutine (lua_State *L) {
|
||||
luaL_newlib(L, co_funcs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
** $Id: lctype.c $
|
||||
** 'ctype' functions for Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lctype_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include "lctype.h"
|
||||
|
||||
#if !LUA_USE_CTYPE /* { */
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
|
||||
0x00, /* EOZ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
|
||||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
|
||||
0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
|
||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05,
|
||||
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
|
||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
#endif /* } */
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
** $Id: lctype.h $
|
||||
** 'ctype' functions for Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef lctype_h
|
||||
#define lctype_h
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/*
|
||||
** WARNING: the functions defined here do not necessarily correspond
|
||||
** to the similar functions in the standard C ctype.h. They are
|
||||
** optimized for the specific needs of Lua
|
||||
*/
|
||||
|
||||
#if !defined(LUA_USE_CTYPE)
|
||||
|
||||
#if 'A' == 65 && '0' == 48
|
||||
/* ASCII case: can use its own tables; faster and fixed */
|
||||
#define LUA_USE_CTYPE 0
|
||||
#else
|
||||
/* must use standard C ctype */
|
||||
#define LUA_USE_CTYPE 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !LUA_USE_CTYPE /* { */
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
#define ALPHABIT 0
|
||||
#define DIGITBIT 1
|
||||
#define PRINTBIT 2
|
||||
#define SPACEBIT 3
|
||||
#define XDIGITBIT 4
|
||||
|
||||
|
||||
#define MASK(B) (1 << (B))
|
||||
|
||||
|
||||
/*
|
||||
** add 1 to char to allow index -1 (EOZ)
|
||||
*/
|
||||
#define testprop(c,p) (luai_ctype_[(c)+1] & (p))
|
||||
|
||||
/*
|
||||
** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_'
|
||||
*/
|
||||
#define lislalpha(c) testprop(c, MASK(ALPHABIT))
|
||||
#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT)))
|
||||
#define lisdigit(c) testprop(c, MASK(DIGITBIT))
|
||||
#define lisspace(c) testprop(c, MASK(SPACEBIT))
|
||||
#define lisprint(c) testprop(c, MASK(PRINTBIT))
|
||||
#define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
|
||||
|
||||
/*
|
||||
** this 'ltolower' only works for alphabetic characters
|
||||
*/
|
||||
#define ltolower(c) ((c) | ('A' ^ 'a'))
|
||||
|
||||
|
||||
/* two more entries for 0 and -1 (EOZ) */
|
||||
LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)
|
||||
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/*
|
||||
** use standard C ctypes
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#define lislalpha(c) (isalpha(c) || (c) == '_')
|
||||
#define lislalnum(c) (isalnum(c) || (c) == '_')
|
||||
#define lisdigit(c) (isdigit(c))
|
||||
#define lisspace(c) (isspace(c))
|
||||
#define lisprint(c) (isprint(c))
|
||||
#define lisxdigit(c) (isxdigit(c))
|
||||
|
||||
#define ltolower(c) (tolower(c))
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,23 +1,48 @@
|
||||
/*
|
||||
** $Id: ldblib.c,v 1.100 2005/08/15 14:12:32 roberto Exp roberto $
|
||||
** $Id: ldblib.c $
|
||||
** Interface from Lua to its debug API
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define ldblib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ldblib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
/*
|
||||
** The hook table at registry[HOOKKEY] maps threads to their current
|
||||
** hook function.
|
||||
*/
|
||||
static const char *const HOOKKEY = "_HOOKKEY";
|
||||
|
||||
|
||||
/*
|
||||
** If L1 != L, L1 can be in any state, and therefore there are no
|
||||
** guarantees about its stack space; any push in L1 must be
|
||||
** checked.
|
||||
*/
|
||||
static void checkstack (lua_State *L, lua_State *L1, int n) {
|
||||
if (L != L1 && !lua_checkstack(L1, n))
|
||||
luaL_error(L, "stack overflow");
|
||||
}
|
||||
|
||||
|
||||
static int db_getregistry (lua_State *L) {
|
||||
lua_pushvalue(L, LUA_REGISTRYINDEX);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_getmetatable (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
@@ -30,42 +55,42 @@ static int db_getmetatable (lua_State *L) {
|
||||
|
||||
static int db_setmetatable (lua_State *L) {
|
||||
int t = lua_type(L, 2);
|
||||
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
|
||||
"nil or table expected");
|
||||
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
||||
lua_settop(L, 2);
|
||||
lua_pushboolean(L, lua_setmetatable(L, 1));
|
||||
lua_setmetatable(L, 1);
|
||||
return 1; /* return 1st argument */
|
||||
}
|
||||
|
||||
|
||||
static int db_getuservalue (lua_State *L) {
|
||||
int n = (int)luaL_optinteger(L, 2, 1);
|
||||
if (lua_type(L, 1) != LUA_TUSERDATA)
|
||||
luaL_pushfail(L);
|
||||
else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_getfenv (lua_State *L) {
|
||||
lua_getfenv(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_setfenv (lua_State *L) {
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
static int db_setuservalue (lua_State *L) {
|
||||
int n = (int)luaL_optinteger(L, 3, 1);
|
||||
luaL_checktype(L, 1, LUA_TUSERDATA);
|
||||
luaL_checkany(L, 2);
|
||||
lua_settop(L, 2);
|
||||
if (lua_setfenv(L, 1) == 0)
|
||||
luaL_error(L, LUA_QL("setfenv")
|
||||
" cannot change environment of given object");
|
||||
if (!lua_setiuservalue(L, 1, n))
|
||||
luaL_pushfail(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void settabss (lua_State *L, const char *i, const char *v) {
|
||||
lua_pushstring(L, v);
|
||||
lua_setfield(L, -2, i);
|
||||
}
|
||||
|
||||
|
||||
static void settabsi (lua_State *L, const char *i, int v) {
|
||||
lua_pushinteger(L, v);
|
||||
lua_setfield(L, -2, i);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Auxiliary function used by several library functions: check for
|
||||
** an optional thread as function's first argument and set 'arg' with
|
||||
** 1 if this argument is present (so that functions can skip it to
|
||||
** access their other arguments)
|
||||
*/
|
||||
static lua_State *getthread (lua_State *L, int *arg) {
|
||||
if (lua_isthread(L, 1)) {
|
||||
*arg = 1;
|
||||
@@ -73,46 +98,77 @@ static lua_State *getthread (lua_State *L, int *arg) {
|
||||
}
|
||||
else {
|
||||
*arg = 0;
|
||||
return L;
|
||||
return L; /* function will operate over current thread */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Variations of 'lua_settable', used by 'db_getinfo' to put results
|
||||
** from 'lua_getinfo' into result table. Key is always a string;
|
||||
** value can be a string, an int, or a boolean.
|
||||
*/
|
||||
static void settabss (lua_State *L, const char *k, const char *v) {
|
||||
lua_pushstring(L, v);
|
||||
lua_setfield(L, -2, k);
|
||||
}
|
||||
|
||||
static void settabsi (lua_State *L, const char *k, int v) {
|
||||
lua_pushinteger(L, v);
|
||||
lua_setfield(L, -2, k);
|
||||
}
|
||||
|
||||
static void settabsb (lua_State *L, const char *k, int v) {
|
||||
lua_pushboolean(L, v);
|
||||
lua_setfield(L, -2, k);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** In function 'db_getinfo', the call to 'lua_getinfo' may push
|
||||
** results on the stack; later it creates the result table to put
|
||||
** these objects. Function 'treatstackoption' puts the result from
|
||||
** 'lua_getinfo' on top of the result table so that it can call
|
||||
** 'lua_setfield'.
|
||||
*/
|
||||
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
|
||||
if (L == L1) {
|
||||
lua_pushvalue(L, -2);
|
||||
lua_remove(L, -3);
|
||||
}
|
||||
if (L == L1)
|
||||
lua_rotate(L, -2, 1); /* exchange object and table */
|
||||
else
|
||||
lua_xmove(L1, L, 1);
|
||||
lua_setfield(L, -2, fname);
|
||||
lua_xmove(L1, L, 1); /* move object to the "main" stack */
|
||||
lua_setfield(L, -2, fname); /* put object into table */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls 'lua_getinfo' and collects all results in a new table.
|
||||
** L1 needs stack space for an optional input (function) plus
|
||||
** two optional outputs (function and line table) from function
|
||||
** 'lua_getinfo'.
|
||||
*/
|
||||
static int db_getinfo (lua_State *L) {
|
||||
lua_Debug ar;
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
const char *options = luaL_optstring(L, arg+2, "flnSu");
|
||||
if (lua_isnumber(L, arg+1)) {
|
||||
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
|
||||
lua_pushnil(L); /* level out of range */
|
||||
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
|
||||
checkstack(L, L1, 3);
|
||||
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
|
||||
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
|
||||
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
|
||||
lua_xmove(L, L1, 1);
|
||||
}
|
||||
else { /* stack level */
|
||||
if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {
|
||||
luaL_pushfail(L); /* level out of range */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (lua_isfunction(L, arg+1)) {
|
||||
lua_pushfstring(L, ">%s", options);
|
||||
options = lua_tostring(L, -1);
|
||||
lua_pushvalue(L, arg+1);
|
||||
lua_xmove(L, L1, 1);
|
||||
}
|
||||
else
|
||||
return luaL_argerror(L, arg+1, "function or level expected");
|
||||
if (!lua_getinfo(L1, options, &ar))
|
||||
return luaL_argerror(L, arg+2, "invalid option");
|
||||
lua_newtable(L);
|
||||
lua_newtable(L); /* table to collect results */
|
||||
if (strchr(options, 'S')) {
|
||||
settabss(L, "source", ar.source);
|
||||
lua_pushlstring(L, ar.source, ar.srclen);
|
||||
lua_setfield(L, -2, "source");
|
||||
settabss(L, "short_src", ar.short_src);
|
||||
settabsi(L, "linedefined", ar.linedefined);
|
||||
settabsi(L, "lastlinedefined", ar.lastlinedefined);
|
||||
@@ -120,64 +176,92 @@ static int db_getinfo (lua_State *L) {
|
||||
}
|
||||
if (strchr(options, 'l'))
|
||||
settabsi(L, "currentline", ar.currentline);
|
||||
if (strchr(options, 'u'))
|
||||
if (strchr(options, 'u')) {
|
||||
settabsi(L, "nups", ar.nups);
|
||||
settabsi(L, "nparams", ar.nparams);
|
||||
settabsb(L, "isvararg", ar.isvararg);
|
||||
}
|
||||
if (strchr(options, 'n')) {
|
||||
settabss(L, "name", ar.name);
|
||||
settabss(L, "namewhat", ar.namewhat);
|
||||
}
|
||||
if (strchr(options, 'r')) {
|
||||
settabsi(L, "ftransfer", ar.ftransfer);
|
||||
settabsi(L, "ntransfer", ar.ntransfer);
|
||||
}
|
||||
if (strchr(options, 't'))
|
||||
settabsb(L, "istailcall", ar.istailcall);
|
||||
if (strchr(options, 'L'))
|
||||
treatstackoption(L, L1, "activelines");
|
||||
if (strchr(options, 'f'))
|
||||
treatstackoption(L, L1, "func");
|
||||
return 1; /* return table */
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int db_getlocal (lua_State *L) {
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
const char *name;
|
||||
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2));
|
||||
if (name) {
|
||||
lua_xmove(L1, L, 1);
|
||||
lua_pushstring(L, name);
|
||||
lua_pushvalue(L, -2);
|
||||
return 2;
|
||||
int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */
|
||||
if (lua_isfunction(L, arg + 1)) { /* function argument? */
|
||||
lua_pushvalue(L, arg + 1); /* push function */
|
||||
lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */
|
||||
return 1; /* return only name (there is no value) */
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
else { /* stack-level argument */
|
||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||
if (!lua_getstack(L1, level, &ar)) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
checkstack(L, L1, 1);
|
||||
name = lua_getlocal(L1, &ar, nvar);
|
||||
if (name) {
|
||||
lua_xmove(L1, L, 1); /* move local value */
|
||||
lua_pushstring(L, name); /* push name */
|
||||
lua_rotate(L, -2, 1); /* re-order */
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
luaL_pushfail(L); /* no name (nor value) */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int db_setlocal (lua_State *L) {
|
||||
int arg;
|
||||
const char *name;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
|
||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||
int nvar = (int)luaL_checkinteger(L, arg + 2);
|
||||
if (!lua_getstack(L1, level, &ar)) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
luaL_checkany(L, arg+3);
|
||||
lua_settop(L, arg+3);
|
||||
checkstack(L, L1, 1);
|
||||
lua_xmove(L, L1, 1);
|
||||
lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
|
||||
name = lua_setlocal(L1, &ar, nvar);
|
||||
if (name == NULL)
|
||||
lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */
|
||||
lua_pushstring(L, name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** get (if 'get' is true) or set an upvalue from a closure
|
||||
*/
|
||||
static int auxupvalue (lua_State *L, int get) {
|
||||
const char *name;
|
||||
int n = luaL_checkint(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */
|
||||
int n = (int)luaL_checkinteger(L, 2); /* upvalue index */
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */
|
||||
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
|
||||
if (name == NULL) return 0;
|
||||
lua_pushstring(L, name);
|
||||
lua_insert(L, -(get+1));
|
||||
lua_insert(L, -(get+1)); /* no-op if get is false */
|
||||
return get + 1;
|
||||
}
|
||||
|
||||
@@ -193,28 +277,59 @@ static int db_setupvalue (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char KEY_HOOK = 'h';
|
||||
/*
|
||||
** Check whether a given upvalue from a given closure exists and
|
||||
** returns its index
|
||||
*/
|
||||
static int checkupval (lua_State *L, int argf, int argnup) {
|
||||
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
|
||||
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
|
||||
luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup,
|
||||
"invalid upvalue index");
|
||||
return nup;
|
||||
}
|
||||
|
||||
|
||||
static int db_upvalueid (lua_State *L) {
|
||||
int n = checkupval(L, 1, 2);
|
||||
lua_pushlightuserdata(L, lua_upvalueid(L, 1, n));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_upvaluejoin (lua_State *L) {
|
||||
int n1 = checkupval(L, 1, 2);
|
||||
int n2 = checkupval(L, 3, 4);
|
||||
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
|
||||
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
|
||||
lua_upvaluejoin(L, 1, n1, 3, n2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Call hook function registered at hook table for the current
|
||||
** thread (if there is one)
|
||||
*/
|
||||
static void hookf (lua_State *L, lua_Debug *ar) {
|
||||
static const char *const hooknames[] =
|
||||
{"call", "return", "line", "count", "tail return"};
|
||||
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
lua_pushlightuserdata(L, L);
|
||||
lua_rawget(L, -2);
|
||||
if (lua_isfunction(L, -1)) {
|
||||
lua_pushstring(L, hooknames[(int)ar->event]);
|
||||
{"call", "return", "line", "count", "tail call"};
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
|
||||
lua_pushthread(L);
|
||||
if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
|
||||
lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
|
||||
if (ar->currentline >= 0)
|
||||
lua_pushinteger(L, ar->currentline);
|
||||
lua_pushinteger(L, ar->currentline); /* push current line */
|
||||
else lua_pushnil(L);
|
||||
lua_assert(lua_getinfo(L, "lS", ar));
|
||||
lua_call(L, 2, 0);
|
||||
lua_call(L, 2, 0); /* call hook function */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a string mask (for 'sethook') into a bit mask
|
||||
*/
|
||||
static int makemask (const char *smask, int count) {
|
||||
int mask = 0;
|
||||
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
|
||||
@@ -225,6 +340,9 @@ static int makemask (const char *smask, int count) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a bit mask (for 'gethook') into a string mask
|
||||
*/
|
||||
static char *unmakemask (int mask, char *smask) {
|
||||
int i = 0;
|
||||
if (mask & LUA_MASKCALL) smask[i++] = 'c';
|
||||
@@ -235,38 +353,32 @@ static char *unmakemask (int mask, char *smask) {
|
||||
}
|
||||
|
||||
|
||||
static void gethooktable (lua_State *L) {
|
||||
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
lua_newtable(L);
|
||||
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int db_sethook (lua_State *L) {
|
||||
int arg;
|
||||
int arg, mask, count;
|
||||
lua_Hook func;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
if (lua_isnoneornil(L, arg+1)) {
|
||||
if (lua_isnoneornil(L, arg+1)) { /* no hook? */
|
||||
lua_settop(L, arg+1);
|
||||
lua_sethook(L1, NULL, 0, 0); /* turn off hooks */
|
||||
func = NULL; mask = 0; count = 0; /* turn off hooks */
|
||||
}
|
||||
else {
|
||||
const char *smask = luaL_checkstring(L, arg+2);
|
||||
int count = luaL_optint(L, arg+3, 0);
|
||||
luaL_checktype(L, arg+1, LUA_TFUNCTION);
|
||||
lua_sethook(L1, hookf, makemask(smask, count), count);
|
||||
count = (int)luaL_optinteger(L, arg + 3, 0);
|
||||
func = hookf; mask = makemask(smask, count);
|
||||
}
|
||||
gethooktable(L1);
|
||||
lua_pushlightuserdata(L1, L1);
|
||||
lua_pushvalue(L, arg+1);
|
||||
lua_xmove(L, L1, 1);
|
||||
lua_rawset(L1, -3); /* set new hook */
|
||||
lua_pop(L1, 1); /* remove hook table */
|
||||
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
|
||||
/* table just created; initialize it */
|
||||
lua_pushstring(L, "k");
|
||||
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
|
||||
}
|
||||
checkstack(L, L1, 1);
|
||||
lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
|
||||
lua_pushvalue(L, arg + 1); /* value (hook function) */
|
||||
lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */
|
||||
lua_sethook(L1, func, mask, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -277,17 +389,21 @@ static int db_gethook (lua_State *L) {
|
||||
char buff[5];
|
||||
int mask = lua_gethookmask(L1);
|
||||
lua_Hook hook = lua_gethook(L1);
|
||||
if (hook != NULL && hook != hookf) /* external hook? */
|
||||
lua_pushliteral(L, "external hook");
|
||||
else {
|
||||
gethooktable(L1);
|
||||
lua_pushlightuserdata(L1, L1);
|
||||
lua_rawget(L1, -2); /* get hook */
|
||||
lua_remove(L1, -2); /* remove hook table */
|
||||
lua_xmove(L1, L, 1);
|
||||
if (hook == NULL) { /* no hook? */
|
||||
luaL_pushfail(L);
|
||||
return 1;
|
||||
}
|
||||
lua_pushstring(L, unmakemask(mask, buff));
|
||||
lua_pushinteger(L, lua_gethookcount(L1));
|
||||
else if (hook != hookf) /* external hook? */
|
||||
lua_pushliteral(L, "external hook");
|
||||
else { /* hook table must exist */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
|
||||
checkstack(L, L1, 1);
|
||||
lua_pushthread(L1); lua_xmove(L1, L, 1);
|
||||
lua_rawget(L, -2); /* 1st result = hooktable[L1] */
|
||||
lua_remove(L, -2); /* remove hook table */
|
||||
}
|
||||
lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */
|
||||
lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */
|
||||
return 3;
|
||||
}
|
||||
|
||||
@@ -295,96 +411,67 @@ static int db_gethook (lua_State *L) {
|
||||
static int db_debug (lua_State *L) {
|
||||
for (;;) {
|
||||
char buffer[250];
|
||||
fputs("lua_debug> ", stderr);
|
||||
lua_writestringerror("%s", "lua_debug> ");
|
||||
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
|
||||
strcmp(buffer, "cont\n") == 0)
|
||||
return 0;
|
||||
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
||||
lua_pcall(L, 0, 0, 0)) {
|
||||
fputs(lua_tostring(L, -1), stderr);
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
lua_pcall(L, 0, 0, 0))
|
||||
lua_writestringerror("%s\n", lua_tostring(L, -1));
|
||||
lua_settop(L, 0); /* remove eventual returns */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define LEVELS1 12 /* size of the first part of the stack */
|
||||
#define LEVELS2 10 /* size of the second part of the stack */
|
||||
|
||||
static int db_errorfb (lua_State *L) {
|
||||
int level;
|
||||
int firstpart = 1; /* still before eventual `...' */
|
||||
static int db_traceback (lua_State *L) {
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
if (lua_isnumber(L, arg+2)) {
|
||||
level = lua_tointeger(L, arg+2);
|
||||
lua_pop(L, 1);
|
||||
const char *msg = lua_tostring(L, arg + 1);
|
||||
if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */
|
||||
lua_pushvalue(L, arg + 1); /* return it untouched */
|
||||
else {
|
||||
int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0);
|
||||
luaL_traceback(L, L1, msg, level);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_setcstacklimit (lua_State *L) {
|
||||
int limit = (int)luaL_checkinteger(L, 1);
|
||||
int res = lua_setcstacklimit(L, limit);
|
||||
if (res == 0)
|
||||
lua_pushboolean(L, 0);
|
||||
else
|
||||
level = (L == L1) ? 1 : 0; /* level 0 may be this own function */
|
||||
if (lua_gettop(L) == arg)
|
||||
lua_pushliteral(L, "");
|
||||
else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */
|
||||
else lua_pushliteral(L, "\n");
|
||||
lua_pushliteral(L, "stack traceback:");
|
||||
while (lua_getstack(L1, level++, &ar)) {
|
||||
if (level > LEVELS1 && firstpart) {
|
||||
/* no more than `LEVELS2' more levels? */
|
||||
if (!lua_getstack(L1, level+LEVELS2, &ar))
|
||||
level--; /* keep going */
|
||||
else {
|
||||
lua_pushliteral(L, "\n\t..."); /* too many levels */
|
||||
while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */
|
||||
level++;
|
||||
}
|
||||
firstpart = 0;
|
||||
continue;
|
||||
}
|
||||
lua_pushliteral(L, "\n\t");
|
||||
lua_getinfo(L1, "Snl", &ar);
|
||||
lua_pushfstring(L, "%s:", ar.short_src);
|
||||
if (ar.currentline > 0)
|
||||
lua_pushfstring(L, "%d:", ar.currentline);
|
||||
if (*ar.namewhat != '\0') /* is there a name? */
|
||||
lua_pushfstring(L, " in function " LUA_QS, ar.name);
|
||||
else {
|
||||
if (*ar.what == 'm') /* main? */
|
||||
lua_pushfstring(L, " in main chunk");
|
||||
else if (*ar.what == 'C' || *ar.what == 't')
|
||||
lua_pushliteral(L, " ?"); /* C function or tail call */
|
||||
else
|
||||
lua_pushfstring(L, " in function <%s:%d>",
|
||||
ar.short_src, ar.linedefined);
|
||||
}
|
||||
lua_concat(L, lua_gettop(L) - arg);
|
||||
}
|
||||
lua_concat(L, lua_gettop(L) - arg);
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg dblib[] = {
|
||||
{"debug", db_debug},
|
||||
{"getfenv", db_getfenv},
|
||||
{"getuservalue", db_getuservalue},
|
||||
{"gethook", db_gethook},
|
||||
{"getinfo", db_getinfo},
|
||||
{"getlocal", db_getlocal},
|
||||
{"getregistry", db_getregistry},
|
||||
{"getmetatable", db_getmetatable},
|
||||
{"getupvalue", db_getupvalue},
|
||||
{"setfenv", db_setfenv},
|
||||
{"upvaluejoin", db_upvaluejoin},
|
||||
{"upvalueid", db_upvalueid},
|
||||
{"setuservalue", db_setuservalue},
|
||||
{"sethook", db_sethook},
|
||||
{"setlocal", db_setlocal},
|
||||
{"setmetatable", db_setmetatable},
|
||||
{"setupvalue", db_setupvalue},
|
||||
{"traceback", db_errorfb},
|
||||
{"traceback", db_traceback},
|
||||
{"setcstacklimit", db_setcstacklimit},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUALIB_API int luaopen_debug (lua_State *L) {
|
||||
luaL_register(L, LUA_DBLIBNAME, dblib);
|
||||
LUAMOD_API int luaopen_debug (lua_State *L) {
|
||||
luaL_newlib(L, dblib);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: ldebug.h,v 2.2 2004/06/02 19:07:55 roberto Exp roberto $
|
||||
** $Id: ldebug.h $
|
||||
** Auxiliary functions from Debug Interface module
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -11,23 +11,37 @@
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1)
|
||||
|
||||
#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0)
|
||||
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
|
||||
|
||||
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
||||
|
||||
/*
|
||||
** mark for entries in 'lineinfo' array that has absolute information in
|
||||
** 'abslineinfo' array
|
||||
*/
|
||||
#define ABSLINEINFO (-0x80)
|
||||
|
||||
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
|
||||
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
|
||||
StkId *pos);
|
||||
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
||||
const char *opname);
|
||||
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
||||
const char *what);
|
||||
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2);
|
||||
LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2,
|
||||
const char *msg);
|
||||
LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2);
|
||||
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2);
|
||||
LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
|
||||
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
|
||||
TString *src, int line);
|
||||
LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
|
||||
LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc);
|
||||
|
||||
LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o,
|
||||
const char *opname);
|
||||
LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2);
|
||||
LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2);
|
||||
LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2);
|
||||
LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...);
|
||||
LUAI_FUNC void luaG_errormsg (lua_State *L);
|
||||
LUAI_FUNC int luaG_checkcode (const Proto *pt);
|
||||
LUAI_FUNC int luaG_checkopenop (Instruction i);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: ldo.h,v 2.6 2005/08/22 19:58:29 roberto Exp roberto $
|
||||
** $Id: ldo.h $
|
||||
** Stack and Call structure of Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -13,45 +13,63 @@
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
#define luaD_checkstack(L,n) \
|
||||
if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \
|
||||
luaD_growstack(L, n); \
|
||||
else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));
|
||||
/*
|
||||
** Macro to check stack size and grow stack if needed. Parameters
|
||||
** 'pre'/'pos' allow the macro to preserve a pointer into the
|
||||
** stack across reallocations, doing the work only when needed.
|
||||
** 'condmovestack' is used in heavy tests to force a stack reallocation
|
||||
** at every check.
|
||||
*/
|
||||
#define luaD_checkstackaux(L,n,pre,pos) \
|
||||
if (L->stack_last - L->top <= (n)) \
|
||||
{ pre; luaD_growstack(L, n, 1); pos; } \
|
||||
else { condmovestack(L,pre,pos); }
|
||||
|
||||
/* In general, 'pre'/'pos' are empty (nothing to save) */
|
||||
#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0)
|
||||
|
||||
|
||||
#define incr_top(L) {luaD_checkstack(L,1); L->top++;}
|
||||
|
||||
#define savestack(L,p) ((char *)(p) - (char *)L->stack)
|
||||
#define restorestack(L,n) ((TValue *)((char *)L->stack + (n)))
|
||||
|
||||
#define saveci(L,p) ((char *)(p) - (char *)L->base_ci)
|
||||
#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n)))
|
||||
#define restorestack(L,n) ((StkId)((char *)L->stack + (n)))
|
||||
|
||||
|
||||
/* results from luaD_precall */
|
||||
#define PCRLUA 0 /* initiated a call to a Lua function */
|
||||
#define PCRC 1 /* did a call to a C function */
|
||||
#define PCRYIELD 2 /* C funtion yielded */
|
||||
/* macro to check stack size, preserving 'p' */
|
||||
#define checkstackp(L,n,p) \
|
||||
luaD_checkstackaux(L, n, \
|
||||
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
|
||||
luaC_checkGC(L), /* stack grow uses memory */ \
|
||||
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
|
||||
|
||||
|
||||
/* type of protected functions, to be ran by `runprotected' */
|
||||
/* macro to check stack size and GC */
|
||||
#define checkstackGC(L,fsize) \
|
||||
luaD_checkstackaux(L, (fsize), (void)0, luaC_checkGC(L))
|
||||
|
||||
|
||||
/* type of protected functions, to be ran by 'runprotected' */
|
||||
typedef void (*Pfunc) (lua_State *L, void *ud);
|
||||
|
||||
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);
|
||||
LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line);
|
||||
LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
|
||||
LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
|
||||
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
||||
const char *mode);
|
||||
LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
|
||||
int fTransfer, int nTransfer);
|
||||
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
|
||||
LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n);
|
||||
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);
|
||||
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
ptrdiff_t oldtop, ptrdiff_t ef);
|
||||
LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult);
|
||||
LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);
|
||||
LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);
|
||||
LUAI_FUNC void luaD_growstack (lua_State *L, int n);
|
||||
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
|
||||
LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror);
|
||||
LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror);
|
||||
LUAI_FUNC void luaD_shrinkstack (lua_State *L);
|
||||
LUAI_FUNC void luaD_inctop (lua_State *L);
|
||||
|
||||
LUAI_FUNC void luaD_throw (lua_State *L, int errcode);
|
||||
LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode);
|
||||
LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
|
||||
|
||||
LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,179 +1,229 @@
|
||||
/*
|
||||
** $Id: ldump.c,v 2.5 2005/05/05 20:47:02 roberto Exp roberto $
|
||||
** save pre-compiled Lua chunks
|
||||
** $Id: ldump.c $
|
||||
** save precompiled Lua chunks
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define ldump_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lopcodes.h"
|
||||
#include "lstate.h"
|
||||
#include "lundump.h"
|
||||
|
||||
#define DumpVector(b,n,size,D) DumpBlock(b,(n)*(size),D)
|
||||
#define DumpLiteral(s,D) DumpBlock("" s,(sizeof(s))-1,D)
|
||||
|
||||
typedef struct {
|
||||
lua_State* L;
|
||||
lua_Writer writer;
|
||||
void* data;
|
||||
int strip;
|
||||
int status;
|
||||
lua_State *L;
|
||||
lua_Writer writer;
|
||||
void *data;
|
||||
int strip;
|
||||
int status;
|
||||
} DumpState;
|
||||
|
||||
static void DumpBlock(const void* b, size_t size, DumpState* D)
|
||||
{
|
||||
if (D->status==0)
|
||||
{
|
||||
lua_unlock(D->L);
|
||||
D->status=(*D->writer)(D->L,b,size,D->data);
|
||||
lua_lock(D->L);
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpByte(int y, DumpState* D)
|
||||
{
|
||||
char x=(char)y;
|
||||
DumpBlock(&x,sizeof(x),D);
|
||||
}
|
||||
/*
|
||||
** All high-level dumps go through DumpVector; you can change it to
|
||||
** change the endianness of the result
|
||||
*/
|
||||
#define DumpVector(v,n,D) DumpBlock(v,(n)*sizeof((v)[0]),D)
|
||||
|
||||
static void DumpInt(int x, DumpState* D)
|
||||
{
|
||||
DumpBlock(&x,sizeof(x),D);
|
||||
}
|
||||
#define DumpLiteral(s,D) DumpBlock(s, sizeof(s) - sizeof(char), D)
|
||||
|
||||
static void DumpSize(size_t x, DumpState* D)
|
||||
{
|
||||
DumpBlock(&x,sizeof(x),D);
|
||||
}
|
||||
|
||||
static void DumpNumber(lua_Number x, DumpState* D)
|
||||
{
|
||||
DumpBlock(&x,sizeof(x),D);
|
||||
}
|
||||
|
||||
static void DumpString(const TString* s, DumpState* D)
|
||||
{
|
||||
if (s==NULL || getstr(s)==NULL)
|
||||
DumpSize(0,D);
|
||||
else
|
||||
{
|
||||
size_t size=s->tsv.len+1; /* include trailing '\0' */
|
||||
DumpSize(size,D);
|
||||
DumpBlock(getstr(s),size,D);
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpCode(const Proto* f, DumpState* D)
|
||||
{
|
||||
DumpInt(f->sizecode,D);
|
||||
DumpVector(f->code,f->sizecode,sizeof(*f->code),D);
|
||||
}
|
||||
|
||||
static void DumpLocals(const Proto* f, DumpState* D)
|
||||
{
|
||||
int i,n=f->sizelocvars;
|
||||
DumpInt(n,D);
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
DumpString(f->locvars[i].varname,D);
|
||||
DumpInt(f->locvars[i].startpc,D);
|
||||
DumpInt(f->locvars[i].endpc,D);
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpLines(const Proto* f, DumpState* D)
|
||||
{
|
||||
DumpInt(f->sizelineinfo,D);
|
||||
DumpVector(f->lineinfo,f->sizelineinfo,sizeof(*f->lineinfo),D);
|
||||
}
|
||||
|
||||
static void DumpUpvalues(const Proto* f, DumpState* D)
|
||||
{
|
||||
int i,n=f->sizeupvalues;
|
||||
DumpInt(n,D);
|
||||
for (i=0; i<n; i++) DumpString(f->upvalues[i],D);
|
||||
}
|
||||
|
||||
static void DumpFunction(const Proto* f, const TString* p, DumpState* D);
|
||||
|
||||
static void DumpConstants(const Proto* f, DumpState* D)
|
||||
{
|
||||
int i,n;
|
||||
DumpInt(n=f->sizek,D);
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
const TValue* o=&f->k[i];
|
||||
DumpByte(ttype(o),D);
|
||||
switch (ttype(o))
|
||||
{
|
||||
case LUA_TNUMBER:
|
||||
DumpNumber(nvalue(o),D);
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
DumpString(rawtsvalue(o),D);
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
DumpByte(bvalue(o),D);
|
||||
break;
|
||||
default:
|
||||
lua_assert(0); /* cannot happen */
|
||||
break;
|
||||
static void DumpBlock (const void *b, size_t size, DumpState *D) {
|
||||
if (D->status == 0 && size > 0) {
|
||||
lua_unlock(D->L);
|
||||
D->status = (*D->writer)(D->L, b, size, D->data);
|
||||
lua_lock(D->L);
|
||||
}
|
||||
}
|
||||
DumpInt(n=f->sizep,D);
|
||||
for (i=0; i<n; i++) DumpFunction(f->p[i],f->source,D);
|
||||
}
|
||||
|
||||
static void DumpFunction(const Proto* f, const TString* p, DumpState* D)
|
||||
{
|
||||
DumpString((f->source==p) ? NULL : f->source,D);
|
||||
DumpInt(f->linedefined,D);
|
||||
DumpInt(f->lastlinedefined,D);
|
||||
DumpByte(f->nups,D);
|
||||
DumpByte(f->numparams,D);
|
||||
DumpByte(f->is_vararg,D);
|
||||
DumpByte(f->maxstacksize,D);
|
||||
if (D->strip) DumpInt(0,D); else DumpLines(f,D);
|
||||
if (D->strip) DumpInt(0,D); else DumpLocals(f,D);
|
||||
if (D->strip) DumpInt(0,D); else DumpUpvalues(f,D);
|
||||
DumpConstants(f,D);
|
||||
DumpCode(f,D);
|
||||
|
||||
#define DumpVar(x,D) DumpVector(&x,1,D)
|
||||
|
||||
|
||||
static void DumpByte (int y, DumpState *D) {
|
||||
lu_byte x = (lu_byte)y;
|
||||
DumpVar(x, D);
|
||||
}
|
||||
|
||||
static void DumpHeader(DumpState* D)
|
||||
{
|
||||
DumpLiteral(LUA_SIGNATURE,D);
|
||||
DumpByte(VERSION,D);
|
||||
DumpByte(luaU_endianness(),D);
|
||||
DumpByte(sizeof(int),D);
|
||||
DumpByte(sizeof(size_t),D);
|
||||
DumpByte(sizeof(Instruction),D);
|
||||
DumpByte(sizeof(lua_Number),D);
|
||||
DumpNumber(TEST_NUMBER,D);
|
||||
|
||||
/* DumpInt Buff Size */
|
||||
#define DIBS ((sizeof(size_t) * 8 / 7) + 1)
|
||||
|
||||
static void DumpSize (size_t x, DumpState *D) {
|
||||
lu_byte buff[DIBS];
|
||||
int n = 0;
|
||||
do {
|
||||
buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */
|
||||
x >>= 7;
|
||||
} while (x != 0);
|
||||
buff[DIBS - 1] |= 0x80; /* mark last byte */
|
||||
DumpVector(buff + DIBS - n, n, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpInt (int x, DumpState *D) {
|
||||
DumpSize(x, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpNumber (lua_Number x, DumpState *D) {
|
||||
DumpVar(x, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpInteger (lua_Integer x, DumpState *D) {
|
||||
DumpVar(x, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpString (const TString *s, DumpState *D) {
|
||||
if (s == NULL)
|
||||
DumpSize(0, D);
|
||||
else {
|
||||
size_t size = tsslen(s);
|
||||
const char *str = getstr(s);
|
||||
DumpSize(size + 1, D);
|
||||
DumpVector(str, size, D);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DumpCode (const Proto *f, DumpState *D) {
|
||||
DumpInt(f->sizecode, D);
|
||||
DumpVector(f->code, f->sizecode, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpFunction(const Proto *f, TString *psource, DumpState *D);
|
||||
|
||||
static void DumpConstants (const Proto *f, DumpState *D) {
|
||||
int i;
|
||||
int n = f->sizek;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++) {
|
||||
const TValue *o = &f->k[i];
|
||||
DumpByte(ttypetag(o), D);
|
||||
switch (ttypetag(o)) {
|
||||
case LUA_TNIL:
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
DumpByte(bvalue(o), D);
|
||||
break;
|
||||
case LUA_TNUMFLT:
|
||||
DumpNumber(fltvalue(o), D);
|
||||
break;
|
||||
case LUA_TNUMINT:
|
||||
DumpInteger(ivalue(o), D);
|
||||
break;
|
||||
case LUA_TSHRSTR:
|
||||
case LUA_TLNGSTR:
|
||||
DumpString(tsvalue(o), D);
|
||||
break;
|
||||
default: lua_assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DumpProtos (const Proto *f, DumpState *D) {
|
||||
int i;
|
||||
int n = f->sizep;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++)
|
||||
DumpFunction(f->p[i], f->source, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpUpvalues (const Proto *f, DumpState *D) {
|
||||
int i, n = f->sizeupvalues;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpByte(f->upvalues[i].instack, D);
|
||||
DumpByte(f->upvalues[i].idx, D);
|
||||
DumpByte(f->upvalues[i].kind, D);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DumpDebug (const Proto *f, DumpState *D) {
|
||||
int i, n;
|
||||
n = (D->strip) ? 0 : f->sizelineinfo;
|
||||
DumpInt(n, D);
|
||||
DumpVector(f->lineinfo, n, D);
|
||||
n = (D->strip) ? 0 : f->sizeabslineinfo;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpInt(f->abslineinfo[i].pc, D);
|
||||
DumpInt(f->abslineinfo[i].line, D);
|
||||
}
|
||||
n = (D->strip) ? 0 : f->sizelocvars;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpString(f->locvars[i].varname, D);
|
||||
DumpInt(f->locvars[i].startpc, D);
|
||||
DumpInt(f->locvars[i].endpc, D);
|
||||
}
|
||||
n = (D->strip) ? 0 : f->sizeupvalues;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++)
|
||||
DumpString(f->upvalues[i].name, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpFunction (const Proto *f, TString *psource, DumpState *D) {
|
||||
if (D->strip || f->source == psource)
|
||||
DumpString(NULL, D); /* no debug info or same source as its parent */
|
||||
else
|
||||
DumpString(f->source, D);
|
||||
DumpInt(f->linedefined, D);
|
||||
DumpInt(f->lastlinedefined, D);
|
||||
DumpByte(f->numparams, D);
|
||||
DumpByte(f->is_vararg, D);
|
||||
DumpByte(f->maxstacksize, D);
|
||||
DumpCode(f, D);
|
||||
DumpConstants(f, D);
|
||||
DumpUpvalues(f, D);
|
||||
DumpProtos(f, D);
|
||||
DumpDebug(f, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpHeader (DumpState *D) {
|
||||
DumpLiteral(LUA_SIGNATURE, D);
|
||||
DumpInt(LUAC_VERSION, D);
|
||||
DumpByte(LUAC_FORMAT, D);
|
||||
DumpLiteral(LUAC_DATA, D);
|
||||
DumpByte(sizeof(Instruction), D);
|
||||
DumpByte(sizeof(lua_Integer), D);
|
||||
DumpByte(sizeof(lua_Number), D);
|
||||
DumpInteger(LUAC_INT, D);
|
||||
DumpNumber(LUAC_NUM, D);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** dump Lua function as precompiled chunk
|
||||
*/
|
||||
int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data,
|
||||
int strip)
|
||||
{
|
||||
DumpState D;
|
||||
D.L=L;
|
||||
D.writer=w;
|
||||
D.data=data;
|
||||
D.strip=strip;
|
||||
D.status=0;
|
||||
DumpHeader(&D);
|
||||
DumpFunction(f,NULL,&D);
|
||||
return D.status;
|
||||
int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data,
|
||||
int strip) {
|
||||
DumpState D;
|
||||
D.L = L;
|
||||
D.writer = w;
|
||||
D.data = data;
|
||||
D.strip = strip;
|
||||
D.status = 0;
|
||||
DumpHeader(&D);
|
||||
DumpByte(f->sizeupvalues, &D);
|
||||
DumpFunction(f, NULL, &D);
|
||||
return D.status;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
/*
|
||||
** $Id: lfunc.c,v 2.10 2005/04/29 13:54:05 roberto Exp roberto $
|
||||
** $Id: lfunc.c $
|
||||
** Auxiliary functions to manipulate prototypes and closures
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define lfunc_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lfunc.h"
|
||||
#include "lgc.h"
|
||||
#include "lmem.h"
|
||||
@@ -20,117 +24,244 @@
|
||||
|
||||
|
||||
|
||||
Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) {
|
||||
Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems)));
|
||||
luaC_link(L, obj2gco(c), LUA_TFUNCTION);
|
||||
c->c.isC = 1;
|
||||
c->c.env = e;
|
||||
c->c.nupvalues = cast(lu_byte, nelems);
|
||||
CClosure *luaF_newCclosure (lua_State *L, int n) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TCCL, sizeCclosure(n));
|
||||
CClosure *c = gco2ccl(o);
|
||||
c->nupvalues = cast_byte(n);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) {
|
||||
Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems)));
|
||||
luaC_link(L, obj2gco(c), LUA_TFUNCTION);
|
||||
c->l.isC = 0;
|
||||
c->l.env = e;
|
||||
c->l.nupvalues = cast(lu_byte, nelems);
|
||||
while (nelems--) c->l.upvals[nelems] = NULL;
|
||||
LClosure *luaF_newLclosure (lua_State *L, int n) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TLCL, sizeLclosure(n));
|
||||
LClosure *c = gco2lcl(o);
|
||||
c->p = NULL;
|
||||
c->nupvalues = cast_byte(n);
|
||||
while (n--) c->upvals[n] = NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
UpVal *luaF_newupval (lua_State *L) {
|
||||
UpVal *uv = luaM_new(L, UpVal);
|
||||
luaC_link(L, obj2gco(uv), LUA_TUPVAL);
|
||||
uv->v = &uv->u.value;
|
||||
setnilvalue(uv->v);
|
||||
/*
|
||||
** fill a closure with new closed upvalues
|
||||
*/
|
||||
void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
int i;
|
||||
for (i = 0; i < cl->nupvalues; i++) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
uv->v = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v);
|
||||
cl->upvals[i] = uv;
|
||||
luaC_objbarrier(L, cl, o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create a new upvalue at the given level, and link it to the list of
|
||||
** open upvalues of 'L' after entry 'prev'.
|
||||
**/
|
||||
static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
UpVal *next = *prev;
|
||||
uv->v = s2v(level); /* current value lives in the stack */
|
||||
uv->tbc = tbc;
|
||||
uv->u.open.next = next; /* link it to list of open upvalues */
|
||||
uv->u.open.previous = prev;
|
||||
if (next)
|
||||
next->u.open.previous = &uv->u.open.next;
|
||||
*prev = uv;
|
||||
if (!isintwups(L)) { /* thread not in list of threads with upvalues? */
|
||||
L->twups = G(L)->twups; /* link it to the list */
|
||||
G(L)->twups = L;
|
||||
}
|
||||
return uv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find and reuse, or create if it does not exist, an upvalue
|
||||
** at the given level.
|
||||
*/
|
||||
UpVal *luaF_findupval (lua_State *L, StkId level) {
|
||||
global_State *g = G(L);
|
||||
GCObject **pp = &L->openupval;
|
||||
UpVal **pp = &L->openupval;
|
||||
UpVal *p;
|
||||
UpVal *uv;
|
||||
while ((p = ngcotouv(*pp)) != NULL && p->v >= level) {
|
||||
lua_assert(p->v != &p->u.value);
|
||||
if (p->v == level) { /* found a corresponding upvalue? */
|
||||
if (isdead(g, obj2gco(p))) /* is it dead? */
|
||||
changewhite(obj2gco(p)); /* ressurect it */
|
||||
return p;
|
||||
}
|
||||
pp = &p->next;
|
||||
lua_assert(isintwups(L) || L->openupval == NULL);
|
||||
while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */
|
||||
lua_assert(!isdead(G(L), p));
|
||||
if (uplevel(p) == level) /* corresponding upvalue? */
|
||||
return p; /* return it */
|
||||
pp = &p->u.open.next;
|
||||
}
|
||||
uv = luaM_new(L, UpVal); /* not found: create a new one */
|
||||
uv->tt = LUA_TUPVAL;
|
||||
uv->marked = luaC_white(g);
|
||||
uv->v = level; /* current value lives in the stack */
|
||||
uv->next = *pp; /* chain it in the proper position */
|
||||
*pp = obj2gco(uv);
|
||||
uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */
|
||||
uv->u.l.next = g->uvhead.u.l.next;
|
||||
uv->u.l.next->u.l.prev = uv;
|
||||
g->uvhead.u.l.next = uv;
|
||||
lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
|
||||
return uv;
|
||||
/* not found: create a new upvalue after 'pp' */
|
||||
return newupval(L, 0, level, pp);
|
||||
}
|
||||
|
||||
|
||||
static void unlinkupval (UpVal *uv) {
|
||||
lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
|
||||
uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */
|
||||
uv->u.l.prev->u.l.next = uv->u.l.next;
|
||||
static void callclose (lua_State *L, void *ud) {
|
||||
UNUSED(ud);
|
||||
luaD_callnoyield(L, L->top - 3, 0);
|
||||
}
|
||||
|
||||
|
||||
void luaF_freeupval (lua_State *L, UpVal *uv) {
|
||||
if (uv->v != &uv->u.value) /* is it open? */
|
||||
unlinkupval(uv); /* remove from open list */
|
||||
luaM_free(L, uv); /* free upvalue */
|
||||
/*
|
||||
** Prepare closing method plus its arguments for object 'obj' with
|
||||
** error message 'err'. (This function assumes EXTRA_STACK.)
|
||||
*/
|
||||
static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
|
||||
StkId top = L->top;
|
||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||
if (ttisnil(tm)) /* no metamethod? */
|
||||
return 0; /* nothing to call */
|
||||
setobj2s(L, top, tm); /* will call metamethod... */
|
||||
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
|
||||
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
|
||||
L->top = top + 3; /* add function and arguments */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void luaF_close (lua_State *L, StkId level) {
|
||||
UpVal *uv;
|
||||
global_State *g = G(L);
|
||||
while ((uv = ngcotouv(L->openupval)) != NULL && uv->v >= level) {
|
||||
GCObject *o = obj2gco(uv);
|
||||
lua_assert(!isblack(o) && uv->v != &uv->u.value);
|
||||
L->openupval = uv->next; /* remove from `open' list */
|
||||
if (isdead(g, o))
|
||||
luaF_freeupval(L, uv); /* free upvalue */
|
||||
else {
|
||||
unlinkupval(uv);
|
||||
setobj(L, &uv->u.value, uv->v);
|
||||
uv->v = &uv->u.value; /* now current value lives here */
|
||||
luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */
|
||||
/*
|
||||
** Raise an error with message 'msg', inserting the name of the
|
||||
** local variable at position 'level' in the stack.
|
||||
*/
|
||||
static void varerror (lua_State *L, StkId level, const char *msg) {
|
||||
int idx = cast_int(level - L->ci->func);
|
||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
||||
if (vname == NULL) vname = "?";
|
||||
luaG_runerror(L, msg, vname);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare and call a closing method. If status is OK, code is still
|
||||
** inside the original protected call, and so any error will be handled
|
||||
** there. Otherwise, a previous error already activated the original
|
||||
** protected call, and so the call to the closing method must be
|
||||
** protected here. (A status == CLOSEPROTECT behaves like a previous
|
||||
** error, to also run the closing method in protected mode).
|
||||
** If status is OK, the call to the closing method will be pushed
|
||||
** at the top of the stack. Otherwise, values are pushed after
|
||||
** the 'level' of the upvalue being closed, as everything after
|
||||
** that won't be used again.
|
||||
*/
|
||||
static int callclosemth (lua_State *L, StkId level, int status) {
|
||||
TValue *uv = s2v(level); /* value being closed */
|
||||
if (likely(status == LUA_OK)) {
|
||||
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
||||
callclose(L, NULL); /* call closing method */
|
||||
else if (!l_isfalse(uv)) /* non-closable non-false value? */
|
||||
varerror(L, level, "attempt to close non-closable variable '%s'");
|
||||
}
|
||||
else { /* must close the object in protected mode */
|
||||
ptrdiff_t oldtop;
|
||||
level++; /* space for error message */
|
||||
oldtop = savestack(L, level + 1); /* top will be after that */
|
||||
luaD_seterrorobj(L, status, level); /* set error message */
|
||||
if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
|
||||
int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
|
||||
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
|
||||
status = newstatus; /* this will be the new error */
|
||||
else {
|
||||
if (newstatus != LUA_OK) /* supressed error? */
|
||||
luaE_warnerror(L, "__close metamethod");
|
||||
/* leave original error (or nil) on top */
|
||||
L->top = restorestack(L, oldtop);
|
||||
}
|
||||
}
|
||||
/* else no metamethod; ignore this case and keep original error */
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to create a to-be-closed upvalue
|
||||
** (can raise a memory-allocation error)
|
||||
*/
|
||||
static void trynewtbcupval (lua_State *L, void *ud) {
|
||||
newupval(L, 1, cast(StkId, ud), &L->openupval);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create a to-be-closed upvalue. If there is a memory error
|
||||
** when creating the upvalue, the closing method must be called here,
|
||||
** as there is no upvalue to call it later.
|
||||
*/
|
||||
void luaF_newtbcupval (lua_State *L, StkId level) {
|
||||
TValue *obj = s2v(level);
|
||||
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
|
||||
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
|
||||
int status;
|
||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||
if (ttisnil(tm)) /* no metamethod? */
|
||||
varerror(L, level, "variable '%s' got a non-closable value");
|
||||
status = luaD_rawrunprotected(L, trynewtbcupval, level);
|
||||
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
|
||||
lua_assert(status == LUA_ERRMEM);
|
||||
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
|
||||
/* next call must succeed, as object is closable */
|
||||
prepclosingmethod(L, s2v(level), s2v(level + 1));
|
||||
callclose(L, NULL); /* call closing method */
|
||||
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void luaF_unlinkupval (UpVal *uv) {
|
||||
lua_assert(upisopen(uv));
|
||||
*uv->u.open.previous = uv->u.open.next;
|
||||
if (uv->u.open.next)
|
||||
uv->u.open.next->u.open.previous = uv->u.open.previous;
|
||||
}
|
||||
|
||||
|
||||
int luaF_close (lua_State *L, StkId level, int status) {
|
||||
UpVal *uv;
|
||||
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
|
||||
TValue *slot = &uv->u.value; /* new position for value */
|
||||
lua_assert(uplevel(uv) < L->top);
|
||||
if (uv->tbc && status != NOCLOSINGMETH) {
|
||||
/* must run closing method, which may change the stack */
|
||||
ptrdiff_t levelrel = savestack(L, level);
|
||||
status = callclosemth(L, uplevel(uv), status);
|
||||
level = restorestack(L, levelrel);
|
||||
}
|
||||
luaF_unlinkupval(uv);
|
||||
setobj(L, slot, uv->v); /* move value to upvalue slot */
|
||||
uv->v = slot; /* now current value lives here */
|
||||
if (!iswhite(uv))
|
||||
gray2black(uv); /* closed upvalues cannot be gray */
|
||||
luaC_barrier(L, uv, slot);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
Proto *luaF_newproto (lua_State *L) {
|
||||
Proto *f = luaM_new(L, Proto);
|
||||
luaC_link(L, obj2gco(f), LUA_TPROTO);
|
||||
GCObject *o = luaC_newobj(L, LUA_TPROTO, sizeof(Proto));
|
||||
Proto *f = gco2p(o);
|
||||
f->k = NULL;
|
||||
f->sizek = 0;
|
||||
f->p = NULL;
|
||||
f->sizep = 0;
|
||||
f->code = NULL;
|
||||
f->sizecode = 0;
|
||||
f->lineinfo = NULL;
|
||||
f->sizelineinfo = 0;
|
||||
f->sizeupvalues = 0;
|
||||
f->nups = 0;
|
||||
f->abslineinfo = NULL;
|
||||
f->sizeabslineinfo = 0;
|
||||
f->upvalues = NULL;
|
||||
f->sizeupvalues = 0;
|
||||
f->numparams = 0;
|
||||
f->is_vararg = 0;
|
||||
f->maxstacksize = 0;
|
||||
f->lineinfo = NULL;
|
||||
f->sizelocvars = 0;
|
||||
f->locvars = NULL;
|
||||
f->sizelocvars = 0;
|
||||
f->linedefined = 0;
|
||||
f->lastlinedefined = 0;
|
||||
f->source = NULL;
|
||||
@@ -139,25 +270,19 @@ Proto *luaF_newproto (lua_State *L) {
|
||||
|
||||
|
||||
void luaF_freeproto (lua_State *L, Proto *f) {
|
||||
luaM_freearray(L, f->code, f->sizecode, Instruction);
|
||||
luaM_freearray(L, f->p, f->sizep, Proto *);
|
||||
luaM_freearray(L, f->k, f->sizek, TValue);
|
||||
luaM_freearray(L, f->lineinfo, f->sizelineinfo, int);
|
||||
luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar);
|
||||
luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *);
|
||||
luaM_freearray(L, f->code, f->sizecode);
|
||||
luaM_freearray(L, f->p, f->sizep);
|
||||
luaM_freearray(L, f->k, f->sizek);
|
||||
luaM_freearray(L, f->lineinfo, f->sizelineinfo);
|
||||
luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo);
|
||||
luaM_freearray(L, f->locvars, f->sizelocvars);
|
||||
luaM_freearray(L, f->upvalues, f->sizeupvalues);
|
||||
luaM_free(L, f);
|
||||
}
|
||||
|
||||
|
||||
void luaF_freeclosure (lua_State *L, Closure *c) {
|
||||
int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) :
|
||||
sizeLclosure(c->l.nupvalues);
|
||||
luaM_freemem(L, c, size);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Look for n-th local variable at line `line' in function `func'.
|
||||
** Look for n-th local variable at line 'line' in function 'func'.
|
||||
** Returns NULL if not found.
|
||||
*/
|
||||
const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lfunc.h,v 2.3 2005/02/18 12:40:02 roberto Exp roberto $
|
||||
** $Id: lfunc.h $
|
||||
** Auxiliary functions to manipulate prototypes and closures
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -11,22 +11,57 @@
|
||||
#include "lobject.h"
|
||||
|
||||
|
||||
#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \
|
||||
cast(int, sizeof(TValue)*((n)-1)))
|
||||
#define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \
|
||||
cast_int(sizeof(TValue)) * (n))
|
||||
|
||||
#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \
|
||||
cast(int, sizeof(TValue *)*((n)-1)))
|
||||
#define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \
|
||||
cast_int(sizeof(TValue *)) * (n))
|
||||
|
||||
|
||||
/* test whether thread is in 'twups' list */
|
||||
#define isintwups(L) (L->twups != L)
|
||||
|
||||
|
||||
/*
|
||||
** maximum number of upvalues in a closure (both C and Lua). (Value
|
||||
** must fit in a VM register.)
|
||||
*/
|
||||
#define MAXUPVAL 255
|
||||
|
||||
|
||||
#define upisopen(up) ((up)->v != &(up)->u.value)
|
||||
|
||||
|
||||
#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v))
|
||||
|
||||
|
||||
/*
|
||||
** maximum number of misses before giving up the cache of closures
|
||||
** in prototypes
|
||||
*/
|
||||
#define MAXMISS 10
|
||||
|
||||
|
||||
/*
|
||||
** Special "status" for 'luaF_close'
|
||||
*/
|
||||
|
||||
/* close upvalues without running their closing methods */
|
||||
#define NOCLOSINGMETH (-1)
|
||||
|
||||
/* close upvalues running all closing methods in protected mode */
|
||||
#define CLOSEPROTECT (-2)
|
||||
|
||||
|
||||
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
|
||||
LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e);
|
||||
LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e);
|
||||
LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
|
||||
LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems);
|
||||
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems);
|
||||
LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
|
||||
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_close (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status);
|
||||
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
|
||||
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
|
||||
LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);
|
||||
LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);
|
||||
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
|
||||
int pc);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lgc.h,v 2.14 2005/06/07 18:53:45 roberto Exp roberto $
|
||||
** $Id: lgc.h $
|
||||
** Garbage Collector
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -9,102 +9,178 @@
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
/*
|
||||
** Collectable objects may have one of three colors: white, which
|
||||
** means the object is not marked; gray, which means the
|
||||
** object is marked, but its references may be not marked; and
|
||||
** black, which means that the object and all its references are marked.
|
||||
** The main invariant of the garbage collector, while marking objects,
|
||||
** is that a black object can never point to a white one. Moreover,
|
||||
** any gray object must be in a "gray list" (gray, grayagain, weak,
|
||||
** allweak, ephemeron) so that it can be visited again before finishing
|
||||
** the collection cycle. These lists have no meaning when the invariant
|
||||
** is not being enforced (e.g., sweep phase).
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Possible states of the Garbage Collector
|
||||
*/
|
||||
#define GCSpause 0
|
||||
#define GCSpropagate 1
|
||||
#define GCSsweepstring 2
|
||||
#define GCSsweep 3
|
||||
#define GCSfinalize 4
|
||||
#define GCSpropagate 0
|
||||
#define GCSenteratomic 1
|
||||
#define GCSatomic 2
|
||||
#define GCSswpallgc 3
|
||||
#define GCSswpfinobj 4
|
||||
#define GCSswptobefnz 5
|
||||
#define GCSswpend 6
|
||||
#define GCScallfin 7
|
||||
#define GCSpause 8
|
||||
|
||||
|
||||
#define issweepphase(g) \
|
||||
(GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend)
|
||||
|
||||
|
||||
/*
|
||||
** some userful bit tricks
|
||||
** macro to tell when main invariant (white objects cannot point to black
|
||||
** ones) must be kept. During a collection, the sweep
|
||||
** phase may break the invariant, as objects turned white may point to
|
||||
** still-black objects. The invariant is restored when sweep ends and
|
||||
** all objects are white again.
|
||||
*/
|
||||
#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m)))
|
||||
#define setbits(x,m) ((x) |= (m))
|
||||
#define testbits(x,m) ((x) & (m))
|
||||
#define bitmask(b) (1<<(b))
|
||||
#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
|
||||
#define l_setbit(x,b) setbits(x, bitmask(b))
|
||||
#define resetbit(x,b) resetbits(x, bitmask(b))
|
||||
#define testbit(x,b) testbits(x, bitmask(b))
|
||||
#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2)))
|
||||
#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2)))
|
||||
#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2)))
|
||||
|
||||
#define keepinvariant(g) ((g)->gcstate <= GCSatomic)
|
||||
|
||||
|
||||
/*
|
||||
** Layout for bit use in `marked' field:
|
||||
** bit 0 - object is white (type 0)
|
||||
** bit 1 - object is white (type 1)
|
||||
** bit 2 - object is black
|
||||
** bit 3 - for userdata: has been finalized
|
||||
** bit 3 - for tables: has weak keys
|
||||
** bit 4 - for tables: has weak values
|
||||
** bit 5 - object is fixed (should not be collected)
|
||||
** bit 6 - object is "super" fixed (only the main thread)
|
||||
** some useful bit tricks
|
||||
*/
|
||||
#define resetbits(x,m) ((x) &= cast_byte(~(m)))
|
||||
#define setbits(x,m) ((x) |= (m))
|
||||
#define testbits(x,m) ((x) & (m))
|
||||
#define bitmask(b) (1<<(b))
|
||||
#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
|
||||
#define l_setbit(x,b) setbits(x, bitmask(b))
|
||||
#define resetbit(x,b) resetbits(x, bitmask(b))
|
||||
#define testbit(x,b) testbits(x, bitmask(b))
|
||||
|
||||
|
||||
/*
|
||||
** Layout for bit use in 'marked' field. First three bits are
|
||||
** used for object "age" in generational mode. Last bit is free
|
||||
** to be used by respective objects.
|
||||
*/
|
||||
#define WHITE0BIT 3 /* object is white (type 0) */
|
||||
#define WHITE1BIT 4 /* object is white (type 1) */
|
||||
#define BLACKBIT 5 /* object is black */
|
||||
#define FINALIZEDBIT 6 /* object has been marked for finalization */
|
||||
|
||||
|
||||
|
||||
#define WHITE0BIT 0
|
||||
#define WHITE1BIT 1
|
||||
#define BLACKBIT 2
|
||||
#define FINALIZEDBIT 3
|
||||
#define KEYWEAKBIT 3
|
||||
#define VALUEWEAKBIT 4
|
||||
#define FIXEDBIT 5
|
||||
#define SFIXEDBIT 6
|
||||
#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
|
||||
|
||||
|
||||
#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
|
||||
#define isblack(x) testbit((x)->gch.marked, BLACKBIT)
|
||||
#define isgray(x) (!isblack(x) && !iswhite(x))
|
||||
#define iswhite(x) testbits((x)->marked, WHITEBITS)
|
||||
#define isblack(x) testbit((x)->marked, BLACKBIT)
|
||||
#define isgray(x) /* neither white nor black */ \
|
||||
(!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT)))
|
||||
|
||||
#define otherwhite(g) (g->currentwhite ^ WHITEBITS)
|
||||
#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS)
|
||||
#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT)
|
||||
|
||||
#define changewhite(x) ((x)->gch.marked ^= WHITEBITS)
|
||||
#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT)
|
||||
#define otherwhite(g) ((g)->currentwhite ^ WHITEBITS)
|
||||
#define isdeadm(ow,m) ((m) & (ow))
|
||||
#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked)
|
||||
|
||||
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
|
||||
#define changewhite(x) ((x)->marked ^= WHITEBITS)
|
||||
#define gray2black(x) l_setbit((x)->marked, BLACKBIT)
|
||||
|
||||
#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)
|
||||
#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS)
|
||||
|
||||
|
||||
#define luaC_checkGC(L) { \
|
||||
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \
|
||||
if (G(L)->totalbytes >= G(L)->GCthreshold) \
|
||||
luaC_step(L); }
|
||||
/* object age in generational mode */
|
||||
#define G_NEW 0 /* created in current cycle */
|
||||
#define G_SURVIVAL 1 /* created in previous cycle */
|
||||
#define G_OLD0 2 /* marked old by frw. barrier in this cycle */
|
||||
#define G_OLD1 3 /* first full cycle as old */
|
||||
#define G_OLD 4 /* really old object (not to be visited) */
|
||||
#define G_TOUCHED1 5 /* old object touched this cycle */
|
||||
#define G_TOUCHED2 6 /* old object touched in previous cycle */
|
||||
|
||||
#define AGEBITS 7 /* all age bits (111) */
|
||||
|
||||
#define getage(o) ((o)->marked & AGEBITS)
|
||||
#define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a))
|
||||
#define isold(o) (getage(o) > G_SURVIVAL)
|
||||
|
||||
#define changeage(o,f,t) \
|
||||
check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t)))
|
||||
|
||||
|
||||
#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \
|
||||
luaC_barrierf(L,obj2gco(p),gcvalue(v)); }
|
||||
/* Default Values for GC parameters */
|
||||
#define LUAI_GENMAJORMUL 100
|
||||
#define LUAI_GENMINORMUL 20
|
||||
|
||||
#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \
|
||||
luaC_barrierback(L,t); }
|
||||
/* wait memory to double before starting new cycle */
|
||||
#define LUAI_GCPAUSE 200
|
||||
|
||||
#define luaC_objbarrier(L,p,o) \
|
||||
{ if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \
|
||||
luaC_barrierf(L,obj2gco(p),obj2gco(o)); }
|
||||
/*
|
||||
** some gc parameters are stored divided by 4 to allow a maximum value
|
||||
** up to 1023 in a 'lu_byte'.
|
||||
*/
|
||||
#define getgcparam(p) ((p) * 4)
|
||||
#define setgcparam(p,v) ((p) = (v) / 4)
|
||||
|
||||
#define luaC_objbarriert(L,t,o) \
|
||||
{ if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); }
|
||||
#define LUAI_GCMUL 100
|
||||
|
||||
LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all);
|
||||
LUAI_FUNC void luaC_callGCTM (lua_State *L);
|
||||
LUAI_FUNC void luaC_freeall (lua_State *L);
|
||||
/* how much to allocate before next GC step (log2) */
|
||||
#define LUAI_GCSTEPSIZE 13 /* 8 KB */
|
||||
|
||||
|
||||
/*
|
||||
** Check whether the declared GC mode is generational. While in
|
||||
** generational mode, the collector can go temporarily to incremental
|
||||
** mode to improve performance. This is signaled by 'g->lastatomic != 0'.
|
||||
*/
|
||||
#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0)
|
||||
|
||||
/*
|
||||
** Does one step of collection when debt becomes positive. 'pre'/'pos'
|
||||
** allows some adjustments to be done only when needed. macro
|
||||
** 'condchangemem' is used only for heavy tests (forcing a full
|
||||
** GC cycle on every opportunity)
|
||||
*/
|
||||
#define luaC_condGC(L,pre,pos) \
|
||||
{ if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \
|
||||
condchangemem(L,pre,pos); }
|
||||
|
||||
/* more often than not, 'pre'/'pos' are empty */
|
||||
#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)
|
||||
|
||||
|
||||
#define luaC_barrier(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0))
|
||||
|
||||
#define luaC_barrierback(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrierback_(L,p) : cast_void(0))
|
||||
|
||||
#define luaC_objbarrier(L,p,o) ( \
|
||||
(isblack(p) && iswhite(o)) ? \
|
||||
luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
|
||||
|
||||
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
|
||||
LUAI_FUNC void luaC_freeallobjects (lua_State *L);
|
||||
LUAI_FUNC void luaC_step (lua_State *L);
|
||||
LUAI_FUNC void luaC_fullgc (lua_State *L);
|
||||
LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);
|
||||
LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);
|
||||
LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);
|
||||
LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t);
|
||||
LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
|
||||
LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
|
||||
LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
|
||||
LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
|
||||
LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
|
||||
LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
|
||||
LUAI_FUNC void luaC_changemode (lua_State *L, int newmode);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
** $Id: linit.c,v 1.12 2005/08/10 18:06:58 roberto Exp roberto $
|
||||
** Initialization of libraries for lua.c
|
||||
** $Id: linit.c $
|
||||
** Initialization of libraries for lua.c and other clients
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
@@ -8,31 +8,58 @@
|
||||
#define linit_c
|
||||
#define LUA_LIB
|
||||
|
||||
/*
|
||||
** If you embed Lua in your program and need to open the standard
|
||||
** libraries, call luaL_openlibs in your program. If you need a
|
||||
** different set of libraries, copy this file to your project and edit
|
||||
** it to suit your needs.
|
||||
**
|
||||
** You can also *preload* libraries, so that a later 'require' can
|
||||
** open the library, which is already linked to the application.
|
||||
** For that, do the following code:
|
||||
**
|
||||
** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
** lua_pushcfunction(L, luaopen_modname);
|
||||
** lua_setfield(L, -2, modname);
|
||||
** lua_pop(L, 1); // remove PRELOAD table
|
||||
*/
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
|
||||
static const luaL_Reg lualibs[] = {
|
||||
{"", luaopen_base},
|
||||
/*
|
||||
** these libs are loaded by lua.c and are readily available to any Lua
|
||||
** program
|
||||
*/
|
||||
static const luaL_Reg loadedlibs[] = {
|
||||
{LUA_GNAME, luaopen_base},
|
||||
{LUA_LOADLIBNAME, luaopen_package},
|
||||
{LUA_COLIBNAME, luaopen_coroutine},
|
||||
{LUA_TABLIBNAME, luaopen_table},
|
||||
{LUA_IOLIBNAME, luaopen_io},
|
||||
{LUA_OSLIBNAME, luaopen_os},
|
||||
{LUA_STRLIBNAME, luaopen_string},
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_UTF8LIBNAME, luaopen_utf8},
|
||||
{LUA_DBLIBNAME, luaopen_debug},
|
||||
{LUA_LOADLIBNAME, luaopen_package},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUALIB_API void luaL_openlibs (lua_State *L) {
|
||||
const luaL_Reg *lib = lualibs;
|
||||
for (; lib->func; lib++) {
|
||||
lua_pushcfunction(L, lib->func);
|
||||
lua_pushstring(L, lib->name);
|
||||
lua_call(L, 1, 0);
|
||||
const luaL_Reg *lib;
|
||||
/* "require" functions from 'loadedlibs' and set results to global table */
|
||||
for (lib = loadedlibs; lib->func; lib++) {
|
||||
luaL_requiref(L, lib->name, lib->func, 1);
|
||||
lua_pop(L, 1); /* remove lib */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
/*
|
||||
** $Id: liolib.c,v 2.66 2005/08/17 19:05:04 roberto Exp roberto $
|
||||
** $Id: liolib.c $
|
||||
** Standard I/O (and system) library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define liolib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define liolib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
@@ -20,47 +24,137 @@
|
||||
|
||||
|
||||
|
||||
#define IO_INPUT 1
|
||||
#define IO_OUTPUT 2
|
||||
|
||||
/*
|
||||
** Change this macro to accept other modes for 'fopen' besides
|
||||
** the standard ones.
|
||||
*/
|
||||
#if !defined(l_checkmode)
|
||||
|
||||
static const char *const fnames[] = {"input", "output"};
|
||||
/* accepted extensions to 'mode' in 'fopen' */
|
||||
#if !defined(L_MODEEXT)
|
||||
#define L_MODEEXT "b"
|
||||
#endif
|
||||
|
||||
|
||||
static int pushresult (lua_State *L, int i, const char *filename) {
|
||||
if (i) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
if (filename)
|
||||
lua_pushfstring(L, "%s: %s", filename, strerror(errno));
|
||||
else
|
||||
lua_pushfstring(L, "%s", strerror(errno));
|
||||
lua_pushinteger(L, errno);
|
||||
return 3;
|
||||
}
|
||||
/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */
|
||||
static int l_checkmode (const char *mode) {
|
||||
return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL &&
|
||||
(*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */
|
||||
(strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void fileerror (lua_State *L, int arg, const char *filename) {
|
||||
lua_pushfstring(L, "%s: %s", filename, strerror(errno));
|
||||
luaL_argerror(L, arg, lua_tostring(L, -1));
|
||||
}
|
||||
/*
|
||||
** {======================================================
|
||||
** l_popen spawns a new process connected to the current
|
||||
** one through the file streams.
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
#if !defined(l_popen) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
#define l_popen(L,c,m) (fflush(NULL), popen(c,m))
|
||||
#define l_pclose(L,file) (pclose(file))
|
||||
|
||||
#elif defined(LUA_USE_WINDOWS) /* }{ */
|
||||
|
||||
#define l_popen(L,c,m) (_popen(c,m))
|
||||
#define l_pclose(L,file) (_pclose(file))
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definitions */
|
||||
#define l_popen(L,c,m) \
|
||||
((void)c, (void)m, \
|
||||
luaL_error(L, "'popen' not supported"), \
|
||||
(FILE*)0)
|
||||
#define l_pclose(L,file) ((void)L, (void)file, -1)
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
#define topfile(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
|
||||
#if !defined(l_getc) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX)
|
||||
#define l_getc(f) getc_unlocked(f)
|
||||
#define l_lockfile(f) flockfile(f)
|
||||
#define l_unlockfile(f) funlockfile(f)
|
||||
#else
|
||||
#define l_getc(f) getc(f)
|
||||
#define l_lockfile(f) ((void)0)
|
||||
#define l_unlockfile(f) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** l_fseek: configuration for longer offsets
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
#if !defined(l_fseek) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define l_fseek(f,o,w) fseeko(f,o,w)
|
||||
#define l_ftell(f) ftello(f)
|
||||
#define l_seeknum off_t
|
||||
|
||||
#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \
|
||||
&& defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */
|
||||
|
||||
/* Windows (but not DDK) and Visual C++ 2005 or higher */
|
||||
#define l_fseek(f,o,w) _fseeki64(f,o,w)
|
||||
#define l_ftell(f) _ftelli64(f)
|
||||
#define l_seeknum __int64
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definitions */
|
||||
#define l_fseek(f,o,w) fseek(f,o,w)
|
||||
#define l_ftell(f) ftell(f)
|
||||
#define l_seeknum long
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
#define IO_PREFIX "_IO_"
|
||||
#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1)
|
||||
#define IO_INPUT (IO_PREFIX "input")
|
||||
#define IO_OUTPUT (IO_PREFIX "output")
|
||||
|
||||
|
||||
typedef luaL_Stream LStream;
|
||||
|
||||
|
||||
#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))
|
||||
|
||||
#define isclosed(p) ((p)->closef == NULL)
|
||||
|
||||
|
||||
static int io_type (lua_State *L) {
|
||||
void *ud;
|
||||
LStream *p;
|
||||
luaL_checkany(L, 1);
|
||||
ud = lua_touserdata(L, 1);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
|
||||
if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
|
||||
lua_pushnil(L); /* not a file */
|
||||
else if (*((FILE **)ud) == NULL)
|
||||
p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
|
||||
if (p == NULL)
|
||||
luaL_pushfail(L); /* not a file */
|
||||
else if (isclosed(p))
|
||||
lua_pushliteral(L, "closed file");
|
||||
else
|
||||
lua_pushliteral(L, "file");
|
||||
@@ -68,135 +162,158 @@ static int io_type (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int f_tostring (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
if (isclosed(p))
|
||||
lua_pushliteral(L, "file (closed)");
|
||||
else
|
||||
lua_pushfstring(L, "file (%p)", p->f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static FILE *tofile (lua_State *L) {
|
||||
FILE **f = topfile(L);
|
||||
if (*f == NULL)
|
||||
LStream *p = tolstream(L);
|
||||
if (isclosed(p))
|
||||
luaL_error(L, "attempt to use a closed file");
|
||||
return *f;
|
||||
lua_assert(p->f);
|
||||
return p->f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** When creating file handles, always creates a `closed' file handle
|
||||
** When creating file handles, always creates a 'closed' file handle
|
||||
** before opening the actual file; so, if there is a memory error, the
|
||||
** file is not left opened.
|
||||
** handle is in a consistent state.
|
||||
*/
|
||||
static FILE **newfile (lua_State *L) {
|
||||
FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
|
||||
*pf = NULL; /* file handle is currently `closed' */
|
||||
luaL_getmetatable(L, LUA_FILEHANDLE);
|
||||
lua_setmetatable(L, -2);
|
||||
return pf;
|
||||
static LStream *newprefile (lua_State *L) {
|
||||
LStream *p = (LStream *)lua_newuserdatauv(L, sizeof(LStream), 0);
|
||||
p->closef = NULL; /* mark file handle as 'closed' */
|
||||
luaL_setmetatable(L, LUA_FILEHANDLE);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** this function has a separated environment, which defines the
|
||||
** correct __close for 'popen' files
|
||||
** Calls the 'close' function from a file handle. The 'volatile' avoids
|
||||
** a bug in some versions of the Clang compiler (e.g., clang 3.0 for
|
||||
** 32 bits).
|
||||
*/
|
||||
static int io_pclose (lua_State *L) {
|
||||
FILE **p = topfile(L);
|
||||
int ok = lua_pclose(L, *p);
|
||||
if (ok) *p = NULL;
|
||||
return pushresult(L, ok, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int io_fclose (lua_State *L) {
|
||||
FILE **p = topfile(L);
|
||||
int ok = (fclose(*p) == 0);
|
||||
if (ok) *p = NULL;
|
||||
return pushresult(L, ok, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int aux_close (lua_State *L) {
|
||||
lua_getfenv(L, 1);
|
||||
lua_getfield(L, -1, "__close");
|
||||
return (lua_tocfunction(L, -1))(L);
|
||||
LStream *p = tolstream(L);
|
||||
volatile lua_CFunction cf = p->closef;
|
||||
p->closef = NULL; /* mark stream as closed */
|
||||
return (*cf)(L); /* close it */
|
||||
}
|
||||
|
||||
|
||||
static int io_close (lua_State *L) {
|
||||
if (lua_isnone(L, 1))
|
||||
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
|
||||
tofile(L); /* make sure argument is a file */
|
||||
static int f_close (lua_State *L) {
|
||||
tofile(L); /* make sure argument is an open stream */
|
||||
return aux_close(L);
|
||||
}
|
||||
|
||||
|
||||
static int io_gc (lua_State *L) {
|
||||
FILE *f = *topfile(L);
|
||||
/* ignore closed files and standard files */
|
||||
if (f != NULL && f != stdin && f != stdout && f != stderr)
|
||||
aux_close(L);
|
||||
static int io_close (lua_State *L) {
|
||||
if (lua_isnone(L, 1)) /* no argument? */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */
|
||||
return f_close(L);
|
||||
}
|
||||
|
||||
|
||||
static int f_gc (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
if (!isclosed(p) && p->f != NULL)
|
||||
aux_close(L); /* ignore closed and incompletely open files */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int io_tostring (lua_State *L) {
|
||||
FILE *f = *topfile(L);
|
||||
if (f == NULL)
|
||||
lua_pushstring(L, "file (closed)");
|
||||
else
|
||||
lua_pushfstring(L, "file (%p)", f);
|
||||
return 1;
|
||||
/*
|
||||
** function to close regular files
|
||||
*/
|
||||
static int io_fclose (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
int res = fclose(p->f);
|
||||
return luaL_fileresult(L, (res == 0), NULL);
|
||||
}
|
||||
|
||||
|
||||
static LStream *newfile (lua_State *L) {
|
||||
LStream *p = newprefile(L);
|
||||
p->f = NULL;
|
||||
p->closef = &io_fclose;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static void opencheck (lua_State *L, const char *fname, const char *mode) {
|
||||
LStream *p = newfile(L);
|
||||
p->f = fopen(fname, mode);
|
||||
if (p->f == NULL)
|
||||
luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
|
||||
}
|
||||
|
||||
|
||||
static int io_open (lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
FILE **pf = newfile(L);
|
||||
*pf = fopen(filename, mode);
|
||||
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
|
||||
LStream *p = newfile(L);
|
||||
const char *md = mode; /* to traverse/check mode */
|
||||
luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
|
||||
p->f = fopen(filename, mode);
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** function to close 'popen' files
|
||||
*/
|
||||
static int io_pclose (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
return luaL_execresult(L, l_pclose(L, p->f));
|
||||
}
|
||||
|
||||
|
||||
static int io_popen (lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
FILE **pf = newfile(L);
|
||||
*pf = lua_popen(L, filename, mode);
|
||||
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
|
||||
LStream *p = newprefile(L);
|
||||
p->f = l_popen(L, filename, mode);
|
||||
p->closef = &io_pclose;
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
|
||||
}
|
||||
|
||||
|
||||
static int io_tmpfile (lua_State *L) {
|
||||
FILE **pf = newfile(L);
|
||||
*pf = tmpfile();
|
||||
return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
|
||||
LStream *p = newfile(L);
|
||||
p->f = tmpfile();
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
|
||||
}
|
||||
|
||||
|
||||
static FILE *getiofile (lua_State *L, int findex) {
|
||||
FILE *f;
|
||||
lua_rawgeti(L, LUA_ENVIRONINDEX, findex);
|
||||
f = *(FILE **)lua_touserdata(L, -1);
|
||||
if (f == NULL)
|
||||
luaL_error(L, "standard %s file is closed", fnames[findex - 1]);
|
||||
return f;
|
||||
static FILE *getiofile (lua_State *L, const char *findex) {
|
||||
LStream *p;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, findex);
|
||||
p = (LStream *)lua_touserdata(L, -1);
|
||||
if (isclosed(p))
|
||||
luaL_error(L, "standard %s file is closed", findex + IOPREF_LEN);
|
||||
return p->f;
|
||||
}
|
||||
|
||||
|
||||
static int g_iofile (lua_State *L, int f, const char *mode) {
|
||||
static int g_iofile (lua_State *L, const char *f, const char *mode) {
|
||||
if (!lua_isnoneornil(L, 1)) {
|
||||
const char *filename = lua_tostring(L, 1);
|
||||
if (filename) {
|
||||
FILE **pf = newfile(L);
|
||||
*pf = fopen(filename, mode);
|
||||
if (*pf == NULL)
|
||||
fileerror(L, 1, filename);
|
||||
}
|
||||
if (filename)
|
||||
opencheck(L, filename, mode);
|
||||
else {
|
||||
tofile(L); /* check that it's a valid file handle */
|
||||
lua_pushvalue(L, 1);
|
||||
}
|
||||
lua_rawseti(L, LUA_ENVIRONINDEX, f);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, f);
|
||||
}
|
||||
/* return current value */
|
||||
lua_rawgeti(L, LUA_ENVIRONINDEX, f);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -214,35 +331,68 @@ static int io_output (lua_State *L) {
|
||||
static int io_readline (lua_State *L);
|
||||
|
||||
|
||||
static void aux_lines (lua_State *L, int idx, int toclose) {
|
||||
lua_pushvalue(L, idx);
|
||||
/*
|
||||
** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit
|
||||
** in the limit for upvalues of a closure)
|
||||
*/
|
||||
#define MAXARGLINE 250
|
||||
|
||||
/*
|
||||
** Auxiliary function to create the iteration function for 'lines'.
|
||||
** The iteration function is a closure over 'io_readline', with
|
||||
** the following upvalues:
|
||||
** 1) The file being read (first value in the stack)
|
||||
** 2) the number of arguments to read
|
||||
** 3) a boolean, true iff file has to be closed when finished ('toclose')
|
||||
** *) a variable number of format arguments (rest of the stack)
|
||||
*/
|
||||
static void aux_lines (lua_State *L, int toclose) {
|
||||
int n = lua_gettop(L) - 1; /* number of arguments to read */
|
||||
luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
|
||||
lua_pushvalue(L, 1); /* file */
|
||||
lua_pushinteger(L, n); /* number of arguments to read */
|
||||
lua_pushboolean(L, toclose); /* close/not close file when finished */
|
||||
lua_pushcclosure(L, io_readline, 2);
|
||||
lua_rotate(L, 2, 3); /* move the three values to their positions */
|
||||
lua_pushcclosure(L, io_readline, 3 + n);
|
||||
}
|
||||
|
||||
|
||||
static int f_lines (lua_State *L) {
|
||||
tofile(L); /* check that it's a valid file handle */
|
||||
aux_lines(L, 1, 0);
|
||||
aux_lines(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return an iteration function for 'io.lines'. If file has to be
|
||||
** closed, also returns the file itself as a second result (to be
|
||||
** closed as the state at the exit of a generic for).
|
||||
*/
|
||||
static int io_lines (lua_State *L) {
|
||||
if (lua_isnoneornil(L, 1)) { /* no arguments? */
|
||||
/* will iterate over default input */
|
||||
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);
|
||||
return f_lines(L);
|
||||
int toclose;
|
||||
if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */
|
||||
if (lua_isnil(L, 1)) { /* no file name? */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */
|
||||
lua_replace(L, 1); /* put it at index 1 */
|
||||
tofile(L); /* check that it's a valid file handle */
|
||||
toclose = 0; /* do not close it after iteration */
|
||||
}
|
||||
else {
|
||||
else { /* open a new file */
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
FILE **pf = newfile(L);
|
||||
*pf = fopen(filename, "r");
|
||||
if (*pf == NULL)
|
||||
fileerror(L, 1, filename);
|
||||
aux_lines(L, lua_gettop(L), 1);
|
||||
return 1;
|
||||
opencheck(L, filename, "r");
|
||||
lua_replace(L, 1); /* put file at index 1 */
|
||||
toclose = 1; /* close it after iteration */
|
||||
}
|
||||
aux_lines(L, toclose); /* push iteration function */
|
||||
if (toclose) {
|
||||
lua_pushnil(L); /* state */
|
||||
lua_pushnil(L); /* control */
|
||||
lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */
|
||||
return 4;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -253,93 +403,184 @@ static int io_lines (lua_State *L) {
|
||||
*/
|
||||
|
||||
|
||||
static int read_number (lua_State *L, FILE *f) {
|
||||
lua_Number d;
|
||||
if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
|
||||
lua_pushnumber(L, d);
|
||||
/* maximum length of a numeral */
|
||||
#if !defined (L_MAXLENNUM)
|
||||
#define L_MAXLENNUM 200
|
||||
#endif
|
||||
|
||||
|
||||
/* auxiliary structure used by 'read_number' */
|
||||
typedef struct {
|
||||
FILE *f; /* file being read */
|
||||
int c; /* current character (look ahead) */
|
||||
int n; /* number of elements in buffer 'buff' */
|
||||
char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */
|
||||
} RN;
|
||||
|
||||
|
||||
/*
|
||||
** Add current char to buffer (if not out of space) and read next one
|
||||
*/
|
||||
static int nextc (RN *rn) {
|
||||
if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */
|
||||
rn->buff[0] = '\0'; /* invalidate result */
|
||||
return 0; /* fail */
|
||||
}
|
||||
else {
|
||||
rn->buff[rn->n++] = rn->c; /* save current char */
|
||||
rn->c = l_getc(rn->f); /* read next one */
|
||||
return 1;
|
||||
}
|
||||
else return 0; /* read fails */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Accept current char if it is in 'set' (of size 2)
|
||||
*/
|
||||
static int test2 (RN *rn, const char *set) {
|
||||
if (rn->c == set[0] || rn->c == set[1])
|
||||
return nextc(rn);
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read a sequence of (hex)digits
|
||||
*/
|
||||
static int readdigits (RN *rn, int hex) {
|
||||
int count = 0;
|
||||
while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read a number: first reads a valid prefix of a numeral into a buffer.
|
||||
** Then it calls 'lua_stringtonumber' to check whether the format is
|
||||
** correct and to convert it to a Lua number.
|
||||
*/
|
||||
static int read_number (lua_State *L, FILE *f) {
|
||||
RN rn;
|
||||
int count = 0;
|
||||
int hex = 0;
|
||||
char decp[2];
|
||||
rn.f = f; rn.n = 0;
|
||||
decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */
|
||||
decp[1] = '.'; /* always accept a dot */
|
||||
l_lockfile(rn.f);
|
||||
do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */
|
||||
test2(&rn, "-+"); /* optional sign */
|
||||
if (test2(&rn, "00")) {
|
||||
if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */
|
||||
else count = 1; /* count initial '0' as a valid digit */
|
||||
}
|
||||
count += readdigits(&rn, hex); /* integral part */
|
||||
if (test2(&rn, decp)) /* decimal point? */
|
||||
count += readdigits(&rn, hex); /* fractional part */
|
||||
if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */
|
||||
test2(&rn, "-+"); /* exponent sign */
|
||||
readdigits(&rn, 0); /* exponent digits */
|
||||
}
|
||||
ungetc(rn.c, rn.f); /* unread look-ahead char */
|
||||
l_unlockfile(rn.f);
|
||||
rn.buff[rn.n] = '\0'; /* finish string */
|
||||
if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */
|
||||
return 1; /* ok */
|
||||
else { /* invalid format */
|
||||
lua_pushnil(L); /* "result" to be removed */
|
||||
return 0; /* read fails */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int test_eof (lua_State *L, FILE *f) {
|
||||
int c = getc(f);
|
||||
ungetc(c, f);
|
||||
lua_pushlstring(L, NULL, 0);
|
||||
ungetc(c, f); /* no-op when c == EOF */
|
||||
lua_pushliteral(L, "");
|
||||
return (c != EOF);
|
||||
}
|
||||
|
||||
|
||||
static int read_line (lua_State *L, FILE *f) {
|
||||
static int read_line (lua_State *L, FILE *f, int chop) {
|
||||
luaL_Buffer b;
|
||||
int c;
|
||||
luaL_buffinit(L, &b);
|
||||
do { /* may need to read several chunks to get whole line */
|
||||
char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */
|
||||
int i = 0;
|
||||
l_lockfile(f); /* no memory errors can happen inside the lock */
|
||||
while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n')
|
||||
buff[i++] = c; /* read up to end of line or buffer limit */
|
||||
l_unlockfile(f);
|
||||
luaL_addsize(&b, i);
|
||||
} while (c != EOF && c != '\n'); /* repeat until end of line */
|
||||
if (!chop && c == '\n') /* want a newline and have one? */
|
||||
luaL_addchar(&b, c); /* add ending newline to result */
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
/* return ok if read something (either a newline or something else) */
|
||||
return (c == '\n' || lua_rawlen(L, -1) > 0);
|
||||
}
|
||||
|
||||
|
||||
static void read_all (lua_State *L, FILE *f) {
|
||||
size_t nr;
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
for (;;) {
|
||||
size_t l;
|
||||
do { /* read file in chunks of LUAL_BUFFERSIZE bytes */
|
||||
char *p = luaL_prepbuffer(&b);
|
||||
if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
return (lua_strlen(L, -1) > 0); /* check whether read something */
|
||||
}
|
||||
l = strlen(p);
|
||||
if (p[l-1] != '\n')
|
||||
luaL_addsize(&b, l);
|
||||
else {
|
||||
luaL_addsize(&b, l - 1); /* do not include `eol' */
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
return 1; /* read at least an `eol' */
|
||||
}
|
||||
}
|
||||
nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f);
|
||||
luaL_addsize(&b, nr);
|
||||
} while (nr == LUAL_BUFFERSIZE);
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
}
|
||||
|
||||
|
||||
static int read_chars (lua_State *L, FILE *f, size_t n) {
|
||||
size_t rlen; /* how much to read */
|
||||
size_t nr; /* number of chars actually read */
|
||||
char *p;
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
rlen = LUAL_BUFFERSIZE; /* try to read that much each time */
|
||||
do {
|
||||
char *p = luaL_prepbuffer(&b);
|
||||
if (rlen > n) rlen = n; /* cannot read more than asked */
|
||||
nr = fread(p, sizeof(char), rlen, f);
|
||||
luaL_addsize(&b, nr);
|
||||
n -= nr; /* still have to read `n' chars */
|
||||
} while (n > 0 && nr == rlen); /* until end of count or eof */
|
||||
p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */
|
||||
nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */
|
||||
luaL_addsize(&b, nr);
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
return (n == 0 || lua_strlen(L, -1) > 0);
|
||||
return (nr > 0); /* true iff read something */
|
||||
}
|
||||
|
||||
|
||||
static int g_read (lua_State *L, FILE *f, int first) {
|
||||
int nargs = lua_gettop(L) - 1;
|
||||
int success;
|
||||
int n;
|
||||
int n, success;
|
||||
clearerr(f);
|
||||
if (nargs == 0) { /* no arguments? */
|
||||
success = read_line(L, f);
|
||||
n = first+1; /* to return 1 result */
|
||||
success = read_line(L, f, 1);
|
||||
n = first + 1; /* to return 1 result */
|
||||
}
|
||||
else { /* ensure stack space for all results and for auxlib's buffer */
|
||||
else {
|
||||
/* ensure stack space for all results and for auxlib's buffer */
|
||||
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
|
||||
success = 1;
|
||||
for (n = first; nargs-- && success; n++) {
|
||||
if (lua_type(L, n) == LUA_TNUMBER) {
|
||||
size_t l = (size_t)lua_tointeger(L, n);
|
||||
size_t l = (size_t)luaL_checkinteger(L, n);
|
||||
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
|
||||
}
|
||||
else {
|
||||
const char *p = lua_tostring(L, n);
|
||||
luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
|
||||
switch (p[1]) {
|
||||
const char *p = luaL_checkstring(L, n);
|
||||
if (*p == '*') p++; /* skip optional '*' (for compatibility) */
|
||||
switch (*p) {
|
||||
case 'n': /* number */
|
||||
success = read_number(L, f);
|
||||
break;
|
||||
case 'l': /* line */
|
||||
success = read_line(L, f);
|
||||
success = read_line(L, f, 1);
|
||||
break;
|
||||
case 'L': /* line with end-of-line */
|
||||
success = read_line(L, f, 0);
|
||||
break;
|
||||
case 'a': /* file */
|
||||
read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */
|
||||
read_all(L, f); /* read entire file */
|
||||
success = 1; /* always success */
|
||||
break;
|
||||
default:
|
||||
@@ -349,10 +590,10 @@ static int g_read (lua_State *L, FILE *f, int first) {
|
||||
}
|
||||
}
|
||||
if (ferror(f))
|
||||
return pushresult(L, 0, NULL);
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
if (!success) {
|
||||
lua_pop(L, 1); /* remove last result */
|
||||
lua_pushnil(L); /* push nil instead */
|
||||
luaL_pushfail(L); /* push nil instead */
|
||||
}
|
||||
return n - first;
|
||||
}
|
||||
@@ -368,19 +609,31 @@ static int f_read (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Iteration function for 'lines'.
|
||||
*/
|
||||
static int io_readline (lua_State *L) {
|
||||
FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
|
||||
int sucess;
|
||||
if (f == NULL) /* file is already closed? */
|
||||
luaL_error(L, "file is already closed");
|
||||
sucess = read_line(L, f);
|
||||
if (ferror(f))
|
||||
return luaL_error(L, "%s", strerror(errno));
|
||||
if (sucess) return 1;
|
||||
else { /* EOF */
|
||||
if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */
|
||||
lua_settop(L, 0);
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
|
||||
int i;
|
||||
int n = (int)lua_tointeger(L, lua_upvalueindex(2));
|
||||
if (isclosed(p)) /* file is already closed? */
|
||||
return luaL_error(L, "file is already closed");
|
||||
lua_settop(L , 1);
|
||||
luaL_checkstack(L, n, "too many arguments");
|
||||
for (i = 1; i <= n; i++) /* push arguments to 'g_read' */
|
||||
lua_pushvalue(L, lua_upvalueindex(3 + i));
|
||||
n = g_read(L, p->f, 2); /* 'n' is number of results */
|
||||
lua_assert(n > 0); /* should return at least a nil */
|
||||
if (lua_toboolean(L, -n)) /* read at least one value? */
|
||||
return n; /* return them */
|
||||
else { /* first result is false: EOF or error */
|
||||
if (n > 1) { /* is there error information? */
|
||||
/* 2nd result is error message */
|
||||
return luaL_error(L, "%s", lua_tostring(L, -n + 1));
|
||||
}
|
||||
if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */
|
||||
lua_settop(L, 0); /* clear stack */
|
||||
lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */
|
||||
aux_close(L); /* close it */
|
||||
}
|
||||
return 0;
|
||||
@@ -391,13 +644,17 @@ static int io_readline (lua_State *L) {
|
||||
|
||||
|
||||
static int g_write (lua_State *L, FILE *f, int arg) {
|
||||
int nargs = lua_gettop(L) - 1;
|
||||
int nargs = lua_gettop(L) - arg;
|
||||
int status = 1;
|
||||
for (; nargs--; arg++) {
|
||||
if (lua_type(L, arg) == LUA_TNUMBER) {
|
||||
/* optimization: could be done exactly as for strings */
|
||||
status = status &&
|
||||
fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
|
||||
int len = lua_isinteger(L, arg)
|
||||
? fprintf(f, LUA_INTEGER_FMT,
|
||||
(LUAI_UACINT)lua_tointeger(L, arg))
|
||||
: fprintf(f, LUA_NUMBER_FMT,
|
||||
(LUAI_UACNUMBER)lua_tonumber(L, arg));
|
||||
status = status && (len > 0);
|
||||
}
|
||||
else {
|
||||
size_t l;
|
||||
@@ -405,7 +662,8 @@ static int g_write (lua_State *L, FILE *f, int arg) {
|
||||
status = status && (fwrite(s, sizeof(char), l, f) == l);
|
||||
}
|
||||
}
|
||||
return pushresult(L, status, NULL);
|
||||
if (status) return 1; /* file handle already on stack top */
|
||||
else return luaL_fileresult(L, status, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -415,7 +673,9 @@ static int io_write (lua_State *L) {
|
||||
|
||||
|
||||
static int f_write (lua_State *L) {
|
||||
return g_write(L, tofile(L), 2);
|
||||
FILE *f = tofile(L);
|
||||
lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */
|
||||
return g_write(L, f, 2);
|
||||
}
|
||||
|
||||
|
||||
@@ -424,12 +684,15 @@ static int f_seek (lua_State *L) {
|
||||
static const char *const modenames[] = {"set", "cur", "end", NULL};
|
||||
FILE *f = tofile(L);
|
||||
int op = luaL_checkoption(L, 2, "cur", modenames);
|
||||
lua_Integer offset = luaL_optinteger(L, 3, 0);
|
||||
op = fseek(f, offset, mode[op]);
|
||||
lua_Integer p3 = luaL_optinteger(L, 3, 0);
|
||||
l_seeknum offset = (l_seeknum)p3;
|
||||
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
|
||||
"not an integer in proper range");
|
||||
op = l_fseek(f, offset, mode[op]);
|
||||
if (op)
|
||||
return pushresult(L, 0, NULL); /* error */
|
||||
return luaL_fileresult(L, 0, NULL); /* error */
|
||||
else {
|
||||
lua_pushinteger(L, ftell(f));
|
||||
lua_pushinteger(L, (lua_Integer)l_ftell(f));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -440,23 +703,26 @@ static int f_setvbuf (lua_State *L) {
|
||||
static const char *const modenames[] = {"no", "full", "line", NULL};
|
||||
FILE *f = tofile(L);
|
||||
int op = luaL_checkoption(L, 2, NULL, modenames);
|
||||
lua_Integer sz = luaL_optinteger(L, 3, BUFSIZ);
|
||||
int res = setvbuf(f, NULL, mode[op], sz);
|
||||
return pushresult(L, res == 0, NULL);
|
||||
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
|
||||
int res = setvbuf(f, NULL, mode[op], (size_t)sz);
|
||||
return luaL_fileresult(L, res == 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int io_flush (lua_State *L) {
|
||||
return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
|
||||
return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int f_flush (lua_State *L) {
|
||||
return pushresult(L, fflush(tofile(L)) == 0, NULL);
|
||||
return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** functions for 'io' library
|
||||
*/
|
||||
static const luaL_Reg iolib[] = {
|
||||
{"close", io_close},
|
||||
{"flush", io_flush},
|
||||
@@ -473,59 +739,75 @@ static const luaL_Reg iolib[] = {
|
||||
};
|
||||
|
||||
|
||||
static const luaL_Reg flib[] = {
|
||||
{"close", io_close},
|
||||
{"flush", f_flush},
|
||||
{"lines", f_lines},
|
||||
/*
|
||||
** methods for file handles
|
||||
*/
|
||||
static const luaL_Reg meth[] = {
|
||||
{"read", f_read},
|
||||
{"seek", f_seek},
|
||||
{"setvbuf", f_setvbuf},
|
||||
{"write", f_write},
|
||||
{"__gc", io_gc},
|
||||
{"__tostring", io_tostring},
|
||||
{"lines", f_lines},
|
||||
{"flush", f_flush},
|
||||
{"seek", f_seek},
|
||||
{"close", f_close},
|
||||
{"setvbuf", f_setvbuf},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** metamethods for file handles
|
||||
*/
|
||||
static const luaL_Reg metameth[] = {
|
||||
{"__index", NULL}, /* place holder */
|
||||
{"__gc", f_gc},
|
||||
{"__close", f_gc},
|
||||
{"__tostring", f_tostring},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
static void createmeta (lua_State *L) {
|
||||
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
|
||||
lua_pushvalue(L, -1); /* push metatable */
|
||||
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
|
||||
luaL_register(L, NULL, flib); /* file methods */
|
||||
luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */
|
||||
luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */
|
||||
luaL_newlibtable(L, meth); /* create method table */
|
||||
luaL_setfuncs(L, meth, 0); /* add file methods to method table */
|
||||
lua_setfield(L, -2, "__index"); /* metatable.__index = method table */
|
||||
lua_pop(L, 1); /* pop metatable */
|
||||
}
|
||||
|
||||
|
||||
static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
|
||||
*newfile(L) = f;
|
||||
if (k > 0) {
|
||||
/*
|
||||
** function to (not) close the standard files stdin, stdout, and stderr
|
||||
*/
|
||||
static int io_noclose (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
p->closef = &io_noclose; /* keep file opened */
|
||||
luaL_pushfail(L);
|
||||
lua_pushliteral(L, "cannot close standard file");
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static void createstdfile (lua_State *L, FILE *f, const char *k,
|
||||
const char *fname) {
|
||||
LStream *p = newprefile(L);
|
||||
p->f = f;
|
||||
p->closef = &io_noclose;
|
||||
if (k != NULL) {
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawseti(L, LUA_ENVIRONINDEX, k);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */
|
||||
}
|
||||
lua_setfield(L, -2, fname);
|
||||
lua_setfield(L, -2, fname); /* add file to module */
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API int luaopen_io (lua_State *L) {
|
||||
LUAMOD_API int luaopen_io (lua_State *L) {
|
||||
luaL_newlib(L, iolib); /* new module */
|
||||
createmeta(L);
|
||||
/* create new (private) environment */
|
||||
lua_newtable(L);
|
||||
lua_replace(L, LUA_ENVIRONINDEX);
|
||||
/* open library */
|
||||
luaL_register(L, LUA_IOLIBNAME, iolib);
|
||||
/* create (and set) default files */
|
||||
createstdfile(L, stdin, IO_INPUT, "stdin");
|
||||
createstdfile(L, stdout, IO_OUTPUT, "stdout");
|
||||
createstdfile(L, stderr, 0, "stderr");
|
||||
/* create environment for 'popen' */
|
||||
lua_getfield(L, -1, "popen");
|
||||
lua_newtable(L);
|
||||
lua_pushcfunction(L, io_pclose);
|
||||
lua_setfield(L, -2, "__close");
|
||||
lua_setfenv(L, -2);
|
||||
lua_pop(L, 1); /* pop 'popen' */
|
||||
/* set default close function */
|
||||
lua_pushcfunction(L, io_fclose);
|
||||
lua_setfield(L, LUA_ENVIRONINDEX, "__close");
|
||||
createstdfile(L, stderr, NULL, "stderr");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
** $Id: ljumptab.h $
|
||||
** Jump Table for the Lua interpreter
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#undef vmdispatch
|
||||
#undef vmcase
|
||||
#undef vmbreak
|
||||
|
||||
#define vmdispatch(x) goto *disptab[x];
|
||||
|
||||
#define vmcase(l) L_##l:
|
||||
|
||||
#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i));
|
||||
|
||||
|
||||
static void *disptab[NUM_OPCODES] = {
|
||||
|
||||
#if 0
|
||||
** you can update the following list with this command:
|
||||
**
|
||||
** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h
|
||||
**
|
||||
#endif
|
||||
|
||||
&&L_OP_MOVE,
|
||||
&&L_OP_LOADI,
|
||||
&&L_OP_LOADF,
|
||||
&&L_OP_LOADK,
|
||||
&&L_OP_LOADKX,
|
||||
&&L_OP_LOADBOOL,
|
||||
&&L_OP_LOADNIL,
|
||||
&&L_OP_GETUPVAL,
|
||||
&&L_OP_SETUPVAL,
|
||||
&&L_OP_GETTABUP,
|
||||
&&L_OP_GETTABLE,
|
||||
&&L_OP_GETI,
|
||||
&&L_OP_GETFIELD,
|
||||
&&L_OP_SETTABUP,
|
||||
&&L_OP_SETTABLE,
|
||||
&&L_OP_SETI,
|
||||
&&L_OP_SETFIELD,
|
||||
&&L_OP_NEWTABLE,
|
||||
&&L_OP_SELF,
|
||||
&&L_OP_ADDI,
|
||||
&&L_OP_ADDK,
|
||||
&&L_OP_SUBK,
|
||||
&&L_OP_MULK,
|
||||
&&L_OP_MODK,
|
||||
&&L_OP_POWK,
|
||||
&&L_OP_DIVK,
|
||||
&&L_OP_IDIVK,
|
||||
&&L_OP_BANDK,
|
||||
&&L_OP_BORK,
|
||||
&&L_OP_BXORK,
|
||||
&&L_OP_SHRI,
|
||||
&&L_OP_SHLI,
|
||||
&&L_OP_ADD,
|
||||
&&L_OP_SUB,
|
||||
&&L_OP_MUL,
|
||||
&&L_OP_MOD,
|
||||
&&L_OP_POW,
|
||||
&&L_OP_DIV,
|
||||
&&L_OP_IDIV,
|
||||
&&L_OP_BAND,
|
||||
&&L_OP_BOR,
|
||||
&&L_OP_BXOR,
|
||||
&&L_OP_SHL,
|
||||
&&L_OP_SHR,
|
||||
&&L_OP_MMBIN,
|
||||
&&L_OP_MMBINI,
|
||||
&&L_OP_MMBINK,
|
||||
&&L_OP_UNM,
|
||||
&&L_OP_BNOT,
|
||||
&&L_OP_NOT,
|
||||
&&L_OP_LEN,
|
||||
&&L_OP_CONCAT,
|
||||
&&L_OP_CLOSE,
|
||||
&&L_OP_TBC,
|
||||
&&L_OP_JMP,
|
||||
&&L_OP_EQ,
|
||||
&&L_OP_LT,
|
||||
&&L_OP_LE,
|
||||
&&L_OP_EQK,
|
||||
&&L_OP_EQI,
|
||||
&&L_OP_LTI,
|
||||
&&L_OP_LEI,
|
||||
&&L_OP_GTI,
|
||||
&&L_OP_GEI,
|
||||
&&L_OP_TEST,
|
||||
&&L_OP_TESTSET,
|
||||
&&L_OP_CALL,
|
||||
&&L_OP_TAILCALL,
|
||||
&&L_OP_RETURN,
|
||||
&&L_OP_RETURN0,
|
||||
&&L_OP_RETURN1,
|
||||
&&L_OP_FORLOOP,
|
||||
&&L_OP_FORPREP,
|
||||
&&L_OP_TFORPREP,
|
||||
&&L_OP_TFORCALL,
|
||||
&&L_OP_TFORLOOP,
|
||||
&&L_OP_SETLIST,
|
||||
&&L_OP_CLOSURE,
|
||||
&&L_OP_VARARG,
|
||||
&&L_OP_VARARGPREP,
|
||||
&&L_OP_EXTRAARG
|
||||
|
||||
};
|
||||
@@ -1,30 +1,35 @@
|
||||
/*
|
||||
** $Id: llex.c,v 2.11 2005/05/16 21:19:00 roberto Exp roberto $
|
||||
** $Id: llex.c $
|
||||
** Lexical Analyzer
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#define llex_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lctype.h"
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lgc.h"
|
||||
#include "llex.h"
|
||||
#include "lobject.h"
|
||||
#include "lparser.h"
|
||||
#include "lstate.h"
|
||||
#include "lstring.h"
|
||||
#include "ltable.h"
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
|
||||
#define next(ls) (ls->current = zgetc(ls->z))
|
||||
|
||||
#define next(ls) (ls->current = zgetc(ls->z))
|
||||
|
||||
|
||||
|
||||
@@ -32,117 +37,144 @@
|
||||
|
||||
|
||||
/* ORDER RESERVED */
|
||||
const char *const luaX_tokens [] = {
|
||||
static const char *const luaX_tokens [] = {
|
||||
"and", "break", "do", "else", "elseif",
|
||||
"end", "false", "for", "function", "if",
|
||||
"end", "false", "for", "function", "goto", "if",
|
||||
"in", "local", "nil", "not", "or", "repeat",
|
||||
"return", "then", "true", "until", "while",
|
||||
"..", "...", "==", ">=", "<=", "~=",
|
||||
"<number>", "<name>", "<string>", "<eof>",
|
||||
NULL
|
||||
"//", "..", "...", "==", ">=", "<=", "~=",
|
||||
"<<", ">>", "::", "<eof>",
|
||||
"<number>", "<integer>", "<name>", "<string>"
|
||||
};
|
||||
|
||||
|
||||
#define save_and_next(ls) (save(ls, ls->current), next(ls))
|
||||
|
||||
|
||||
static l_noret lexerror (LexState *ls, const char *msg, int token);
|
||||
|
||||
|
||||
static void save (LexState *ls, int c) {
|
||||
Mbuffer *b = ls->buff;
|
||||
if (b->n + 1 > b->buffsize) {
|
||||
if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) {
|
||||
size_t newsize;
|
||||
if (b->buffsize >= MAX_SIZET/2)
|
||||
luaX_lexerror(ls, "lexical element too long", 0);
|
||||
newsize = b->buffsize * 2;
|
||||
if (luaZ_sizebuffer(b) >= MAX_SIZE/2)
|
||||
lexerror(ls, "lexical element too long", 0);
|
||||
newsize = luaZ_sizebuffer(b) * 2;
|
||||
luaZ_resizebuffer(ls->L, b, newsize);
|
||||
}
|
||||
b->buffer[b->n++] = cast(char, c);
|
||||
b->buffer[luaZ_bufflen(b)++] = cast_char(c);
|
||||
}
|
||||
|
||||
|
||||
void luaX_init (lua_State *L) {
|
||||
int i;
|
||||
TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */
|
||||
luaC_fix(L, obj2gco(e)); /* never collect this name */
|
||||
for (i=0; i<NUM_RESERVED; i++) {
|
||||
TString *ts = luaS_new(L, luaX_tokens[i]);
|
||||
luaS_fix(ts); /* reserved words are never collected */
|
||||
lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);
|
||||
ts->tsv.reserved = cast(lu_byte, i+1); /* reserved word */
|
||||
luaC_fix(L, obj2gco(ts)); /* reserved words are never collected */
|
||||
ts->extra = cast_byte(i+1); /* reserved word */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define MAXSRC 80
|
||||
|
||||
|
||||
const char *luaX_token2str (LexState *ls, int token) {
|
||||
if (token < FIRST_RESERVED) {
|
||||
lua_assert(token == cast(unsigned char, token));
|
||||
return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) :
|
||||
luaO_pushfstring(ls->L, "%c", token);
|
||||
if (token < FIRST_RESERVED) { /* single-byte symbols? */
|
||||
lua_assert(token == cast_uchar(token));
|
||||
if (lisprint(token))
|
||||
return luaO_pushfstring(ls->L, "'%c'", token);
|
||||
else /* control character */
|
||||
return luaO_pushfstring(ls->L, "'<\\%d>'", token);
|
||||
}
|
||||
else {
|
||||
const char *s = luaX_tokens[token - FIRST_RESERVED];
|
||||
if (token < TK_EOS) /* fixed format (symbols and reserved words)? */
|
||||
return luaO_pushfstring(ls->L, "'%s'", s);
|
||||
else /* names, strings, and numerals */
|
||||
return s;
|
||||
}
|
||||
else
|
||||
return luaX_tokens[token-FIRST_RESERVED];
|
||||
}
|
||||
|
||||
|
||||
static const char *txtToken (LexState *ls, int token) {
|
||||
switch (token) {
|
||||
case TK_NAME:
|
||||
case TK_STRING:
|
||||
case TK_NUMBER:
|
||||
case TK_NAME: case TK_STRING:
|
||||
case TK_FLT: case TK_INT:
|
||||
save(ls, '\0');
|
||||
return luaZ_buffer(ls->buff);
|
||||
return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff));
|
||||
default:
|
||||
return luaX_token2str(ls, token);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void luaX_lexerror (LexState *ls, const char *msg, int token) {
|
||||
char buff[MAXSRC];
|
||||
luaO_chunkid(buff, getstr(ls->source), MAXSRC);
|
||||
msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg);
|
||||
static l_noret lexerror (LexState *ls, const char *msg, int token) {
|
||||
msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber);
|
||||
if (token)
|
||||
luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token));
|
||||
luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token));
|
||||
luaD_throw(ls->L, LUA_ERRSYNTAX);
|
||||
}
|
||||
|
||||
|
||||
void luaX_syntaxerror (LexState *ls, const char *msg) {
|
||||
luaX_lexerror(ls, msg, ls->t.token);
|
||||
l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
|
||||
lexerror(ls, msg, ls->t.token);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** creates a new string and anchors it in scanner's table so that
|
||||
** it will not be collected until the end of the compilation
|
||||
** (by that time it should be anchored somewhere)
|
||||
*/
|
||||
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
||||
lua_State *L = ls->L;
|
||||
TString *ts = luaS_newlstr(L, str, l);
|
||||
TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */
|
||||
if (ttisnil(o))
|
||||
setbvalue(o, 1); /* make sure `str' will not be collected */
|
||||
TValue *o; /* entry for 'str' */
|
||||
TString *ts = luaS_newlstr(L, str, l); /* create new string */
|
||||
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
|
||||
o = luaH_set(L, ls->h, s2v(L->top - 1));
|
||||
if (isempty(o)) { /* not in use yet? */
|
||||
/* boolean value does not need GC barrier;
|
||||
table is not a metatable, so it does not need to invalidate cache */
|
||||
setbvalue(o, 1); /* t[string] = true */
|
||||
luaC_checkGC(L);
|
||||
}
|
||||
else { /* string already present */
|
||||
ts = keystrval(nodefromval(o)); /* re-use value previously stored */
|
||||
}
|
||||
L->top--; /* remove string from stack */
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** increment line number and skips newline sequence (any of
|
||||
** \n, \r, \n\r, or \r\n)
|
||||
*/
|
||||
static void inclinenumber (LexState *ls) {
|
||||
int old = ls->current;
|
||||
lua_assert(currIsNewline(ls));
|
||||
next(ls); /* skip `\n' or `\r' */
|
||||
next(ls); /* skip '\n' or '\r' */
|
||||
if (currIsNewline(ls) && ls->current != old)
|
||||
next(ls); /* skip `\n\r' or `\r\n' */
|
||||
next(ls); /* skip '\n\r' or '\r\n' */
|
||||
if (++ls->linenumber >= MAX_INT)
|
||||
luaX_syntaxerror(ls, "chunk has too many lines");
|
||||
lexerror(ls, "chunk has too many lines", 0);
|
||||
}
|
||||
|
||||
|
||||
void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {
|
||||
void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source,
|
||||
int firstchar) {
|
||||
ls->t.token = 0;
|
||||
ls->L = L;
|
||||
ls->current = firstchar;
|
||||
ls->lookahead.token = TK_EOS; /* no look-ahead token */
|
||||
ls->z = z;
|
||||
ls->fs = NULL;
|
||||
ls->linenumber = 1;
|
||||
ls->lastline = 1;
|
||||
ls->source = source;
|
||||
ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */
|
||||
luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
|
||||
next(ls); /* read first char */
|
||||
}
|
||||
|
||||
|
||||
@@ -154,41 +186,81 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/* LUA_NUMBER */
|
||||
static void read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
while (isdigit(ls->current)) {
|
||||
save_and_next(ls);
|
||||
static int check_next1 (LexState *ls, int c) {
|
||||
if (ls->current == c) {
|
||||
next(ls);
|
||||
return 1;
|
||||
}
|
||||
if (ls->current == '.') {
|
||||
save_and_next(ls);
|
||||
if (ls->current == '.') {
|
||||
save_and_next(ls);
|
||||
luaX_lexerror(ls,
|
||||
"ambiguous syntax (decimal point x string concatenation)",
|
||||
TK_NUMBER);
|
||||
}
|
||||
}
|
||||
while (isdigit(ls->current)) {
|
||||
save_and_next(ls);
|
||||
}
|
||||
if (ls->current == 'e' || ls->current == 'E') {
|
||||
save_and_next(ls); /* read `E' */
|
||||
if (ls->current == '+' || ls->current == '-')
|
||||
save_and_next(ls); /* optional exponent sign */
|
||||
while (isdigit(ls->current)) {
|
||||
save_and_next(ls);
|
||||
}
|
||||
}
|
||||
save(ls, '\0');
|
||||
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r))
|
||||
luaX_lexerror(ls, "malformed number", TK_NUMBER);
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
static int skip_sep (LexState *ls) {
|
||||
int count = 0;
|
||||
/*
|
||||
** Check whether current char is in set 'set' (with two chars) and
|
||||
** saves it
|
||||
*/
|
||||
static int check_next2 (LexState *ls, const char *set) {
|
||||
lua_assert(set[2] == '\0');
|
||||
if (ls->current == set[0] || ls->current == set[1]) {
|
||||
save_and_next(ls);
|
||||
return 1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
/* LUA_NUMBER */
|
||||
/*
|
||||
** This function is quite liberal in what it accepts, as 'luaO_str2num'
|
||||
** will reject ill-formed numerals. Roughly, it accepts the following
|
||||
** pattern:
|
||||
**
|
||||
** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))*
|
||||
**
|
||||
** The only tricky part is to accept [+-] only after a valid exponent
|
||||
** mark, to avoid reading '3-4' or '0xe+1' as a single number.
|
||||
**
|
||||
** The caller might have already read an initial dot.
|
||||
*/
|
||||
static int read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
TValue obj;
|
||||
const char *expo = "Ee";
|
||||
int first = ls->current;
|
||||
lua_assert(lisdigit(ls->current));
|
||||
save_and_next(ls);
|
||||
if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */
|
||||
expo = "Pp";
|
||||
for (;;) {
|
||||
if (check_next2(ls, expo)) /* exponent mark? */
|
||||
check_next2(ls, "-+"); /* optional exponent sign */
|
||||
else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */
|
||||
save_and_next(ls);
|
||||
else break;
|
||||
}
|
||||
if (lislalpha(ls->current)) /* is numeral touching a letter? */
|
||||
save_and_next(ls); /* force an error */
|
||||
save(ls, '\0');
|
||||
if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */
|
||||
lexerror(ls, "malformed number", TK_FLT);
|
||||
if (ttisinteger(&obj)) {
|
||||
seminfo->i = ivalue(&obj);
|
||||
return TK_INT;
|
||||
}
|
||||
else {
|
||||
lua_assert(ttisfloat(&obj));
|
||||
seminfo->r = fltvalue(&obj);
|
||||
return TK_FLT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** reads a sequence '[=*[' or ']=*]', leaving the last bracket.
|
||||
** If sequence is well formed, return its number of '='s + 2; otherwise,
|
||||
** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...').
|
||||
*/
|
||||
static size_t skip_sep (LexState *ls) {
|
||||
size_t count = 0;
|
||||
int s = ls->current;
|
||||
lua_assert(s == '[' || s == ']');
|
||||
save_and_next(ls);
|
||||
@@ -196,48 +268,34 @@ static int skip_sep (LexState *ls) {
|
||||
save_and_next(ls);
|
||||
count++;
|
||||
}
|
||||
return (ls->current == s) ? count : (-count) - 1;
|
||||
return (ls->current == s) ? count + 2
|
||||
: (count == 0) ? 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
|
||||
int cont = 0;
|
||||
(void)(cont); /* avoid warnings when `cont' is not used */
|
||||
save_and_next(ls); /* skip 2nd `[' */
|
||||
static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) {
|
||||
int line = ls->linenumber; /* initial line (for error message) */
|
||||
save_and_next(ls); /* skip 2nd '[' */
|
||||
if (currIsNewline(ls)) /* string starts with a newline? */
|
||||
inclinenumber(ls); /* skip it */
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case EOZ:
|
||||
luaX_lexerror(ls, (seminfo) ? "unfinished long string" :
|
||||
"unfinished long comment", TK_EOS);
|
||||
case EOZ: { /* error */
|
||||
const char *what = (seminfo ? "string" : "comment");
|
||||
const char *msg = luaO_pushfstring(ls->L,
|
||||
"unfinished long %s (starting at line %d)", what, line);
|
||||
lexerror(ls, msg, TK_EOS);
|
||||
break; /* to avoid warnings */
|
||||
#if defined(LUA_COMPAT_LSTR)
|
||||
case '[': {
|
||||
if (skip_sep(ls) == sep) {
|
||||
save_and_next(ls); /* skip 2nd `[' */
|
||||
cont++;
|
||||
#if LUA_COMPAT_LSTR == 1
|
||||
if (sep == 0)
|
||||
luaX_lexerror(ls, "nesting of [[...]] is deprecated", '[');
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case ']': {
|
||||
if (skip_sep(ls) == sep) {
|
||||
save_and_next(ls); /* skip 2nd `]' */
|
||||
#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2
|
||||
cont--;
|
||||
if (sep == 0 && cont >= 0) break;
|
||||
#endif
|
||||
save_and_next(ls); /* skip 2nd ']' */
|
||||
goto endloop;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '\n':
|
||||
case '\r': {
|
||||
case '\n': case '\r': {
|
||||
save(ls, '\n');
|
||||
inclinenumber(ls);
|
||||
if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
|
||||
@@ -250,56 +308,126 @@ static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
|
||||
}
|
||||
} endloop:
|
||||
if (seminfo)
|
||||
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),
|
||||
luaZ_bufflen(ls->buff) - 2*(2 + sep));
|
||||
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep,
|
||||
luaZ_bufflen(ls->buff) - 2 * sep);
|
||||
}
|
||||
|
||||
|
||||
static void esccheck (LexState *ls, int c, const char *msg) {
|
||||
if (!c) {
|
||||
if (ls->current != EOZ)
|
||||
save_and_next(ls); /* add current to buffer for error message */
|
||||
lexerror(ls, msg, TK_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int gethexa (LexState *ls) {
|
||||
save_and_next(ls);
|
||||
esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected");
|
||||
return luaO_hexavalue(ls->current);
|
||||
}
|
||||
|
||||
|
||||
static int readhexaesc (LexState *ls) {
|
||||
int r = gethexa(ls);
|
||||
r = (r << 4) + gethexa(ls);
|
||||
luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static unsigned long readutf8esc (LexState *ls) {
|
||||
unsigned long r;
|
||||
int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */
|
||||
save_and_next(ls); /* skip 'u' */
|
||||
esccheck(ls, ls->current == '{', "missing '{'");
|
||||
r = gethexa(ls); /* must have at least one digit */
|
||||
while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) {
|
||||
i++;
|
||||
esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large");
|
||||
r = (r << 4) + luaO_hexavalue(ls->current);
|
||||
}
|
||||
esccheck(ls, ls->current == '}', "missing '}'");
|
||||
next(ls); /* skip '}' */
|
||||
luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static void utf8esc (LexState *ls) {
|
||||
char buff[UTF8BUFFSZ];
|
||||
int n = luaO_utf8esc(buff, readutf8esc(ls));
|
||||
for (; n > 0; n--) /* add 'buff' to string */
|
||||
save(ls, buff[UTF8BUFFSZ - n]);
|
||||
}
|
||||
|
||||
|
||||
static int readdecesc (LexState *ls) {
|
||||
int i;
|
||||
int r = 0; /* result accumulator */
|
||||
for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */
|
||||
r = 10*r + ls->current - '0';
|
||||
save_and_next(ls);
|
||||
}
|
||||
esccheck(ls, r <= UCHAR_MAX, "decimal escape too large");
|
||||
luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static void read_string (LexState *ls, int del, SemInfo *seminfo) {
|
||||
save_and_next(ls);
|
||||
save_and_next(ls); /* keep delimiter (for error messages) */
|
||||
while (ls->current != del) {
|
||||
switch (ls->current) {
|
||||
case EOZ:
|
||||
luaX_lexerror(ls, "unfinished string", TK_EOS);
|
||||
continue; /* to avoid warnings */
|
||||
lexerror(ls, "unfinished string", TK_EOS);
|
||||
break; /* to avoid warnings */
|
||||
case '\n':
|
||||
case '\r':
|
||||
luaX_lexerror(ls, "unfinished string", TK_STRING);
|
||||
continue; /* to avoid warnings */
|
||||
case '\\': {
|
||||
int c;
|
||||
next(ls); /* do not save the `\' */
|
||||
lexerror(ls, "unfinished string", TK_STRING);
|
||||
break; /* to avoid warnings */
|
||||
case '\\': { /* escape sequences */
|
||||
int c; /* final character to be saved */
|
||||
save_and_next(ls); /* keep '\\' for error messages */
|
||||
switch (ls->current) {
|
||||
case 'a': c = '\a'; break;
|
||||
case 'b': c = '\b'; break;
|
||||
case 'f': c = '\f'; break;
|
||||
case 'n': c = '\n'; break;
|
||||
case 'r': c = '\r'; break;
|
||||
case 't': c = '\t'; break;
|
||||
case 'v': c = '\v'; break;
|
||||
case '\n': /* go through */
|
||||
case '\r': save(ls, '\n'); inclinenumber(ls); continue;
|
||||
case EOZ: continue; /* will raise an error next loop */
|
||||
default: {
|
||||
if (!isdigit(ls->current))
|
||||
save_and_next(ls); /* handles \\, \", \', and \? */
|
||||
else { /* \xxx */
|
||||
int i = 0;
|
||||
c = 0;
|
||||
do {
|
||||
c = 10*c + (ls->current-'0');
|
||||
next(ls);
|
||||
} while (++i<3 && isdigit(ls->current));
|
||||
if (c > UCHAR_MAX)
|
||||
luaX_lexerror(ls, "escape sequence too large", TK_STRING);
|
||||
save(ls, c);
|
||||
case 'a': c = '\a'; goto read_save;
|
||||
case 'b': c = '\b'; goto read_save;
|
||||
case 'f': c = '\f'; goto read_save;
|
||||
case 'n': c = '\n'; goto read_save;
|
||||
case 'r': c = '\r'; goto read_save;
|
||||
case 't': c = '\t'; goto read_save;
|
||||
case 'v': c = '\v'; goto read_save;
|
||||
case 'x': c = readhexaesc(ls); goto read_save;
|
||||
case 'u': utf8esc(ls); goto no_save;
|
||||
case '\n': case '\r':
|
||||
inclinenumber(ls); c = '\n'; goto only_save;
|
||||
case '\\': case '\"': case '\'':
|
||||
c = ls->current; goto read_save;
|
||||
case EOZ: goto no_save; /* will raise an error next loop */
|
||||
case 'z': { /* zap following span of spaces */
|
||||
luaZ_buffremove(ls->buff, 1); /* remove '\\' */
|
||||
next(ls); /* skip the 'z' */
|
||||
while (lisspace(ls->current)) {
|
||||
if (currIsNewline(ls)) inclinenumber(ls);
|
||||
else next(ls);
|
||||
}
|
||||
continue;
|
||||
goto no_save;
|
||||
}
|
||||
default: {
|
||||
esccheck(ls, lisdigit(ls->current), "invalid escape sequence");
|
||||
c = readdecesc(ls); /* digital escape '\ddd' */
|
||||
goto only_save;
|
||||
}
|
||||
}
|
||||
save(ls, c);
|
||||
next(ls);
|
||||
continue;
|
||||
read_save:
|
||||
next(ls);
|
||||
/* go through */
|
||||
only_save:
|
||||
luaZ_buffremove(ls->buff, 1); /* remove '\\' */
|
||||
save(ls, c);
|
||||
/* go through */
|
||||
no_save: break;
|
||||
}
|
||||
default:
|
||||
save_and_next(ls);
|
||||
@@ -311,120 +439,140 @@ static void read_string (LexState *ls, int del, SemInfo *seminfo) {
|
||||
}
|
||||
|
||||
|
||||
int luaX_lex (LexState *ls, SemInfo *seminfo) {
|
||||
static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
luaZ_resetbuffer(ls->buff);
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case '\n':
|
||||
case '\r': {
|
||||
case '\n': case '\r': { /* line breaks */
|
||||
inclinenumber(ls);
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
case '-': {
|
||||
case ' ': case '\f': case '\t': case '\v': { /* spaces */
|
||||
next(ls);
|
||||
break;
|
||||
}
|
||||
case '-': { /* '-' or '--' (comment) */
|
||||
next(ls);
|
||||
if (ls->current != '-') return '-';
|
||||
/* else is a comment */
|
||||
next(ls);
|
||||
if (ls->current == '[') {
|
||||
int sep = skip_sep(ls);
|
||||
luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */
|
||||
if (sep >= 0) {
|
||||
read_long_string(ls, NULL, sep); /* long comment */
|
||||
luaZ_resetbuffer(ls->buff);
|
||||
continue;
|
||||
if (ls->current == '[') { /* long comment? */
|
||||
size_t sep = skip_sep(ls);
|
||||
luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */
|
||||
if (sep >= 2) {
|
||||
read_long_string(ls, NULL, sep); /* skip long comment */
|
||||
luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else short comment */
|
||||
while (!currIsNewline(ls) && ls->current != EOZ)
|
||||
next(ls);
|
||||
continue;
|
||||
next(ls); /* skip until end of line (or end of file) */
|
||||
break;
|
||||
}
|
||||
case '[': {
|
||||
int sep = skip_sep(ls);
|
||||
if (sep >= 0) {
|
||||
case '[': { /* long string or simply '[' */
|
||||
size_t sep = skip_sep(ls);
|
||||
if (sep >= 2) {
|
||||
read_long_string(ls, seminfo, sep);
|
||||
return TK_STRING;
|
||||
}
|
||||
else if (sep == -1) return '[';
|
||||
else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING);
|
||||
else if (sep == 0) /* '[=...' missing second bracket? */
|
||||
lexerror(ls, "invalid long string delimiter", TK_STRING);
|
||||
return '[';
|
||||
}
|
||||
case '=': {
|
||||
next(ls);
|
||||
if (ls->current != '=') return '=';
|
||||
else { next(ls); return TK_EQ; }
|
||||
if (check_next1(ls, '=')) return TK_EQ;
|
||||
else return '=';
|
||||
}
|
||||
case '<': {
|
||||
next(ls);
|
||||
if (ls->current != '=') return '<';
|
||||
else { next(ls); return TK_LE; }
|
||||
if (check_next1(ls, '=')) return TK_LE;
|
||||
else if (check_next1(ls, '<')) return TK_SHL;
|
||||
else return '<';
|
||||
}
|
||||
case '>': {
|
||||
next(ls);
|
||||
if (ls->current != '=') return '>';
|
||||
else { next(ls); return TK_GE; }
|
||||
if (check_next1(ls, '=')) return TK_GE;
|
||||
else if (check_next1(ls, '>')) return TK_SHR;
|
||||
else return '>';
|
||||
}
|
||||
case '/': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '/')) return TK_IDIV;
|
||||
else return '/';
|
||||
}
|
||||
case '~': {
|
||||
next(ls);
|
||||
if (ls->current != '=') return '~';
|
||||
else { next(ls); return TK_NE; }
|
||||
if (check_next1(ls, '=')) return TK_NE;
|
||||
else return '~';
|
||||
}
|
||||
case '"':
|
||||
case '\'': {
|
||||
case ':': {
|
||||
next(ls);
|
||||
if (check_next1(ls, ':')) return TK_DBCOLON;
|
||||
else return ':';
|
||||
}
|
||||
case '"': case '\'': { /* short literal strings */
|
||||
read_string(ls, ls->current, seminfo);
|
||||
return TK_STRING;
|
||||
}
|
||||
case '.': {
|
||||
case '.': { /* '.', '..', '...', or number */
|
||||
save_and_next(ls);
|
||||
if (ls->current == '.') {
|
||||
next(ls);
|
||||
if (ls->current == '.') {
|
||||
next(ls);
|
||||
return TK_DOTS; /* ... */
|
||||
}
|
||||
else return TK_CONCAT; /* .. */
|
||||
}
|
||||
else if (!isdigit(ls->current)) return '.';
|
||||
else {
|
||||
read_numeral(ls, seminfo);
|
||||
return TK_NUMBER;
|
||||
if (check_next1(ls, '.')) {
|
||||
if (check_next1(ls, '.'))
|
||||
return TK_DOTS; /* '...' */
|
||||
else return TK_CONCAT; /* '..' */
|
||||
}
|
||||
else if (!lisdigit(ls->current)) return '.';
|
||||
else return read_numeral(ls, seminfo);
|
||||
}
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9': {
|
||||
return read_numeral(ls, seminfo);
|
||||
}
|
||||
case EOZ: {
|
||||
return TK_EOS;
|
||||
}
|
||||
default: {
|
||||
if (isspace(ls->current)) {
|
||||
lua_assert(!currIsNewline(ls));
|
||||
next(ls);
|
||||
continue;
|
||||
}
|
||||
else if (isdigit(ls->current)) {
|
||||
read_numeral(ls, seminfo);
|
||||
return TK_NUMBER;
|
||||
}
|
||||
else if (isalpha(ls->current) || ls->current == '_') {
|
||||
/* identifier or reserved word */
|
||||
if (lislalpha(ls->current)) { /* identifier or reserved word? */
|
||||
TString *ts;
|
||||
do {
|
||||
save_and_next(ls);
|
||||
} while (isalnum(ls->current) || ls->current == '_');
|
||||
} while (lislalnum(ls->current));
|
||||
ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
|
||||
luaZ_bufflen(ls->buff));
|
||||
if (ts->tsv.reserved > 0) /* reserved word? */
|
||||
return ts->tsv.reserved - 1 + FIRST_RESERVED;
|
||||
seminfo->ts = ts;
|
||||
if (isreserved(ts)) /* reserved word? */
|
||||
return ts->extra - 1 + FIRST_RESERVED;
|
||||
else {
|
||||
seminfo->ts = ts;
|
||||
return TK_NAME;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else { /* single-char tokens (+ - / ...) */
|
||||
int c = ls->current;
|
||||
next(ls);
|
||||
return c; /* single-char tokens (+ - / ...) */
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef next
|
||||
|
||||
void luaX_next (LexState *ls) {
|
||||
ls->lastline = ls->linenumber;
|
||||
if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
|
||||
ls->t = ls->lookahead; /* use this one */
|
||||
ls->lookahead.token = TK_EOS; /* and discharge it */
|
||||
}
|
||||
else
|
||||
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
|
||||
}
|
||||
|
||||
|
||||
int luaX_lookahead (LexState *ls) {
|
||||
lua_assert(ls->lookahead.token == TK_EOS);
|
||||
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
|
||||
return ls->lookahead.token;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: llex.h,v 1.54 2005/04/25 19:24:10 roberto Exp roberto $
|
||||
** $Id: llex.h $
|
||||
** Lexical Analyzer
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -13,8 +13,10 @@
|
||||
|
||||
#define FIRST_RESERVED 257
|
||||
|
||||
/* maximum length of a reserved word */
|
||||
#define TOKEN_LEN (sizeof("function")/sizeof(char))
|
||||
|
||||
#if !defined(LUA_ENV)
|
||||
#define LUA_ENV "_ENV"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -25,23 +27,22 @@ enum RESERVED {
|
||||
/* terminal symbols denoted by reserved words */
|
||||
TK_AND = FIRST_RESERVED, TK_BREAK,
|
||||
TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
|
||||
TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
|
||||
TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
|
||||
TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
|
||||
/* other terminal symbols */
|
||||
TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
|
||||
TK_NAME, TK_STRING, TK_EOS
|
||||
TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
|
||||
TK_SHL, TK_SHR,
|
||||
TK_DBCOLON, TK_EOS,
|
||||
TK_FLT, TK_INT, TK_NAME, TK_STRING
|
||||
};
|
||||
|
||||
/* number of reserved words */
|
||||
#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1))
|
||||
|
||||
|
||||
/* array with token `names' */
|
||||
LUAI_DATA const char *const luaX_tokens [];
|
||||
#define NUM_RESERVED (cast_int(TK_WHILE-FIRST_RESERVED + 1))
|
||||
|
||||
|
||||
typedef union {
|
||||
lua_Number r;
|
||||
lua_Integer i;
|
||||
TString *ts;
|
||||
} SemInfo; /* semantics information */
|
||||
|
||||
@@ -52,27 +53,32 @@ typedef struct Token {
|
||||
} Token;
|
||||
|
||||
|
||||
/* state of the lexer plus state of the parser when shared by all
|
||||
functions */
|
||||
typedef struct LexState {
|
||||
int current; /* current character (charint) */
|
||||
int linenumber; /* input line counter */
|
||||
int lastline; /* line of last token `consumed' */
|
||||
int lastline; /* line of last token 'consumed' */
|
||||
Token t; /* current token */
|
||||
Token lookahead; /* look ahead token */
|
||||
struct FuncState *fs; /* `FuncState' is private to the parser */
|
||||
struct FuncState *fs; /* current function (parser) */
|
||||
struct lua_State *L;
|
||||
ZIO *z; /* input stream */
|
||||
Mbuffer *buff; /* buffer for tokens */
|
||||
Table *h; /* to avoid collection/reuse strings */
|
||||
struct Dyndata *dyd; /* dynamic structures used by the parser */
|
||||
TString *source; /* current source name */
|
||||
TString *envn; /* environment variable name */
|
||||
} LexState;
|
||||
|
||||
|
||||
LUAI_FUNC void luaX_init (lua_State *L);
|
||||
LUAI_FUNC void luaX_setinput (lua_State *L, LexState *LS, ZIO *z,
|
||||
TString *source);
|
||||
LUAI_FUNC TString *luaX_newstring (LexState *LS, const char *str, size_t l);
|
||||
LUAI_FUNC int luaX_lex (LexState *LS, SemInfo *seminfo);
|
||||
LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token);
|
||||
LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s);
|
||||
LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,
|
||||
TString *source, int firstchar);
|
||||
LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);
|
||||
LUAI_FUNC void luaX_next (LexState *ls);
|
||||
LUAI_FUNC int luaX_lookahead (LexState *ls);
|
||||
LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s);
|
||||
LUAI_FUNC const char *luaX_token2str (LexState *ls, int token);
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
** $Id: llimits.h,v 1.66 2005/08/04 13:37:10 roberto Exp roberto $
|
||||
** Limits, basic types, and some other `installation-dependent' definitions
|
||||
** $Id: llimits.h $
|
||||
** Limits, basic types, and some other 'installation-dependent' definitions
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
@@ -15,101 +15,335 @@
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
#define api_check luai_apicheck
|
||||
|
||||
|
||||
typedef LUAI_UINT32 lu_int32;
|
||||
|
||||
/*
|
||||
** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count
|
||||
** the total memory used by Lua (in bytes). Usually, 'size_t' and
|
||||
** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines.
|
||||
*/
|
||||
#if defined(LUAI_MEM) /* { external definitions? */
|
||||
typedef LUAI_UMEM lu_mem;
|
||||
|
||||
typedef LUAI_MEM l_mem;
|
||||
#elif LUAI_IS32INT /* }{ */
|
||||
typedef size_t lu_mem;
|
||||
typedef ptrdiff_t l_mem;
|
||||
#else /* 16-bit ints */ /* }{ */
|
||||
typedef unsigned long lu_mem;
|
||||
typedef long l_mem;
|
||||
#endif /* } */
|
||||
|
||||
|
||||
|
||||
/* chars used as small naturals (so that `char' is reserved for characters) */
|
||||
/* chars used as small naturals (so that 'char' is reserved for characters) */
|
||||
typedef unsigned char lu_byte;
|
||||
typedef signed char ls_byte;
|
||||
|
||||
|
||||
#define MAX_SIZET ((size_t)(~(size_t)0)-2)
|
||||
/* maximum value for size_t */
|
||||
#define MAX_SIZET ((size_t)(~(size_t)0))
|
||||
|
||||
#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2)
|
||||
/* maximum size visible for Lua (must be representable in a lua_Integer) */
|
||||
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
|
||||
: (size_t)(LUA_MAXINTEGER))
|
||||
|
||||
|
||||
#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
|
||||
#define MAX_LUMEM ((lu_mem)(~(lu_mem)0))
|
||||
|
||||
#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1))
|
||||
|
||||
|
||||
#define MAX_INT INT_MAX /* maximum value of an int */
|
||||
|
||||
|
||||
/*
|
||||
** conversion of pointer to integer
|
||||
** floor of the log2 of the maximum signed value for integral type 't'.
|
||||
** (That is, maximum 'n' such that '2^n' fits in the given signed type.)
|
||||
*/
|
||||
#define log2maxs(t) (sizeof(t) * 8 - 2)
|
||||
|
||||
|
||||
/*
|
||||
** test whether an unsigned value is a power of 2 (or zero)
|
||||
*/
|
||||
#define ispow2(x) (((x) & ((x) - 1)) == 0)
|
||||
|
||||
|
||||
/* number of chars of a literal string without the ending \0 */
|
||||
#define LL(x) (sizeof(x)/sizeof(char) - 1)
|
||||
|
||||
|
||||
/*
|
||||
** conversion of pointer to unsigned integer:
|
||||
** this is for hashing only; there is no problem if the integer
|
||||
** cannot hold the whole pointer value
|
||||
*/
|
||||
#define IntPoint(p) ((unsigned int)(lu_mem)(p))
|
||||
#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX))
|
||||
|
||||
|
||||
|
||||
/* type to ensure maximum alignment */
|
||||
typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
|
||||
|
||||
|
||||
/* result of a `usual argument conversion' over lua_Number */
|
||||
/* types of 'usual argument conversions' for lua_Number and lua_Integer */
|
||||
typedef LUAI_UACNUMBER l_uacNumber;
|
||||
typedef LUAI_UACINT l_uacInt;
|
||||
|
||||
|
||||
#define check_exp(c,e) (lua_assert(c), (e))
|
||||
/* internal assertions for in-house debugging */
|
||||
#if defined(lua_assert)
|
||||
#define check_exp(c,e) (lua_assert(c), (e))
|
||||
/* to avoid problems with conditions too long */
|
||||
#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0))
|
||||
#else
|
||||
#define lua_assert(c) ((void)0)
|
||||
#define check_exp(c,e) (e)
|
||||
#define lua_longassert(c) ((void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** assertion for checking API calls
|
||||
*/
|
||||
#if !defined(luai_apicheck)
|
||||
#define luai_apicheck(l,e) lua_assert(e)
|
||||
#endif
|
||||
|
||||
#define api_check(l,e,msg) luai_apicheck(l,(e) && msg)
|
||||
|
||||
|
||||
#ifndef UNUSED
|
||||
#define UNUSED(x) ((void)(x)) /* to avoid warnings */
|
||||
/* macro to avoid warnings about unused variables */
|
||||
#if !defined(UNUSED)
|
||||
#define UNUSED(x) ((void)(x))
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef cast
|
||||
/* type casts (a macro highlights casts in the code) */
|
||||
#define cast(t, exp) ((t)(exp))
|
||||
|
||||
#define cast_void(i) cast(void, (i))
|
||||
#define cast_voidp(i) cast(void *, (i))
|
||||
#define cast_num(i) cast(lua_Number, (i))
|
||||
#define cast_int(i) cast(int, (i))
|
||||
#define cast_uint(i) cast(unsigned int, (i))
|
||||
#define cast_byte(i) cast(lu_byte, (i))
|
||||
#define cast_uchar(i) cast(unsigned char, (i))
|
||||
#define cast_char(i) cast(char, (i))
|
||||
#define cast_charp(i) cast(char *, (i))
|
||||
#define cast_sizet(i) cast(size_t, (i))
|
||||
|
||||
|
||||
/* cast a signed lua_Integer to lua_Unsigned */
|
||||
#if !defined(l_castS2U)
|
||||
#define l_castS2U(i) ((lua_Unsigned)(i))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** cast a lua_Unsigned to a signed lua_Integer; this cast is
|
||||
** not strict ISO C, but two-complement architectures should
|
||||
** work fine.
|
||||
*/
|
||||
#if !defined(l_castU2S)
|
||||
#define l_castU2S(i) ((lua_Integer)(i))
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** macros to improve jump prediction (used mainly for error handling)
|
||||
*/
|
||||
#if !defined(likely)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define likely(x) (__builtin_expect(((x) != 0), 1))
|
||||
#define unlikely(x) (__builtin_expect(((x) != 0), 0))
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** non-return type
|
||||
*/
|
||||
#if !defined(l_noret)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define l_noret void __attribute__((noreturn))
|
||||
#elif defined(_MSC_VER) && _MSC_VER >= 1200
|
||||
#define l_noret void __declspec(noreturn)
|
||||
#else
|
||||
#define l_noret void
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** type for virtual-machine instructions;
|
||||
** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
|
||||
*/
|
||||
#if LUAI_IS32INT
|
||||
typedef unsigned int l_uint32;
|
||||
#else
|
||||
typedef unsigned long l_uint32;
|
||||
#endif
|
||||
|
||||
typedef l_uint32 Instruction;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** type for virtual-machine instructions
|
||||
** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
|
||||
** Maximum length for short strings, that is, strings that are
|
||||
** internalized. (Cannot be smaller than reserved words or tags for
|
||||
** metamethods, as these strings must be internalized;
|
||||
** #("function") = 8, #("__newindex") = 10.)
|
||||
*/
|
||||
typedef lu_int32 Instruction;
|
||||
#if !defined(LUAI_MAXSHORTLEN)
|
||||
#define LUAI_MAXSHORTLEN 40
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* maximum stack for a Lua function */
|
||||
#define MAXSTACK 250
|
||||
/*
|
||||
** Initial size for the string table (must be power of 2).
|
||||
** The Lua core alone registers ~50 strings (reserved words +
|
||||
** metaevent keys + a few others). Libraries would typically add
|
||||
** a few dozens more.
|
||||
*/
|
||||
#if !defined(MINSTRTABSIZE)
|
||||
#define MINSTRTABSIZE 128
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* minimum size for the string table (must be power of 2) */
|
||||
#ifndef MINSTRTABSIZE
|
||||
#define MINSTRTABSIZE 32
|
||||
/*
|
||||
** Size of cache for strings in the API. 'N' is the number of
|
||||
** sets (better be a prime) and "M" is the size of each set (M == 1
|
||||
** makes a direct cache.)
|
||||
*/
|
||||
#if !defined(STRCACHE_N)
|
||||
#define STRCACHE_N 53
|
||||
#define STRCACHE_M 2
|
||||
#endif
|
||||
|
||||
|
||||
/* minimum size for string buffer */
|
||||
#ifndef LUA_MINBUFFER
|
||||
#if !defined(LUA_MINBUFFER)
|
||||
#define LUA_MINBUFFER 32
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef lua_lock
|
||||
#define lua_lock(L) ((void) 0)
|
||||
#define lua_unlock(L) ((void) 0)
|
||||
/*
|
||||
** macros that are executed whenever program enters the Lua core
|
||||
** ('lua_lock') and leaves the core ('lua_unlock')
|
||||
*/
|
||||
#if !defined(lua_lock)
|
||||
#define lua_lock(L) ((void) 0)
|
||||
#define lua_unlock(L) ((void) 0)
|
||||
#endif
|
||||
|
||||
#ifndef luai_threadyield
|
||||
#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
|
||||
/*
|
||||
** macro executed during Lua functions at points where the
|
||||
** function can yield.
|
||||
*/
|
||||
#if !defined(luai_threadyield)
|
||||
#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** these macros allow user-specific actions when a thread is
|
||||
** created/deleted/resumed/yielded.
|
||||
*/
|
||||
#if !defined(luai_userstateopen)
|
||||
#define luai_userstateopen(L) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstateclose)
|
||||
#define luai_userstateclose(L) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstatethread)
|
||||
#define luai_userstatethread(L,L1) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstatefree)
|
||||
#define luai_userstatefree(L,L1) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstateresume)
|
||||
#define luai_userstateresume(L,n) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstateyield)
|
||||
#define luai_userstateyield(L,n) ((void)L)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** The luai_num* macros define the primitive operations over numbers.
|
||||
*/
|
||||
|
||||
/* floor division (defined as 'floor(a/b)') */
|
||||
#if !defined(luai_numidiv)
|
||||
#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b)))
|
||||
#endif
|
||||
|
||||
/* float division */
|
||||
#if !defined(luai_numdiv)
|
||||
#define luai_numdiv(L,a,b) ((a)/(b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** modulo: defined as 'a - floor(a/b)*b'; the direct computation
|
||||
** using this definition has several problems with rounding errors,
|
||||
** so it is better to use 'fmod'. 'fmod' gives the result of
|
||||
** 'a - trunc(a/b)*b', and therefore must be corrected when
|
||||
** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a
|
||||
** non-integer negative result: non-integer result is equivalent to
|
||||
** a non-zero remainder 'm'; negative result is equivalent to 'a' and
|
||||
** 'b' with different signs, or 'm' and 'b' with different signs
|
||||
** (as the result 'm' of 'fmod' has the same sign of 'a').
|
||||
*/
|
||||
#if !defined(luai_nummod)
|
||||
#define luai_nummod(L,a,b,m) \
|
||||
{ (void)L; (m) = l_mathop(fmod)(a,b); \
|
||||
if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); }
|
||||
#endif
|
||||
|
||||
/* exponentiation */
|
||||
#if !defined(luai_numpow)
|
||||
#define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b))
|
||||
#endif
|
||||
|
||||
/* the others are quite standard operations */
|
||||
#if !defined(luai_numadd)
|
||||
#define luai_numadd(L,a,b) ((a)+(b))
|
||||
#define luai_numsub(L,a,b) ((a)-(b))
|
||||
#define luai_nummul(L,a,b) ((a)*(b))
|
||||
#define luai_numunm(L,a) (-(a))
|
||||
#define luai_numeq(a,b) ((a)==(b))
|
||||
#define luai_numlt(a,b) ((a)<(b))
|
||||
#define luai_numle(a,b) ((a)<=(b))
|
||||
#define luai_numgt(a,b) ((a)>(b))
|
||||
#define luai_numge(a,b) ((a)>=(b))
|
||||
#define luai_numisnan(a) (!luai_numeq((a), (a)))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** macro to control inclusion of some hard tests on stack reallocation
|
||||
*/
|
||||
#ifndef HARDSTACKTESTS
|
||||
#define condhardstacktests(x) ((void)0)
|
||||
*/
|
||||
#if !defined(HARDSTACKTESTS)
|
||||
#define condmovestack(L,pre,pos) ((void)0)
|
||||
#else
|
||||
#define condhardstacktests(x) x
|
||||
/* realloc stack keeping its size */
|
||||
#define condmovestack(L,pre,pos) \
|
||||
{ int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_, 0); pos; }
|
||||
#endif
|
||||
|
||||
#if !defined(HARDMEMTESTS)
|
||||
#define condchangemem(L,pre,pos) ((void)0)
|
||||
#else
|
||||
#define condchangemem(L,pre,pos) \
|
||||
{ if (G(L)->gcrunning) { pre; luaC_fullgc(L, 0); pos; } }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+608
-110
@@ -1,16 +1,21 @@
|
||||
/*
|
||||
** $Id: lmathlib.c,v 1.66 2005/08/15 14:12:32 roberto Exp roberto $
|
||||
** $Id: lmathlib.c $
|
||||
** Standard mathematical library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#define lmathlib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
@@ -18,229 +23,721 @@
|
||||
|
||||
|
||||
#undef PI
|
||||
#define PI (3.14159265358979323846)
|
||||
#define RADIANS_PER_DEGREE (PI/180.0)
|
||||
|
||||
#define PI (l_mathop(3.141592653589793238462643383279502884))
|
||||
|
||||
|
||||
static int math_abs (lua_State *L) {
|
||||
lua_pushnumber(L, fabs(luaL_checknumber(L, 1)));
|
||||
if (lua_isinteger(L, 1)) {
|
||||
lua_Integer n = lua_tointeger(L, 1);
|
||||
if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n);
|
||||
lua_pushinteger(L, n);
|
||||
}
|
||||
else
|
||||
lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_sin (lua_State *L) {
|
||||
lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_sinh (lua_State *L) {
|
||||
lua_pushnumber(L, sinh(luaL_checknumber(L, 1)));
|
||||
lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_cos (lua_State *L) {
|
||||
lua_pushnumber(L, cos(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_cosh (lua_State *L) {
|
||||
lua_pushnumber(L, cosh(luaL_checknumber(L, 1)));
|
||||
lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_tan (lua_State *L) {
|
||||
lua_pushnumber(L, tan(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_tanh (lua_State *L) {
|
||||
lua_pushnumber(L, tanh(luaL_checknumber(L, 1)));
|
||||
lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_asin (lua_State *L) {
|
||||
lua_pushnumber(L, asin(luaL_checknumber(L, 1)));
|
||||
lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_acos (lua_State *L) {
|
||||
lua_pushnumber(L, acos(luaL_checknumber(L, 1)));
|
||||
lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_atan (lua_State *L) {
|
||||
lua_pushnumber(L, atan(luaL_checknumber(L, 1)));
|
||||
lua_Number y = luaL_checknumber(L, 1);
|
||||
lua_Number x = luaL_optnumber(L, 2, 1);
|
||||
lua_pushnumber(L, l_mathop(atan2)(y, x));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_atan2 (lua_State *L) {
|
||||
lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
|
||||
|
||||
static int math_toint (lua_State *L) {
|
||||
int valid;
|
||||
lua_Integer n = lua_tointegerx(L, 1, &valid);
|
||||
if (valid)
|
||||
lua_pushinteger(L, n);
|
||||
else {
|
||||
luaL_checkany(L, 1);
|
||||
luaL_pushfail(L); /* value is not convertible to integer */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_ceil (lua_State *L) {
|
||||
lua_pushnumber(L, ceil(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
|
||||
static void pushnumint (lua_State *L, lua_Number d) {
|
||||
lua_Integer n;
|
||||
if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */
|
||||
lua_pushinteger(L, n); /* result is integer */
|
||||
else
|
||||
lua_pushnumber(L, d); /* result is float */
|
||||
}
|
||||
|
||||
|
||||
static int math_floor (lua_State *L) {
|
||||
lua_pushnumber(L, floor(luaL_checknumber(L, 1)));
|
||||
if (lua_isinteger(L, 1))
|
||||
lua_settop(L, 1); /* integer is its own floor */
|
||||
else {
|
||||
lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1));
|
||||
pushnumint(L, d);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_ceil (lua_State *L) {
|
||||
if (lua_isinteger(L, 1))
|
||||
lua_settop(L, 1); /* integer is its own ceil */
|
||||
else {
|
||||
lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1));
|
||||
pushnumint(L, d);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_fmod (lua_State *L) {
|
||||
lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
|
||||
if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) {
|
||||
lua_Integer d = lua_tointeger(L, 2);
|
||||
if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */
|
||||
luaL_argcheck(L, d != 0, 2, "zero");
|
||||
lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */
|
||||
}
|
||||
else
|
||||
lua_pushinteger(L, lua_tointeger(L, 1) % d);
|
||||
}
|
||||
else
|
||||
lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1),
|
||||
luaL_checknumber(L, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** next function does not use 'modf', avoiding problems with 'double*'
|
||||
** (which is not compatible with 'float*') when lua_Number is not
|
||||
** 'double'.
|
||||
*/
|
||||
static int math_modf (lua_State *L) {
|
||||
double ip;
|
||||
double fp = modf(luaL_checknumber(L, 1), &ip);
|
||||
lua_pushnumber(L, ip);
|
||||
lua_pushnumber(L, fp);
|
||||
if (lua_isinteger(L ,1)) {
|
||||
lua_settop(L, 1); /* number is its own integer part */
|
||||
lua_pushnumber(L, 0); /* no fractional part */
|
||||
}
|
||||
else {
|
||||
lua_Number n = luaL_checknumber(L, 1);
|
||||
/* integer part (rounds toward zero) */
|
||||
lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n);
|
||||
pushnumint(L, ip);
|
||||
/* fractional part (test needed for inf/-inf) */
|
||||
lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip));
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static int math_sqrt (lua_State *L) {
|
||||
lua_pushnumber(L, sqrt(luaL_checknumber(L, 1)));
|
||||
lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_pow (lua_State *L) {
|
||||
lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
|
||||
|
||||
static int math_ult (lua_State *L) {
|
||||
lua_Integer a = luaL_checkinteger(L, 1);
|
||||
lua_Integer b = luaL_checkinteger(L, 2);
|
||||
lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_log (lua_State *L) {
|
||||
lua_pushnumber(L, log(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_log10 (lua_State *L) {
|
||||
lua_pushnumber(L, log10(luaL_checknumber(L, 1)));
|
||||
lua_Number x = luaL_checknumber(L, 1);
|
||||
lua_Number res;
|
||||
if (lua_isnoneornil(L, 2))
|
||||
res = l_mathop(log)(x);
|
||||
else {
|
||||
lua_Number base = luaL_checknumber(L, 2);
|
||||
#if !defined(LUA_USE_C89)
|
||||
if (base == l_mathop(2.0))
|
||||
res = l_mathop(log2)(x); else
|
||||
#endif
|
||||
if (base == l_mathop(10.0))
|
||||
res = l_mathop(log10)(x);
|
||||
else
|
||||
res = l_mathop(log)(x)/l_mathop(log)(base);
|
||||
}
|
||||
lua_pushnumber(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_exp (lua_State *L) {
|
||||
lua_pushnumber(L, exp(luaL_checknumber(L, 1)));
|
||||
lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_deg (lua_State *L) {
|
||||
lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE);
|
||||
lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_rad (lua_State *L) {
|
||||
lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE);
|
||||
lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_frexp (lua_State *L) {
|
||||
int e;
|
||||
lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e));
|
||||
lua_pushinteger(L, e);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int math_ldexp (lua_State *L) {
|
||||
lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int math_min (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
lua_Number dmin = luaL_checknumber(L, 1);
|
||||
int imin = 1; /* index of current minimum value */
|
||||
int i;
|
||||
for (i=2; i<=n; i++) {
|
||||
lua_Number d = luaL_checknumber(L, i);
|
||||
if (d < dmin)
|
||||
dmin = d;
|
||||
luaL_argcheck(L, n >= 1, 1, "value expected");
|
||||
for (i = 2; i <= n; i++) {
|
||||
if (lua_compare(L, i, imin, LUA_OPLT))
|
||||
imin = i;
|
||||
}
|
||||
lua_pushnumber(L, dmin);
|
||||
lua_pushvalue(L, imin);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_max (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
lua_Number dmax = luaL_checknumber(L, 1);
|
||||
int imax = 1; /* index of current maximum value */
|
||||
int i;
|
||||
for (i=2; i<=n; i++) {
|
||||
lua_Number d = luaL_checknumber(L, i);
|
||||
if (d > dmax)
|
||||
dmax = d;
|
||||
luaL_argcheck(L, n >= 1, 1, "value expected");
|
||||
for (i = 2; i <= n; i++) {
|
||||
if (lua_compare(L, imax, i, LUA_OPLT))
|
||||
imax = i;
|
||||
}
|
||||
lua_pushnumber(L, dmax);
|
||||
lua_pushvalue(L, imax);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_type (lua_State *L) {
|
||||
if (lua_type(L, 1) == LUA_TNUMBER)
|
||||
lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float");
|
||||
else {
|
||||
luaL_checkany(L, 1);
|
||||
luaL_pushfail(L);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Pseudo-Random Number Generator based on 'xoshiro256**'.
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* number of binary digits in the mantissa of a float */
|
||||
#define FIGS l_mathlim(MANT_DIG)
|
||||
|
||||
#if FIGS > 64
|
||||
/* there are only 64 random bits; use them all */
|
||||
#undef FIGS
|
||||
#define FIGS 64
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** LUA_RAND32 forces the use of 32-bit integers in the implementation
|
||||
** of the PRN generator (mainly for testing).
|
||||
*/
|
||||
#if !defined(LUA_RAND32) && !defined(Rand64)
|
||||
|
||||
/* try to find an integer type with at least 64 bits */
|
||||
|
||||
#if (ULONG_MAX >> 31 >> 31) >= 3
|
||||
|
||||
/* 'long' has at least 64 bits */
|
||||
#define Rand64 unsigned long
|
||||
|
||||
#elif !defined(LUA_USE_C89) && defined(LLONG_MAX)
|
||||
|
||||
/* there is a 'long long' type (which must have at least 64 bits) */
|
||||
#define Rand64 unsigned long long
|
||||
|
||||
#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3
|
||||
|
||||
/* 'lua_Integer' has at least 64 bits */
|
||||
#define Rand64 lua_Unsigned
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(Rand64) /* { */
|
||||
|
||||
/*
|
||||
** Standard implementation, using 64-bit integers.
|
||||
** If 'Rand64' has more than 64 bits, the extra bits do not interfere
|
||||
** with the 64 initial bits, except in a right shift. Moreover, the
|
||||
** final result has to discard the extra bits.
|
||||
*/
|
||||
|
||||
/* avoid using extra bits when needed */
|
||||
#define trim64(x) ((x) & 0xffffffffffffffffu)
|
||||
|
||||
|
||||
/* rotate left 'x' by 'n' bits */
|
||||
static Rand64 rotl (Rand64 x, int n) {
|
||||
return (x << n) | (trim64(x) >> (64 - n));
|
||||
}
|
||||
|
||||
static Rand64 nextrand (Rand64 *state) {
|
||||
Rand64 state0 = state[0];
|
||||
Rand64 state1 = state[1];
|
||||
Rand64 state2 = state[2] ^ state0;
|
||||
Rand64 state3 = state[3] ^ state1;
|
||||
Rand64 res = rotl(state1 * 5, 7) * 9;
|
||||
state[0] = state0 ^ state3;
|
||||
state[1] = state1 ^ state2;
|
||||
state[2] = state2 ^ (state1 << 17);
|
||||
state[3] = rotl(state3, 45);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* must take care to not shift stuff by more than 63 slots */
|
||||
|
||||
|
||||
/*
|
||||
** Convert bits from a random integer into a float in the
|
||||
** interval [0,1), getting the higher FIG bits from the
|
||||
** random unsigned integer and converting that to a float.
|
||||
*/
|
||||
|
||||
/* must throw out the extra (64 - FIGS) bits */
|
||||
#define shift64_FIG (64 - FIGS)
|
||||
|
||||
/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */
|
||||
#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
|
||||
|
||||
static lua_Number I2d (Rand64 x) {
|
||||
return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG;
|
||||
}
|
||||
|
||||
/* convert a 'Rand64' to a 'lua_Unsigned' */
|
||||
#define I2UInt(x) ((lua_Unsigned)trim64(x))
|
||||
|
||||
/* convert a 'lua_Unsigned' to a 'Rand64' */
|
||||
#define Int2I(x) ((Rand64)(x))
|
||||
|
||||
|
||||
#else /* no 'Rand64' }{ */
|
||||
|
||||
/* get an integer with at least 32 bits */
|
||||
#if LUAI_IS32INT
|
||||
typedef unsigned int lu_int32;
|
||||
#else
|
||||
typedef unsigned long lu_int32;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Use two 32-bit integers to represent a 64-bit quantity.
|
||||
*/
|
||||
typedef struct Rand64 {
|
||||
lu_int32 h; /* higher half */
|
||||
lu_int32 l; /* lower half */
|
||||
} Rand64;
|
||||
|
||||
|
||||
/*
|
||||
** If 'lu_int32' has more than 32 bits, the extra bits do not interfere
|
||||
** with the 32 initial bits, except in a right shift and comparisons.
|
||||
** Moreover, the final result has to discard the extra bits.
|
||||
*/
|
||||
|
||||
/* avoid using extra bits when needed */
|
||||
#define trim32(x) ((x) & 0xffffffffu)
|
||||
|
||||
|
||||
/*
|
||||
** basic operations on 'Rand64' values
|
||||
*/
|
||||
|
||||
/* build a new Rand64 value */
|
||||
static Rand64 packI (lu_int32 h, lu_int32 l) {
|
||||
Rand64 result;
|
||||
result.h = h;
|
||||
result.l = l;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* return i << n */
|
||||
static Rand64 Ishl (Rand64 i, int n) {
|
||||
lua_assert(n > 0 && n < 32);
|
||||
return packI((i.h << n) | (trim32(i.l) >> (32 - n)), i.l << n);
|
||||
}
|
||||
|
||||
/* i1 ^= i2 */
|
||||
static void Ixor (Rand64 *i1, Rand64 i2) {
|
||||
i1->h ^= i2.h;
|
||||
i1->l ^= i2.l;
|
||||
}
|
||||
|
||||
/* return i1 + i2 */
|
||||
static Rand64 Iadd (Rand64 i1, Rand64 i2) {
|
||||
Rand64 result = packI(i1.h + i2.h, i1.l + i2.l);
|
||||
if (trim32(result.l) < trim32(i1.l)) /* carry? */
|
||||
result.h++;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* return i * 5 */
|
||||
static Rand64 times5 (Rand64 i) {
|
||||
return Iadd(Ishl(i, 2), i); /* i * 5 == (i << 2) + i */
|
||||
}
|
||||
|
||||
/* return i * 9 */
|
||||
static Rand64 times9 (Rand64 i) {
|
||||
return Iadd(Ishl(i, 3), i); /* i * 9 == (i << 3) + i */
|
||||
}
|
||||
|
||||
/* return 'i' rotated left 'n' bits */
|
||||
static Rand64 rotl (Rand64 i, int n) {
|
||||
lua_assert(n > 0 && n < 32);
|
||||
return packI((i.h << n) | (trim32(i.l) >> (32 - n)),
|
||||
(trim32(i.h) >> (32 - n)) | (i.l << n));
|
||||
}
|
||||
|
||||
/* for offsets larger than 32, rotate right by 64 - offset */
|
||||
static Rand64 rotl1 (Rand64 i, int n) {
|
||||
lua_assert(n > 32 && n < 64);
|
||||
n = 64 - n;
|
||||
return packI((trim32(i.h) >> n) | (i.l << (32 - n)),
|
||||
(i.h << (32 - n)) | (trim32(i.l) >> n));
|
||||
}
|
||||
|
||||
/*
|
||||
** implementation of 'xoshiro256**' algorithm on 'Rand64' values
|
||||
*/
|
||||
static Rand64 nextrand (Rand64 *state) {
|
||||
Rand64 res = times9(rotl(times5(state[1]), 7));
|
||||
Rand64 t = Ishl(state[1], 17);
|
||||
Ixor(&state[2], state[0]);
|
||||
Ixor(&state[3], state[1]);
|
||||
Ixor(&state[1], state[2]);
|
||||
Ixor(&state[0], state[3]);
|
||||
Ixor(&state[2], t);
|
||||
state[3] = rotl1(state[3], 45);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Converts a 'Rand64' into a float.
|
||||
*/
|
||||
|
||||
/* an unsigned 1 with proper type */
|
||||
#define UONE ((lu_int32)1)
|
||||
|
||||
|
||||
#if FIGS <= 32
|
||||
|
||||
/* 2^(-FIGS) */
|
||||
#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1)))
|
||||
|
||||
/*
|
||||
** get up to 32 bits from higher half, shifting right to
|
||||
** throw out the extra bits.
|
||||
*/
|
||||
static lua_Number I2d (Rand64 x) {
|
||||
lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS));
|
||||
return h * scaleFIG;
|
||||
}
|
||||
|
||||
#else /* 32 < FIGS <= 64 */
|
||||
|
||||
/* must take care to not shift stuff by more than 31 slots */
|
||||
|
||||
/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
|
||||
#define scaleFIG \
|
||||
((lua_Number)1.0 / (UONE << 30) / 8.0 / (UONE << (FIGS - 33)))
|
||||
|
||||
/*
|
||||
** use FIGS - 32 bits from lower half, throwing out the other
|
||||
** (32 - (FIGS - 32)) = (64 - FIGS) bits
|
||||
*/
|
||||
#define shiftLOW (64 - FIGS)
|
||||
|
||||
/*
|
||||
** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32)
|
||||
*/
|
||||
#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * 2.0)
|
||||
|
||||
|
||||
static lua_Number I2d (Rand64 x) {
|
||||
lua_Number h = (lua_Number)trim32(x.h) * shiftHI;
|
||||
lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW);
|
||||
return (h + l) * scaleFIG;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* convert a 'Rand64' to a 'lua_Unsigned' */
|
||||
static lua_Unsigned I2UInt (Rand64 x) {
|
||||
return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l);
|
||||
}
|
||||
|
||||
/* convert a 'lua_Unsigned' to a 'Rand64' */
|
||||
static Rand64 Int2I (lua_Unsigned n) {
|
||||
return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n);
|
||||
}
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** A state uses four 'Rand64' values.
|
||||
*/
|
||||
typedef struct {
|
||||
Rand64 s[4];
|
||||
} RanState;
|
||||
|
||||
|
||||
/*
|
||||
** Project the random integer 'ran' into the interval [0, n].
|
||||
** Because 'ran' has 2^B possible values, the projection can only be
|
||||
** uniform when the size of the interval is a power of 2 (exact
|
||||
** division). To get a uniform projection into [0, n], we first compute
|
||||
** 'lim', the smallest Mersenne number not smaller than 'n'. We then
|
||||
** project 'ran' into the interval [0, lim]. If the result is inside
|
||||
** [0, n], we are done. Otherwise, we try with another 'ran', until we
|
||||
** have a result inside the interval.
|
||||
*/
|
||||
static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n,
|
||||
RanState *state) {
|
||||
lua_Unsigned lim = n;
|
||||
if ((lim & (lim + 1)) > 0) { /* 'lim + 1' is not a power of 2? */
|
||||
/* compute the smallest (2^b - 1) not smaller than 'n' */
|
||||
lim |= (lim >> 1);
|
||||
lim |= (lim >> 2);
|
||||
lim |= (lim >> 4);
|
||||
lim |= (lim >> 8);
|
||||
lim |= (lim >> 16);
|
||||
#if (LUA_MAXUNSIGNED >> 31) >= 3
|
||||
lim |= (lim >> 32); /* integer type has more than 32 bits */
|
||||
#endif
|
||||
}
|
||||
lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */
|
||||
&& lim >= n /* not smaller than 'n', */
|
||||
&& (lim == 0 || (lim >> 1) < n)); /* and it is the smallest one */
|
||||
while ((ran &= lim) > n) /* project 'ran' into [0..lim] */
|
||||
ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */
|
||||
return ran;
|
||||
}
|
||||
|
||||
|
||||
static int math_random (lua_State *L) {
|
||||
/* the `%' avoids the (rare) case of r==1, and is needed also because on
|
||||
some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
|
||||
lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
|
||||
lua_Integer low, up;
|
||||
lua_Unsigned p;
|
||||
RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
|
||||
Rand64 rv = nextrand(state->s); /* next pseudo-random value */
|
||||
switch (lua_gettop(L)) { /* check number of arguments */
|
||||
case 0: { /* no arguments */
|
||||
lua_pushnumber(L, r); /* Number between 0 and 1 */
|
||||
break;
|
||||
lua_pushnumber(L, I2d(rv)); /* float between 0 and 1 */
|
||||
return 1;
|
||||
}
|
||||
case 1: { /* only upper limit */
|
||||
int u = luaL_checkint(L, 1);
|
||||
luaL_argcheck(L, 1<=u, 1, "interval is empty");
|
||||
lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */
|
||||
low = 1;
|
||||
up = luaL_checkinteger(L, 1);
|
||||
if (up == 0) { /* single 0 as argument? */
|
||||
lua_pushinteger(L, I2UInt(rv)); /* full random integer */
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: { /* lower and upper limits */
|
||||
int l = luaL_checkint(L, 1);
|
||||
int u = luaL_checkint(L, 2);
|
||||
luaL_argcheck(L, l<=u, 2, "interval is empty");
|
||||
lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */
|
||||
low = luaL_checkinteger(L, 1);
|
||||
up = luaL_checkinteger(L, 2);
|
||||
break;
|
||||
}
|
||||
default: return luaL_error(L, "wrong number of arguments");
|
||||
}
|
||||
/* random integer in the interval [low, up] */
|
||||
luaL_argcheck(L, low <= up, 1, "interval is empty");
|
||||
/* project random integer into the interval [0, up - low] */
|
||||
p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state);
|
||||
lua_pushinteger(L, p + (lua_Unsigned)low);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_randomseed (lua_State *L) {
|
||||
srand(luaL_checkint(L, 1));
|
||||
return 0;
|
||||
static void setseed (lua_State *L, Rand64 *state,
|
||||
lua_Unsigned n1, lua_Unsigned n2) {
|
||||
int i;
|
||||
state[0] = Int2I(n1);
|
||||
state[1] = Int2I(0xff); /* avoid a zero state */
|
||||
state[2] = Int2I(n2);
|
||||
state[3] = Int2I(0);
|
||||
for (i = 0; i < 16; i++)
|
||||
nextrand(state); /* discard initial values to "spread" seed */
|
||||
lua_pushinteger(L, n1);
|
||||
lua_pushinteger(L, n2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Set a "random" seed. To get some randomness, use the current time
|
||||
** and the address of 'L' (in case the machine does address space layout
|
||||
** randomization).
|
||||
*/
|
||||
static void randseed (lua_State *L, RanState *state) {
|
||||
lua_Unsigned seed1 = (lua_Unsigned)time(NULL);
|
||||
lua_Unsigned seed2 = (lua_Unsigned)(size_t)L;
|
||||
setseed(L, state->s, seed1, seed2);
|
||||
}
|
||||
|
||||
|
||||
static int math_randomseed (lua_State *L) {
|
||||
RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
|
||||
if (lua_isnone(L, 1)) {
|
||||
randseed(L, state);
|
||||
}
|
||||
else {
|
||||
lua_Integer n1 = luaL_checkinteger(L, 1);
|
||||
lua_Integer n2 = luaL_optinteger(L, 2, 0);
|
||||
setseed(L, state->s, n1, n2);
|
||||
}
|
||||
return 2; /* return seeds */
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg randfuncs[] = {
|
||||
{"random", math_random},
|
||||
{"randomseed", math_randomseed},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Register the random functions and initialize their state.
|
||||
*/
|
||||
static void setrandfunc (lua_State *L) {
|
||||
RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0);
|
||||
randseed(L, state); /* initialize with a "random" seed */
|
||||
lua_pop(L, 2); /* remove pushed seeds */
|
||||
luaL_setfuncs(L, randfuncs, 1);
|
||||
}
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Deprecated functions (for compatibility only)
|
||||
** ===================================================================
|
||||
*/
|
||||
#if defined(LUA_COMPAT_MATHLIB)
|
||||
|
||||
static int math_cosh (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_sinh (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_tanh (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_pow (lua_State *L) {
|
||||
lua_Number x = luaL_checknumber(L, 1);
|
||||
lua_Number y = luaL_checknumber(L, 2);
|
||||
lua_pushnumber(L, l_mathop(pow)(x, y));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_frexp (lua_State *L) {
|
||||
int e;
|
||||
lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e));
|
||||
lua_pushinteger(L, e);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int math_ldexp (lua_State *L) {
|
||||
lua_Number x = luaL_checknumber(L, 1);
|
||||
int ep = (int)luaL_checkinteger(L, 2);
|
||||
lua_pushnumber(L, l_mathop(ldexp)(x, ep));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_log10 (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
|
||||
static const luaL_Reg mathlib[] = {
|
||||
{"abs", math_abs},
|
||||
{"acos", math_acos},
|
||||
{"asin", math_asin},
|
||||
{"atan2", math_atan2},
|
||||
{"atan", math_atan},
|
||||
{"ceil", math_ceil},
|
||||
{"cosh", math_cosh},
|
||||
{"cos", math_cos},
|
||||
{"deg", math_deg},
|
||||
{"exp", math_exp},
|
||||
{"tointeger", math_toint},
|
||||
{"floor", math_floor},
|
||||
{"fmod", math_fmod},
|
||||
{"frexp", math_frexp},
|
||||
{"ldexp", math_ldexp},
|
||||
{"log10", math_log10},
|
||||
{"ult", math_ult},
|
||||
{"log", math_log},
|
||||
{"max", math_max},
|
||||
{"min", math_min},
|
||||
{"modf", math_modf},
|
||||
{"pow", math_pow},
|
||||
{"rad", math_rad},
|
||||
{"random", math_random},
|
||||
{"randomseed", math_randomseed},
|
||||
{"sinh", math_sinh},
|
||||
{"sin", math_sin},
|
||||
{"sqrt", math_sqrt},
|
||||
{"tanh", math_tanh},
|
||||
{"tan", math_tan},
|
||||
{"type", math_type},
|
||||
#if defined(LUA_COMPAT_MATHLIB)
|
||||
{"atan2", math_atan},
|
||||
{"cosh", math_cosh},
|
||||
{"sinh", math_sinh},
|
||||
{"tanh", math_tanh},
|
||||
{"pow", math_pow},
|
||||
{"frexp", math_frexp},
|
||||
{"ldexp", math_ldexp},
|
||||
{"log10", math_log10},
|
||||
#endif
|
||||
/* placeholders */
|
||||
{"random", NULL},
|
||||
{"randomseed", NULL},
|
||||
{"pi", NULL},
|
||||
{"huge", NULL},
|
||||
{"maxinteger", NULL},
|
||||
{"mininteger", NULL},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -248,16 +745,17 @@ static const luaL_Reg mathlib[] = {
|
||||
/*
|
||||
** Open math library
|
||||
*/
|
||||
LUALIB_API int luaopen_math (lua_State *L) {
|
||||
luaL_register(L, LUA_MATHLIBNAME, mathlib);
|
||||
LUAMOD_API int luaopen_math (lua_State *L) {
|
||||
luaL_newlib(L, mathlib);
|
||||
lua_pushnumber(L, PI);
|
||||
lua_setfield(L, -2, "pi");
|
||||
lua_pushnumber(L, HUGE_VAL);
|
||||
lua_pushnumber(L, (lua_Number)HUGE_VAL);
|
||||
lua_setfield(L, -2, "huge");
|
||||
#if defined(LUA_COMPAT_MOD)
|
||||
lua_getfield(L, -1, "fmod");
|
||||
lua_setfield(L, -2, "mod");
|
||||
#endif
|
||||
lua_pushinteger(L, LUA_MAXINTEGER);
|
||||
lua_setfield(L, -2, "maxinteger");
|
||||
lua_pushinteger(L, LUA_MININTEGER);
|
||||
lua_setfield(L, -2, "mininteger");
|
||||
setrandfunc(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,102 +1,202 @@
|
||||
/*
|
||||
** $Id: lmem.c,v 1.68 2005/01/14 14:21:16 roberto Exp roberto $
|
||||
** $Id: lmem.c $
|
||||
** Interface to Memory Manager
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define lmem_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lgc.h"
|
||||
#include "lmem.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
#if defined(HARDMEMTESTS)
|
||||
/*
|
||||
** First allocation will fail whenever not building initial state
|
||||
** and not shrinking a block. (This fail will trigger 'tryagain' and
|
||||
** a full GC cycle at every alocation.)
|
||||
*/
|
||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
if (ttisnil(&g->nilvalue) && ns > os)
|
||||
return NULL; /* fail */
|
||||
else /* normal allocation */
|
||||
return (*g->frealloc)(g->ud, block, os, ns);
|
||||
}
|
||||
#else
|
||||
#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** About the realloc function:
|
||||
** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
** (`osize' is the old size, `nsize' is the new size)
|
||||
** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
** ('osize' is the old size, 'nsize' is the new size)
|
||||
**
|
||||
** Lua ensures that (ptr == NULL) iff (osize == 0).
|
||||
** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL.
|
||||
** Particularly, frealloc(ud, NULL, 0, 0) does nothing,
|
||||
** which is equivalent to free(NULL) in ISO C.
|
||||
**
|
||||
** * frealloc(ud, NULL, 0, x) creates a new block of size `x'
|
||||
** - frealloc(ud, NULL, x, s) creates a new block of size 's'
|
||||
** (no matter 'x'). Returns NULL if it cannot create the new block.
|
||||
**
|
||||
** * frealloc(ud, p, x, 0) frees the block `p'
|
||||
** (in this specific case, frealloc must return NULL).
|
||||
** particularly, frealloc(ud, NULL, 0, 0) does nothing
|
||||
** (which is equivalent to free(NULL) in ANSI C)
|
||||
**
|
||||
** frealloc returns NULL if it cannot create or reallocate the area
|
||||
** (any reallocation to an equal or smaller size cannot fail!)
|
||||
** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from
|
||||
** size 'x' to size 'y'. Returns NULL if it cannot reallocate the
|
||||
** block to the new size.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Functions to allocate/deallocate arrays for the Parser
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Minimum size for arrays during parsing, to avoid overhead of
|
||||
** reallocating to size 1, then 2, and then 4. All these arrays
|
||||
** will be reallocated to exact sizes or erased when parsing ends.
|
||||
*/
|
||||
#define MINSIZEARRAY 4
|
||||
|
||||
|
||||
void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,
|
||||
int limit, const char *errormsg) {
|
||||
void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
|
||||
int size_elems, int limit, const char *what) {
|
||||
void *newblock;
|
||||
int newsize;
|
||||
if (*size >= limit/2) { /* cannot double it? */
|
||||
if (*size >= limit) /* cannot grow even a little? */
|
||||
luaG_runerror(L, errormsg);
|
||||
newsize = limit; /* still have at least one free place */
|
||||
int size = *psize;
|
||||
if (nelems + 1 <= size) /* does one extra element still fit? */
|
||||
return block; /* nothing to be done */
|
||||
if (size >= limit / 2) { /* cannot double it? */
|
||||
if (unlikely(size >= limit)) /* cannot grow even a little? */
|
||||
luaG_runerror(L, "too many %s (limit is %d)", what, limit);
|
||||
size = limit; /* still have at least one free place */
|
||||
}
|
||||
else {
|
||||
newsize = (*size)*2;
|
||||
if (newsize < MINSIZEARRAY)
|
||||
newsize = MINSIZEARRAY; /* minimum size */
|
||||
size *= 2;
|
||||
if (size < MINSIZEARRAY)
|
||||
size = MINSIZEARRAY; /* minimum size */
|
||||
}
|
||||
newblock = luaM_reallocv(L, block, *size, newsize, size_elems);
|
||||
*size = newsize; /* update only when everything else is OK */
|
||||
lua_assert(nelems + 1 <= size && size <= limit);
|
||||
/* 'limit' ensures that multiplication will not overflow */
|
||||
newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems,
|
||||
cast_sizet(size) * size_elems);
|
||||
*psize = size; /* update only when everything else is OK */
|
||||
return newblock;
|
||||
}
|
||||
|
||||
|
||||
void *luaM_toobig (lua_State *L) {
|
||||
luaG_runerror(L, "memory allocation error: block too big");
|
||||
return NULL; /* to avoid warnings */
|
||||
/*
|
||||
** In prototypes, the size of the array is also its number of
|
||||
** elements (to save memory). So, if it cannot shrink an array
|
||||
** to its number of elements, the only option is to raise an
|
||||
** error.
|
||||
*/
|
||||
void *luaM_shrinkvector_ (lua_State *L, void *block, int *size,
|
||||
int final_n, int size_elem) {
|
||||
void *newblock;
|
||||
size_t oldsize = cast_sizet((*size) * size_elem);
|
||||
size_t newsize = cast_sizet(final_n * size_elem);
|
||||
lua_assert(newsize <= oldsize);
|
||||
newblock = luaM_saferealloc_(L, block, oldsize, newsize);
|
||||
*size = final_n;
|
||||
return newblock;
|
||||
}
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
l_noret luaM_toobig (lua_State *L) {
|
||||
luaG_runerror(L, "memory allocation error: block too big");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** generic allocation routine.
|
||||
** Free memory
|
||||
*/
|
||||
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||
void luaM_free_ (lua_State *L, void *block, size_t osize) {
|
||||
global_State *g = G(L);
|
||||
lua_assert((osize == 0) == (block == NULL));
|
||||
block = (*g->frealloc)(g->ud, block, osize, nsize);
|
||||
if (block == NULL && nsize > 0)
|
||||
luaD_throw(L, LUA_ERRMEM);
|
||||
lua_assert((nsize == 0) == (block == NULL));
|
||||
g->totalbytes = (g->totalbytes - osize) + nsize;
|
||||
#if 0
|
||||
{ /* auxiliar patch to monitor garbage collection */
|
||||
static unsigned long total = 0; /* our "time" */
|
||||
static lu_mem last = 0; /* last totalmem that generated an output */
|
||||
static FILE *f = NULL; /* output file */
|
||||
if (nsize <= osize) total += 1; /* "time" always grow */
|
||||
else total += (nsize - osize);
|
||||
if ((int)g->totalbytes - (int)last > 1000 ||
|
||||
(int)g->totalbytes - (int)last < -1000) {
|
||||
last = g->totalbytes;
|
||||
if (f == NULL) f = fopen("trace", "w");
|
||||
fprintf(f, "%lu %u %u %u %d\n", total, g->totalbytes, g->GCthreshold,
|
||||
g->estimate, g->gcstate);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return block;
|
||||
(*g->frealloc)(g->ud, block, osize, 0);
|
||||
g->GCdebt -= osize;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** In case of allocation fail, this function will call the GC to try
|
||||
** to free some memory and then try the allocation again.
|
||||
** (It should not be called when shrinking a block, because then the
|
||||
** interpreter may be in the middle of a collection step.)
|
||||
*/
|
||||
static void *tryagain (lua_State *L, void *block,
|
||||
size_t osize, size_t nsize) {
|
||||
global_State *g = G(L);
|
||||
if (ttisnil(&g->nilvalue)) { /* is state fully build? */
|
||||
luaC_fullgc(L, 1); /* try to free some memory... */
|
||||
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
||||
}
|
||||
else return NULL; /* cannot free any memory without a full state */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generic allocation routine.
|
||||
** If allocation fails while shrinking a block, do not try again; the
|
||||
** GC shrinks some blocks and it is not reentrant.
|
||||
*/
|
||||
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||
void *newblock;
|
||||
global_State *g = G(L);
|
||||
lua_assert((osize == 0) == (block == NULL));
|
||||
newblock = firsttry(g, block, osize, nsize);
|
||||
if (unlikely(newblock == NULL && nsize > 0)) {
|
||||
if (nsize > osize) /* not shrinking a block? */
|
||||
newblock = tryagain(L, block, osize, nsize);
|
||||
if (newblock == NULL) /* still no memory? */
|
||||
return NULL; /* do not update 'GCdebt' */
|
||||
}
|
||||
lua_assert((nsize == 0) == (newblock == NULL));
|
||||
g->GCdebt = (g->GCdebt + nsize) - osize;
|
||||
return newblock;
|
||||
}
|
||||
|
||||
|
||||
void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize,
|
||||
size_t nsize) {
|
||||
void *newblock = luaM_realloc_(L, block, osize, nsize);
|
||||
if (unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */
|
||||
luaM_error(L);
|
||||
return newblock;
|
||||
}
|
||||
|
||||
|
||||
void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
|
||||
if (size == 0)
|
||||
return NULL; /* that's all */
|
||||
else {
|
||||
global_State *g = G(L);
|
||||
void *newblock = firsttry(g, NULL, tag, size);
|
||||
if (unlikely(newblock == NULL)) {
|
||||
newblock = tryagain(L, NULL, tag, size);
|
||||
if (newblock == NULL)
|
||||
luaM_error(L);
|
||||
}
|
||||
g->GCdebt += size;
|
||||
return newblock;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lmem.h,v 1.30 2005/03/18 16:38:02 roberto Exp roberto $
|
||||
** $Id: lmem.h $
|
||||
** Interface to Memory Manager
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -13,37 +13,81 @@
|
||||
#include "llimits.h"
|
||||
#include "lua.h"
|
||||
|
||||
#define MEMERRMSG "not enough memory"
|
||||
|
||||
#define luaM_error(L) luaD_throw(L, LUA_ERRMEM)
|
||||
|
||||
|
||||
#define luaM_reallocv(L,b,on,n,e) \
|
||||
((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \
|
||||
luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \
|
||||
luaM_toobig(L))
|
||||
/*
|
||||
** This macro tests whether it is safe to multiply 'n' by the size of
|
||||
** type 't' without overflows. Because 'e' is always constant, it avoids
|
||||
** the runtime division MAX_SIZET/(e).
|
||||
** (The macro is somewhat complex to avoid warnings: The 'sizeof'
|
||||
** comparison avoids a runtime comparison when overflow cannot occur.
|
||||
** The compiler should be able to optimize the real test by itself, but
|
||||
** when it does it, it may give a warning about "comparison is always
|
||||
** false due to limited range of data type"; the +1 tricks the compiler,
|
||||
** avoiding this warning but also this optimization.)
|
||||
*/
|
||||
#define luaM_testsize(n,e) \
|
||||
(sizeof(n) >= sizeof(size_t) && cast_sizet((n)) + 1 > MAX_SIZET/(e))
|
||||
|
||||
#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)
|
||||
#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0)
|
||||
#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t))
|
||||
#define luaM_checksize(L,n,e) \
|
||||
(luaM_testsize(n,e) ? luaM_toobig(L) : cast_void(0))
|
||||
|
||||
#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t))
|
||||
#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t)))
|
||||
#define luaM_newvector(L,n,t) \
|
||||
cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t)))
|
||||
|
||||
/*
|
||||
** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that
|
||||
** the result is not larger than 'n' and cannot overflow a 'size_t'
|
||||
** when multiplied by the size of type 't'. (Assumes that 'n' is an
|
||||
** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.)
|
||||
*/
|
||||
#define luaM_limitN(n,t) \
|
||||
((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \
|
||||
cast_uint((MAX_SIZET/sizeof(t))))
|
||||
|
||||
|
||||
/*
|
||||
** Arrays of chars do not need any test
|
||||
*/
|
||||
#define luaM_reallocvchar(L,b,on,n) \
|
||||
cast_charp(luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char)))
|
||||
|
||||
#define luaM_freemem(L, b, s) luaM_free_(L, (b), (s))
|
||||
#define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b)))
|
||||
#define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b)))
|
||||
|
||||
#define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0))
|
||||
#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0))
|
||||
#define luaM_newvectorchecked(L,n,t) \
|
||||
(luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t))
|
||||
|
||||
#define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag)
|
||||
|
||||
#define luaM_growvector(L,v,nelems,size,t,limit,e) \
|
||||
if ((nelems)+1 > (size)) \
|
||||
((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
|
||||
((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \
|
||||
luaM_limitN(limit,t),e)))
|
||||
|
||||
#define luaM_reallocvector(L, v,oldn,n,t) \
|
||||
((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))
|
||||
(cast(t *, luaM_realloc_(L, v, cast_sizet(oldn) * sizeof(t), \
|
||||
cast_sizet(n) * sizeof(t))))
|
||||
|
||||
#define luaM_shrinkvector(L,v,size,fs,t) \
|
||||
((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t))))
|
||||
|
||||
LUAI_FUNC l_noret luaM_toobig (lua_State *L);
|
||||
|
||||
/* not to be called directly */
|
||||
LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,
|
||||
size_t size);
|
||||
LUAI_FUNC void *luaM_toobig (lua_State *L);
|
||||
LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size,
|
||||
size_t size_elem, int limit,
|
||||
const char *errormsg);
|
||||
LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize,
|
||||
size_t size);
|
||||
LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize);
|
||||
LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems,
|
||||
int *size, int size_elem, int limit,
|
||||
const char *what);
|
||||
LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem,
|
||||
int final_n, int size_elem);
|
||||
LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
/*
|
||||
** $Id: lobject.c,v 2.17 2005/07/31 17:11:50 roberto Exp roberto $
|
||||
** $Id: lobject.c $
|
||||
** Some generic functions over Lua objects
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#define lobject_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <locale.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define lobject_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lctype.h"
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lmem.h"
|
||||
#include "lobject.h"
|
||||
@@ -23,36 +29,11 @@
|
||||
#include "lvm.h"
|
||||
|
||||
|
||||
|
||||
const TValue luaO_nilobject = {{NULL}, LUA_TNIL};
|
||||
|
||||
|
||||
/*
|
||||
** converts an integer to a "floating point byte", represented as
|
||||
** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
|
||||
** eeeee != 0 and (xxx) otherwise.
|
||||
** Computes ceil(log2(x))
|
||||
*/
|
||||
int luaO_int2fb (unsigned int x) {
|
||||
int e = 0; /* expoent */
|
||||
while (x >= 16) {
|
||||
x = (x+1) >> 1;
|
||||
e++;
|
||||
}
|
||||
if (x < 8) return x;
|
||||
else return ((e+1) << 3) | (cast(int, x) - 8);
|
||||
}
|
||||
|
||||
|
||||
/* converts back */
|
||||
int luaO_fb2int (int x) {
|
||||
int e = (x >> 3) & 31;
|
||||
if (e == 0) return x;
|
||||
else return ((x & 7)+8) << (e - 1);
|
||||
}
|
||||
|
||||
|
||||
int luaO_log2 (unsigned int x) {
|
||||
static const lu_byte log_2[256] = {
|
||||
int luaO_ceillog2 (unsigned int x) {
|
||||
static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */
|
||||
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
@@ -62,108 +43,487 @@ int luaO_log2 (unsigned int x) {
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
|
||||
};
|
||||
int l = -1;
|
||||
int l = 0;
|
||||
x--;
|
||||
while (x >= 256) { l += 8; x >>= 8; }
|
||||
return l + log_2[x];
|
||||
|
||||
}
|
||||
|
||||
|
||||
int luaO_rawequalObj (const TValue *t1, const TValue *t2) {
|
||||
if (ttype(t1) != ttype(t2)) return 0;
|
||||
else switch (ttype(t1)) {
|
||||
case LUA_TNIL:
|
||||
return 1;
|
||||
case LUA_TNUMBER:
|
||||
return luai_numeq(L, nvalue(t1), nvalue(t2));
|
||||
case LUA_TBOOLEAN:
|
||||
return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
return pvalue(t1) == pvalue(t2);
|
||||
default:
|
||||
lua_assert(iscollectable(t1));
|
||||
return gcvalue(t1) == gcvalue(t2);
|
||||
static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
|
||||
lua_Integer v2) {
|
||||
switch (op) {
|
||||
case LUA_OPADD: return intop(+, v1, v2);
|
||||
case LUA_OPSUB:return intop(-, v1, v2);
|
||||
case LUA_OPMUL:return intop(*, v1, v2);
|
||||
case LUA_OPMOD: return luaV_mod(L, v1, v2);
|
||||
case LUA_OPIDIV: return luaV_idiv(L, v1, v2);
|
||||
case LUA_OPBAND: return intop(&, v1, v2);
|
||||
case LUA_OPBOR: return intop(|, v1, v2);
|
||||
case LUA_OPBXOR: return intop(^, v1, v2);
|
||||
case LUA_OPSHL: return luaV_shiftl(v1, v2);
|
||||
case LUA_OPSHR: return luaV_shiftl(v1, -v2);
|
||||
case LUA_OPUNM: return intop(-, 0, v1);
|
||||
case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1);
|
||||
default: lua_assert(0); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int luaO_str2d (const char *s, lua_Number *result) {
|
||||
static lua_Number numarith (lua_State *L, int op, lua_Number v1,
|
||||
lua_Number v2) {
|
||||
switch (op) {
|
||||
case LUA_OPADD: return luai_numadd(L, v1, v2);
|
||||
case LUA_OPSUB: return luai_numsub(L, v1, v2);
|
||||
case LUA_OPMUL: return luai_nummul(L, v1, v2);
|
||||
case LUA_OPDIV: return luai_numdiv(L, v1, v2);
|
||||
case LUA_OPPOW: return luai_numpow(L, v1, v2);
|
||||
case LUA_OPIDIV: return luai_numidiv(L, v1, v2);
|
||||
case LUA_OPUNM: return luai_numunm(L, v1);
|
||||
case LUA_OPMOD: return luaV_modf(L, v1, v2);
|
||||
default: lua_assert(0); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2,
|
||||
TValue *res) {
|
||||
switch (op) {
|
||||
case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
|
||||
case LUA_OPSHL: case LUA_OPSHR:
|
||||
case LUA_OPBNOT: { /* operate only on integers */
|
||||
lua_Integer i1; lua_Integer i2;
|
||||
if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) {
|
||||
setivalue(res, intarith(L, op, i1, i2));
|
||||
return 1;
|
||||
}
|
||||
else return 0; /* fail */
|
||||
}
|
||||
case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */
|
||||
lua_Number n1; lua_Number n2;
|
||||
if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
|
||||
setfltvalue(res, numarith(L, op, n1, n2));
|
||||
return 1;
|
||||
}
|
||||
else return 0; /* fail */
|
||||
}
|
||||
default: { /* other operations */
|
||||
lua_Number n1; lua_Number n2;
|
||||
if (ttisinteger(p1) && ttisinteger(p2)) {
|
||||
setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2)));
|
||||
return 1;
|
||||
}
|
||||
else if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
|
||||
setfltvalue(res, numarith(L, op, n1, n2));
|
||||
return 1;
|
||||
}
|
||||
else return 0; /* fail */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2,
|
||||
StkId res) {
|
||||
if (!luaO_rawarith(L, op, p1, p2, s2v(res))) {
|
||||
/* could not perform raw operation; try metamethod */
|
||||
luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int luaO_hexavalue (int c) {
|
||||
if (lisdigit(c)) return c - '0';
|
||||
else return (ltolower(c) - 'a') + 10;
|
||||
}
|
||||
|
||||
|
||||
static int isneg (const char **s) {
|
||||
if (**s == '-') { (*s)++; return 1; }
|
||||
else if (**s == '+') (*s)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Lua's implementation for 'lua_strx2number'
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#if !defined(lua_strx2number)
|
||||
|
||||
/* maximum number of significant digits to read (to avoid overflows
|
||||
even with single floats) */
|
||||
#define MAXSIGDIG 30
|
||||
|
||||
/*
|
||||
** convert a hexadecimal numeric string to a number, following
|
||||
** C99 specification for 'strtod'
|
||||
*/
|
||||
static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
int dot = lua_getlocaledecpoint();
|
||||
lua_Number r = 0.0; /* result (accumulator) */
|
||||
int sigdig = 0; /* number of significant digits */
|
||||
int nosigdig = 0; /* number of non-significant digits */
|
||||
int e = 0; /* exponent correction */
|
||||
int neg; /* 1 if number is negative */
|
||||
int hasdot = 0; /* true after seen a dot */
|
||||
*endptr = cast_charp(s); /* nothing is valid yet */
|
||||
while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
|
||||
neg = isneg(&s); /* check sign */
|
||||
if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
|
||||
return 0.0; /* invalid format (no '0x') */
|
||||
for (s += 2; ; s++) { /* skip '0x' and read numeral */
|
||||
if (*s == dot) {
|
||||
if (hasdot) break; /* second dot? stop loop */
|
||||
else hasdot = 1;
|
||||
}
|
||||
else if (lisxdigit(cast_uchar(*s))) {
|
||||
if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */
|
||||
nosigdig++;
|
||||
else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */
|
||||
r = (r * cast_num(16.0)) + luaO_hexavalue(*s);
|
||||
else e++; /* too many digits; ignore, but still count for exponent */
|
||||
if (hasdot) e--; /* decimal digit? correct exponent */
|
||||
}
|
||||
else break; /* neither a dot nor a digit */
|
||||
}
|
||||
if (nosigdig + sigdig == 0) /* no digits? */
|
||||
return 0.0; /* invalid format */
|
||||
*endptr = cast_charp(s); /* valid up to here */
|
||||
e *= 4; /* each digit multiplies/divides value by 2^4 */
|
||||
if (*s == 'p' || *s == 'P') { /* exponent part? */
|
||||
int exp1 = 0; /* exponent value */
|
||||
int neg1; /* exponent sign */
|
||||
s++; /* skip 'p' */
|
||||
neg1 = isneg(&s); /* sign */
|
||||
if (!lisdigit(cast_uchar(*s)))
|
||||
return 0.0; /* invalid; must have at least one digit */
|
||||
while (lisdigit(cast_uchar(*s))) /* read exponent */
|
||||
exp1 = exp1 * 10 + *(s++) - '0';
|
||||
if (neg1) exp1 = -exp1;
|
||||
e += exp1;
|
||||
*endptr = cast_charp(s); /* valid up to here */
|
||||
}
|
||||
if (neg) r = -r;
|
||||
return l_mathop(ldexp)(r, e);
|
||||
}
|
||||
|
||||
#endif
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/* maximum length of a numeral */
|
||||
#if !defined (L_MAXLENNUM)
|
||||
#define L_MAXLENNUM 200
|
||||
#endif
|
||||
|
||||
static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
|
||||
char *endptr;
|
||||
*result = lua_str2number(s, &endptr);
|
||||
if (endptr == s) return 0; /* conversion failed */
|
||||
if (*endptr == '\0') return 1; /* most common case */
|
||||
while (isspace(cast(unsigned char, *endptr))) endptr++;
|
||||
if (*endptr != '\0') return 0; /* invalid trailing characters? */
|
||||
return 1;
|
||||
*result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
|
||||
: lua_str2number(s, &endptr);
|
||||
if (endptr == s) return NULL; /* nothing recognized? */
|
||||
while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */
|
||||
return (*endptr == '\0') ? endptr : NULL; /* OK if no trailing characters */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert string 's' to a Lua number (put in 'result'). Return NULL
|
||||
** on fail or the address of the ending '\0' on success.
|
||||
** 'pmode' points to (and 'mode' contains) special things in the string:
|
||||
** - 'x'/'X' means a hexadecimal numeral
|
||||
** - 'n'/'N' means 'inf' or 'nan' (which should be rejected)
|
||||
** - '.' just optimizes the search for the common case (nothing special)
|
||||
** This function accepts both the current locale or a dot as the radix
|
||||
** mark. If the conversion fails, it may mean number has a dot but
|
||||
** locale accepts something else. In that case, the code copies 's'
|
||||
** to a buffer (because 's' is read-only), changes the dot to the
|
||||
** current locale radix mark, and tries to convert again.
|
||||
*/
|
||||
static const char *l_str2d (const char *s, lua_Number *result) {
|
||||
const char *endptr;
|
||||
const char *pmode = strpbrk(s, ".xXnN");
|
||||
int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
|
||||
if (mode == 'n') /* reject 'inf' and 'nan' */
|
||||
return NULL;
|
||||
endptr = l_str2dloc(s, result, mode); /* try to convert */
|
||||
if (endptr == NULL) { /* failed? may be a different locale */
|
||||
char buff[L_MAXLENNUM + 1];
|
||||
const char *pdot = strchr(s, '.');
|
||||
if (strlen(s) > L_MAXLENNUM || pdot == NULL)
|
||||
return NULL; /* string too long or no dot; fail */
|
||||
strcpy(buff, s); /* copy string to buffer */
|
||||
buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */
|
||||
endptr = l_str2dloc(buff, result, mode); /* try again */
|
||||
if (endptr != NULL)
|
||||
endptr = s + (endptr - buff); /* make relative to 's' */
|
||||
}
|
||||
return endptr;
|
||||
}
|
||||
|
||||
|
||||
#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10)
|
||||
#define MAXLASTD cast_int(LUA_MAXINTEGER % 10)
|
||||
|
||||
static const char *l_str2int (const char *s, lua_Integer *result) {
|
||||
lua_Unsigned a = 0;
|
||||
int empty = 1;
|
||||
int neg;
|
||||
while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
|
||||
neg = isneg(&s);
|
||||
if (s[0] == '0' &&
|
||||
(s[1] == 'x' || s[1] == 'X')) { /* hex? */
|
||||
s += 2; /* skip '0x' */
|
||||
for (; lisxdigit(cast_uchar(*s)); s++) {
|
||||
a = a * 16 + luaO_hexavalue(*s);
|
||||
empty = 0;
|
||||
}
|
||||
}
|
||||
else { /* decimal */
|
||||
for (; lisdigit(cast_uchar(*s)); s++) {
|
||||
int d = *s - '0';
|
||||
if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */
|
||||
return NULL; /* do not accept it (as integer) */
|
||||
a = a * 10 + d;
|
||||
empty = 0;
|
||||
}
|
||||
}
|
||||
while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */
|
||||
if (empty || *s != '\0') return NULL; /* something wrong in the numeral */
|
||||
else {
|
||||
*result = l_castU2S((neg) ? 0u - a : a);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t luaO_str2num (const char *s, TValue *o) {
|
||||
lua_Integer i; lua_Number n;
|
||||
const char *e;
|
||||
if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */
|
||||
setivalue(o, i);
|
||||
}
|
||||
else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */
|
||||
setfltvalue(o, n);
|
||||
}
|
||||
else
|
||||
return 0; /* conversion failed */
|
||||
return (e - s) + 1; /* success; return string size */
|
||||
}
|
||||
|
||||
|
||||
int luaO_utf8esc (char *buff, unsigned long x) {
|
||||
int n = 1; /* number of bytes put in buffer (backwards) */
|
||||
lua_assert(x <= 0x7FFFFFFFu);
|
||||
if (x < 0x80) /* ascii? */
|
||||
buff[UTF8BUFFSZ - 1] = cast_char(x);
|
||||
else { /* need continuation bytes */
|
||||
unsigned int mfb = 0x3f; /* maximum that fits in first byte */
|
||||
do { /* add continuation bytes */
|
||||
buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f));
|
||||
x >>= 6; /* remove added bits */
|
||||
mfb >>= 1; /* now there is one less bit available in first byte */
|
||||
} while (x > mfb); /* still needs continuation byte? */
|
||||
buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/* maximum length of the conversion of a number to a string */
|
||||
#define MAXNUMBER2STR 50
|
||||
|
||||
|
||||
/*
|
||||
** Convert a number object to a string, adding it to a buffer
|
||||
*/
|
||||
static int tostringbuff (TValue *obj, char *buff) {
|
||||
int len;
|
||||
lua_assert(ttisnumber(obj));
|
||||
if (ttisinteger(obj))
|
||||
len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj));
|
||||
else {
|
||||
len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj));
|
||||
if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */
|
||||
buff[len++] = lua_getlocaledecpoint();
|
||||
buff[len++] = '0'; /* adds '.0' to result */
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a number object to a Lua string, replacing the value at 'obj'
|
||||
*/
|
||||
void luaO_tostring (lua_State *L, TValue *obj) {
|
||||
char buff[MAXNUMBER2STR];
|
||||
int len = tostringbuff(obj, buff);
|
||||
setsvalue(L, obj, luaS_newlstr(L, buff, len));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void pushstr (lua_State *L, const char *str) {
|
||||
setsvalue2s(L, L->top, luaS_new(L, str));
|
||||
incr_top(L);
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** 'luaO_pushvfstring'
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* size for buffer space used by 'luaO_pushvfstring' */
|
||||
#define BUFVFS 400
|
||||
|
||||
/* buffer used by 'luaO_pushvfstring' */
|
||||
typedef struct BuffFS {
|
||||
lua_State *L;
|
||||
int pushed; /* number of string pieces already on the stack */
|
||||
int blen; /* length of partial string in 'space' */
|
||||
char space[BUFVFS]; /* holds last part of the result */
|
||||
} BuffFS;
|
||||
|
||||
|
||||
/*
|
||||
** Push given string to the stack, as part of the buffer. If the stack
|
||||
** is almost full, join all partial strings in the stack into one.
|
||||
*/
|
||||
static void pushstr (BuffFS *buff, const char *str, size_t l) {
|
||||
lua_State *L = buff->L;
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
|
||||
L->top++; /* may use one extra slot */
|
||||
buff->pushed++;
|
||||
if (buff->pushed > 1 && L->top + 1 >= L->stack_last) {
|
||||
luaV_concat(L, buff->pushed); /* join all partial results into one */
|
||||
buff->pushed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* this function handles only `%d', `%c', %f, %p, and `%s' formats */
|
||||
/*
|
||||
** empty the buffer space into the stack
|
||||
*/
|
||||
static void clearbuff (BuffFS *buff) {
|
||||
pushstr(buff, buff->space, buff->blen); /* push buffer contents */
|
||||
buff->blen = 0; /* space now is empty */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get a space of size 'sz' in the buffer. If buffer has not enough
|
||||
** space, empty it. 'sz' must fit in an empty buffer.
|
||||
*/
|
||||
static char *getbuff (BuffFS *buff, int sz) {
|
||||
lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS);
|
||||
if (sz > BUFVFS - buff->blen) /* not enough space? */
|
||||
clearbuff(buff);
|
||||
return buff->space + buff->blen;
|
||||
}
|
||||
|
||||
|
||||
#define addsize(b,sz) ((b)->blen += (sz))
|
||||
|
||||
|
||||
/*
|
||||
** Add 'str' to the buffer. If string is larger than the buffer space,
|
||||
** push the string directly to the stack.
|
||||
*/
|
||||
static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
|
||||
if (slen <= BUFVFS) { /* does string fit into buffer? */
|
||||
char *bf = getbuff(buff, cast_int(slen));
|
||||
memcpy(bf, str, slen); /* add string to buffer */
|
||||
addsize(buff, cast_int(slen));
|
||||
}
|
||||
else { /* string larger than buffer */
|
||||
clearbuff(buff); /* string comes after buffer's content */
|
||||
pushstr(buff, str, slen); /* push string */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add a number to the buffer.
|
||||
*/
|
||||
static void addnum2buff (BuffFS *buff, TValue *num) {
|
||||
char *numbuff = getbuff(buff, MAXNUMBER2STR);
|
||||
int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */
|
||||
addsize(buff, len);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%'
|
||||
conventional formats, plus Lua-specific '%I' and '%U'
|
||||
*/
|
||||
const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
||||
int n = 1;
|
||||
pushstr(L, "");
|
||||
for (;;) {
|
||||
const char *e = strchr(fmt, '%');
|
||||
if (e == NULL) break;
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt));
|
||||
incr_top(L);
|
||||
switch (*(e+1)) {
|
||||
case 's': {
|
||||
BuffFS buff; /* holds last part of the result */
|
||||
const char *e; /* points to next '%' */
|
||||
buff.pushed = buff.blen = 0;
|
||||
buff.L = L;
|
||||
while ((e = strchr(fmt, '%')) != NULL) {
|
||||
addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */
|
||||
switch (*(e + 1)) { /* conversion specifier */
|
||||
case 's': { /* zero-terminated string */
|
||||
const char *s = va_arg(argp, char *);
|
||||
if (s == NULL) s = "(null)";
|
||||
pushstr(L, s);
|
||||
addstr2buff(&buff, s, strlen(s));
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
char buff[2];
|
||||
buff[0] = cast(char, va_arg(argp, int));
|
||||
buff[1] = '\0';
|
||||
pushstr(L, buff);
|
||||
case 'c': { /* an 'int' as a character */
|
||||
char c = cast_uchar(va_arg(argp, int));
|
||||
addstr2buff(&buff, &c, sizeof(char));
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
setnvalue(L->top, cast(lua_Number, va_arg(argp, int)));
|
||||
incr_top(L);
|
||||
case 'd': { /* an 'int' */
|
||||
TValue num;
|
||||
setivalue(&num, va_arg(argp, int));
|
||||
addnum2buff(&buff, &num);
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
setnvalue(L->top, cast(lua_Number, va_arg(argp, l_uacNumber)));
|
||||
incr_top(L);
|
||||
case 'I': { /* a 'lua_Integer' */
|
||||
TValue num;
|
||||
setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt)));
|
||||
addnum2buff(&buff, &num);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */
|
||||
sprintf(buff, "%p", va_arg(argp, void *));
|
||||
pushstr(L, buff);
|
||||
case 'f': { /* a 'lua_Number' */
|
||||
TValue num;
|
||||
setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber)));
|
||||
addnum2buff(&buff, &num);
|
||||
break;
|
||||
}
|
||||
case 'p': { /* a pointer */
|
||||
const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */
|
||||
char *bf = getbuff(&buff, sz);
|
||||
void *p = va_arg(argp, void *);
|
||||
int len = lua_pointer2str(bf, sz, p);
|
||||
addsize(&buff, len);
|
||||
break;
|
||||
}
|
||||
case 'U': { /* a 'long' as a UTF-8 sequence */
|
||||
char bf[UTF8BUFFSZ];
|
||||
int len = luaO_utf8esc(bf, va_arg(argp, long));
|
||||
addstr2buff(&buff, bf + UTF8BUFFSZ - len, len);
|
||||
break;
|
||||
}
|
||||
case '%': {
|
||||
pushstr(L, "%");
|
||||
addstr2buff(&buff, "%", 1);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
char buff[3];
|
||||
buff[0] = '%';
|
||||
buff[1] = *(e+1);
|
||||
buff[2] = '\0';
|
||||
pushstr(L, buff);
|
||||
break;
|
||||
luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'",
|
||||
*(e + 1));
|
||||
}
|
||||
}
|
||||
n += 2;
|
||||
fmt = e+2;
|
||||
fmt = e + 2; /* skip '%' and the specifier */
|
||||
}
|
||||
pushstr(L, fmt);
|
||||
luaV_concat(L, n+1, cast(int, L->top - L->base) - 1);
|
||||
L->top -= n;
|
||||
return svalue(L->top - 1);
|
||||
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
|
||||
clearbuff(&buff); /* empty buffer into the stack */
|
||||
if (buff.pushed > 1)
|
||||
luaV_concat(L, buff.pushed); /* join all partial results */
|
||||
return svalue(s2v(L->top - 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -176,37 +536,48 @@ const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
void luaO_chunkid (char *out, const char *source, size_t bufflen) {
|
||||
if (*source == '=') {
|
||||
strncpy(out, source+1, bufflen); /* remove first char */
|
||||
out[bufflen-1] = '\0'; /* ensures null termination */
|
||||
|
||||
#define RETS "..."
|
||||
#define PRE "[string \""
|
||||
#define POS "\"]"
|
||||
|
||||
#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) )
|
||||
|
||||
void luaO_chunkid (char *out, const char *source, size_t srclen) {
|
||||
size_t bufflen = LUA_IDSIZE; /* free space in buffer */
|
||||
if (*source == '=') { /* 'literal' source */
|
||||
if (srclen <= bufflen) /* small enough? */
|
||||
memcpy(out, source + 1, srclen * sizeof(char));
|
||||
else { /* truncate it */
|
||||
addstr(out, source + 1, bufflen - 1);
|
||||
*out = '\0';
|
||||
}
|
||||
}
|
||||
else { /* out = "source", or "...source" */
|
||||
if (*source == '@') {
|
||||
size_t l;
|
||||
source++; /* skip the `@' */
|
||||
bufflen -= sizeof(" '...' ");
|
||||
l = strlen(source);
|
||||
strcpy(out, "");
|
||||
if (l > bufflen) {
|
||||
source += (l-bufflen); /* get last part of file name */
|
||||
strcat(out, "...");
|
||||
}
|
||||
strcat(out, source);
|
||||
else if (*source == '@') { /* file name */
|
||||
if (srclen <= bufflen) /* small enough? */
|
||||
memcpy(out, source + 1, srclen * sizeof(char));
|
||||
else { /* add '...' before rest of name */
|
||||
addstr(out, RETS, LL(RETS));
|
||||
bufflen -= LL(RETS);
|
||||
memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char));
|
||||
}
|
||||
else { /* out = [string "string"] */
|
||||
size_t len = strcspn(source, "\n\r"); /* stop at first newline */
|
||||
bufflen -= sizeof(" [string \"...\"] ");
|
||||
if (len > bufflen) len = bufflen;
|
||||
strcpy(out, "[string \"");
|
||||
if (source[len] != '\0') { /* must truncate? */
|
||||
strncat(out, source, len);
|
||||
strcat(out, "...");
|
||||
}
|
||||
else
|
||||
strcat(out, source);
|
||||
strcat(out, "\"]");
|
||||
}
|
||||
else { /* string; format as [string "source"] */
|
||||
const char *nl = strchr(source, '\n'); /* find first new line (if any) */
|
||||
addstr(out, PRE, LL(PRE)); /* add prefix */
|
||||
bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */
|
||||
if (srclen < bufflen && nl == NULL) { /* small one-line source? */
|
||||
addstr(out, source, srclen); /* keep it */
|
||||
}
|
||||
else {
|
||||
if (nl != NULL) srclen = nl - source; /* stop at first newline */
|
||||
if (srclen > bufflen) srclen = bufflen;
|
||||
addstr(out, source, srclen);
|
||||
addstr(out, RETS, LL(RETS));
|
||||
}
|
||||
memcpy(out, POS, (LL(POS) + 1) * sizeof(char));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lobject.h,v 2.16 2005/06/13 14:15:54 roberto Exp roberto $
|
||||
** $Id: lobject.h $
|
||||
** Type definitions for Lua objects
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -16,252 +16,458 @@
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/* tags for values visible from Lua */
|
||||
#define LAST_TAG LUA_TTHREAD
|
||||
|
||||
#define NUM_TAGS (LAST_TAG+1)
|
||||
|
||||
|
||||
/*
|
||||
** Extra tags for non-values
|
||||
*/
|
||||
#define LUA_TPROTO (LAST_TAG+1)
|
||||
#define LUA_TUPVAL (LAST_TAG+2)
|
||||
#define LUA_TDEADKEY (LAST_TAG+3)
|
||||
#define LUA_TUPVAL LUA_NUMTAGS /* upvalues */
|
||||
#define LUA_TPROTO (LUA_NUMTAGS+1) /* function prototypes */
|
||||
|
||||
/*
|
||||
** number of all possible tags (including LUA_TNONE)
|
||||
*/
|
||||
#define LUA_TOTALTAGS (LUA_TPROTO + 2)
|
||||
|
||||
|
||||
/*
|
||||
** Union of all collectable objects
|
||||
** tags for Tagged Values have the following use of bits:
|
||||
** bits 0-3: actual tag (a LUA_T* value)
|
||||
** bits 4-5: variant bits
|
||||
** bit 6: whether value is collectable
|
||||
*/
|
||||
typedef union GCObject GCObject;
|
||||
|
||||
|
||||
/*
|
||||
** Common Header for all collectable objects (in macro form, to be
|
||||
** included in other objects)
|
||||
*/
|
||||
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
|
||||
|
||||
|
||||
/*
|
||||
** Common header in struct form
|
||||
*/
|
||||
typedef struct GCheader {
|
||||
CommonHeader;
|
||||
} GCheader;
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Union of all Lua values
|
||||
*/
|
||||
typedef union {
|
||||
GCObject *gc;
|
||||
void *p;
|
||||
lua_Number n;
|
||||
int b;
|
||||
typedef union Value {
|
||||
struct GCObject *gc; /* collectable objects */
|
||||
void *p; /* light userdata */
|
||||
int b; /* booleans */
|
||||
lua_CFunction f; /* light C functions */
|
||||
lua_Integer i; /* integer numbers */
|
||||
lua_Number n; /* float numbers */
|
||||
} Value;
|
||||
|
||||
|
||||
/*
|
||||
** Tagged Values
|
||||
** Tagged Values. This is the basic representation of values in Lua:
|
||||
** an actual value plus a tag with its type.
|
||||
*/
|
||||
|
||||
#define TValuefields Value value; int tt
|
||||
#define TValuefields Value value_; lu_byte tt_
|
||||
|
||||
typedef struct lua_TValue {
|
||||
typedef struct TValue {
|
||||
TValuefields;
|
||||
} TValue;
|
||||
|
||||
|
||||
#define val_(o) ((o)->value_)
|
||||
#define valraw(o) (&val_(o))
|
||||
|
||||
|
||||
/* raw type tag of a TValue */
|
||||
#define rawtt(o) ((o)->tt_)
|
||||
|
||||
/* tag with no variants (bits 0-3) */
|
||||
#define novariant(t) ((t) & 0x0F)
|
||||
|
||||
/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
|
||||
#define withvariant(t) ((t) & 0x3F)
|
||||
#define ttypetag(o) withvariant(rawtt(o))
|
||||
|
||||
/* type of a TValue */
|
||||
#define ttype(o) (novariant(rawtt(o)))
|
||||
|
||||
|
||||
/* Macros to test type */
|
||||
#define ttisnil(o) (ttype(o) == LUA_TNIL)
|
||||
#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
|
||||
#define ttisstring(o) (ttype(o) == LUA_TSTRING)
|
||||
#define ttistable(o) (ttype(o) == LUA_TTABLE)
|
||||
#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
|
||||
#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
|
||||
#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
|
||||
#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
|
||||
#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
|
||||
#define checktag(o,t) (rawtt(o) == (t))
|
||||
#define checktype(o,t) (ttype(o) == (t))
|
||||
|
||||
/* Macros to access values */
|
||||
#define ttype(o) ((o)->tt)
|
||||
#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
|
||||
#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
|
||||
#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
|
||||
#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
|
||||
#define tsvalue(o) (&rawtsvalue(o)->tsv)
|
||||
#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
|
||||
#define uvalue(o) (&rawuvalue(o)->uv)
|
||||
#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
|
||||
#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
|
||||
#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
|
||||
#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
|
||||
|
||||
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
|
||||
/* Macros for internal tests */
|
||||
#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt)
|
||||
|
||||
/*
|
||||
** for internal debug only
|
||||
*/
|
||||
#define checkconsistency(obj) \
|
||||
lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))
|
||||
|
||||
#define checkliveness(g,obj) \
|
||||
lua_assert(!iscollectable(obj) || \
|
||||
((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
|
||||
#define checkliveness(L,obj) \
|
||||
((void)L, lua_longassert(!iscollectable(obj) || \
|
||||
(righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj))))))
|
||||
|
||||
|
||||
/* Macros to set values */
|
||||
#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
|
||||
|
||||
#define setnvalue(obj,x) \
|
||||
{ TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
|
||||
|
||||
#define chgnvalue(obj,x) \
|
||||
check_exp(ttype(obj)==LUA_TNUMBER, (obj)->value.n=(x))
|
||||
|
||||
#define setpvalue(obj,x) \
|
||||
{ TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }
|
||||
|
||||
#define setbvalue(obj,x) \
|
||||
{ TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
|
||||
|
||||
#define setsvalue(L,obj,x) \
|
||||
{ TValue *i_o=(obj); \
|
||||
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \
|
||||
checkliveness(G(L),i_o); }
|
||||
|
||||
#define setuvalue(L,obj,x) \
|
||||
{ TValue *i_o=(obj); \
|
||||
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \
|
||||
checkliveness(G(L),i_o); }
|
||||
|
||||
#define setthvalue(L,obj,x) \
|
||||
{ TValue *i_o=(obj); \
|
||||
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \
|
||||
checkliveness(G(L),i_o); }
|
||||
|
||||
#define setclvalue(L,obj,x) \
|
||||
{ TValue *i_o=(obj); \
|
||||
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \
|
||||
checkliveness(G(L),i_o); }
|
||||
|
||||
#define sethvalue(L,obj,x) \
|
||||
{ TValue *i_o=(obj); \
|
||||
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
|
||||
checkliveness(G(L),i_o); }
|
||||
|
||||
#define setptvalue(L,obj,x) \
|
||||
{ TValue *i_o=(obj); \
|
||||
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \
|
||||
checkliveness(G(L),i_o); }
|
||||
|
||||
|
||||
#define settt_(o,t) ((o)->tt_=(t))
|
||||
|
||||
|
||||
#define setobj(L,obj1,obj2) \
|
||||
{ const TValue *o2=(obj2); TValue *o1=(obj1); \
|
||||
o1->value = o2->value; o1->tt=o2->tt; \
|
||||
checkliveness(G(L),o1); }
|
||||
|
||||
{ TValue *io1=(obj1); const TValue *io2=(obj2); \
|
||||
io1->value_ = io2->value_; io1->tt_ = io2->tt_; \
|
||||
checkliveness(L,io1); lua_assert(!isreallyempty(io1)); }
|
||||
|
||||
/*
|
||||
** different types of sets, according to destination
|
||||
** different types of assignments, according to destination
|
||||
*/
|
||||
|
||||
/* from stack to (same) stack */
|
||||
#define setobjs2s setobj
|
||||
/* from stack to stack */
|
||||
#define setobjs2s(L,o1,o2) setobj(L,s2v(o1),s2v(o2))
|
||||
/* to stack (not from same stack) */
|
||||
#define setobj2s setobj
|
||||
#define setsvalue2s setsvalue
|
||||
#define sethvalue2s sethvalue
|
||||
#define setptvalue2s setptvalue
|
||||
#define setobj2s(L,o1,o2) setobj(L,s2v(o1),o2)
|
||||
/* from table to same table */
|
||||
#define setobjt2t setobj
|
||||
/* to table */
|
||||
#define setobj2t setobj
|
||||
/* to new object */
|
||||
#define setobj2n setobj
|
||||
#define setsvalue2n setsvalue
|
||||
|
||||
#define setttype(obj, tt) (ttype(obj) = (tt))
|
||||
|
||||
|
||||
#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
|
||||
/* to table */
|
||||
#define setobj2t setobj
|
||||
|
||||
|
||||
|
||||
typedef TValue *StkId; /* index to stack elements */
|
||||
typedef union StackValue {
|
||||
TValue val;
|
||||
} StackValue;
|
||||
|
||||
|
||||
typedef StackValue *StkId; /* index to stack elements */
|
||||
|
||||
/* convert a 'StackValue' to a 'TValue' */
|
||||
#define s2v(o) (&(o)->val)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** String headers for string table
|
||||
** {==================================================================
|
||||
** Nil
|
||||
** ===================================================================
|
||||
*/
|
||||
typedef union TString {
|
||||
L_Umaxalign dummy; /* ensures maximum alignment for strings */
|
||||
struct {
|
||||
CommonHeader;
|
||||
lu_byte reserved;
|
||||
unsigned int hash;
|
||||
size_t len;
|
||||
} tsv;
|
||||
|
||||
/* macro to test for (any kind of) nil */
|
||||
#define ttisnil(v) checktype((v), LUA_TNIL)
|
||||
|
||||
/* macro to test for a "pure" nil */
|
||||
#define ttisstrictnil(o) checktag((o), LUA_TNIL)
|
||||
|
||||
|
||||
#define setnilvalue(obj) settt_(obj, LUA_TNIL)
|
||||
|
||||
|
||||
/*
|
||||
** Variant tag, used only in tables to signal an empty slot
|
||||
** (which might be different from a slot containing nil)
|
||||
*/
|
||||
#define LUA_TEMPTY (LUA_TNIL | (1 << 4))
|
||||
|
||||
/*
|
||||
** Variant used only in the value returned for a key not found in a
|
||||
** table (absent key).
|
||||
*/
|
||||
#define LUA_TABSTKEY (LUA_TNIL | (2 << 4))
|
||||
|
||||
|
||||
#define isabstkey(v) checktag((v), LUA_TABSTKEY)
|
||||
|
||||
|
||||
/*
|
||||
** macro to detect non-standard nils (used only in assertions)
|
||||
*/
|
||||
#define isreallyempty(v) (ttisnil(v) && !ttisstrictnil(v))
|
||||
|
||||
|
||||
/*
|
||||
** By default, entries with any kind of nil are considered empty.
|
||||
** (In any definition, values associated with absent keys must also
|
||||
** be accepted as empty.)
|
||||
*/
|
||||
#define isempty(v) ttisnil(v)
|
||||
|
||||
|
||||
/* macro defining a value corresponding to an absent key */
|
||||
#define ABSTKEYCONSTANT {NULL}, LUA_TABSTKEY
|
||||
|
||||
|
||||
/* mark an entry as empty */
|
||||
#define setempty(v) settt_(v, LUA_TEMPTY)
|
||||
|
||||
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Booleans
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define ttisboolean(o) checktag((o), LUA_TBOOLEAN)
|
||||
|
||||
#define bvalue(o) check_exp(ttisboolean(o), val_(o).b)
|
||||
|
||||
#define bvalueraw(v) ((v).b)
|
||||
|
||||
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
|
||||
|
||||
#define setbvalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); }
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Threads
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define ttisthread(o) checktag((o), ctb(LUA_TTHREAD))
|
||||
|
||||
#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc))
|
||||
|
||||
#define setthvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); lua_State *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTHREAD)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
#define setthvalue2s(L,o,t) setthvalue(L,s2v(o),t)
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Collectable Objects
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Common Header for all collectable objects (in macro form, to be
|
||||
** included in other objects)
|
||||
*/
|
||||
#define CommonHeader struct GCObject *next; lu_byte tt; lu_byte marked
|
||||
|
||||
|
||||
/* Common type for all collectable objects */
|
||||
typedef struct GCObject {
|
||||
CommonHeader;
|
||||
} GCObject;
|
||||
|
||||
|
||||
/* Bit mark for collectable types */
|
||||
#define BIT_ISCOLLECTABLE (1 << 6)
|
||||
|
||||
#define iscollectable(o) (rawtt(o) & BIT_ISCOLLECTABLE)
|
||||
|
||||
/* mark a tag as collectable */
|
||||
#define ctb(t) ((t) | BIT_ISCOLLECTABLE)
|
||||
|
||||
#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc)
|
||||
|
||||
#define gcvalueraw(v) ((v).gc)
|
||||
|
||||
#define setgcovalue(L,obj,x) \
|
||||
{ TValue *io = (obj); GCObject *i_g=(x); \
|
||||
val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); }
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Numbers
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* Variant tags for numbers */
|
||||
#define LUA_TNUMFLT (LUA_TNUMBER | (1 << 4)) /* float numbers */
|
||||
#define LUA_TNUMINT (LUA_TNUMBER | (2 << 4)) /* integer numbers */
|
||||
|
||||
#define ttisnumber(o) checktype((o), LUA_TNUMBER)
|
||||
#define ttisfloat(o) checktag((o), LUA_TNUMFLT)
|
||||
#define ttisinteger(o) checktag((o), LUA_TNUMINT)
|
||||
|
||||
#define nvalue(o) check_exp(ttisnumber(o), \
|
||||
(ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o)))
|
||||
#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n)
|
||||
#define ivalue(o) check_exp(ttisinteger(o), val_(o).i)
|
||||
|
||||
#define fltvalueraw(v) ((v).n)
|
||||
#define ivalueraw(v) ((v).i)
|
||||
|
||||
#define setfltvalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_TNUMFLT); }
|
||||
|
||||
#define chgfltvalue(obj,x) \
|
||||
{ TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); }
|
||||
|
||||
#define setivalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); }
|
||||
|
||||
#define chgivalue(obj,x) \
|
||||
{ TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); }
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Strings
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* Variant tags for strings */
|
||||
#define LUA_TSHRSTR (LUA_TSTRING | (1 << 4)) /* short strings */
|
||||
#define LUA_TLNGSTR (LUA_TSTRING | (2 << 4)) /* long strings */
|
||||
|
||||
#define ttisstring(o) checktype((o), LUA_TSTRING)
|
||||
#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR))
|
||||
#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR))
|
||||
|
||||
#define tsvalueraw(v) (gco2ts((v).gc))
|
||||
|
||||
#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc))
|
||||
|
||||
#define setsvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); TString *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
/* set a string to the stack */
|
||||
#define setsvalue2s(L,o,s) setsvalue(L,s2v(o),s)
|
||||
|
||||
/* set a string to a new object */
|
||||
#define setsvalue2n setsvalue
|
||||
|
||||
|
||||
/*
|
||||
** Header for string value; string bytes follow the end of this structure
|
||||
** (aligned according to 'UTString'; see next).
|
||||
*/
|
||||
typedef struct TString {
|
||||
CommonHeader;
|
||||
lu_byte extra; /* reserved words for short strings; "has hash" for longs */
|
||||
lu_byte shrlen; /* length for short strings */
|
||||
unsigned int hash;
|
||||
union {
|
||||
size_t lnglen; /* length for long strings */
|
||||
struct TString *hnext; /* linked list for hash table */
|
||||
} u;
|
||||
} TString;
|
||||
|
||||
|
||||
#define getstr(ts) cast(const char *, (ts) + 1)
|
||||
|
||||
/*
|
||||
** Get the actual string (array of bytes) from a 'TString'.
|
||||
** (Access to 'extra' ensures that value is really a 'TString'.)
|
||||
*/
|
||||
#define getstr(ts) \
|
||||
check_exp(sizeof((ts)->extra), cast_charp((ts)) + sizeof(TString))
|
||||
|
||||
|
||||
/* get the actual string (array of bytes) from a Lua value */
|
||||
#define svalue(o) getstr(tsvalue(o))
|
||||
|
||||
/* get string length from 'TString *s' */
|
||||
#define tsslen(s) ((s)->tt == LUA_TSHRSTR ? (s)->shrlen : (s)->u.lnglen)
|
||||
|
||||
/* get string length from 'TValue *o' */
|
||||
#define vslen(o) tsslen(tsvalue(o))
|
||||
|
||||
typedef union Udata {
|
||||
L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
|
||||
struct {
|
||||
CommonHeader;
|
||||
struct Table *metatable;
|
||||
struct Table *env;
|
||||
size_t len;
|
||||
} uv;
|
||||
} Udata;
|
||||
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** Function Prototypes
|
||||
** {==================================================================
|
||||
** Userdata
|
||||
** ===================================================================
|
||||
*/
|
||||
typedef struct Proto {
|
||||
|
||||
#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA)
|
||||
#define ttisfulluserdata(o) checktype((o), LUA_TUSERDATA)
|
||||
|
||||
#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p)
|
||||
#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc))
|
||||
|
||||
#define pvalueraw(v) ((v).p)
|
||||
|
||||
#define setpvalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); }
|
||||
|
||||
#define setuvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); Udata *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TUSERDATA)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
|
||||
/* Ensures that addresses after this type are always fully aligned. */
|
||||
typedef union UValue {
|
||||
TValue uv;
|
||||
LUAI_MAXALIGN; /* ensures maximum alignment for udata bytes */
|
||||
} UValue;
|
||||
|
||||
|
||||
/*
|
||||
** Header for userdata with user values;
|
||||
** memory area follows the end of this structure.
|
||||
*/
|
||||
typedef struct Udata {
|
||||
CommonHeader;
|
||||
TValue *k; /* constants used by the function */
|
||||
Instruction *code;
|
||||
struct Proto **p; /* functions defined inside the function */
|
||||
int *lineinfo; /* map from opcodes to source lines */
|
||||
struct LocVar *locvars; /* information about local variables */
|
||||
TString **upvalues; /* upvalue names */
|
||||
TString *source;
|
||||
int sizeupvalues;
|
||||
int sizek; /* size of `k' */
|
||||
int sizecode;
|
||||
int sizelineinfo;
|
||||
int sizep; /* size of `p' */
|
||||
int sizelocvars;
|
||||
int linedefined;
|
||||
int lastlinedefined;
|
||||
unsigned short nuvalue; /* number of user values */
|
||||
size_t len; /* number of bytes */
|
||||
struct Table *metatable;
|
||||
GCObject *gclist;
|
||||
lu_byte nups; /* number of upvalues */
|
||||
lu_byte numparams;
|
||||
lu_byte is_vararg;
|
||||
lu_byte maxstacksize;
|
||||
} Proto;
|
||||
UValue uv[1]; /* user values */
|
||||
} Udata;
|
||||
|
||||
|
||||
/* masks for new-style vararg */
|
||||
#define VARARG_HASARG 1
|
||||
#define VARARG_ISVARARG 2
|
||||
#define VARARG_NEEDSARG 4
|
||||
/*
|
||||
** Header for userdata with no user values. These userdata do not need
|
||||
** to be gray during GC, and therefore do not need a 'gclist' field.
|
||||
** To simplify, the code always use 'Udata' for both kinds of userdata,
|
||||
** making sure it never accesses 'gclist' on userdata with no user values.
|
||||
** This structure here is used only to compute the correct size for
|
||||
** this representation. (The 'bindata' field in its end ensures correct
|
||||
** alignment for binary data following this header.)
|
||||
*/
|
||||
typedef struct Udata0 {
|
||||
CommonHeader;
|
||||
unsigned short nuvalue; /* number of user values */
|
||||
size_t len; /* number of bytes */
|
||||
struct Table *metatable;
|
||||
union {LUAI_MAXALIGN;} bindata;
|
||||
} Udata0;
|
||||
|
||||
|
||||
/* compute the offset of the memory area of a userdata */
|
||||
#define udatamemoffset(nuv) \
|
||||
((nuv) == 0 ? offsetof(Udata0, bindata) \
|
||||
: offsetof(Udata, uv) + (sizeof(UValue) * (nuv)))
|
||||
|
||||
/* get the address of the memory block inside 'Udata' */
|
||||
#define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue))
|
||||
|
||||
/* compute the size of a userdata */
|
||||
#define sizeudata(nuv,nb) (udatamemoffset(nuv) + (nb))
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Prototypes
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Description of an upvalue for function prototypes
|
||||
*/
|
||||
typedef struct Upvaldesc {
|
||||
TString *name; /* upvalue name (for debug information) */
|
||||
lu_byte instack; /* whether it is in stack (register) */
|
||||
lu_byte idx; /* index of upvalue (in stack or in outer function's list) */
|
||||
lu_byte kind; /* kind of corresponding variable */
|
||||
} Upvaldesc;
|
||||
|
||||
|
||||
/*
|
||||
** Description of a local variable for function prototypes
|
||||
** (used for debug information)
|
||||
*/
|
||||
typedef struct LocVar {
|
||||
TString *varname;
|
||||
int startpc; /* first point where variable is active */
|
||||
@@ -269,43 +475,126 @@ typedef struct LocVar {
|
||||
} LocVar;
|
||||
|
||||
|
||||
/*
|
||||
** Associates the absolute line source for a given instruction ('pc').
|
||||
** The array 'lineinfo' gives, for each instruction, the difference in
|
||||
** lines from the previous instruction. When that difference does not
|
||||
** fit into a byte, Lua saves the absolute line for that instruction.
|
||||
** (Lua also saves the absolute line periodically, to speed up the
|
||||
** computation of a line number: we can use binary search in the
|
||||
** absolute-line array, but we must traverse the 'lineinfo' array
|
||||
** linearly to compute a line.)
|
||||
*/
|
||||
typedef struct AbsLineInfo {
|
||||
int pc;
|
||||
int line;
|
||||
} AbsLineInfo;
|
||||
|
||||
/*
|
||||
** Upvalues
|
||||
** Function Prototypes
|
||||
*/
|
||||
typedef struct Proto {
|
||||
CommonHeader;
|
||||
lu_byte numparams; /* number of fixed (named) parameters */
|
||||
lu_byte is_vararg;
|
||||
lu_byte maxstacksize; /* number of registers needed by this function */
|
||||
int sizeupvalues; /* size of 'upvalues' */
|
||||
int sizek; /* size of 'k' */
|
||||
int sizecode;
|
||||
int sizelineinfo;
|
||||
int sizep; /* size of 'p' */
|
||||
int sizelocvars;
|
||||
int sizeabslineinfo; /* size of 'abslineinfo' */
|
||||
int linedefined; /* debug information */
|
||||
int lastlinedefined; /* debug information */
|
||||
TValue *k; /* constants used by the function */
|
||||
Instruction *code; /* opcodes */
|
||||
struct Proto **p; /* functions defined inside the function */
|
||||
Upvaldesc *upvalues; /* upvalue information */
|
||||
ls_byte *lineinfo; /* information about source lines (debug information) */
|
||||
AbsLineInfo *abslineinfo; /* idem */
|
||||
LocVar *locvars; /* information about local variables (debug information) */
|
||||
TString *source; /* used for debug information */
|
||||
GCObject *gclist;
|
||||
} Proto;
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Closures
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* Variant tags for functions */
|
||||
#define LUA_TLCL (LUA_TFUNCTION | (1 << 4)) /* Lua closure */
|
||||
#define LUA_TLCF (LUA_TFUNCTION | (2 << 4)) /* light C function */
|
||||
#define LUA_TCCL (LUA_TFUNCTION | (3 << 4)) /* C closure */
|
||||
|
||||
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
|
||||
#define ttisclosure(o) ((rawtt(o) & 0x1F) == LUA_TLCL)
|
||||
#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL))
|
||||
#define ttislcf(o) checktag((o), LUA_TLCF)
|
||||
#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL))
|
||||
|
||||
#define isLfunction(o) ttisLclosure(o)
|
||||
|
||||
#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc))
|
||||
#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc))
|
||||
#define fvalue(o) check_exp(ttislcf(o), val_(o).f)
|
||||
#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc))
|
||||
|
||||
#define fvalueraw(v) ((v).f)
|
||||
|
||||
#define setclLvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); LClosure *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TLCL)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
#define setclLvalue2s(L,o,cl) setclLvalue(L,s2v(o),cl)
|
||||
|
||||
#define setfvalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_TLCF); }
|
||||
|
||||
#define setclCvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); CClosure *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TCCL)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
|
||||
/*
|
||||
** Upvalues for Lua closures
|
||||
*/
|
||||
typedef struct UpVal {
|
||||
CommonHeader;
|
||||
lu_byte tbc; /* true if it represents a to-be-closed variable */
|
||||
TValue *v; /* points to stack or to its own value */
|
||||
union {
|
||||
struct { /* (when open) */
|
||||
struct UpVal *next; /* linked list */
|
||||
struct UpVal **previous;
|
||||
} open;
|
||||
TValue value; /* the value (when closed) */
|
||||
struct { /* double linked list (when open) */
|
||||
struct UpVal *prev;
|
||||
struct UpVal *next;
|
||||
} l;
|
||||
} u;
|
||||
} UpVal;
|
||||
|
||||
|
||||
/*
|
||||
** Closures
|
||||
*/
|
||||
|
||||
#define ClosureHeader \
|
||||
CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
|
||||
struct Table *env
|
||||
CommonHeader; lu_byte nupvalues; GCObject *gclist
|
||||
|
||||
typedef struct CClosure {
|
||||
ClosureHeader;
|
||||
lua_CFunction f;
|
||||
TValue upvalue[1];
|
||||
TValue upvalue[1]; /* list of upvalues */
|
||||
} CClosure;
|
||||
|
||||
|
||||
typedef struct LClosure {
|
||||
ClosureHeader;
|
||||
struct Proto *p;
|
||||
UpVal *upvals[1];
|
||||
UpVal *upvals[1]; /* list of upvalues */
|
||||
} LClosure;
|
||||
|
||||
|
||||
@@ -315,65 +604,147 @@ typedef union Closure {
|
||||
} Closure;
|
||||
|
||||
|
||||
#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)
|
||||
#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)
|
||||
#define getproto(o) (clLvalue(o)->p)
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Tables
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
typedef struct TKey {
|
||||
TValuefields;
|
||||
struct Node *next; /* for chaining */
|
||||
} TKey;
|
||||
#define ttistable(o) checktag((o), ctb(LUA_TTABLE))
|
||||
|
||||
#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))
|
||||
|
||||
#define sethvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); Table *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
#define sethvalue2s(L,o,h) sethvalue(L,s2v(o),h)
|
||||
|
||||
|
||||
typedef struct Node {
|
||||
TValue i_val;
|
||||
TKey i_key;
|
||||
/*
|
||||
** Nodes for Hash tables: A pack of two TValue's (key-value pairs)
|
||||
** plus a 'next' field to link colliding entries. The distribution
|
||||
** of the key's fields ('key_tt' and 'key_val') not forming a proper
|
||||
** 'TValue' allows for a smaller size for 'Node' both in 4-byte
|
||||
** and 8-byte alignments.
|
||||
*/
|
||||
typedef union Node {
|
||||
struct NodeKey {
|
||||
TValuefields; /* fields for value */
|
||||
lu_byte key_tt; /* key type */
|
||||
int next; /* for chaining */
|
||||
Value key_val; /* key value */
|
||||
} u;
|
||||
TValue i_val; /* direct access to node's value as a proper 'TValue' */
|
||||
} Node;
|
||||
|
||||
|
||||
/* copy a value into a key */
|
||||
#define setnodekey(L,node,obj) \
|
||||
{ Node *n_=(node); const TValue *io_=(obj); \
|
||||
n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \
|
||||
checkliveness(L,io_); }
|
||||
|
||||
|
||||
/* copy a value from a key */
|
||||
#define getnodekey(L,obj,node) \
|
||||
{ TValue *io_=(obj); const Node *n_=(node); \
|
||||
io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \
|
||||
checkliveness(L,io_); }
|
||||
|
||||
|
||||
/*
|
||||
** About 'alimit': if 'isrealasize(t)' is true, then 'alimit' is the
|
||||
** real size of 'array'. Otherwise, the real size of 'array' is the
|
||||
** smallest power of two not smaller than 'alimit' (or zero iff 'alimit'
|
||||
** is zero); 'alimit' is then used as a hint for #t.
|
||||
*/
|
||||
|
||||
#define BITRAS (1 << 7)
|
||||
#define isrealasize(t) (!((t)->marked & BITRAS))
|
||||
#define setrealasize(t) ((t)->marked &= cast_byte(~BITRAS))
|
||||
#define setnorealasize(t) ((t)->marked |= BITRAS)
|
||||
|
||||
|
||||
typedef struct Table {
|
||||
CommonHeader;
|
||||
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
|
||||
lu_byte lsizenode; /* log2 of size of `node' array */
|
||||
struct Table *metatable;
|
||||
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
|
||||
lu_byte lsizenode; /* log2 of size of 'node' array */
|
||||
unsigned int alimit; /* "limit" of 'array' array */
|
||||
TValue *array; /* array part */
|
||||
Node *node;
|
||||
Node *lastfree; /* any free position is before this position */
|
||||
struct Table *metatable;
|
||||
GCObject *gclist;
|
||||
int sizearray; /* size of `array' array */
|
||||
} Table;
|
||||
|
||||
|
||||
/*
|
||||
** Macros to manipulate keys inserted in nodes
|
||||
*/
|
||||
#define keytt(node) ((node)->u.key_tt)
|
||||
#define keyval(node) ((node)->u.key_val)
|
||||
|
||||
#define keyisnil(node) (keytt(node) == LUA_TNIL)
|
||||
#define keyisinteger(node) (keytt(node) == LUA_TNUMINT)
|
||||
#define keyival(node) (keyval(node).i)
|
||||
#define keyisshrstr(node) (keytt(node) == ctb(LUA_TSHRSTR))
|
||||
#define keystrval(node) (gco2ts(keyval(node).gc))
|
||||
|
||||
#define setnilkey(node) (keytt(node) = LUA_TNIL)
|
||||
|
||||
#define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE)
|
||||
|
||||
#define gckey(n) (keyval(n).gc)
|
||||
#define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL)
|
||||
|
||||
|
||||
/*
|
||||
** Use a "nil table" to mark dead keys in a table. Those keys serve
|
||||
** to keep space for removed entries, which may still be part of
|
||||
** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE
|
||||
** set, so these values are considered not collectable and are different
|
||||
** from any valid value.
|
||||
*/
|
||||
#define setdeadkey(n) (keytt(n) = LUA_TTABLE, gckey(n) = NULL)
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** `module' operation for hashing (size is always a power of 2)
|
||||
** 'module' operation for hashing (size is always a power of 2)
|
||||
*/
|
||||
#define lmod(s,size) \
|
||||
check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1))))
|
||||
(check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1)))))
|
||||
|
||||
|
||||
#define twoto(x) (1<<(x))
|
||||
#define sizenode(t) (twoto((t)->lsizenode))
|
||||
|
||||
|
||||
/* size of buffer for 'luaO_utf8esc' function */
|
||||
#define UTF8BUFFSZ 8
|
||||
|
||||
LUAI_DATA const TValue luaO_nilobject;
|
||||
|
||||
#define ceillog2(x) (luaO_log2((x)-1) + 1)
|
||||
|
||||
LUAI_FUNC int luaO_log2 (unsigned int x);
|
||||
LUAI_FUNC int luaO_int2fb (unsigned int x);
|
||||
LUAI_FUNC int luaO_fb2int (int x);
|
||||
LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);
|
||||
LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);
|
||||
LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x);
|
||||
LUAI_FUNC int luaO_ceillog2 (unsigned int x);
|
||||
LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1,
|
||||
const TValue *p2, TValue *res);
|
||||
LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1,
|
||||
const TValue *p2, StkId res);
|
||||
LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o);
|
||||
LUAI_FUNC int luaO_hexavalue (int c);
|
||||
LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj);
|
||||
LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
|
||||
va_list argp);
|
||||
LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
|
||||
LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);
|
||||
LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t srclen);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+89
-89
@@ -1,104 +1,104 @@
|
||||
/*
|
||||
** $Id: lopcodes.c,v 1.34 2005/05/20 15:53:42 roberto Exp $
|
||||
** $Id: lopcodes.c $
|
||||
** Opcodes for Lua virtual machine
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#define lopcodes_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lua.h"
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lopcodes.h"
|
||||
|
||||
|
||||
/* ORDER OP */
|
||||
|
||||
const char *const luaP_opnames[NUM_OPCODES+1] = {
|
||||
"MOVE",
|
||||
"LOADK",
|
||||
"LOADBOOL",
|
||||
"LOADNIL",
|
||||
"GETUPVAL",
|
||||
"GETGLOBAL",
|
||||
"GETTABLE",
|
||||
"SETGLOBAL",
|
||||
"SETUPVAL",
|
||||
"SETTABLE",
|
||||
"NEWTABLE",
|
||||
"SELF",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"MUL",
|
||||
"DIV",
|
||||
"MOD",
|
||||
"POW",
|
||||
"UNM",
|
||||
"NOT",
|
||||
"LEN",
|
||||
"CONCAT",
|
||||
"JMP",
|
||||
"EQ",
|
||||
"LT",
|
||||
"LE",
|
||||
"TEST",
|
||||
"TESTSET",
|
||||
"CALL",
|
||||
"TAILCALL",
|
||||
"RETURN",
|
||||
"FORLOOP",
|
||||
"FORPREP",
|
||||
"TFORLOOP",
|
||||
"SETLIST",
|
||||
"CLOSE",
|
||||
"CLOSURE",
|
||||
"VARARG",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
|
||||
|
||||
const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
||||
/* T A B C mode opcode */
|
||||
opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */
|
||||
,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */
|
||||
,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */
|
||||
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */
|
||||
,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */
|
||||
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */
|
||||
,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */
|
||||
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */
|
||||
,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */
|
||||
,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */
|
||||
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */
|
||||
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */
|
||||
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */
|
||||
,opmode(1, 0, OpArgR, OpArgU, iABC) /* OP_TEST */
|
||||
,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */
|
||||
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */
|
||||
,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */
|
||||
,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */
|
||||
,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */
|
||||
LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
||||
/* MM OT IT T A mode opcode */
|
||||
opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */
|
||||
,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */
|
||||
,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADBOOL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */
|
||||
,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */
|
||||
,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/
|
||||
,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */
|
||||
,opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */
|
||||
,opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */
|
||||
,opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */
|
||||
,opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */
|
||||
,opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */
|
||||
,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */
|
||||
,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
|
||||
,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
|
||||
,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
|
||||
,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
|
||||
};
|
||||
|
||||
|
||||
+254
-136
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lopcodes.h,v 1.121 2005/06/06 13:30:25 roberto Exp $
|
||||
** $Id: lopcodes.h $
|
||||
** Opcodes for Lua virtual machine
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -11,66 +11,100 @@
|
||||
|
||||
|
||||
/*===========================================================================
|
||||
We assume that instructions are unsigned numbers.
|
||||
All instructions have an opcode in the first 6 bits.
|
||||
Instructions can have the following fields:
|
||||
`A' : 8 bits
|
||||
`B' : 9 bits
|
||||
`C' : 9 bits
|
||||
`Bx' : 18 bits (`B' and `C' together)
|
||||
`sBx' : signed Bx
|
||||
We assume that instructions are unsigned 32-bit integers.
|
||||
All instructions have an opcode in the first 7 bits.
|
||||
Instructions can have the following formats:
|
||||
|
||||
A signed argument is represented in excess K; that is, the number
|
||||
value is the unsigned value minus K. K is exactly the maximum value
|
||||
for that argument (so that -max is represented by 0, and +max is
|
||||
represented by 2*max), which is half the maximum for the corresponding
|
||||
unsigned argument.
|
||||
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
|
||||
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
iABC C(8) | B(8) |k| A(8) | Op(7) |
|
||||
iABx Bx(17) | A(8) | Op(7) |
|
||||
iAsB sBx (signed)(17) | A(8) | Op(7) |
|
||||
iAx Ax(25) | Op(7) |
|
||||
isJ sJ(25) | Op(7) |
|
||||
|
||||
A signed argument is represented in excess K: the represented value is
|
||||
the written unsigned value minus K, where K is half the maximum for the
|
||||
corresponding unsigned argument.
|
||||
===========================================================================*/
|
||||
|
||||
|
||||
enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
|
||||
enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
|
||||
|
||||
/*
|
||||
** size and position of opcode arguments.
|
||||
*/
|
||||
#define SIZE_C 9
|
||||
#define SIZE_B 9
|
||||
#define SIZE_Bx (SIZE_C + SIZE_B)
|
||||
#define SIZE_C 8
|
||||
#define SIZE_B 8
|
||||
#define SIZE_Bx (SIZE_C + SIZE_B + 1)
|
||||
#define SIZE_A 8
|
||||
#define SIZE_Ax (SIZE_Bx + SIZE_A)
|
||||
#define SIZE_sJ (SIZE_Bx + SIZE_A)
|
||||
|
||||
#define SIZE_OP 6
|
||||
#define SIZE_OP 7
|
||||
|
||||
#define POS_OP 0
|
||||
|
||||
#define POS_A (POS_OP + SIZE_OP)
|
||||
#define POS_C (POS_A + SIZE_A)
|
||||
#define POS_B (POS_C + SIZE_C)
|
||||
#define POS_Bx POS_C
|
||||
#define POS_k (POS_A + SIZE_A)
|
||||
#define POS_B (POS_k + 1)
|
||||
#define POS_C (POS_B + SIZE_B)
|
||||
|
||||
#define POS_Bx POS_k
|
||||
|
||||
#define POS_Ax POS_A
|
||||
|
||||
#define POS_sJ POS_A
|
||||
|
||||
|
||||
/*
|
||||
** limits for opcode arguments.
|
||||
** we use (signed) int to manipulate most arguments,
|
||||
** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
|
||||
** we use (signed) 'int' to manipulate most arguments,
|
||||
** so they must fit in ints.
|
||||
*/
|
||||
#if SIZE_Bx < LUAI_BITSINT-1
|
||||
#define MAXARG_Bx ((1<<SIZE_Bx)-1)
|
||||
#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
|
||||
|
||||
/* Check whether type 'int' has at least 'b' bits ('b' < 32) */
|
||||
#define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1)
|
||||
|
||||
|
||||
#if L_INTHASBITS(SIZE_Bx)
|
||||
#define MAXARG_Bx ((1<<SIZE_Bx)-1)
|
||||
#else
|
||||
#define MAXARG_Bx MAX_INT
|
||||
#define MAXARG_sBx MAX_INT
|
||||
#define MAXARG_Bx MAX_INT
|
||||
#endif
|
||||
|
||||
|
||||
#define MAXARG_A ((1<<SIZE_A)-1)
|
||||
#define MAXARG_B ((1<<SIZE_B)-1)
|
||||
#define MAXARG_C ((1<<SIZE_C)-1)
|
||||
#define OFFSET_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */
|
||||
|
||||
|
||||
/* creates a mask with `n' 1 bits at position `p' */
|
||||
#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p)
|
||||
#if L_INTHASBITS(SIZE_Ax)
|
||||
#define MAXARG_Ax ((1<<SIZE_Ax)-1)
|
||||
#else
|
||||
#define MAXARG_Ax MAX_INT
|
||||
#endif
|
||||
|
||||
/* creates a mask with `n' 0 bits at position `p' */
|
||||
#if L_INTHASBITS(SIZE_sJ)
|
||||
#define MAXARG_sJ ((1 << SIZE_sJ) - 1)
|
||||
#else
|
||||
#define MAXARG_sJ MAX_INT
|
||||
#endif
|
||||
|
||||
#define OFFSET_sJ (MAXARG_sJ >> 1)
|
||||
|
||||
|
||||
#define MAXARG_A ((1<<SIZE_A)-1)
|
||||
#define MAXARG_B ((1<<SIZE_B)-1)
|
||||
#define MAXARG_C ((1<<SIZE_C)-1)
|
||||
#define OFFSET_sC (MAXARG_C >> 1)
|
||||
|
||||
#define int2sC(i) ((i) + OFFSET_sC)
|
||||
#define sC2int(i) ((i) - OFFSET_sC)
|
||||
|
||||
|
||||
/* creates a mask with 'n' 1 bits at position 'p' */
|
||||
#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p))
|
||||
|
||||
/* creates a mask with 'n' 0 bits at position 'p' */
|
||||
#define MASK0(n,p) (~MASK1(n,p))
|
||||
|
||||
/*
|
||||
@@ -81,53 +115,65 @@ enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
|
||||
#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
|
||||
((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
|
||||
|
||||
#define GETARG_A(i) (cast(int, ((i)>>POS_A) & MASK1(SIZE_A,0)))
|
||||
#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
|
||||
((cast(Instruction, u)<<POS_A)&MASK1(SIZE_A,POS_A))))
|
||||
|
||||
#define GETARG_B(i) (cast(int, ((i)>>POS_B) & MASK1(SIZE_B,0)))
|
||||
#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
|
||||
((cast(Instruction, b)<<POS_B)&MASK1(SIZE_B,POS_B))))
|
||||
|
||||
#define GETARG_C(i) (cast(int, ((i)>>POS_C) & MASK1(SIZE_C,0)))
|
||||
#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \
|
||||
((cast(Instruction, b)<<POS_C)&MASK1(SIZE_C,POS_C))))
|
||||
|
||||
#define GETARG_Bx(i) (cast(int, ((i)>>POS_Bx) & MASK1(SIZE_Bx,0)))
|
||||
#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \
|
||||
((cast(Instruction, b)<<POS_Bx)&MASK1(SIZE_Bx,POS_Bx))))
|
||||
|
||||
#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
|
||||
#define SETARG_sBx(i,b) SETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))
|
||||
#define checkopm(i,m) (getOpMode(GET_OPCODE(i)) == m)
|
||||
|
||||
|
||||
#define CREATE_ABC(o,a,b,c) ((cast(Instruction, o)<<POS_OP) \
|
||||
#define getarg(i,pos,size) (cast_int(((i)>>(pos)) & MASK1(size,0)))
|
||||
#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \
|
||||
((cast(Instruction, v)<<pos)&MASK1(size,pos))))
|
||||
|
||||
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
|
||||
#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
|
||||
|
||||
#define GETARG_B(i) check_exp(checkopm(i, iABC), getarg(i, POS_B, SIZE_B))
|
||||
#define GETARG_sB(i) sC2int(GETARG_B(i))
|
||||
#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
|
||||
|
||||
#define GETARG_C(i) check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C))
|
||||
#define GETARG_sC(i) sC2int(GETARG_C(i))
|
||||
#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
|
||||
|
||||
#define TESTARG_k(i) (cast_int(((i) & (1u << POS_k))))
|
||||
#define GETARG_k(i) check_exp(checkopm(i, iABC), getarg(i, POS_k, 1))
|
||||
#define SETARG_k(i,v) setarg(i, v, POS_k, 1)
|
||||
|
||||
#define GETARG_Bx(i) check_exp(checkopm(i, iABx), getarg(i, POS_Bx, SIZE_Bx))
|
||||
#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx)
|
||||
|
||||
#define GETARG_Ax(i) check_exp(checkopm(i, iAx), getarg(i, POS_Ax, SIZE_Ax))
|
||||
#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax)
|
||||
|
||||
#define GETARG_sBx(i) \
|
||||
check_exp(checkopm(i, iAsBx), getarg(i, POS_Bx, SIZE_Bx) - OFFSET_sBx)
|
||||
#define SETARG_sBx(i,b) SETARG_Bx((i),cast_uint((b)+OFFSET_sBx))
|
||||
|
||||
#define GETARG_sJ(i) \
|
||||
check_exp(checkopm(i, isJ), getarg(i, POS_sJ, SIZE_sJ) - OFFSET_sJ)
|
||||
#define SETARG_sJ(i,j) \
|
||||
setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ)
|
||||
|
||||
|
||||
#define CREATE_ABCk(o,a,b,c,k) ((cast(Instruction, o)<<POS_OP) \
|
||||
| (cast(Instruction, a)<<POS_A) \
|
||||
| (cast(Instruction, b)<<POS_B) \
|
||||
| (cast(Instruction, c)<<POS_C))
|
||||
| (cast(Instruction, c)<<POS_C) \
|
||||
| (cast(Instruction, k)<<POS_k))
|
||||
|
||||
#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
|
||||
| (cast(Instruction, a)<<POS_A) \
|
||||
| (cast(Instruction, bc)<<POS_Bx))
|
||||
|
||||
#define CREATE_Ax(o,a) ((cast(Instruction, o)<<POS_OP) \
|
||||
| (cast(Instruction, a)<<POS_Ax))
|
||||
|
||||
/*
|
||||
** Macros to operate RK indices
|
||||
*/
|
||||
#define CREATE_sJ(o,j,k) ((cast(Instruction, o) << POS_OP) \
|
||||
| (cast(Instruction, j) << POS_sJ) \
|
||||
| (cast(Instruction, k) << POS_k))
|
||||
|
||||
/* this bit 1 means constant (0 means register) */
|
||||
#define BITRK (1 << (SIZE_B - 1))
|
||||
|
||||
/* test whether value is a constant */
|
||||
#define ISK(x) ((x) & BITRK)
|
||||
|
||||
/* gets the index of the constant */
|
||||
#define INDEXK(r) ((int)(r) & ~BITRK)
|
||||
|
||||
#define MAXINDEXRK (BITRK - 1)
|
||||
|
||||
/* code a constant index as a RK value */
|
||||
#define RKASK(x) ((x) | BITRK)
|
||||
#if !defined(MAXINDEXRK) /* (for debugging only) */
|
||||
#define MAXINDEXRK MAXARG_B
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -138,8 +184,8 @@ enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
|
||||
|
||||
/*
|
||||
** R(x) - register
|
||||
** Kst(x) - constant (in constant table)
|
||||
** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
|
||||
** K(x) - constant (in constant table)
|
||||
** RK(x) == if k(i) then K(x) else R(x)
|
||||
*/
|
||||
|
||||
|
||||
@@ -152,117 +198,189 @@ typedef enum {
|
||||
name args description
|
||||
------------------------------------------------------------------------*/
|
||||
OP_MOVE,/* A B R(A) := R(B) */
|
||||
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
|
||||
OP_LOADI,/* A sBx R(A) := sBx */
|
||||
OP_LOADF,/* A sBx R(A) := (lua_Number)sBx */
|
||||
OP_LOADK,/* A Bx R(A) := K(Bx) */
|
||||
OP_LOADKX,/* A R(A) := K(extra arg) */
|
||||
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */
|
||||
OP_LOADNIL,/* A B R(A), R(A+1), ..., R(A+B) := nil */
|
||||
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
|
||||
|
||||
OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */
|
||||
OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
|
||||
|
||||
OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */
|
||||
OP_SETUPVAL,/* A B UpValue[B] := R(A) */
|
||||
OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
|
||||
|
||||
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
|
||||
OP_GETTABUP,/* A B C R(A) := UpValue[B][K(C):string] */
|
||||
OP_GETTABLE,/* A B C R(A) := R(B)[R(C)] */
|
||||
OP_GETI,/* A B C R(A) := R(B)[C] */
|
||||
OP_GETFIELD,/* A B C R(A) := R(B)[K(C):string] */
|
||||
|
||||
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
OP_SETTABUP,/* A B C UpValue[A][K(B):string] := RK(C) */
|
||||
OP_SETTABLE,/* A B C R(A)[R(B)] := RK(C) */
|
||||
OP_SETI,/* A B C R(A)[B] := RK(C) */
|
||||
OP_SETFIELD,/* A B C R(A)[K(B):string] := RK(C) */
|
||||
|
||||
OP_NEWTABLE,/* A B C R(A) := {} */
|
||||
|
||||
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C):string] */
|
||||
|
||||
OP_ADDI,/* A B sC R(A) := R(B) + sC */
|
||||
|
||||
OP_ADDK,/* A B C R(A) := R(B) + K(C) */
|
||||
OP_SUBK,/* A B C R(A) := R(B) - K(C) */
|
||||
OP_MULK,/* A B C R(A) := R(B) * K(C) */
|
||||
OP_MODK,/* A B C R(A) := R(B) % K(C) */
|
||||
OP_POWK,/* A B C R(A) := R(B) ^ K(C) */
|
||||
OP_DIVK,/* A B C R(A) := R(B) / K(C) */
|
||||
OP_IDIVK,/* A B C R(A) := R(B) // K(C) */
|
||||
|
||||
OP_BANDK,/* A B C R(A) := R(B) & K(C):integer */
|
||||
OP_BORK,/* A B C R(A) := R(B) | K(C):integer */
|
||||
OP_BXORK,/* A B C R(A) := R(B) ~ K(C):integer */
|
||||
|
||||
OP_SHRI,/* A B sC R(A) := R(B) >> sC */
|
||||
OP_SHLI,/* A B sC R(A) := sC << R(B) */
|
||||
|
||||
OP_ADD,/* A B C R(A) := R(B) + R(C) */
|
||||
OP_SUB,/* A B C R(A) := R(B) - R(C) */
|
||||
OP_MUL,/* A B C R(A) := R(B) * R(C) */
|
||||
OP_MOD,/* A B C R(A) := R(B) % R(C) */
|
||||
OP_POW,/* A B C R(A) := R(B) ^ R(C) */
|
||||
OP_DIV,/* A B C R(A) := R(B) / R(C) */
|
||||
OP_IDIV,/* A B C R(A) := R(B) // R(C) */
|
||||
|
||||
OP_BAND,/* A B C R(A) := R(B) & R(C) */
|
||||
OP_BOR,/* A B C R(A) := R(B) | R(C) */
|
||||
OP_BXOR,/* A B C R(A) := R(B) ~ R(C) */
|
||||
OP_SHL,/* A B C R(A) := R(B) << R(C) */
|
||||
OP_SHR,/* A B C R(A) := R(B) >> R(C) */
|
||||
|
||||
OP_MMBIN,/* A B C call C metamethod over R(A) and R(B) */
|
||||
OP_MMBINI,/* A sB C call C metamethod over R(A) and sB */
|
||||
OP_MMBINK,/* A B C call C metamethod over R(A) and K(B) */
|
||||
|
||||
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
|
||||
OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
|
||||
OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
|
||||
OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
|
||||
OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
|
||||
OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
|
||||
OP_UNM,/* A B R(A) := -R(B) */
|
||||
OP_BNOT,/* A B R(A) := ~R(B) */
|
||||
OP_NOT,/* A B R(A) := not R(B) */
|
||||
OP_LEN,/* A B R(A) := length of R(B) */
|
||||
|
||||
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
|
||||
OP_CONCAT,/* A B R(A) := R(A).. ... ..R(A + B - 1) */
|
||||
|
||||
OP_JMP,/* sBx pc+=sBx */
|
||||
OP_CLOSE,/* A close all upvalues >= R(A) */
|
||||
OP_TBC,/* A mark variable A "to be closed" */
|
||||
OP_JMP,/* k sJ pc += sJ (k is used in code generation) */
|
||||
OP_EQ,/* A B if ((R(A) == R(B)) ~= k) then pc++ */
|
||||
OP_LT,/* A B if ((R(A) < R(B)) ~= k) then pc++ */
|
||||
OP_LE,/* A B if ((R(A) <= R(B)) ~= k) then pc++ */
|
||||
|
||||
OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
|
||||
OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
|
||||
OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
|
||||
OP_EQK,/* A B if ((R(A) == K(B)) ~= k) then pc++ */
|
||||
OP_EQI,/* A sB if ((R(A) == sB) ~= k) then pc++ */
|
||||
OP_LTI,/* A sB if ((R(A) < sB) ~= k) then pc++ */
|
||||
OP_LEI,/* A sB if ((R(A) <= sB) ~= k) then pc++ */
|
||||
OP_GTI,/* A sB if ((R(A) > sB) ~= k) then pc++ */
|
||||
OP_GEI,/* A sB if ((R(A) >= sB) ~= k) then pc++ */
|
||||
|
||||
OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
|
||||
OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
|
||||
OP_TEST,/* A if (not R(A) == k) then pc++ */
|
||||
OP_TESTSET,/* A B if (not R(B) == k) then pc++ else R(A) := R(B) */
|
||||
|
||||
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
|
||||
|
||||
OP_FORLOOP,/* A sBx R(A)+=R(A+2); if R(A) <?= R(A+1) then pc+=sBx */
|
||||
OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
OP_RETURN,/* A B C return R(A), ... ,R(A+B-2) (see note) */
|
||||
OP_RETURN0,/* return */
|
||||
OP_RETURN1,/* A return R(A) */
|
||||
|
||||
OP_TFORLOOP,/* A C R(A+2), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
|
||||
if R(A+2) ~= nil then pc++ */
|
||||
OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */
|
||||
OP_FORPREP,/* A Bx <check values and prepare counters>;
|
||||
if not to run then pc+=Bx+1; */
|
||||
|
||||
OP_TFORPREP,/* A Bx create upvalue for R(A + 3); pc+=Bx */
|
||||
OP_TFORCALL,/* A C R(A+4), ... ,R(A+3+C) := R(A)(R(A+1), R(A+2)); */
|
||||
OP_TFORLOOP,/* A Bx if R(A+2) ~= nil then { R(A)=R(A+2); pc -= Bx } */
|
||||
|
||||
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
|
||||
OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
|
||||
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
|
||||
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */
|
||||
|
||||
OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
|
||||
OP_VARARG,/* A C R(A), R(A+1), ..., R(A+C-2) = vararg */
|
||||
|
||||
OP_VARARGPREP,/*A (adjust vararg parameters) */
|
||||
|
||||
OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
} OpCode;
|
||||
|
||||
|
||||
#define NUM_OPCODES (cast(int, OP_VARARG+1))
|
||||
#define NUM_OPCODES ((int)(OP_EXTRAARG) + 1)
|
||||
|
||||
|
||||
|
||||
/*===========================================================================
|
||||
Notes:
|
||||
(*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
|
||||
and can be 0: OP_CALL then sets `top' to last_result+1, so
|
||||
next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
|
||||
(*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then
|
||||
'top' is set to last_result+1, so next open instruction (OP_CALL,
|
||||
OP_RETURN*, OP_SETLIST) may use 'top'.
|
||||
|
||||
(*) In OP_VARARG, if (B == 0) then use actual number of varargs and
|
||||
set top (like in OP_CALL with C == 0).
|
||||
(*) In OP_VARARG, if (C == 0) then use actual number of varargs and
|
||||
set top (like in OP_CALL with C == 0).
|
||||
|
||||
(*) In OP_RETURN, if (B == 0) then return up to `top'
|
||||
(*) In OP_RETURN, if (B == 0) then return up to 'top'.
|
||||
|
||||
(*) In OP_SETLIST, if (B == 0) then B = `top';
|
||||
if (C == 0) then next `instruction' is real C
|
||||
(*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always
|
||||
EXTRAARG.
|
||||
|
||||
(*) For comparisons, A specifies what condition the test should accept
|
||||
(true or false).
|
||||
(*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then
|
||||
real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the
|
||||
bits of C).
|
||||
|
||||
(*) In OP_NEWTABLE, B is log2 of the hash size (which is always a
|
||||
power of 2) plus 1, or zero for size zero. If not k, the array size
|
||||
is C. Otherwise, the array size is EXTRAARG _ C.
|
||||
|
||||
(*) For comparisons, k specifies what condition the test should accept
|
||||
(true or false).
|
||||
|
||||
(*) All 'skips' (pc++) assume that next instruction is a jump.
|
||||
|
||||
(*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the
|
||||
function builds upvalues, which may need to be closed. C > 0 means
|
||||
the function is vararg, so that its 'func' must be corrected before
|
||||
returning; in this case, (C - 1) is its number of fixed parameters.
|
||||
|
||||
(*) In comparisons with an immediate operand, C signals whether the
|
||||
original operand was a float.
|
||||
|
||||
(*) All `skips' (pc++) assume that next instruction is a jump
|
||||
===========================================================================*/
|
||||
|
||||
|
||||
/*
|
||||
** masks for instruction properties. The format is:
|
||||
** bits 0-1: op mode
|
||||
** bits 2-3: C arg mode
|
||||
** bits 4-5: B arg mode
|
||||
** bit 6: instruction set register A
|
||||
** bit 7: operator is a test
|
||||
*/
|
||||
** bits 0-2: op mode
|
||||
** bit 3: instruction set register A
|
||||
** bit 4: operator is a test (next instruction must be a jump)
|
||||
** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0)
|
||||
** bit 6: instruction sets 'L->top' for next instruction (when C == 0)
|
||||
** bit 7: instruction is an MM instruction (call a metamethod)
|
||||
*/
|
||||
|
||||
enum OpArgMask {
|
||||
OpArgN, /* argument is not used */
|
||||
OpArgU, /* argument is used */
|
||||
OpArgR, /* argument is a register or a jump offset */
|
||||
OpArgK /* argument is a constant or register/constant */
|
||||
};
|
||||
LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
|
||||
|
||||
LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];
|
||||
#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7))
|
||||
#define testAMode(m) (luaP_opmodes[m] & (1 << 3))
|
||||
#define testTMode(m) (luaP_opmodes[m] & (1 << 4))
|
||||
#define testITMode(m) (luaP_opmodes[m] & (1 << 5))
|
||||
#define testOTMode(m) (luaP_opmodes[m] & (1 << 6))
|
||||
#define testMMMode(m) (luaP_opmodes[m] & (1 << 7))
|
||||
|
||||
#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
|
||||
#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
|
||||
#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
|
||||
#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
|
||||
#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
|
||||
/* "out top" (set top for next instruction) */
|
||||
#define isOT(i) \
|
||||
((testOTMode(GET_OPCODE(i)) && GETARG_C(i) == 0) || \
|
||||
GET_OPCODE(i) == OP_TAILCALL)
|
||||
|
||||
/* "in top" (uses top from previous instruction) */
|
||||
#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0)
|
||||
|
||||
LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */
|
||||
#define opmode(mm,ot,it,t,a,m) \
|
||||
(((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m))
|
||||
|
||||
|
||||
/* number of list items to accumulate before a SETLIST instruction */
|
||||
#define LFIELDS_PER_FLUSH 50
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
** $Id: lopnames.h $
|
||||
** Opcode names
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#if !defined(lopnames_h)
|
||||
#define lopnames_h
|
||||
|
||||
/* ORDER OP */
|
||||
|
||||
static const char *const opnames[] = {
|
||||
"MOVE",
|
||||
"LOADI",
|
||||
"LOADF",
|
||||
"LOADK",
|
||||
"LOADKX",
|
||||
"LOADBOOL",
|
||||
"LOADNIL",
|
||||
"GETUPVAL",
|
||||
"SETUPVAL",
|
||||
"GETTABUP",
|
||||
"GETTABLE",
|
||||
"GETI",
|
||||
"GETFIELD",
|
||||
"SETTABUP",
|
||||
"SETTABLE",
|
||||
"SETI",
|
||||
"SETFIELD",
|
||||
"NEWTABLE",
|
||||
"SELF",
|
||||
"ADDI",
|
||||
"ADDK",
|
||||
"SUBK",
|
||||
"MULK",
|
||||
"MODK",
|
||||
"POWK",
|
||||
"DIVK",
|
||||
"IDIVK",
|
||||
"BANDK",
|
||||
"BORK",
|
||||
"BXORK",
|
||||
"SHRI",
|
||||
"SHLI",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"MUL",
|
||||
"MOD",
|
||||
"POW",
|
||||
"DIV",
|
||||
"IDIV",
|
||||
"BAND",
|
||||
"BOR",
|
||||
"BXOR",
|
||||
"SHL",
|
||||
"SHR",
|
||||
"MMBIN",
|
||||
"MMBINI",
|
||||
"MMBINK",
|
||||
"UNM",
|
||||
"BNOT",
|
||||
"NOT",
|
||||
"LEN",
|
||||
"CONCAT",
|
||||
"CLOSE",
|
||||
"TBC",
|
||||
"JMP",
|
||||
"EQ",
|
||||
"LT",
|
||||
"LE",
|
||||
"EQK",
|
||||
"EQI",
|
||||
"LTI",
|
||||
"LEI",
|
||||
"GTI",
|
||||
"GEI",
|
||||
"TEST",
|
||||
"TESTSET",
|
||||
"CALL",
|
||||
"TAILCALL",
|
||||
"RETURN",
|
||||
"RETURN0",
|
||||
"RETURN1",
|
||||
"FORLOOP",
|
||||
"FORPREP",
|
||||
"TFORPREP",
|
||||
"TFORCALL",
|
||||
"TFORLOOP",
|
||||
"SETLIST",
|
||||
"CLOSURE",
|
||||
"VARARG",
|
||||
"VARARGPREP",
|
||||
"EXTRAARG",
|
||||
NULL
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,62 +1,170 @@
|
||||
/*
|
||||
** $Id: loslib.c,v 1.12 2005/08/26 17:36:32 roberto Exp roberto $
|
||||
** $Id: loslib.c $
|
||||
** Standard Operating System library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define loslib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define loslib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
static int os_pushresult (lua_State *L, int i, const char *filename) {
|
||||
if (i) {
|
||||
lua_pushboolean(L, 1);
|
||||
/*
|
||||
** {==================================================================
|
||||
** List of valid conversion specifiers for the 'strftime' function;
|
||||
** options are grouped by length; group of length 2 start with '||'.
|
||||
** ===================================================================
|
||||
*/
|
||||
#if !defined(LUA_STRFTIMEOPTIONS) /* { */
|
||||
|
||||
/* options for ANSI C 89 (only 1-char options) */
|
||||
#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%"
|
||||
|
||||
/* options for ISO C 99 and POSIX */
|
||||
#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
|
||||
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
|
||||
|
||||
/* options for Windows */
|
||||
#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \
|
||||
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
|
||||
|
||||
#if defined(LUA_USE_WINDOWS)
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN
|
||||
#elif defined(LUA_USE_C89)
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89
|
||||
#else /* C99 specification */
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Configuration for time-related stuff
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** type to represent time_t in Lua
|
||||
*/
|
||||
#if !defined(LUA_NUMTIME) /* { */
|
||||
|
||||
#define l_timet lua_Integer
|
||||
#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t))
|
||||
#define l_gettime(L,arg) luaL_checkinteger(L, arg)
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#define l_timet lua_Number
|
||||
#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t))
|
||||
#define l_gettime(L,arg) luaL_checknumber(L, arg)
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
#if !defined(l_gmtime) /* { */
|
||||
/*
|
||||
** By default, Lua uses gmtime/localtime, except when POSIX is available,
|
||||
** where it uses gmtime_r/localtime_r
|
||||
*/
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
#define l_gmtime(t,r) gmtime_r(t,r)
|
||||
#define l_localtime(t,r) localtime_r(t,r)
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definitions */
|
||||
#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t))
|
||||
#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t))
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Configuration for 'tmpnam':
|
||||
** By default, Lua uses tmpnam except when POSIX is available, where
|
||||
** it uses mkstemp.
|
||||
** ===================================================================
|
||||
*/
|
||||
#if !defined(lua_tmpnam) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define LUA_TMPNAMBUFSIZE 32
|
||||
|
||||
#if !defined(LUA_TMPNAMTEMPLATE)
|
||||
#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX"
|
||||
#endif
|
||||
|
||||
#define lua_tmpnam(b,e) { \
|
||||
strcpy(b, LUA_TMPNAMTEMPLATE); \
|
||||
e = mkstemp(b); \
|
||||
if (e != -1) close(e); \
|
||||
e = (e == -1); }
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definitions */
|
||||
#define LUA_TMPNAMBUFSIZE L_tmpnam
|
||||
#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); }
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
|
||||
|
||||
static int os_execute (lua_State *L) {
|
||||
const char *cmd = luaL_optstring(L, 1, NULL);
|
||||
int stat = system(cmd);
|
||||
if (cmd != NULL)
|
||||
return luaL_execresult(L, stat);
|
||||
else {
|
||||
lua_pushboolean(L, stat); /* true if there is a shell */
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
if (filename)
|
||||
lua_pushfstring(L, "%s: %s", filename, strerror(errno));
|
||||
else
|
||||
lua_pushfstring(L, "%s", strerror(errno));
|
||||
lua_pushinteger(L, errno);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int io_execute (lua_State *L) {
|
||||
lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int io_remove (lua_State *L) {
|
||||
static int os_remove (lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
return os_pushresult(L, remove(filename) == 0, filename);
|
||||
return luaL_fileresult(L, remove(filename) == 0, filename);
|
||||
}
|
||||
|
||||
|
||||
static int io_rename (lua_State *L) {
|
||||
static int os_rename (lua_State *L) {
|
||||
const char *fromname = luaL_checkstring(L, 1);
|
||||
const char *toname = luaL_checkstring(L, 2);
|
||||
return os_pushresult(L, rename(fromname, toname) == 0, fromname);
|
||||
return luaL_fileresult(L, rename(fromname, toname) == 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int io_tmpname (lua_State *L) {
|
||||
static int os_tmpname (lua_State *L) {
|
||||
char buff[LUA_TMPNAMBUFSIZE];
|
||||
int err;
|
||||
lua_tmpnam(buff, err);
|
||||
@@ -67,13 +175,13 @@ static int io_tmpname (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int io_getenv (lua_State *L) {
|
||||
static int os_getenv (lua_State *L) {
|
||||
lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int io_clock (lua_State *L) {
|
||||
static int os_clock (lua_State *L) {
|
||||
lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
|
||||
return 1;
|
||||
}
|
||||
@@ -87,11 +195,25 @@ static int io_clock (lua_State *L) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static void setfield (lua_State *L, const char *key, int value) {
|
||||
lua_pushinteger(L, value);
|
||||
/*
|
||||
** About the overflow check: an overflow cannot occurr when time
|
||||
** is represented by a lua_Integer, because either lua_Integer is
|
||||
** large enough to represent all int fields or it is not large enough
|
||||
** to represent a time that cause a field to overflow. However, if
|
||||
** times are represented as doubles and lua_Integer is int, then the
|
||||
** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900
|
||||
** to compute the year.
|
||||
*/
|
||||
static void setfield (lua_State *L, const char *key, int value, int delta) {
|
||||
#if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX)
|
||||
if (value > LUA_MAXINTEGER - delta)
|
||||
luaL_error(L, "field '%s' is out-of-bound", key);
|
||||
#endif
|
||||
lua_pushinteger(L, (lua_Integer)value + delta);
|
||||
lua_setfield(L, -2, key);
|
||||
}
|
||||
|
||||
|
||||
static void setboolfield (lua_State *L, const char *key, int value) {
|
||||
if (value < 0) /* undefined? */
|
||||
return; /* does not set field */
|
||||
@@ -99,67 +221,127 @@ static void setboolfield (lua_State *L, const char *key, int value) {
|
||||
lua_setfield(L, -2, key);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Set all fields from structure 'tm' in the table on top of the stack
|
||||
*/
|
||||
static void setallfields (lua_State *L, struct tm *stm) {
|
||||
setfield(L, "year", stm->tm_year, 1900);
|
||||
setfield(L, "month", stm->tm_mon, 1);
|
||||
setfield(L, "day", stm->tm_mday, 0);
|
||||
setfield(L, "hour", stm->tm_hour, 0);
|
||||
setfield(L, "min", stm->tm_min, 0);
|
||||
setfield(L, "sec", stm->tm_sec, 0);
|
||||
setfield(L, "yday", stm->tm_yday, 1);
|
||||
setfield(L, "wday", stm->tm_wday, 1);
|
||||
setboolfield(L, "isdst", stm->tm_isdst);
|
||||
}
|
||||
|
||||
|
||||
static int getboolfield (lua_State *L, const char *key) {
|
||||
int res;
|
||||
lua_getfield(L, -1, key);
|
||||
res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
|
||||
res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static int getfield (lua_State *L, const char *key, int d) {
|
||||
int res;
|
||||
lua_getfield(L, -1, key);
|
||||
if (lua_isnumber(L, -1))
|
||||
res = (int)lua_tointeger(L, -1);
|
||||
else {
|
||||
if (d < 0)
|
||||
return luaL_error(L, "field " LUA_QS " missing in date table", key);
|
||||
static int getfield (lua_State *L, const char *key, int d, int delta) {
|
||||
int isnum;
|
||||
int t = lua_getfield(L, -1, key); /* get field and its type */
|
||||
lua_Integer res = lua_tointegerx(L, -1, &isnum);
|
||||
if (!isnum) { /* field is not an integer? */
|
||||
if (t != LUA_TNIL) /* some other value? */
|
||||
return luaL_error(L, "field '%s' is not an integer", key);
|
||||
else if (d < 0) /* absent field; no default? */
|
||||
return luaL_error(L, "field '%s' missing in date table", key);
|
||||
res = d;
|
||||
}
|
||||
else {
|
||||
/* unsigned avoids overflow when lua_Integer has 32 bits */
|
||||
if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta
|
||||
: (lua_Integer)INT_MIN + delta <= res))
|
||||
return luaL_error(L, "field '%s' is out-of-bound", key);
|
||||
res -= delta;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return res;
|
||||
return (int)res;
|
||||
}
|
||||
|
||||
|
||||
static int io_date (lua_State *L) {
|
||||
const char *s = luaL_optstring(L, 1, "%c");
|
||||
lua_Number n = luaL_optnumber(L, 2, -1);
|
||||
time_t t = (n == -1) ? time(NULL) : (time_t)n;
|
||||
struct tm *stm;
|
||||
static const char *checkoption (lua_State *L, const char *conv,
|
||||
ptrdiff_t convlen, char *buff) {
|
||||
const char *option = LUA_STRFTIMEOPTIONS;
|
||||
int oplen = 1; /* length of options being checked */
|
||||
for (; *option != '\0' && oplen <= convlen; option += oplen) {
|
||||
if (*option == '|') /* next block? */
|
||||
oplen++; /* will check options with next length (+1) */
|
||||
else if (memcmp(conv, option, oplen) == 0) { /* match? */
|
||||
memcpy(buff, conv, oplen); /* copy valid option to buffer */
|
||||
buff[oplen] = '\0';
|
||||
return conv + oplen; /* return next item */
|
||||
}
|
||||
}
|
||||
luaL_argerror(L, 1,
|
||||
lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv));
|
||||
return conv; /* to avoid warnings */
|
||||
}
|
||||
|
||||
|
||||
static time_t l_checktime (lua_State *L, int arg) {
|
||||
l_timet t = l_gettime(L, arg);
|
||||
luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
|
||||
return (time_t)t;
|
||||
}
|
||||
|
||||
|
||||
/* maximum size for an individual 'strftime' item */
|
||||
#define SIZETIMEFMT 250
|
||||
|
||||
|
||||
static int os_date (lua_State *L) {
|
||||
size_t slen;
|
||||
const char *s = luaL_optlstring(L, 1, "%c", &slen);
|
||||
time_t t = luaL_opt(L, l_checktime, 2, time(NULL));
|
||||
const char *se = s + slen; /* 's' end */
|
||||
struct tm tmr, *stm;
|
||||
if (*s == '!') { /* UTC? */
|
||||
stm = gmtime(&t);
|
||||
s++; /* skip `!' */
|
||||
stm = l_gmtime(&t, &tmr);
|
||||
s++; /* skip '!' */
|
||||
}
|
||||
else
|
||||
stm = localtime(&t);
|
||||
stm = l_localtime(&t, &tmr);
|
||||
if (stm == NULL) /* invalid date? */
|
||||
lua_pushnil(L);
|
||||
else if (strcmp(s, "*t") == 0) {
|
||||
return luaL_error(L,
|
||||
"date result cannot be represented in this installation");
|
||||
if (strcmp(s, "*t") == 0) {
|
||||
lua_createtable(L, 0, 9); /* 9 = number of fields */
|
||||
setfield(L, "sec", stm->tm_sec);
|
||||
setfield(L, "min", stm->tm_min);
|
||||
setfield(L, "hour", stm->tm_hour);
|
||||
setfield(L, "day", stm->tm_mday);
|
||||
setfield(L, "month", stm->tm_mon+1);
|
||||
setfield(L, "year", stm->tm_year+1900);
|
||||
setfield(L, "wday", stm->tm_wday+1);
|
||||
setfield(L, "yday", stm->tm_yday+1);
|
||||
setboolfield(L, "isdst", stm->tm_isdst);
|
||||
setallfields(L, stm);
|
||||
}
|
||||
else {
|
||||
char b[256];
|
||||
if (strftime(b, sizeof(b), s, stm))
|
||||
lua_pushstring(L, b);
|
||||
else
|
||||
return luaL_error(L, LUA_QL("date") " format too long");
|
||||
char cc[4]; /* buffer for individual conversion specifiers */
|
||||
luaL_Buffer b;
|
||||
cc[0] = '%';
|
||||
luaL_buffinit(L, &b);
|
||||
while (s < se) {
|
||||
if (*s != '%') /* not a conversion specifier? */
|
||||
luaL_addchar(&b, *s++);
|
||||
else {
|
||||
size_t reslen;
|
||||
char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT);
|
||||
s++; /* skip '%' */
|
||||
s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */
|
||||
reslen = strftime(buff, SIZETIMEFMT, cc, stm);
|
||||
luaL_addsize(&b, reslen);
|
||||
}
|
||||
}
|
||||
luaL_pushresult(&b);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int io_time (lua_State *L) {
|
||||
static int os_time (lua_State *L) {
|
||||
time_t t;
|
||||
if (lua_isnoneornil(L, 1)) /* called without args? */
|
||||
t = time(NULL); /* get current time */
|
||||
@@ -167,62 +349,71 @@ static int io_time (lua_State *L) {
|
||||
struct tm ts;
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_settop(L, 1); /* make sure table is at the top */
|
||||
ts.tm_sec = getfield(L, "sec", 0);
|
||||
ts.tm_min = getfield(L, "min", 0);
|
||||
ts.tm_hour = getfield(L, "hour", 12);
|
||||
ts.tm_mday = getfield(L, "day", -1);
|
||||
ts.tm_mon = getfield(L, "month", -1) - 1;
|
||||
ts.tm_year = getfield(L, "year", -1) - 1900;
|
||||
ts.tm_year = getfield(L, "year", -1, 1900);
|
||||
ts.tm_mon = getfield(L, "month", -1, 1);
|
||||
ts.tm_mday = getfield(L, "day", -1, 0);
|
||||
ts.tm_hour = getfield(L, "hour", 12, 0);
|
||||
ts.tm_min = getfield(L, "min", 0, 0);
|
||||
ts.tm_sec = getfield(L, "sec", 0, 0);
|
||||
ts.tm_isdst = getboolfield(L, "isdst");
|
||||
t = mktime(&ts);
|
||||
setallfields(L, &ts); /* update fields with normalized values */
|
||||
}
|
||||
if (t == (time_t)(-1))
|
||||
lua_pushnil(L);
|
||||
else
|
||||
lua_pushnumber(L, t);
|
||||
if (t != (time_t)(l_timet)t || t == (time_t)(-1))
|
||||
return luaL_error(L,
|
||||
"time result cannot be represented in this installation");
|
||||
l_pushtime(L, t);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int io_difftime (lua_State *L) {
|
||||
lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
|
||||
(time_t)(luaL_optnumber(L, 2, 0))));
|
||||
static int os_difftime (lua_State *L) {
|
||||
time_t t1 = l_checktime(L, 1);
|
||||
time_t t2 = l_checktime(L, 2);
|
||||
lua_pushnumber(L, (lua_Number)difftime(t1, t2));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
static int io_setloc (lua_State *L) {
|
||||
static int os_setlocale (lua_State *L) {
|
||||
static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
|
||||
LC_NUMERIC, LC_TIME};
|
||||
static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
|
||||
"numeric", "time", NULL};
|
||||
const char *l = lua_tostring(L, 1);
|
||||
const char *l = luaL_optstring(L, 1, NULL);
|
||||
int op = luaL_checkoption(L, 2, "all", catnames);
|
||||
luaL_argcheck(L, l || lua_isnoneornil(L, 1), 1, "string expected");
|
||||
lua_pushstring(L, setlocale(cat[op], l));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int io_exit (lua_State *L) {
|
||||
exit(luaL_optint(L, 1, EXIT_SUCCESS));
|
||||
return 0; /* to avoid warnings */
|
||||
static int os_exit (lua_State *L) {
|
||||
int status;
|
||||
if (lua_isboolean(L, 1))
|
||||
status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
else
|
||||
status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS);
|
||||
if (lua_toboolean(L, 2))
|
||||
lua_close(L);
|
||||
if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg syslib[] = {
|
||||
{"clock", io_clock},
|
||||
{"date", io_date},
|
||||
{"difftime", io_difftime},
|
||||
{"execute", io_execute},
|
||||
{"exit", io_exit},
|
||||
{"getenv", io_getenv},
|
||||
{"remove", io_remove},
|
||||
{"rename", io_rename},
|
||||
{"setlocale", io_setloc},
|
||||
{"time", io_time},
|
||||
{"tmpname", io_tmpname},
|
||||
{"clock", os_clock},
|
||||
{"date", os_date},
|
||||
{"difftime", os_difftime},
|
||||
{"execute", os_execute},
|
||||
{"exit", os_exit},
|
||||
{"getenv", os_getenv},
|
||||
{"remove", os_remove},
|
||||
{"rename", os_rename},
|
||||
{"setlocale", os_setlocale},
|
||||
{"time", os_time},
|
||||
{"tmpname", os_tmpname},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -230,8 +421,8 @@ static const luaL_Reg syslib[] = {
|
||||
|
||||
|
||||
|
||||
LUALIB_API int luaopen_os (lua_State *L) {
|
||||
luaL_register(L, LUA_OSLIBNAME, syslib);
|
||||
LUAMOD_API int luaopen_os (lua_State *L) {
|
||||
luaL_newlib(L, syslib);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lparser.h,v 1.54 2005/03/09 16:28:07 roberto Exp roberto $
|
||||
** $Id: lparser.h $
|
||||
** Lua Parser
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -9,71 +9,162 @@
|
||||
|
||||
#include "llimits.h"
|
||||
#include "lobject.h"
|
||||
#include "ltable.h"
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
/*
|
||||
** Expression descriptor
|
||||
** Expression and variable descriptor.
|
||||
** Code generation for variables and expressions can be delayed to allow
|
||||
** optimizations; An 'expdesc' structure describes a potentially-delayed
|
||||
** variable/expression. It has a description of its "main" value plus a
|
||||
** list of conditional jumps that can also produce its value (generated
|
||||
** by short-circuit operators 'and'/'or').
|
||||
*/
|
||||
|
||||
/* kinds of variables/expressions */
|
||||
typedef enum {
|
||||
VVOID, /* no value */
|
||||
VNIL,
|
||||
VTRUE,
|
||||
VFALSE,
|
||||
VK, /* info = index of constant in `k' */
|
||||
VLOCAL, /* info = local register */
|
||||
VUPVAL, /* info = index of upvalue in `upvalues' */
|
||||
VGLOBAL, /* info = index of table; aux = index of global name in `k' */
|
||||
VINDEXED, /* info = table register; aux = index register (or `k') */
|
||||
VJMP, /* info = instruction pc */
|
||||
VRELOCABLE, /* info = instruction pc */
|
||||
VNONRELOC, /* info = result register */
|
||||
VCALL, /* info = instruction pc */
|
||||
VVARARG /* info = instruction pc */
|
||||
VVOID, /* when 'expdesc' describes the last expression a list,
|
||||
this kind means an empty list (so, no expression) */
|
||||
VNIL, /* constant nil */
|
||||
VTRUE, /* constant true */
|
||||
VFALSE, /* constant false */
|
||||
VK, /* constant in 'k'; info = index of constant in 'k' */
|
||||
VKFLT, /* floating constant; nval = numerical float value */
|
||||
VKINT, /* integer constant; ival = numerical integer value */
|
||||
VKSTR, /* string constant; strval = TString address;
|
||||
(string is fixed by the lexer) */
|
||||
VNONRELOC, /* expression has its value in a fixed register;
|
||||
info = result register */
|
||||
VLOCAL, /* local variable; var.ridx = local register;
|
||||
var.vidx = relative index in 'actvar.arr' */
|
||||
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
|
||||
VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */
|
||||
VINDEXED, /* indexed variable;
|
||||
ind.t = table register;
|
||||
ind.idx = key's R index */
|
||||
VINDEXUP, /* indexed upvalue;
|
||||
ind.t = table upvalue;
|
||||
ind.idx = key's K index */
|
||||
VINDEXI, /* indexed variable with constant integer;
|
||||
ind.t = table register;
|
||||
ind.idx = key's value */
|
||||
VINDEXSTR, /* indexed variable with literal string;
|
||||
ind.t = table register;
|
||||
ind.idx = key's K index */
|
||||
VJMP, /* expression is a test/comparison;
|
||||
info = pc of corresponding jump instruction */
|
||||
VRELOC, /* expression can put result in any register;
|
||||
info = instruction pc */
|
||||
VCALL, /* expression is a function call; info = instruction pc */
|
||||
VVARARG /* vararg expression; info = instruction pc */
|
||||
} expkind;
|
||||
|
||||
|
||||
#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR)
|
||||
#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR)
|
||||
|
||||
|
||||
typedef struct expdesc {
|
||||
expkind k;
|
||||
int info, aux;
|
||||
int t; /* patch list of `exit when true' */
|
||||
int f; /* patch list of `exit when false' */
|
||||
union {
|
||||
lua_Integer ival; /* for VKINT */
|
||||
lua_Number nval; /* for VKFLT */
|
||||
TString *strval; /* for VKSTR */
|
||||
int info; /* for generic use */
|
||||
struct { /* for indexed variables */
|
||||
short idx; /* index (R or "long" K) */
|
||||
lu_byte t; /* table (register or upvalue) */
|
||||
} ind;
|
||||
struct { /* for local variables */
|
||||
lu_byte sidx; /* index in the stack */
|
||||
unsigned short vidx; /* index in 'actvar.arr' */
|
||||
} var;
|
||||
} u;
|
||||
int t; /* patch list of 'exit when true' */
|
||||
int f; /* patch list of 'exit when false' */
|
||||
} expdesc;
|
||||
|
||||
|
||||
typedef struct upvaldesc {
|
||||
lu_byte k;
|
||||
lu_byte info;
|
||||
} upvaldesc;
|
||||
/* kinds of variables */
|
||||
#define VDKREG 0 /* regular */
|
||||
#define RDKCONST 1 /* constant */
|
||||
#define RDKTOCLOSE 2 /* to-be-closed */
|
||||
#define RDKCTC 3 /* compile-time constant */
|
||||
|
||||
/* description of an active local variable */
|
||||
typedef union Vardesc {
|
||||
struct {
|
||||
TValuefields; /* constant value (if it is a compile-time constant) */
|
||||
lu_byte kind;
|
||||
lu_byte sidx; /* index of the variable in the stack */
|
||||
short pidx; /* index of the variable in the Proto's 'locvars' array */
|
||||
TString *name; /* variable name */
|
||||
} vd;
|
||||
TValue k; /* constant value (if any) */
|
||||
} Vardesc;
|
||||
|
||||
|
||||
|
||||
/* description of pending goto statements and label statements */
|
||||
typedef struct Labeldesc {
|
||||
TString *name; /* label identifier */
|
||||
int pc; /* position in code */
|
||||
int line; /* line where it appeared */
|
||||
lu_byte nactvar; /* number of active variables in that position */
|
||||
lu_byte close; /* goto that escapes upvalues */
|
||||
} Labeldesc;
|
||||
|
||||
|
||||
/* list of labels or gotos */
|
||||
typedef struct Labellist {
|
||||
Labeldesc *arr; /* array */
|
||||
int n; /* number of entries in use */
|
||||
int size; /* array size */
|
||||
} Labellist;
|
||||
|
||||
|
||||
/* dynamic structures used by the parser */
|
||||
typedef struct Dyndata {
|
||||
struct { /* list of active local variables */
|
||||
Vardesc *arr;
|
||||
int n;
|
||||
int size;
|
||||
} actvar;
|
||||
Labellist gt; /* list of pending gotos */
|
||||
Labellist label; /* list of active labels */
|
||||
} Dyndata;
|
||||
|
||||
|
||||
/* control of blocks */
|
||||
struct BlockCnt; /* defined in lparser.c */
|
||||
|
||||
|
||||
/* state needed to generate code for a given function */
|
||||
typedef struct FuncState {
|
||||
Proto *f; /* current function header */
|
||||
Table *h; /* table to find (and reuse) elements in `k' */
|
||||
struct FuncState *prev; /* enclosing function */
|
||||
struct LexState *ls; /* lexical state */
|
||||
struct lua_State *L; /* copy of the Lua state */
|
||||
struct BlockCnt *bl; /* chain of current blocks */
|
||||
int pc; /* next position to code (equivalent to `ncode') */
|
||||
int lasttarget; /* `pc' of last `jump target' */
|
||||
int jpc; /* list of pending jumps to `pc' */
|
||||
int freereg; /* first free register */
|
||||
int nk; /* number of elements in `k' */
|
||||
int np; /* number of elements in `p' */
|
||||
short nlocvars; /* number of elements in `locvars' */
|
||||
int pc; /* next position to code (equivalent to 'ncode') */
|
||||
int lasttarget; /* 'label' of last 'jump label' */
|
||||
int previousline; /* last line that was saved in 'lineinfo' */
|
||||
int nk; /* number of elements in 'k' */
|
||||
int np; /* number of elements in 'p' */
|
||||
int nabslineinfo; /* number of elements in 'abslineinfo' */
|
||||
int firstlocal; /* index of first local var (in Dyndata array) */
|
||||
int firstlabel; /* index of first label (in 'dyd->label->arr') */
|
||||
short ndebugvars; /* number of elements in 'f->locvars' */
|
||||
lu_byte nactvar; /* number of active local variables */
|
||||
upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */
|
||||
unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */
|
||||
lu_byte nups; /* number of upvalues */
|
||||
lu_byte freereg; /* first free register */
|
||||
lu_byte iwthabs; /* instructions issued since last absolute line info */
|
||||
lu_byte needclose; /* function needs to close upvalues when returning */
|
||||
} FuncState;
|
||||
|
||||
|
||||
LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
||||
const char *name);
|
||||
LUAI_FUNC int luaY_nvarstack (FuncState *fs);
|
||||
LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
||||
Dyndata *dyd, const char *name, int firstchar);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
** $Id: lprefix.h $
|
||||
** Definitions for Lua code that must come before any other header file
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef lprefix_h
|
||||
#define lprefix_h
|
||||
|
||||
|
||||
/*
|
||||
** Allows POSIX/XSI stuff
|
||||
*/
|
||||
#if !defined(LUA_USE_C89) /* { */
|
||||
|
||||
#if !defined(_XOPEN_SOURCE)
|
||||
#define _XOPEN_SOURCE 600
|
||||
#elif _XOPEN_SOURCE == 0
|
||||
#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Allows manipulation of large files in gcc and some other compilers
|
||||
*/
|
||||
#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS)
|
||||
#define _LARGEFILE_SOURCE 1
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** Windows stuff
|
||||
*/
|
||||
#if defined(_WIN32) /* { */
|
||||
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
/*
|
||||
** $Id: lstate.c,v 2.32 2005/06/03 20:15:58 roberto Exp roberto $
|
||||
** $Id: lstate.c $
|
||||
** Global State
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define lstate_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lapi.h"
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lfunc.h"
|
||||
@@ -24,118 +28,349 @@
|
||||
#include "ltm.h"
|
||||
|
||||
|
||||
#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE)
|
||||
#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE)
|
||||
#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))
|
||||
|
||||
/*
|
||||
** thread state + extra space
|
||||
*/
|
||||
typedef struct LX {
|
||||
lu_byte extra_[LUA_EXTRASPACE];
|
||||
lua_State l;
|
||||
} LX;
|
||||
|
||||
|
||||
/*
|
||||
** Main thread combines a thread state and the global state
|
||||
*/
|
||||
typedef struct LG {
|
||||
lua_State l;
|
||||
LX l;
|
||||
global_State g;
|
||||
} LG;
|
||||
|
||||
|
||||
|
||||
static void stack_init (lua_State *L1, lua_State *L) {
|
||||
/* initialize CallInfo array */
|
||||
L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo);
|
||||
L1->ci = L1->base_ci;
|
||||
L1->size_ci = BASIC_CI_SIZE;
|
||||
L1->end_ci = L1->base_ci + L1->size_ci - 1;
|
||||
/* initialize stack array */
|
||||
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue);
|
||||
L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
|
||||
L1->top = L1->stack;
|
||||
L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1;
|
||||
/* initialize first ci */
|
||||
L1->ci->func = L1->top;
|
||||
setnilvalue(L1->top++); /* `function' entry for this `ci' */
|
||||
L1->base = L1->ci->base = L1->top;
|
||||
L1->ci->top = L1->top + LUA_MINSTACK;
|
||||
|
||||
#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l)))
|
||||
|
||||
|
||||
/*
|
||||
** A macro to create a "random" seed when a state is created;
|
||||
** the seed is used to randomize string hashes.
|
||||
*/
|
||||
#if !defined(luai_makeseed)
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
** Compute an initial seed with some level of randomness.
|
||||
** Rely on Address Space Layout Randomization (if present) and
|
||||
** current time.
|
||||
*/
|
||||
#define addbuff(b,p,e) \
|
||||
{ size_t t = cast_sizet(e); \
|
||||
memcpy(b + p, &t, sizeof(t)); p += sizeof(t); }
|
||||
|
||||
static unsigned int luai_makeseed (lua_State *L) {
|
||||
char buff[3 * sizeof(size_t)];
|
||||
unsigned int h = cast_uint(time(NULL));
|
||||
int p = 0;
|
||||
addbuff(buff, p, L); /* heap variable */
|
||||
addbuff(buff, p, &h); /* local variable */
|
||||
addbuff(buff, p, &lua_newstate); /* public function */
|
||||
lua_assert(p == sizeof(buff));
|
||||
return luaS_hash(buff, p, h);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** set GCdebt to a new value keeping the value (totalbytes + GCdebt)
|
||||
** invariant (and avoiding underflows in 'totalbytes')
|
||||
*/
|
||||
void luaE_setdebt (global_State *g, l_mem debt) {
|
||||
l_mem tb = gettotalbytes(g);
|
||||
lua_assert(tb > 0);
|
||||
if (debt < tb - MAX_LMEM)
|
||||
debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */
|
||||
g->totalbytes = tb - debt;
|
||||
g->GCdebt = debt;
|
||||
}
|
||||
|
||||
|
||||
static void freestack (lua_State *L, lua_State *L1) {
|
||||
luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);
|
||||
luaM_freearray(L, L1->stack, L1->stacksize, TValue);
|
||||
LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
|
||||
global_State *g = G(L);
|
||||
int ccalls;
|
||||
luaE_freeCI(L); /* release unused CIs */
|
||||
ccalls = getCcalls(L);
|
||||
if (limit >= 40000)
|
||||
return 0; /* out of bounds */
|
||||
limit += CSTACKERR;
|
||||
if (L != g-> mainthread)
|
||||
return 0; /* only main thread can change the C stack */
|
||||
else if (ccalls <= CSTACKERR)
|
||||
return 0; /* handling overflow */
|
||||
else {
|
||||
int diff = limit - g->Cstacklimit;
|
||||
if (ccalls + diff <= CSTACKERR)
|
||||
return 0; /* new limit would cause an overflow */
|
||||
g->Cstacklimit = limit; /* set new limit */
|
||||
L->nCcalls += diff; /* correct 'nCcalls' */
|
||||
return limit - diff - CSTACKERR; /* success; return previous limit */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** open parts that may cause memory-allocation errors
|
||||
** Decrement count of "C calls" and check for overflows. In case of
|
||||
** a stack overflow, check appropriate error ("regular" overflow or
|
||||
** overflow while handling stack overflow). If 'nCcalls' is smaller
|
||||
** than CSTACKERR but larger than CSTACKMARK, it means it has just
|
||||
** entered the "overflow zone", so the function raises an overflow
|
||||
** error. If 'nCcalls' is smaller than CSTACKMARK (which means it is
|
||||
** already handling an overflow) but larger than CSTACKERRMARK, does
|
||||
** not report an error (to allow message handling to work). Otherwise,
|
||||
** report a stack overflow while handling a stack overflow (probably
|
||||
** caused by a repeating error in the message handling function).
|
||||
*/
|
||||
|
||||
void luaE_enterCcall (lua_State *L) {
|
||||
int ncalls = getCcalls(L);
|
||||
L->nCcalls--;
|
||||
if (ncalls <= CSTACKERR) { /* possible overflow? */
|
||||
luaE_freeCI(L); /* release unused CIs */
|
||||
ncalls = getCcalls(L); /* update call count */
|
||||
if (ncalls <= CSTACKERR) { /* still overflow? */
|
||||
if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
||||
else if (ncalls >= CSTACKMARK) {
|
||||
/* not in error-handling zone; raise the error now */
|
||||
L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */
|
||||
luaG_runerror(L, "C stack overflow");
|
||||
}
|
||||
/* else stack is in the error-handling zone;
|
||||
allow message handler to work */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CallInfo *luaE_extendCI (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
lua_assert(L->ci->next == NULL);
|
||||
luaE_enterCcall(L);
|
||||
ci = luaM_new(L, CallInfo);
|
||||
lua_assert(L->ci->next == NULL);
|
||||
L->ci->next = ci;
|
||||
ci->previous = L->ci;
|
||||
ci->next = NULL;
|
||||
ci->u.l.trap = 0;
|
||||
L->nci++;
|
||||
return ci;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** free all CallInfo structures not in use by a thread
|
||||
*/
|
||||
void luaE_freeCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *next = ci->next;
|
||||
ci->next = NULL;
|
||||
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
|
||||
while ((ci = next) != NULL) {
|
||||
next = ci->next;
|
||||
luaM_free(L, ci);
|
||||
L->nci--;
|
||||
}
|
||||
L->nCcalls -= L->nci; /* adjust result */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** free half of the CallInfo structures not in use by a thread
|
||||
*/
|
||||
void luaE_shrinkCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *next2; /* next's next */
|
||||
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
|
||||
/* while there are two nexts */
|
||||
while (ci->next != NULL && (next2 = ci->next->next) != NULL) {
|
||||
luaM_free(L, ci->next); /* free next */
|
||||
L->nci--;
|
||||
ci->next = next2; /* remove 'next' from the list */
|
||||
next2->previous = ci;
|
||||
ci = next2; /* keep next's next */
|
||||
}
|
||||
L->nCcalls -= L->nci; /* adjust result */
|
||||
}
|
||||
|
||||
|
||||
static void stack_init (lua_State *L1, lua_State *L) {
|
||||
int i; CallInfo *ci;
|
||||
/* initialize stack array */
|
||||
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, StackValue);
|
||||
L1->stacksize = BASIC_STACK_SIZE;
|
||||
for (i = 0; i < BASIC_STACK_SIZE; i++)
|
||||
setnilvalue(s2v(L1->stack + i)); /* erase new stack */
|
||||
L1->top = L1->stack;
|
||||
L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK;
|
||||
/* initialize first ci */
|
||||
ci = &L1->base_ci;
|
||||
ci->next = ci->previous = NULL;
|
||||
ci->callstatus = CIST_C;
|
||||
ci->func = L1->top;
|
||||
ci->u.c.k = NULL;
|
||||
ci->nresults = 0;
|
||||
setnilvalue(s2v(L1->top)); /* 'function' entry for this 'ci' */
|
||||
L1->top++;
|
||||
ci->top = L1->top + LUA_MINSTACK;
|
||||
L1->ci = ci;
|
||||
}
|
||||
|
||||
|
||||
static void freestack (lua_State *L) {
|
||||
if (L->stack == NULL)
|
||||
return; /* stack not completely built yet */
|
||||
L->ci = &L->base_ci; /* free the entire 'ci' list */
|
||||
luaE_freeCI(L);
|
||||
lua_assert(L->nci == 0);
|
||||
luaM_freearray(L, L->stack, L->stacksize); /* free stack array */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create registry table and its predefined values
|
||||
*/
|
||||
static void init_registry (lua_State *L, global_State *g) {
|
||||
TValue temp;
|
||||
/* create registry */
|
||||
Table *registry = luaH_new(L);
|
||||
sethvalue(L, &g->l_registry, registry);
|
||||
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
|
||||
/* registry[LUA_RIDX_MAINTHREAD] = L */
|
||||
setthvalue(L, &temp, L); /* temp = L */
|
||||
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp);
|
||||
/* registry[LUA_RIDX_GLOBALS] = table of globals */
|
||||
sethvalue(L, &temp, luaH_new(L)); /* temp = new table (global table) */
|
||||
luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** open parts of the state that may cause memory-allocation errors.
|
||||
** ('g->nilvalue' being a nil value flags that the state was completely
|
||||
** build.)
|
||||
*/
|
||||
static void f_luaopen (lua_State *L, void *ud) {
|
||||
global_State *g = G(L);
|
||||
UNUSED(ud);
|
||||
stack_init(L, L); /* init stack */
|
||||
sethvalue(L, gt(L), luaH_new(L, 0, 20)); /* table of globals */
|
||||
sethvalue(L, registry(L), luaH_new(L, 6, 20)); /* registry */
|
||||
luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */
|
||||
init_registry(L, g);
|
||||
luaS_init(L);
|
||||
luaT_init(L);
|
||||
luaX_init(L);
|
||||
luaS_fix(luaS_newliteral(L, MEMERRMSG));
|
||||
g->GCthreshold = 4*g->totalbytes;
|
||||
g->gcrunning = 1; /* allow gc */
|
||||
setnilvalue(&g->nilvalue);
|
||||
luai_userstateopen(L);
|
||||
}
|
||||
|
||||
|
||||
static void preinit_state (lua_State *L, global_State *g) {
|
||||
/*
|
||||
** preinitialize a thread with consistent values without allocating
|
||||
** any memory (to avoid errors)
|
||||
*/
|
||||
static void preinit_thread (lua_State *L, global_State *g) {
|
||||
G(L) = g;
|
||||
L->stack = NULL;
|
||||
L->ci = NULL;
|
||||
L->nci = 0;
|
||||
L->stacksize = 0;
|
||||
L->twups = L; /* thread has no upvalues */
|
||||
L->errorJmp = NULL;
|
||||
L->nCcalls = CSTACKTHREAD;
|
||||
L->hook = NULL;
|
||||
L->hookmask = 0;
|
||||
L->basehookcount = 0;
|
||||
L->allowhook = 1;
|
||||
resethookcount(L);
|
||||
L->openupval = NULL;
|
||||
L->size_ci = 0;
|
||||
L->nCcalls = 0;
|
||||
L->status = 0;
|
||||
L->base_ci = L->ci = NULL;
|
||||
L->savedpc = NULL;
|
||||
L->status = LUA_OK;
|
||||
L->errfunc = 0;
|
||||
setnilvalue(gt(L));
|
||||
}
|
||||
|
||||
|
||||
static void close_state (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
luaF_close(L, L->stack); /* close all upvalues for this thread */
|
||||
luaC_freeall(L); /* collect all objects */
|
||||
lua_assert(g->rootgc == obj2gco(L));
|
||||
lua_assert(g->strt.nuse == 0);
|
||||
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *);
|
||||
luaZ_freebuffer(L, &g->buff);
|
||||
freestack(L, L);
|
||||
lua_assert(g->totalbytes == sizeof(LG));
|
||||
(*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0);
|
||||
luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */
|
||||
luaC_freeallobjects(L); /* collect all objects */
|
||||
if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
|
||||
luai_userstateclose(L);
|
||||
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
|
||||
freestack(L);
|
||||
lua_assert(gettotalbytes(g) == sizeof(LG));
|
||||
(*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */
|
||||
}
|
||||
|
||||
|
||||
lua_State *luaE_newthread (lua_State *L) {
|
||||
lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State)));
|
||||
luaC_link(L, obj2gco(L1), LUA_TTHREAD);
|
||||
preinit_state(L1, G(L));
|
||||
stack_init(L1, L); /* init stack */
|
||||
setobj2n(L, gt(L1), gt(L)); /* share table of globals */
|
||||
LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
lua_State *L1;
|
||||
lua_lock(L);
|
||||
luaC_checkGC(L);
|
||||
/* create new thread */
|
||||
L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l;
|
||||
L1->marked = luaC_white(g);
|
||||
L1->tt = LUA_TTHREAD;
|
||||
/* link it on list 'allgc' */
|
||||
L1->next = g->allgc;
|
||||
g->allgc = obj2gco(L1);
|
||||
/* anchor it on L stack */
|
||||
setthvalue2s(L, L->top, L1);
|
||||
api_incr_top(L);
|
||||
preinit_thread(L1, g);
|
||||
L1->hookmask = L->hookmask;
|
||||
L1->basehookcount = L->basehookcount;
|
||||
L1->hook = L->hook;
|
||||
resethookcount(L1);
|
||||
lua_assert(iswhite(obj2gco(L1)));
|
||||
/* initialize L1 extra space */
|
||||
memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread),
|
||||
LUA_EXTRASPACE);
|
||||
luai_userstatethread(L, L1);
|
||||
stack_init(L1, L); /* init stack */
|
||||
lua_unlock(L);
|
||||
return L1;
|
||||
}
|
||||
|
||||
|
||||
void luaE_freethread (lua_State *L, lua_State *L1) {
|
||||
luaF_close(L1, L1->stack); /* close all upvalues for this thread */
|
||||
LX *l = fromstate(L1);
|
||||
luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */
|
||||
lua_assert(L1->openupval == NULL);
|
||||
freestack(L, L1);
|
||||
luaM_freemem(L, fromstate(L1), state_size(lua_State));
|
||||
luai_userstatefree(L, L1);
|
||||
freestack(L1);
|
||||
luaM_free(L, l);
|
||||
}
|
||||
|
||||
|
||||
int lua_resetthread (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
int status;
|
||||
lua_lock(L);
|
||||
ci = &L->base_ci;
|
||||
status = luaF_close(L, L->stack, CLOSEPROTECT);
|
||||
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
|
||||
if (status != CLOSEPROTECT) /* real errors? */
|
||||
luaD_seterrorobj(L, status, L->stack + 1);
|
||||
else {
|
||||
status = LUA_OK;
|
||||
L->top = L->stack + 1;
|
||||
}
|
||||
ci->callstatus = CIST_C;
|
||||
ci->func = L->stack;
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
L->ci = ci;
|
||||
L->status = status;
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,70 +378,84 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
int i;
|
||||
lua_State *L;
|
||||
global_State *g;
|
||||
void *l = (*f)(ud, NULL, 0, state_size(LG));
|
||||
LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
|
||||
if (l == NULL) return NULL;
|
||||
L = tostate(l);
|
||||
g = &((LG *)L)->g;
|
||||
L->next = NULL;
|
||||
L = &l->l.l;
|
||||
g = &l->g;
|
||||
L->tt = LUA_TTHREAD;
|
||||
g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
|
||||
g->currentwhite = bitmask(WHITE0BIT);
|
||||
L->marked = luaC_white(g);
|
||||
set2bits(L->marked, FIXEDBIT, SFIXEDBIT);
|
||||
preinit_state(L, g);
|
||||
preinit_thread(L, g);
|
||||
g->allgc = obj2gco(L); /* by now, only object is the main thread */
|
||||
L->next = NULL;
|
||||
g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR;
|
||||
g->frealloc = f;
|
||||
g->ud = ud;
|
||||
g->warnf = NULL;
|
||||
g->ud_warn = NULL;
|
||||
g->mainthread = L;
|
||||
g->uvhead.u.l.prev = &g->uvhead;
|
||||
g->uvhead.u.l.next = &g->uvhead;
|
||||
g->GCthreshold = 0; /* mark it as unfinished state */
|
||||
g->strt.size = 0;
|
||||
g->strt.nuse = 0;
|
||||
g->seed = luai_makeseed(L);
|
||||
g->gcrunning = 0; /* no GC while building state */
|
||||
g->strt.size = g->strt.nuse = 0;
|
||||
g->strt.hash = NULL;
|
||||
setnilvalue(registry(L));
|
||||
luaZ_initbuffer(L, &g->buff);
|
||||
setnilvalue(&g->l_registry);
|
||||
g->panic = NULL;
|
||||
g->gcstate = GCSpause;
|
||||
g->rootgc = obj2gco(L);
|
||||
g->sweepstrgc = 0;
|
||||
g->sweepgc = &g->rootgc;
|
||||
g->gray = NULL;
|
||||
g->grayagain = NULL;
|
||||
g->weak = NULL;
|
||||
g->tmudata = NULL;
|
||||
g->gckind = KGC_INC;
|
||||
g->gcemergency = 0;
|
||||
g->finobj = g->tobefnz = g->fixedgc = NULL;
|
||||
g->survival = g->old = g->reallyold = NULL;
|
||||
g->finobjsur = g->finobjold = g->finobjrold = NULL;
|
||||
g->sweepgc = NULL;
|
||||
g->gray = g->grayagain = NULL;
|
||||
g->weak = g->ephemeron = g->allweak = NULL;
|
||||
g->twups = NULL;
|
||||
g->totalbytes = sizeof(LG);
|
||||
g->gcpause = LUAI_GCPAUSE;
|
||||
g->gcstepmul = LUAI_GCMUL;
|
||||
g->gcdept = 0;
|
||||
for (i=0; i<NUM_TAGS; i++) g->mt[i] = NULL;
|
||||
if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {
|
||||
g->GCdebt = 0;
|
||||
g->lastatomic = 0;
|
||||
setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */
|
||||
setgcparam(g->gcpause, LUAI_GCPAUSE);
|
||||
setgcparam(g->gcstepmul, LUAI_GCMUL);
|
||||
g->gcstepsize = LUAI_GCSTEPSIZE;
|
||||
setgcparam(g->genmajormul, LUAI_GENMAJORMUL);
|
||||
g->genminormul = LUAI_GENMINORMUL;
|
||||
for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL;
|
||||
if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
|
||||
/* memory allocation error: free partial state */
|
||||
close_state(L);
|
||||
L = NULL;
|
||||
}
|
||||
else
|
||||
luai_userstateopen(L);
|
||||
return L;
|
||||
}
|
||||
|
||||
|
||||
static void callallgcTM (lua_State *L, void *ud) {
|
||||
UNUSED(ud);
|
||||
luaC_callGCTM(L); /* call GC metamethods for all udata */
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_close (lua_State *L) {
|
||||
lua_lock(L);
|
||||
L = G(L)->mainthread; /* only the main thread can be closed */
|
||||
luaF_close(L, L->stack); /* close all upvalues for this thread */
|
||||
luaC_separateudata(L, 1); /* separate udata that have GC metamethods */
|
||||
L->errfunc = 0; /* no error function during GC metamethods */
|
||||
do { /* repeat until no more errors */
|
||||
L->ci = L->base_ci;
|
||||
L->base = L->top = L->ci->base;
|
||||
L->nCcalls = 0;
|
||||
} while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0);
|
||||
lua_assert(G(L)->tmudata == NULL);
|
||||
lua_lock(L);
|
||||
close_state(L);
|
||||
}
|
||||
|
||||
|
||||
void luaE_warning (lua_State *L, const char *msg, int tocont) {
|
||||
lua_WarnFunction wf = G(L)->warnf;
|
||||
if (wf != NULL)
|
||||
wf(G(L)->ud_warn, msg, tocont);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate a warning from an error message
|
||||
*/
|
||||
void luaE_warnerror (lua_State *L, const char *where) {
|
||||
TValue *errobj = s2v(L->top - 1); /* error object */
|
||||
const char *msg = (ttisstring(errobj))
|
||||
? svalue(errobj)
|
||||
: "error object is not a string";
|
||||
/* produce warning "error in %s (%s)" (where, msg) */
|
||||
luaE_warning(L, "error in ", 1);
|
||||
luaE_warning(L, where, 1);
|
||||
luaE_warning(L, " (", 1);
|
||||
luaE_warning(L, msg, 1);
|
||||
luaE_warning(L, ")", 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lstate.h,v 2.22 2005/06/03 20:15:58 roberto Exp roberto $
|
||||
** $Id: lstate.h $
|
||||
** Global State
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -14,115 +14,293 @@
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
/*
|
||||
** Some notes about garbage-collected objects: All objects in Lua must
|
||||
** be kept somehow accessible until being freed, so all objects always
|
||||
** belong to one (and only one) of these lists, using field 'next' of
|
||||
** the 'CommonHeader' for the link:
|
||||
**
|
||||
** 'allgc': all objects not marked for finalization;
|
||||
** 'finobj': all objects marked for finalization;
|
||||
** 'tobefnz': all objects ready to be finalized;
|
||||
** 'fixedgc': all objects that are not to be collected (currently
|
||||
** only small strings, such as reserved words).
|
||||
**
|
||||
** For the generational collector, some of these lists have marks for
|
||||
** generations. Each mark points to the first element in the list for
|
||||
** that particular generation; that generation goes until the next mark.
|
||||
**
|
||||
** 'allgc' -> 'survival': new objects;
|
||||
** 'survival' -> 'old': objects that survived one collection;
|
||||
** 'old' -> 'reallyold': objects that became old in last collection;
|
||||
** 'reallyold' -> NULL: objects old for more than one cycle.
|
||||
**
|
||||
** 'finobj' -> 'finobjsur': new objects marked for finalization;
|
||||
** 'finobjsur' -> 'finobjold': survived """";
|
||||
** 'finobjold' -> 'finobjrold': just old """";
|
||||
** 'finobjrold' -> NULL: really old """".
|
||||
*/
|
||||
|
||||
/*
|
||||
** Moreover, there is another set of lists that control gray objects.
|
||||
** These lists are linked by fields 'gclist'. (All objects that
|
||||
** can become gray have such a field. The field is not the same
|
||||
** in all objects, but it always has this name.) Any gray object
|
||||
** must belong to one of these lists, and all objects in these lists
|
||||
** must be gray:
|
||||
**
|
||||
** 'gray': regular gray objects, still waiting to be visited.
|
||||
** 'grayagain': objects that must be revisited at the atomic phase.
|
||||
** That includes
|
||||
** - black objects got in a write barrier;
|
||||
** - all kinds of weak tables during propagation phase;
|
||||
** - all threads.
|
||||
** 'weak': tables with weak values to be cleared;
|
||||
** 'ephemeron': ephemeron tables with white->white entries;
|
||||
** 'allweak': tables with weak keys and/or weak values to be cleared.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
|
||||
** how many "C calls" it still can do in the C stack, to avoid C-stack
|
||||
** overflow. This count is very rough approximation; it considers only
|
||||
** recursive functions inside the interpreter, as non-recursive calls
|
||||
** can be considered using a fixed (although unknown) amount of stack
|
||||
** space.
|
||||
**
|
||||
** The count has two parts: the lower part is the count itself; the
|
||||
** higher part counts the number of non-yieldable calls in the stack.
|
||||
** (They are together so that we can change both with one instruction.)
|
||||
**
|
||||
** Because calls to external C functions can use an unknown amount
|
||||
** of space (e.g., functions using an auxiliary buffer), calls
|
||||
** to these functions add more than one to the count (see CSTACKCF).
|
||||
**
|
||||
** The proper count excludes the number of CallInfo structures allocated
|
||||
** by Lua, as a kind of "potential" calls. So, when Lua calls a function
|
||||
** (and "consumes" one CallInfo), it needs neither to decrement nor to
|
||||
** check 'nCcalls', as its use of C stack is already accounted for.
|
||||
*/
|
||||
|
||||
/* number of "C stack slots" used by an external C function */
|
||||
#define CSTACKCF 10
|
||||
|
||||
|
||||
/*
|
||||
** The C-stack size is sliced in the following zones:
|
||||
** - larger than CSTACKERR: normal stack;
|
||||
** - [CSTACKMARK, CSTACKERR]: buffer zone to signal a stack overflow;
|
||||
** - [CSTACKCF, CSTACKERRMARK]: error-handling zone;
|
||||
** - below CSTACKERRMARK: buffer zone to signal overflow during overflow;
|
||||
** (Because the counter can be decremented CSTACKCF at once, we need
|
||||
** the so called "buffer zones", with at least that size, to properly
|
||||
** detect a change from one zone to the next.)
|
||||
*/
|
||||
#define CSTACKERR (8 * CSTACKCF)
|
||||
#define CSTACKMARK (CSTACKERR - (CSTACKCF + 2))
|
||||
#define CSTACKERRMARK (CSTACKCF + 2)
|
||||
|
||||
|
||||
/* initial limit for the C-stack of threads */
|
||||
#define CSTACKTHREAD (2 * CSTACKERR)
|
||||
|
||||
|
||||
/* true if this thread does not have non-yieldable calls in the stack */
|
||||
#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
|
||||
|
||||
/* real number of C calls */
|
||||
#define getCcalls(L) ((L)->nCcalls & 0xffff)
|
||||
|
||||
|
||||
/* Increment the number of non-yieldable calls */
|
||||
#define incnny(L) ((L)->nCcalls += 0x10000)
|
||||
|
||||
/* Decrement the number of non-yieldable calls */
|
||||
#define decnny(L) ((L)->nCcalls -= 0x10000)
|
||||
|
||||
/* Increment the number of non-yieldable calls and decrement nCcalls */
|
||||
#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF)
|
||||
|
||||
/* Decrement the number of non-yieldable calls and increment nCcalls */
|
||||
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct lua_longjmp; /* defined in ldo.c */
|
||||
|
||||
|
||||
/* table of globals */
|
||||
#define gt(L) (&L->l_gt)
|
||||
|
||||
/* registry */
|
||||
#define registry(L) (&G(L)->l_registry)
|
||||
/*
|
||||
** Atomic type (relative to signals) to better ensure that 'lua_sethook'
|
||||
** is thread safe
|
||||
*/
|
||||
#if !defined(l_signalT)
|
||||
#include <signal.h>
|
||||
#define l_signalT sig_atomic_t
|
||||
#endif
|
||||
|
||||
|
||||
/* extra stack space to handle TM calls and some other extras */
|
||||
#define EXTRA_STACK 5
|
||||
|
||||
|
||||
#define BASIC_CI_SIZE 8
|
||||
|
||||
#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
|
||||
|
||||
|
||||
/* kinds of Garbage Collection */
|
||||
#define KGC_INC 0 /* incremental gc */
|
||||
#define KGC_GEN 1 /* generational gc */
|
||||
|
||||
|
||||
typedef struct stringtable {
|
||||
GCObject **hash;
|
||||
lu_int32 nuse; /* number of elements */
|
||||
TString **hash;
|
||||
int nuse; /* number of elements */
|
||||
int size;
|
||||
} stringtable;
|
||||
|
||||
|
||||
/*
|
||||
** informations about a call
|
||||
** Information about a call.
|
||||
*/
|
||||
typedef struct CallInfo {
|
||||
StkId base; /* base for this function */
|
||||
StkId func; /* function index in the stack */
|
||||
StkId top; /* top for this function */
|
||||
int nresults; /* expected number of results from this function */
|
||||
const Instruction *savedpc;
|
||||
int tailcalls; /* number of tail calls lost under this entry */
|
||||
struct CallInfo *previous, *next; /* dynamic call link */
|
||||
union {
|
||||
struct { /* only for Lua functions */
|
||||
const Instruction *savedpc;
|
||||
l_signalT trap;
|
||||
int nextraargs; /* # of extra arguments in vararg functions */
|
||||
} l;
|
||||
struct { /* only for C functions */
|
||||
lua_KFunction k; /* continuation in case of yields */
|
||||
ptrdiff_t old_errfunc;
|
||||
lua_KContext ctx; /* context info. in case of yields */
|
||||
} c;
|
||||
} u;
|
||||
union {
|
||||
int funcidx; /* called-function index */
|
||||
int nyield; /* number of values yielded */
|
||||
struct { /* info about transferred values (for call/return hooks) */
|
||||
unsigned short ftransfer; /* offset of first value transferred */
|
||||
unsigned short ntransfer; /* number of values transferred */
|
||||
} transferinfo;
|
||||
} u2;
|
||||
short nresults; /* expected number of results from this function */
|
||||
unsigned short callstatus;
|
||||
} CallInfo;
|
||||
|
||||
|
||||
/*
|
||||
** Bits in CallInfo status
|
||||
*/
|
||||
#define CIST_OAH (1<<0) /* original value of 'allowhook' */
|
||||
#define CIST_C (1<<1) /* call is running a C function */
|
||||
#define CIST_HOOKED (1<<2) /* call is running a debug hook */
|
||||
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */
|
||||
#define CIST_TAIL (1<<4) /* call was tail called */
|
||||
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */
|
||||
#define CIST_FIN (1<<6) /* call is running a finalizer */
|
||||
#define CIST_TRAN (1<<7) /* 'ci' has transfer information */
|
||||
#if defined(LUA_COMPAT_LT_LE)
|
||||
#define CIST_LEQ (1<<8) /* using __lt for __le */
|
||||
#endif
|
||||
|
||||
#define curr_func(L) (clvalue(L->ci->func))
|
||||
#define ci_func(ci) (clvalue((ci)->func))
|
||||
#define f_isLua(ci) (!ci_func(ci)->c.isC)
|
||||
#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci))
|
||||
/* active function is a Lua function */
|
||||
#define isLua(ci) (!((ci)->callstatus & CIST_C))
|
||||
|
||||
/* call is running Lua code (not a hook) */
|
||||
#define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED)))
|
||||
|
||||
/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */
|
||||
#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v))
|
||||
#define getoah(st) ((st) & CIST_OAH)
|
||||
|
||||
|
||||
/*
|
||||
** `global state', shared by all threads of this state
|
||||
** 'global state', shared by all threads of this state
|
||||
*/
|
||||
typedef struct global_State {
|
||||
stringtable strt; /* hash table for strings */
|
||||
lua_Alloc frealloc; /* function to reallocate memory */
|
||||
void *ud; /* auxiliary data to `frealloc' */
|
||||
void *ud; /* auxiliary data to 'frealloc' */
|
||||
l_mem totalbytes; /* number of bytes currently allocated - GCdebt */
|
||||
l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
|
||||
lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
|
||||
lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */
|
||||
stringtable strt; /* hash table for strings */
|
||||
TValue l_registry;
|
||||
TValue nilvalue; /* a nil value */
|
||||
unsigned int seed; /* randomized seed for hashes */
|
||||
lu_byte currentwhite;
|
||||
lu_byte gcstate; /* state of garbage collector */
|
||||
GCObject *rootgc; /* list of all collectable objects */
|
||||
GCObject **sweepgc; /* position of sweep in `rootgc' */
|
||||
int sweepstrgc; /* position of sweep in `strt' */
|
||||
lu_byte gckind; /* kind of GC running */
|
||||
lu_byte genminormul; /* control for minor generational collections */
|
||||
lu_byte genmajormul; /* control for major generational collections */
|
||||
lu_byte gcrunning; /* true if GC is running */
|
||||
lu_byte gcemergency; /* true if this is an emergency collection */
|
||||
lu_byte gcpause; /* size of pause between successive GCs */
|
||||
lu_byte gcstepmul; /* GC "speed" */
|
||||
lu_byte gcstepsize; /* (log2 of) GC granularity */
|
||||
GCObject *allgc; /* list of all collectable objects */
|
||||
GCObject **sweepgc; /* current position of sweep in list */
|
||||
GCObject *finobj; /* list of collectable objects with finalizers */
|
||||
GCObject *gray; /* list of gray objects */
|
||||
GCObject *grayagain; /* list of objects to be traversed atomically */
|
||||
GCObject *weak; /* list of weak tables (to be cleared) */
|
||||
GCObject *tmudata; /* last element of list of userdata to be GC */
|
||||
Mbuffer buff; /* temporary buffer for string concatentation */
|
||||
lu_mem GCthreshold;
|
||||
lu_mem totalbytes; /* number of bytes currently allocated */
|
||||
lu_mem estimate; /* an estimate of number of bytes actually in use */
|
||||
lu_mem gcdept; /* how much GC is `behind schedule' */
|
||||
int gcpause; /* size of pause between successive GCs */
|
||||
int gcstepmul; /* GC `granularity' */
|
||||
GCObject *weak; /* list of tables with weak values */
|
||||
GCObject *ephemeron; /* list of ephemeron tables (weak keys) */
|
||||
GCObject *allweak; /* list of all-weak tables */
|
||||
GCObject *tobefnz; /* list of userdata to be GC */
|
||||
GCObject *fixedgc; /* list of objects not to be collected */
|
||||
/* fields for generational collector */
|
||||
GCObject *survival; /* start of objects that survived one GC cycle */
|
||||
GCObject *old; /* start of old objects */
|
||||
GCObject *reallyold; /* old objects with more than one cycle */
|
||||
GCObject *finobjsur; /* list of survival objects with finalizers */
|
||||
GCObject *finobjold; /* list of old objects with finalizers */
|
||||
GCObject *finobjrold; /* list of really old objects with finalizers */
|
||||
struct lua_State *twups; /* list of threads with open upvalues */
|
||||
lua_CFunction panic; /* to be called in unprotected errors */
|
||||
TValue l_registry;
|
||||
struct lua_State *mainthread;
|
||||
UpVal uvhead; /* head of double-linked list of all open upvalues */
|
||||
struct Table *mt[NUM_TAGS]; /* metatables for basic types */
|
||||
TString *memerrmsg; /* message for memory-allocation errors */
|
||||
TString *tmname[TM_N]; /* array with tag-method names */
|
||||
struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */
|
||||
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
|
||||
lua_WarnFunction warnf; /* warning function */
|
||||
void *ud_warn; /* auxiliary data to 'warnf' */
|
||||
unsigned int Cstacklimit; /* current limit for the C stack */
|
||||
} global_State;
|
||||
|
||||
|
||||
/*
|
||||
** `per thread' state
|
||||
** 'per thread' state
|
||||
*/
|
||||
struct lua_State {
|
||||
CommonHeader;
|
||||
lu_byte status;
|
||||
lu_byte allowhook;
|
||||
unsigned short nci; /* number of items in 'ci' list */
|
||||
StkId top; /* first free slot in the stack */
|
||||
StkId base; /* base of current function */
|
||||
global_State *l_G;
|
||||
CallInfo *ci; /* call info for current function */
|
||||
const Instruction *savedpc; /* `savedpc' of current function */
|
||||
const Instruction *oldpc; /* last pc traced */
|
||||
StkId stack_last; /* last free slot in the stack */
|
||||
StkId stack; /* stack base */
|
||||
UpVal *openupval; /* list of open upvalues in this stack */
|
||||
GCObject *gclist;
|
||||
struct lua_State *twups; /* list of threads with open upvalues */
|
||||
struct lua_longjmp *errorJmp; /* current error recover point */
|
||||
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
|
||||
volatile lua_Hook hook;
|
||||
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
||||
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
|
||||
int stacksize;
|
||||
CallInfo *end_ci; /* points after end of ci array*/
|
||||
CallInfo *base_ci; /* array of CallInfo's */
|
||||
int size_ci; /* size of array `base_ci' */
|
||||
unsigned short nCcalls; /* number of nested C calls */
|
||||
lu_byte hookmask;
|
||||
lu_byte allowhook;
|
||||
lu_byte status;
|
||||
int basehookcount;
|
||||
int hookcount;
|
||||
lua_Hook hook;
|
||||
TValue l_gt; /* table of globals */
|
||||
TValue env; /* temporary place for environments */
|
||||
GCObject *openupval; /* list of open upvalues in this stack */
|
||||
GCObject *gclist;
|
||||
struct lua_longjmp *errorJmp; /* current error recover point */
|
||||
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
||||
l_signalT hookmask;
|
||||
};
|
||||
|
||||
|
||||
@@ -130,39 +308,57 @@ struct lua_State {
|
||||
|
||||
|
||||
/*
|
||||
** Union of all collectable objects
|
||||
** Union of all collectable objects (only for conversions)
|
||||
*/
|
||||
union GCObject {
|
||||
GCheader gch;
|
||||
union TString ts;
|
||||
union Udata u;
|
||||
union GCUnion {
|
||||
GCObject gc; /* common header */
|
||||
struct TString ts;
|
||||
struct Udata u;
|
||||
union Closure cl;
|
||||
struct Table h;
|
||||
struct Proto p;
|
||||
struct UpVal uv;
|
||||
struct lua_State th; /* thread */
|
||||
struct UpVal upv;
|
||||
};
|
||||
|
||||
|
||||
#define cast_u(o) cast(union GCUnion *, (o))
|
||||
|
||||
/* macros to convert a GCObject into a specific value */
|
||||
#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
|
||||
#define gco2ts(o) (&rawgco2ts(o)->tsv)
|
||||
#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
|
||||
#define gco2u(o) (&rawgco2u(o)->uv)
|
||||
#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
|
||||
#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
|
||||
#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
|
||||
#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
|
||||
#define ngcotouv(o) \
|
||||
check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))
|
||||
#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
|
||||
|
||||
/* macro to convert any Lua object into a GCObject */
|
||||
#define obj2gco(v) (cast(GCObject *, (v)))
|
||||
#define gco2ts(o) \
|
||||
check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))
|
||||
#define gco2u(o) check_exp((o)->tt == LUA_TUSERDATA, &((cast_u(o))->u))
|
||||
#define gco2lcl(o) check_exp((o)->tt == LUA_TLCL, &((cast_u(o))->cl.l))
|
||||
#define gco2ccl(o) check_exp((o)->tt == LUA_TCCL, &((cast_u(o))->cl.c))
|
||||
#define gco2cl(o) \
|
||||
check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))
|
||||
#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))
|
||||
#define gco2p(o) check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p))
|
||||
#define gco2th(o) check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th))
|
||||
#define gco2upv(o) check_exp((o)->tt == LUA_TUPVAL, &((cast_u(o))->upv))
|
||||
|
||||
|
||||
LUAI_FUNC lua_State *luaE_newthread (lua_State *L);
|
||||
/*
|
||||
** macro to convert a Lua object into a GCObject
|
||||
** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.)
|
||||
*/
|
||||
#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
|
||||
|
||||
|
||||
/* actual number of total bytes allocated */
|
||||
#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt)
|
||||
|
||||
LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
|
||||
LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
|
||||
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
|
||||
LUAI_FUNC void luaE_freeCI (lua_State *L);
|
||||
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
|
||||
LUAI_FUNC void luaE_enterCcall (lua_State *L);
|
||||
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
||||
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
||||
|
||||
|
||||
#define luaE_exitCcall(L) ((L)->nCcalls++)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,111 +1,283 @@
|
||||
/*
|
||||
** $Id: lstring.c,v 2.6 2005/01/18 17:18:09 roberto Exp roberto $
|
||||
** $Id: lstring.c $
|
||||
** String table (keeps all strings handled by Lua)
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define lstring_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lmem.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
#include "lstring.h"
|
||||
|
||||
|
||||
/*
|
||||
** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to
|
||||
** compute its hash
|
||||
*/
|
||||
#if !defined(LUAI_HASHLIMIT)
|
||||
#define LUAI_HASHLIMIT 5
|
||||
#endif
|
||||
|
||||
void luaS_resize (lua_State *L, int newsize) {
|
||||
GCObject **newhash;
|
||||
stringtable *tb;
|
||||
int i;
|
||||
if (G(L)->gcstate == GCSsweepstring)
|
||||
return; /* cannot resize during GC traverse */
|
||||
newhash = luaM_newvector(L, newsize, GCObject *);
|
||||
tb = &G(L)->strt;
|
||||
for (i=0; i<newsize; i++) newhash[i] = NULL;
|
||||
/* rehash */
|
||||
for (i=0; i<tb->size; i++) {
|
||||
GCObject *p = tb->hash[i];
|
||||
while (p) { /* for each node in the list */
|
||||
GCObject *next = p->gch.next; /* save next */
|
||||
unsigned int h = gco2ts(p)->hash;
|
||||
int h1 = lmod(h, newsize); /* new position */
|
||||
lua_assert(cast(int, h%newsize) == lmod(h, newsize));
|
||||
p->gch.next = newhash[h1]; /* chain it */
|
||||
newhash[h1] = p;
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
luaM_freearray(L, tb->hash, tb->size, TString *);
|
||||
tb->size = newsize;
|
||||
tb->hash = newhash;
|
||||
|
||||
|
||||
/*
|
||||
** Maximum size for string table.
|
||||
*/
|
||||
#define MAXSTRTB cast_int(luaM_limitN(MAX_INT, TString*))
|
||||
|
||||
|
||||
/*
|
||||
** equality for long strings
|
||||
*/
|
||||
int luaS_eqlngstr (TString *a, TString *b) {
|
||||
size_t len = a->u.lnglen;
|
||||
lua_assert(a->tt == LUA_TLNGSTR && b->tt == LUA_TLNGSTR);
|
||||
return (a == b) || /* same instance or... */
|
||||
((len == b->u.lnglen) && /* equal length and ... */
|
||||
(memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */
|
||||
}
|
||||
|
||||
|
||||
static TString *newlstr (lua_State *L, const char *str, size_t l,
|
||||
unsigned int h) {
|
||||
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
|
||||
unsigned int h = seed ^ cast_uint(l);
|
||||
size_t step = (l >> LUAI_HASHLIMIT) + 1;
|
||||
for (; l >= step; l -= step)
|
||||
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
unsigned int luaS_hashlongstr (TString *ts) {
|
||||
lua_assert(ts->tt == LUA_TLNGSTR);
|
||||
if (ts->extra == 0) { /* no hash? */
|
||||
ts->hash = luaS_hash(getstr(ts), ts->u.lnglen, ts->hash);
|
||||
ts->extra = 1; /* now it has its hash */
|
||||
}
|
||||
return ts->hash;
|
||||
}
|
||||
|
||||
|
||||
static void tablerehash (TString **vect, int osize, int nsize) {
|
||||
int i;
|
||||
for (i = osize; i < nsize; i++) /* clear new elements */
|
||||
vect[i] = NULL;
|
||||
for (i = 0; i < osize; i++) { /* rehash old part of the array */
|
||||
TString *p = vect[i];
|
||||
vect[i] = NULL;
|
||||
while (p) { /* for each string in the list */
|
||||
TString *hnext = p->u.hnext; /* save next */
|
||||
unsigned int h = lmod(p->hash, nsize); /* new position */
|
||||
p->u.hnext = vect[h]; /* chain it into array */
|
||||
vect[h] = p;
|
||||
p = hnext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Resize the string table. If allocation fails, keep the current size.
|
||||
** (This can degrade performance, but any non-zero size should work
|
||||
** correctly.)
|
||||
*/
|
||||
void luaS_resize (lua_State *L, int nsize) {
|
||||
stringtable *tb = &G(L)->strt;
|
||||
int osize = tb->size;
|
||||
TString **newvect;
|
||||
if (nsize < osize) /* shrinking table? */
|
||||
tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */
|
||||
newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*);
|
||||
if (unlikely(newvect == NULL)) { /* reallocation failed? */
|
||||
if (nsize < osize) /* was it shrinking table? */
|
||||
tablerehash(tb->hash, nsize, osize); /* restore to original size */
|
||||
/* leave table as it was */
|
||||
}
|
||||
else { /* allocation succeeded */
|
||||
tb->hash = newvect;
|
||||
tb->size = nsize;
|
||||
if (nsize > osize)
|
||||
tablerehash(newvect, osize, nsize); /* rehash for new size */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Clear API string cache. (Entries cannot be empty, so fill them with
|
||||
** a non-collectable string.)
|
||||
*/
|
||||
void luaS_clearcache (global_State *g) {
|
||||
int i, j;
|
||||
for (i = 0; i < STRCACHE_N; i++)
|
||||
for (j = 0; j < STRCACHE_M; j++) {
|
||||
if (iswhite(g->strcache[i][j])) /* will entry be collected? */
|
||||
g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Initialize the string table and the string cache
|
||||
*/
|
||||
void luaS_init (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
int i, j;
|
||||
stringtable *tb = &G(L)->strt;
|
||||
tb->hash = luaM_newvector(L, MINSTRTABSIZE, TString*);
|
||||
tablerehash(tb->hash, 0, MINSTRTABSIZE); /* clear array */
|
||||
tb->size = MINSTRTABSIZE;
|
||||
/* pre-create memory-error message */
|
||||
g->memerrmsg = luaS_newliteral(L, MEMERRMSG);
|
||||
luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */
|
||||
for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */
|
||||
for (j = 0; j < STRCACHE_M; j++)
|
||||
g->strcache[i][j] = g->memerrmsg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** creates a new string object
|
||||
*/
|
||||
static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) {
|
||||
TString *ts;
|
||||
stringtable *tb;
|
||||
if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
|
||||
luaM_toobig(L);
|
||||
ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString)));
|
||||
ts->tsv.len = l;
|
||||
ts->tsv.hash = h;
|
||||
ts->tsv.marked = luaC_white(G(L));
|
||||
ts->tsv.tt = LUA_TSTRING;
|
||||
ts->tsv.reserved = 0;
|
||||
memcpy(ts+1, str, l*sizeof(char));
|
||||
((char *)(ts+1))[l] = '\0'; /* ending 0 */
|
||||
tb = &G(L)->strt;
|
||||
h = lmod(h, tb->size);
|
||||
ts->tsv.next = tb->hash[h]; /* chain new entry */
|
||||
tb->hash[h] = obj2gco(ts);
|
||||
tb->nuse++;
|
||||
if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
|
||||
luaS_resize(L, tb->size*2); /* too crowded */
|
||||
GCObject *o;
|
||||
size_t totalsize; /* total size of TString object */
|
||||
totalsize = sizelstring(l);
|
||||
o = luaC_newobj(L, tag, totalsize);
|
||||
ts = gco2ts(o);
|
||||
ts->hash = h;
|
||||
ts->extra = 0;
|
||||
getstr(ts)[l] = '\0'; /* ending 0 */
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
|
||||
GCObject *o;
|
||||
unsigned int h = cast(unsigned int, l); /* seed */
|
||||
size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */
|
||||
size_t l1;
|
||||
for (l1=l; l1>=step; l1-=step) /* compute hash */
|
||||
h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
|
||||
for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
|
||||
o != NULL;
|
||||
o = o->gch.next) {
|
||||
TString *ts = rawgco2ts(o);
|
||||
if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {
|
||||
/* string may be dead */
|
||||
if (isdead(G(L), o)) changewhite(o);
|
||||
TString *luaS_createlngstrobj (lua_State *L, size_t l) {
|
||||
TString *ts = createstrobj(L, l, LUA_TLNGSTR, G(L)->seed);
|
||||
ts->u.lnglen = l;
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
void luaS_remove (lua_State *L, TString *ts) {
|
||||
stringtable *tb = &G(L)->strt;
|
||||
TString **p = &tb->hash[lmod(ts->hash, tb->size)];
|
||||
while (*p != ts) /* find previous element */
|
||||
p = &(*p)->u.hnext;
|
||||
*p = (*p)->u.hnext; /* remove element from its list */
|
||||
tb->nuse--;
|
||||
}
|
||||
|
||||
|
||||
static void growstrtab (lua_State *L, stringtable *tb) {
|
||||
if (unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
|
||||
luaC_fullgc(L, 1); /* try to free some... */
|
||||
if (tb->nuse == MAX_INT) /* still too many? */
|
||||
luaM_error(L); /* cannot even create a message... */
|
||||
}
|
||||
if (tb->size <= MAXSTRTB / 2) /* can grow string table? */
|
||||
luaS_resize(L, tb->size * 2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Checks whether short string exists and reuses it or creates a new one.
|
||||
*/
|
||||
static TString *internshrstr (lua_State *L, const char *str, size_t l) {
|
||||
TString *ts;
|
||||
global_State *g = G(L);
|
||||
stringtable *tb = &g->strt;
|
||||
unsigned int h = luaS_hash(str, l, g->seed);
|
||||
TString **list = &tb->hash[lmod(h, tb->size)];
|
||||
lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
|
||||
for (ts = *list; ts != NULL; ts = ts->u.hnext) {
|
||||
if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) {
|
||||
/* found! */
|
||||
if (isdead(g, ts)) /* dead (but not collected yet)? */
|
||||
changewhite(ts); /* resurrect it */
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
return newlstr(L, str, l, h); /* not found */
|
||||
/* else must create a new string */
|
||||
if (tb->nuse >= tb->size) { /* need to grow string table? */
|
||||
growstrtab(L, tb);
|
||||
list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */
|
||||
}
|
||||
ts = createstrobj(L, l, LUA_TSHRSTR, h);
|
||||
memcpy(getstr(ts), str, l * sizeof(char));
|
||||
ts->shrlen = cast_byte(l);
|
||||
ts->u.hnext = *list;
|
||||
*list = ts;
|
||||
tb->nuse++;
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
Udata *luaS_newudata (lua_State *L, size_t s, Table *e) {
|
||||
/*
|
||||
** new string (with explicit length)
|
||||
*/
|
||||
TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
|
||||
if (l <= LUAI_MAXSHORTLEN) /* short string? */
|
||||
return internshrstr(L, str, l);
|
||||
else {
|
||||
TString *ts;
|
||||
if (unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
|
||||
luaM_toobig(L);
|
||||
ts = luaS_createlngstrobj(L, l);
|
||||
memcpy(getstr(ts), str, l * sizeof(char));
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create or reuse a zero-terminated string, first checking in the
|
||||
** cache (using the string address as a key). The cache can contain
|
||||
** only zero-terminated strings, so it is safe to use 'strcmp' to
|
||||
** check hits.
|
||||
*/
|
||||
TString *luaS_new (lua_State *L, const char *str) {
|
||||
unsigned int i = point2uint(str) % STRCACHE_N; /* hash */
|
||||
int j;
|
||||
TString **p = G(L)->strcache[i];
|
||||
for (j = 0; j < STRCACHE_M; j++) {
|
||||
if (strcmp(str, getstr(p[j])) == 0) /* hit? */
|
||||
return p[j]; /* that is it */
|
||||
}
|
||||
/* normal route */
|
||||
for (j = STRCACHE_M - 1; j > 0; j--)
|
||||
p[j] = p[j - 1]; /* move out last element */
|
||||
/* new element is first in the list */
|
||||
p[0] = luaS_newlstr(L, str, strlen(str));
|
||||
return p[0];
|
||||
}
|
||||
|
||||
|
||||
Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
|
||||
Udata *u;
|
||||
if (s > MAX_SIZET - sizeof(Udata))
|
||||
int i;
|
||||
GCObject *o;
|
||||
if (unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
|
||||
luaM_toobig(L);
|
||||
u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata)));
|
||||
u->uv.marked = luaC_white(G(L)); /* is not finalized */
|
||||
u->uv.tt = LUA_TUSERDATA;
|
||||
u->uv.len = s;
|
||||
u->uv.metatable = NULL;
|
||||
u->uv.env = e;
|
||||
/* chain it on udata list (after main thread) */
|
||||
u->uv.next = G(L)->mainthread->next;
|
||||
G(L)->mainthread->next = obj2gco(u);
|
||||
o = luaC_newobj(L, LUA_TUSERDATA, sizeudata(nuvalue, s));
|
||||
u = gco2u(o);
|
||||
u->len = s;
|
||||
u->nuvalue = nuvalue;
|
||||
u->metatable = NULL;
|
||||
for (i = 0; i < nuvalue; i++)
|
||||
setnilvalue(&u->uv[i].uv);
|
||||
return u;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lstring.h,v 1.42 2005/02/23 17:30:22 roberto Exp roberto $
|
||||
** $Id: lstring.h $
|
||||
** String table (keep all strings handled by Lua)
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -7,25 +7,47 @@
|
||||
#ifndef lstring_h
|
||||
#define lstring_h
|
||||
|
||||
|
||||
#include "lgc.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char))
|
||||
/*
|
||||
** Memory-allocation error message must be preallocated (it cannot
|
||||
** be created after memory is exhausted)
|
||||
*/
|
||||
#define MEMERRMSG "not enough memory"
|
||||
|
||||
#define sizeudata(u) (sizeof(union Udata)+(u)->len)
|
||||
|
||||
#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s)))
|
||||
#define sizelstring(l) (sizeof(TString) + ((l) + 1) * sizeof(char))
|
||||
|
||||
#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
|
||||
(sizeof(s)/sizeof(char))-1))
|
||||
|
||||
#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT)
|
||||
|
||||
/*
|
||||
** test whether a string is a reserved word
|
||||
*/
|
||||
#define isreserved(s) ((s)->tt == LUA_TSHRSTR && (s)->extra > 0)
|
||||
|
||||
|
||||
/*
|
||||
** equality for short strings, which are always internalized
|
||||
*/
|
||||
#define eqshrstr(a,b) check_exp((a)->tt == LUA_TSHRSTR, (a) == (b))
|
||||
|
||||
|
||||
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
|
||||
LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts);
|
||||
LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
|
||||
LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
|
||||
LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);
|
||||
LUAI_FUNC void luaS_clearcache (global_State *g);
|
||||
LUAI_FUNC void luaS_init (lua_State *L);
|
||||
LUAI_FUNC void luaS_remove (lua_State *L, TString *ts);
|
||||
LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue);
|
||||
LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
|
||||
LUAI_FUNC TString *luaS_new (lua_State *L, const char *str);
|
||||
LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: ltable.h,v 2.7 2005/04/25 19:24:10 roberto Exp roberto $
|
||||
** $Id: ltable.h $
|
||||
** Lua tables (hash)
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -11,29 +11,47 @@
|
||||
|
||||
|
||||
#define gnode(t,i) (&(t)->node[i])
|
||||
#define gkey(n) (&(n)->i_key)
|
||||
#define gval(n) (&(n)->i_val)
|
||||
#define gnext(n) ((n)->i_key.next)
|
||||
|
||||
#define key2tval(n) (cast(const TValue *, gkey(n)))
|
||||
#define gnext(n) ((n)->u.next)
|
||||
|
||||
|
||||
LUAI_DATA const Node luaH_dummynode;
|
||||
#define invalidateTMcache(t) ((t)->flags = 0)
|
||||
|
||||
LUAI_FUNC const TValue *luaH_getnum (Table *t, int key);
|
||||
LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key);
|
||||
|
||||
/* true when 't' is using 'dummynode' as its hash part */
|
||||
#define isdummy(t) ((t)->lastfree == NULL)
|
||||
|
||||
|
||||
/* allocated size for hash nodes */
|
||||
#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t))
|
||||
|
||||
|
||||
/* returns the Node, given the value of a table entry */
|
||||
#define nodefromval(v) cast(Node *, (v))
|
||||
|
||||
|
||||
LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);
|
||||
LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
|
||||
TValue *value);
|
||||
LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
|
||||
LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
|
||||
LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key);
|
||||
LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
|
||||
LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key);
|
||||
LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);
|
||||
LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash);
|
||||
LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize);
|
||||
LUAI_FUNC Table *luaH_new (lua_State *L);
|
||||
LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
|
||||
unsigned int nhsize);
|
||||
LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize);
|
||||
LUAI_FUNC void luaH_free (lua_State *L, Table *t);
|
||||
LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
|
||||
LUAI_FUNC int luaH_getn (Table *t);
|
||||
LUAI_FUNC lua_Unsigned luaH_getn (Table *t);
|
||||
LUAI_FUNC unsigned int luaH_realasize (const Table *t);
|
||||
|
||||
/* exported only for debugging */
|
||||
|
||||
#if defined(LUA_DEBUG)
|
||||
LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
|
||||
LUAI_FUNC int luaH_isdummy (const Table *t);
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,235 +1,408 @@
|
||||
/*
|
||||
** $Id: ltablib.c,v 1.34 2005/08/15 14:12:32 roberto Exp roberto $
|
||||
** $Id: ltablib.c $
|
||||
** Library for Table Manipulation
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define ltablib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n))
|
||||
/*
|
||||
** Operations that an object must define to mimic a table
|
||||
** (some functions only need some of them)
|
||||
*/
|
||||
#define TAB_R 1 /* read */
|
||||
#define TAB_W 2 /* write */
|
||||
#define TAB_L 4 /* length */
|
||||
#define TAB_RW (TAB_R | TAB_W) /* read/write */
|
||||
|
||||
|
||||
static int foreachi (lua_State *L) {
|
||||
int i;
|
||||
int n = aux_getn(L, 1);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
for (i=1; i <= n; i++) {
|
||||
lua_pushvalue(L, 2); /* function */
|
||||
lua_pushinteger(L, i); /* 1st argument */
|
||||
lua_rawgeti(L, 1, i); /* 2nd argument */
|
||||
lua_call(L, 2, 1);
|
||||
if (!lua_isnil(L, -1))
|
||||
return 1;
|
||||
lua_pop(L, 1); /* remove nil result */
|
||||
#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n))
|
||||
|
||||
|
||||
static int checkfield (lua_State *L, const char *key, int n) {
|
||||
lua_pushstring(L, key);
|
||||
return (lua_rawget(L, -n) != LUA_TNIL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check that 'arg' either is a table or can behave like one (that is,
|
||||
** has a metatable with the required metamethods)
|
||||
*/
|
||||
static void checktab (lua_State *L, int arg, int what) {
|
||||
if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */
|
||||
int n = 1; /* number of elements to pop */
|
||||
if (lua_getmetatable(L, arg) && /* must have metatable */
|
||||
(!(what & TAB_R) || checkfield(L, "__index", ++n)) &&
|
||||
(!(what & TAB_W) || checkfield(L, "__newindex", ++n)) &&
|
||||
(!(what & TAB_L) || checkfield(L, "__len", ++n))) {
|
||||
lua_pop(L, n); /* pop metatable and tested metamethods */
|
||||
}
|
||||
else
|
||||
luaL_checktype(L, arg, LUA_TTABLE); /* force an error */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int foreach (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
lua_pushnil(L); /* first key */
|
||||
for (;;) {
|
||||
if (lua_next(L, 1) == 0)
|
||||
return 0;
|
||||
lua_pushvalue(L, 2); /* function */
|
||||
lua_pushvalue(L, -3); /* key */
|
||||
lua_pushvalue(L, -3); /* value */
|
||||
lua_call(L, 2, 1);
|
||||
if (!lua_isnil(L, -1))
|
||||
return 1;
|
||||
lua_pop(L, 2); /* remove value and result */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int getn (lua_State *L) {
|
||||
lua_pushinteger(L, aux_getn(L, 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int setn (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
#ifndef luaL_setn
|
||||
luaL_setn(L, 1, luaL_checkint(L, 2));
|
||||
#else
|
||||
luaL_error(L, LUA_QL("setn") " is obsolete");
|
||||
#endif
|
||||
lua_pushvalue(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int tinsert (lua_State *L) {
|
||||
int e = aux_getn(L, 1) + 1; /* first empty element */
|
||||
int pos; /* where to insert new element */
|
||||
if (lua_isnone(L, 3)) /* called with only 2 arguments */
|
||||
pos = e; /* insert new element at the end */
|
||||
else {
|
||||
int i;
|
||||
pos = luaL_checkint(L, 2); /* 2nd argument is the position */
|
||||
if (pos > e) e = pos; /* `grow' array if necessary */
|
||||
lua_settop(L, 3); /* function may be called with more than 3 args */
|
||||
for (i = e; i > pos; i--) { /* move up elements */
|
||||
lua_rawgeti(L, 1, i-1);
|
||||
lua_rawseti(L, 1, i); /* t[i] = t[i-1] */
|
||||
lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */
|
||||
lua_Integer pos; /* where to insert new element */
|
||||
switch (lua_gettop(L)) {
|
||||
case 2: { /* called with only 2 arguments */
|
||||
pos = e; /* insert new element at the end */
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
lua_Integer i;
|
||||
pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */
|
||||
/* check whether 'pos' is in [1, e] */
|
||||
luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2,
|
||||
"position out of bounds");
|
||||
for (i = e; i > pos; i--) { /* move up elements */
|
||||
lua_geti(L, 1, i - 1);
|
||||
lua_seti(L, 1, i); /* t[i] = t[i - 1] */
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return luaL_error(L, "wrong number of arguments to 'insert'");
|
||||
}
|
||||
}
|
||||
luaL_setn(L, 1, e); /* new size */
|
||||
lua_rawseti(L, 1, pos); /* t[pos] = v */
|
||||
lua_seti(L, 1, pos); /* t[pos] = v */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tremove (lua_State *L) {
|
||||
int e = aux_getn(L, 1);
|
||||
int pos = luaL_optint(L, 2, e);
|
||||
if (e == 0) return 0; /* table is `empty' */
|
||||
luaL_setn(L, 1, e - 1); /* t.n = n-1 */
|
||||
lua_rawgeti(L, 1, pos); /* result = t[pos] */
|
||||
for ( ;pos<e; pos++) {
|
||||
lua_rawgeti(L, 1, pos+1);
|
||||
lua_rawseti(L, 1, pos); /* t[pos] = t[pos+1] */
|
||||
lua_Integer size = aux_getn(L, 1, TAB_RW);
|
||||
lua_Integer pos = luaL_optinteger(L, 2, size);
|
||||
if (pos != size) /* validate 'pos' if given */
|
||||
/* check whether 'pos' is in [1, size + 1] */
|
||||
luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1,
|
||||
"position out of bounds");
|
||||
lua_geti(L, 1, pos); /* result = t[pos] */
|
||||
for ( ; pos < size; pos++) {
|
||||
lua_geti(L, 1, pos + 1);
|
||||
lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */
|
||||
}
|
||||
lua_pushnil(L);
|
||||
lua_rawseti(L, 1, e); /* t[e] = nil */
|
||||
lua_seti(L, 1, pos); /* remove entry t[pos] */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever
|
||||
** possible, copy in increasing order, which is better for rehashing.
|
||||
** "possible" means destination after original range, or smaller
|
||||
** than origin, or copying to another table.
|
||||
*/
|
||||
static int tmove (lua_State *L) {
|
||||
lua_Integer f = luaL_checkinteger(L, 2);
|
||||
lua_Integer e = luaL_checkinteger(L, 3);
|
||||
lua_Integer t = luaL_checkinteger(L, 4);
|
||||
int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */
|
||||
checktab(L, 1, TAB_R);
|
||||
checktab(L, tt, TAB_W);
|
||||
if (e >= f) { /* otherwise, nothing to move */
|
||||
lua_Integer n, i;
|
||||
luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3,
|
||||
"too many elements to move");
|
||||
n = e - f + 1; /* number of elements to move */
|
||||
luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4,
|
||||
"destination wrap around");
|
||||
if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) {
|
||||
for (i = 0; i < n; i++) {
|
||||
lua_geti(L, 1, f + i);
|
||||
lua_seti(L, tt, t + i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = n - 1; i >= 0; i--) {
|
||||
lua_geti(L, 1, f + i);
|
||||
lua_seti(L, tt, t + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_pushvalue(L, tt); /* return destination table */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
|
||||
lua_geti(L, 1, i);
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "invalid value (%s) at index %d in table for 'concat'",
|
||||
luaL_typename(L, -1), i);
|
||||
luaL_addvalue(b);
|
||||
}
|
||||
|
||||
|
||||
static int tconcat (lua_State *L) {
|
||||
luaL_Buffer b;
|
||||
lua_Integer last = aux_getn(L, 1, TAB_R);
|
||||
size_t lsep;
|
||||
const char *sep = luaL_optlstring(L, 2, "", &lsep);
|
||||
int i = luaL_optint(L, 3, 1);
|
||||
int last = luaL_optint(L, 4, -2);
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
if (last == -2)
|
||||
last = luaL_getn(L, 1);
|
||||
lua_Integer i = luaL_optinteger(L, 3, 1);
|
||||
last = luaL_optinteger(L, 4, last);
|
||||
luaL_buffinit(L, &b);
|
||||
for (; i <= last; i++) {
|
||||
lua_rawgeti(L, 1, i);
|
||||
luaL_argcheck(L, lua_isstring(L, -1), 1, "table contains non-strings");
|
||||
luaL_addvalue(&b);
|
||||
if (i != last)
|
||||
luaL_addlstring(&b, sep, lsep);
|
||||
for (; i < last; i++) {
|
||||
addfield(L, &b, i);
|
||||
luaL_addlstring(&b, sep, lsep);
|
||||
}
|
||||
if (i == last) /* add last value (if interval was not empty) */
|
||||
addfield(L, &b, i);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Pack/unpack
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static int tpack (lua_State *L) {
|
||||
int i;
|
||||
int n = lua_gettop(L); /* number of elements to pack */
|
||||
lua_createtable(L, n, 1); /* create result table */
|
||||
lua_insert(L, 1); /* put it at index 1 */
|
||||
for (i = n; i >= 1; i--) /* assign elements */
|
||||
lua_seti(L, 1, i);
|
||||
lua_pushinteger(L, n);
|
||||
lua_setfield(L, 1, "n"); /* t.n = number of elements */
|
||||
return 1; /* return table */
|
||||
}
|
||||
|
||||
|
||||
static int tunpack (lua_State *L) {
|
||||
lua_Unsigned n;
|
||||
lua_Integer i = luaL_optinteger(L, 2, 1);
|
||||
lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
|
||||
if (i > e) return 0; /* empty range */
|
||||
n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
|
||||
if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n)))
|
||||
return luaL_error(L, "too many results to unpack");
|
||||
for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */
|
||||
lua_geti(L, 1, i);
|
||||
}
|
||||
lua_geti(L, 1, e); /* push last element */
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Quicksort
|
||||
** (based on `Algorithms in MODULA-3', Robert Sedgewick;
|
||||
** (based on 'Algorithms in MODULA-3', Robert Sedgewick;
|
||||
** Addison-Wesley, 1993.)
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
static void set2 (lua_State *L, int i, int j) {
|
||||
lua_rawseti(L, 1, i);
|
||||
lua_rawseti(L, 1, j);
|
||||
/* type for array indices */
|
||||
typedef unsigned int IdxT;
|
||||
|
||||
|
||||
/*
|
||||
** Produce a "random" 'unsigned int' to randomize pivot choice. This
|
||||
** macro is used only when 'sort' detects a big imbalance in the result
|
||||
** of a partition. (If you don't want/need this "randomness", ~0 is a
|
||||
** good choice.)
|
||||
*/
|
||||
#if !defined(l_randomizePivot) /* { */
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/* size of 'e' measured in number of 'unsigned int's */
|
||||
#define sof(e) (sizeof(e) / sizeof(unsigned int))
|
||||
|
||||
/*
|
||||
** Use 'time' and 'clock' as sources of "randomness". Because we don't
|
||||
** know the types 'clock_t' and 'time_t', we cannot cast them to
|
||||
** anything without risking overflows. A safe way to use their values
|
||||
** is to copy them to an array of a known type and use the array values.
|
||||
*/
|
||||
static unsigned int l_randomizePivot (void) {
|
||||
clock_t c = clock();
|
||||
time_t t = time(NULL);
|
||||
unsigned int buff[sof(c) + sof(t)];
|
||||
unsigned int i, rnd = 0;
|
||||
memcpy(buff, &c, sof(c) * sizeof(unsigned int));
|
||||
memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int));
|
||||
for (i = 0; i < sof(buff); i++)
|
||||
rnd += buff[i];
|
||||
return rnd;
|
||||
}
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/* arrays larger than 'RANLIMIT' may use randomized pivots */
|
||||
#define RANLIMIT 100u
|
||||
|
||||
|
||||
static void set2 (lua_State *L, IdxT i, IdxT j) {
|
||||
lua_seti(L, 1, i);
|
||||
lua_seti(L, 1, j);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return true iff value at stack index 'a' is less than the value at
|
||||
** index 'b' (according to the order of the sort).
|
||||
*/
|
||||
static int sort_comp (lua_State *L, int a, int b) {
|
||||
if (!lua_isnil(L, 2)) { /* function? */
|
||||
if (lua_isnil(L, 2)) /* no function? */
|
||||
return lua_compare(L, a, b, LUA_OPLT); /* a < b */
|
||||
else { /* function */
|
||||
int res;
|
||||
lua_pushvalue(L, 2);
|
||||
lua_pushvalue(L, 2); /* push function */
|
||||
lua_pushvalue(L, a-1); /* -1 to compensate function */
|
||||
lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */
|
||||
lua_call(L, 2, 1);
|
||||
res = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */
|
||||
lua_call(L, 2, 1); /* call function */
|
||||
res = lua_toboolean(L, -1); /* get result */
|
||||
lua_pop(L, 1); /* pop result */
|
||||
return res;
|
||||
}
|
||||
else /* a < b? */
|
||||
return lua_lessthan(L, a, b);
|
||||
}
|
||||
|
||||
static void auxsort (lua_State *L, int l, int u) {
|
||||
while (l < u) { /* for tail recursion */
|
||||
int i, j;
|
||||
/* sort elements a[l], a[(l+u)/2] and a[u] */
|
||||
lua_rawgeti(L, 1, l);
|
||||
lua_rawgeti(L, 1, u);
|
||||
if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
|
||||
set2(L, l, u); /* swap a[l] - a[u] */
|
||||
|
||||
/*
|
||||
** Does the partition: Pivot P is at the top of the stack.
|
||||
** precondition: a[lo] <= P == a[up-1] <= a[up],
|
||||
** so it only needs to do the partition from lo + 1 to up - 2.
|
||||
** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up]
|
||||
** returns 'i'.
|
||||
*/
|
||||
static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
|
||||
IdxT i = lo; /* will be incremented before first use */
|
||||
IdxT j = up - 1; /* will be decremented before first use */
|
||||
/* loop invariant: a[lo .. i] <= P <= a[j .. up] */
|
||||
for (;;) {
|
||||
/* next loop: repeat ++i while a[i] < P */
|
||||
while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
|
||||
if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */
|
||||
luaL_error(L, "invalid order function for sorting");
|
||||
lua_pop(L, 1); /* remove a[i] */
|
||||
}
|
||||
/* after the loop, a[i] >= P and a[lo .. i - 1] < P */
|
||||
/* next loop: repeat --j while P < a[j] */
|
||||
while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
|
||||
if (j < i) /* j < i but a[j] > P ?? */
|
||||
luaL_error(L, "invalid order function for sorting");
|
||||
lua_pop(L, 1); /* remove a[j] */
|
||||
}
|
||||
/* after the loop, a[j] <= P and a[j + 1 .. up] >= P */
|
||||
if (j < i) { /* no elements out of place? */
|
||||
/* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */
|
||||
lua_pop(L, 1); /* pop a[j] */
|
||||
/* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */
|
||||
set2(L, up - 1, i);
|
||||
return i;
|
||||
}
|
||||
/* otherwise, swap a[i] - a[j] to restore invariant and repeat */
|
||||
set2(L, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Choose an element in the middle (2nd-3th quarters) of [lo,up]
|
||||
** "randomized" by 'rnd'
|
||||
*/
|
||||
static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) {
|
||||
IdxT r4 = (up - lo) / 4; /* range/4 */
|
||||
IdxT p = rnd % (r4 * 2) + (lo + r4);
|
||||
lua_assert(lo + r4 <= p && p <= up - r4);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Quicksort algorithm (recursive function)
|
||||
*/
|
||||
static void auxsort (lua_State *L, IdxT lo, IdxT up,
|
||||
unsigned int rnd) {
|
||||
while (lo < up) { /* loop for tail recursion */
|
||||
IdxT p; /* Pivot index */
|
||||
IdxT n; /* to be used later */
|
||||
/* sort elements 'lo', 'p', and 'up' */
|
||||
lua_geti(L, 1, lo);
|
||||
lua_geti(L, 1, up);
|
||||
if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */
|
||||
set2(L, lo, up); /* swap a[lo] - a[up] */
|
||||
else
|
||||
lua_pop(L, 2);
|
||||
if (u-l == 1) break; /* only 2 elements */
|
||||
i = (l+u)/2;
|
||||
lua_rawgeti(L, 1, i);
|
||||
lua_rawgeti(L, 1, l);
|
||||
if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */
|
||||
set2(L, i, l);
|
||||
lua_pop(L, 2); /* remove both values */
|
||||
if (up - lo == 1) /* only 2 elements? */
|
||||
return; /* already sorted */
|
||||
if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */
|
||||
p = (lo + up)/2; /* middle element is a good pivot */
|
||||
else /* for larger intervals, it is worth a random pivot */
|
||||
p = choosePivot(lo, up, rnd);
|
||||
lua_geti(L, 1, p);
|
||||
lua_geti(L, 1, lo);
|
||||
if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */
|
||||
set2(L, p, lo); /* swap a[p] - a[lo] */
|
||||
else {
|
||||
lua_pop(L, 1); /* remove a[l] */
|
||||
lua_rawgeti(L, 1, u);
|
||||
if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */
|
||||
set2(L, i, u);
|
||||
lua_pop(L, 1); /* remove a[lo] */
|
||||
lua_geti(L, 1, up);
|
||||
if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */
|
||||
set2(L, p, up); /* swap a[up] - a[p] */
|
||||
else
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
if (u-l == 2) break; /* only 3 elements */
|
||||
lua_rawgeti(L, 1, i); /* Pivot */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawgeti(L, 1, u-1);
|
||||
set2(L, i, u-1);
|
||||
/* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
|
||||
i = l; j = u-1;
|
||||
for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
|
||||
/* repeat ++i until a[i] >= P */
|
||||
while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
|
||||
if (i>u) luaL_error(L, "invalid order function for sorting");
|
||||
lua_pop(L, 1); /* remove a[i] */
|
||||
}
|
||||
/* repeat --j until a[j] <= P */
|
||||
while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
|
||||
if (j<l) luaL_error(L, "invalid order function for sorting");
|
||||
lua_pop(L, 1); /* remove a[j] */
|
||||
}
|
||||
if (j<i) {
|
||||
lua_pop(L, 3); /* pop pivot, a[i], a[j] */
|
||||
break;
|
||||
}
|
||||
set2(L, i, j);
|
||||
}
|
||||
lua_rawgeti(L, 1, u-1);
|
||||
lua_rawgeti(L, 1, i);
|
||||
set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */
|
||||
/* a[l..i-1] <= a[i] == P <= a[i+1..u] */
|
||||
/* adjust so that smaller half is in [j..i] and larger one in [l..u] */
|
||||
if (i-l < u-i) {
|
||||
j=l; i=i-1; l=i+2;
|
||||
if (up - lo == 2) /* only 3 elements? */
|
||||
return; /* already sorted */
|
||||
lua_geti(L, 1, p); /* get middle element (Pivot) */
|
||||
lua_pushvalue(L, -1); /* push Pivot */
|
||||
lua_geti(L, 1, up - 1); /* push a[up - 1] */
|
||||
set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */
|
||||
p = partition(L, lo, up);
|
||||
/* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */
|
||||
if (p - lo < up - p) { /* lower interval is smaller? */
|
||||
auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */
|
||||
n = p - lo; /* size of smaller interval */
|
||||
lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */
|
||||
}
|
||||
else {
|
||||
j=i+1; i=u; u=j-2;
|
||||
auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */
|
||||
n = up - p; /* size of smaller interval */
|
||||
up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */
|
||||
}
|
||||
auxsort(L, j, i); /* call recursively the smaller one */
|
||||
} /* repeat the routine for the larger one */
|
||||
if ((up - lo) / 128 > n) /* partition too imbalanced? */
|
||||
rnd = l_randomizePivot(); /* try a new randomization */
|
||||
} /* tail call auxsort(L, lo, up, rnd) */
|
||||
}
|
||||
|
||||
|
||||
static int sort (lua_State *L) {
|
||||
int n = aux_getn(L, 1);
|
||||
luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */
|
||||
if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
lua_settop(L, 2); /* make sure there is two arguments */
|
||||
auxsort(L, 1, n);
|
||||
lua_Integer n = aux_getn(L, 1, TAB_RW);
|
||||
if (n > 1) { /* non-trivial interval? */
|
||||
luaL_argcheck(L, n < INT_MAX, 1, "array too big");
|
||||
if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */
|
||||
lua_settop(L, 2); /* make sure there are two arguments */
|
||||
auxsort(L, 1, (IdxT)n, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -238,19 +411,18 @@ static int sort (lua_State *L) {
|
||||
|
||||
static const luaL_Reg tab_funcs[] = {
|
||||
{"concat", tconcat},
|
||||
{"foreach", foreach},
|
||||
{"foreachi", foreachi},
|
||||
{"getn", getn},
|
||||
{"insert", tinsert},
|
||||
{"pack", tpack},
|
||||
{"unpack", tunpack},
|
||||
{"remove", tremove},
|
||||
{"setn", setn},
|
||||
{"move", tmove},
|
||||
{"sort", sort},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUALIB_API int luaopen_table (lua_State *L) {
|
||||
luaL_register(L, LUA_TABLIBNAME, tab_funcs);
|
||||
LUAMOD_API int luaopen_table (lua_State *L) {
|
||||
luaL_newlib(L, tab_funcs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: ltests.h,v 2.14 2005/05/03 19:01:17 roberto Exp roberto $
|
||||
** $Id: ltests.h $
|
||||
** Internal Header for Debugging of the Lua Implementation
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -8,83 +8,131 @@
|
||||
#define ltests_h
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* test Lua with compatibility code */
|
||||
#define LUA_COMPAT_MATHLIB
|
||||
#define LUA_COMPAT_LT_LE
|
||||
|
||||
|
||||
#define LUA_DEBUG
|
||||
|
||||
|
||||
/* turn on assertions */
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#undef lua_assert
|
||||
#define lua_assert(c) assert(c)
|
||||
|
||||
|
||||
/* include opcode names */
|
||||
#define LUAI_DEFOPNAMES
|
||||
|
||||
|
||||
/* compiled with -O0, Lua uses a lot of C stack space... */
|
||||
#undef LUAI_MAXCSTACK
|
||||
#define LUAI_MAXCSTACK 400
|
||||
|
||||
/* to avoid warnings, and to make sure value is really unused */
|
||||
#define UNUSED(x) (x=0, (void)(x))
|
||||
|
||||
|
||||
/* memory allocator control variables */
|
||||
/* test for sizes in 'l_sprintf' (make sure whole buffer is available) */
|
||||
#undef l_sprintf
|
||||
#if !defined(LUA_USE_C89)
|
||||
#define l_sprintf(s,sz,f,i) (memset(s,0xAB,sz), snprintf(s,sz,f,i))
|
||||
#else
|
||||
#define l_sprintf(s,sz,f,i) (memset(s,0xAB,sz), sprintf(s,f,i))
|
||||
#endif
|
||||
|
||||
|
||||
/* get a chance to test code without jump tables */
|
||||
#define LUA_USE_JUMPTABLE 0
|
||||
|
||||
|
||||
/* use 32-bit integers in random generator */
|
||||
#define LUA_RAND32
|
||||
|
||||
|
||||
/* memory-allocator control variables */
|
||||
typedef struct Memcontrol {
|
||||
unsigned long numblocks;
|
||||
unsigned long total;
|
||||
unsigned long maxmem;
|
||||
unsigned long memlimit;
|
||||
unsigned long countlimit;
|
||||
unsigned long objcount[LUA_NUMTAGS];
|
||||
} Memcontrol;
|
||||
|
||||
LUAI_DATA Memcontrol memcontrol;
|
||||
LUA_API Memcontrol l_memcontrol;
|
||||
|
||||
|
||||
/*
|
||||
** generic variable for debug tricks
|
||||
*/
|
||||
LUAI_DATA int Trick;
|
||||
extern void *l_Trick;
|
||||
|
||||
|
||||
void *debug_realloc (void *ud, void *block, size_t osize, size_t nsize);
|
||||
|
||||
#ifdef lua_c
|
||||
#define luaL_newstate() lua_newstate(debug_realloc, &memcontrol)
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct CallInfo *pCallInfo;
|
||||
|
||||
/*
|
||||
** Function to traverse and check all memory used by Lua
|
||||
*/
|
||||
int lua_checkmemory (lua_State *L);
|
||||
int lua_checkpc (lua_State *L, pCallInfo ci);
|
||||
|
||||
|
||||
/* test for lock/unlock */
|
||||
#undef luai_userstateopen
|
||||
#undef lua_lock
|
||||
#undef lua_unlock
|
||||
#undef LUAI_EXTRASPACE
|
||||
|
||||
LUAI_DATA int islocked;
|
||||
#define LUAI_EXTRASPACE sizeof(double)
|
||||
#define getlock(l) (*(cast(int **, l) - 1))
|
||||
#define luai_userstateopen(l) getlock(l) = &islocked;
|
||||
#define lua_lock(l) lua_assert((*getlock(l))++ == 0)
|
||||
#define lua_unlock(l) lua_assert(--(*getlock(l)) == 0)
|
||||
struct L_EXTRA { int lock; int *plock; };
|
||||
#undef LUA_EXTRASPACE
|
||||
#define LUA_EXTRASPACE sizeof(struct L_EXTRA)
|
||||
#define getlock(l) cast(struct L_EXTRA*, lua_getextraspace(l))
|
||||
#define luai_userstateopen(l) \
|
||||
(getlock(l)->lock = 0, getlock(l)->plock = &(getlock(l)->lock))
|
||||
#define luai_userstateclose(l) \
|
||||
lua_assert(getlock(l)->lock == 1 && getlock(l)->plock == &(getlock(l)->lock))
|
||||
#define luai_userstatethread(l,l1) \
|
||||
lua_assert(getlock(l1)->plock == getlock(l)->plock)
|
||||
#define luai_userstatefree(l,l1) \
|
||||
lua_assert(getlock(l)->plock == getlock(l1)->plock)
|
||||
#define lua_lock(l) lua_assert((*getlock(l)->plock)++ == 0)
|
||||
#define lua_unlock(l) lua_assert(--(*getlock(l)->plock) == 0)
|
||||
|
||||
|
||||
int luaB_opentests (lua_State *L);
|
||||
|
||||
#ifdef lua_c
|
||||
#define luaL_openlibs(L) { (luaL_openlibs)(L); luaB_opentests(L); }
|
||||
LUA_API int luaB_opentests (lua_State *L);
|
||||
|
||||
LUA_API void *debug_realloc (void *ud, void *block,
|
||||
size_t osize, size_t nsize);
|
||||
|
||||
#if defined(lua_c)
|
||||
#define luaL_newstate() lua_newstate(debug_realloc, &l_memcontrol)
|
||||
#define luaL_openlibs(L) \
|
||||
{ (luaL_openlibs)(L); \
|
||||
luaL_requiref(L, "T", luaB_opentests, 1); \
|
||||
lua_pop(L, 1); }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* real main will be defined at `ltests.c' */
|
||||
int l_main (int argc, char *argv[]);
|
||||
#define main l_main
|
||||
|
||||
|
||||
|
||||
/* change some sizes to give some bugs a chance */
|
||||
|
||||
#undef LUAL_BUFFERSIZE
|
||||
#define LUAL_BUFFERSIZE 27
|
||||
#define LUAL_BUFFERSIZE 23
|
||||
#define MINSTRTABSIZE 2
|
||||
#define MAXIWTHABS 3
|
||||
|
||||
|
||||
/* make stack-overflow tests run faster */
|
||||
#undef LUAI_MAXSTACK
|
||||
#define LUAI_MAXSTACK 50000
|
||||
|
||||
|
||||
#undef LUAI_USER_ALIGNMENT_T
|
||||
#define LUAI_USER_ALIGNMENT_T union { char b[sizeof(void*) * 8]; }
|
||||
|
||||
|
||||
#define STRCACHE_N 23
|
||||
#define STRCACHE_M 5
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,44 +1,54 @@
|
||||
/*
|
||||
** $Id: ltm.c,v 2.5 2005/05/05 15:34:03 roberto Exp roberto $
|
||||
** $Id: ltm.c $
|
||||
** Tag methods
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define ltm_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lgc.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
#include "lstring.h"
|
||||
#include "ltable.h"
|
||||
#include "ltm.h"
|
||||
#include "lvm.h"
|
||||
|
||||
|
||||
static const char udatatypename[] = "userdata";
|
||||
|
||||
const char *const luaT_typenames[] = {
|
||||
"nil", "boolean", "userdata", "number",
|
||||
"string", "table", "function", "userdata", "thread",
|
||||
"proto", "upval"
|
||||
LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTAGS] = {
|
||||
"no value",
|
||||
"nil", "boolean", udatatypename, "number",
|
||||
"string", "table", "function", udatatypename, "thread",
|
||||
"upvalue", "proto" /* these last cases are used for tests only */
|
||||
};
|
||||
|
||||
|
||||
void luaT_init (lua_State *L) {
|
||||
static const char *const luaT_eventname[] = { /* ORDER TM */
|
||||
"__index", "__newindex",
|
||||
"__gc", "__mode", "__eq",
|
||||
"__add", "__sub", "__mul", "__div", "__mod",
|
||||
"__pow", "__unm", "__len", "__lt", "__le",
|
||||
"__concat", "__call"
|
||||
"__gc", "__mode", "__len", "__eq",
|
||||
"__add", "__sub", "__mul", "__mod", "__pow",
|
||||
"__div", "__idiv",
|
||||
"__band", "__bor", "__bxor", "__shl", "__shr",
|
||||
"__unm", "__bnot", "__lt", "__le",
|
||||
"__concat", "__call", "__close"
|
||||
};
|
||||
int i;
|
||||
for (i=0; i<TM_N; i++) {
|
||||
G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);
|
||||
luaS_fix(G(L)->tmname[i]); /* never collect these names */
|
||||
luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +58,10 @@ void luaT_init (lua_State *L) {
|
||||
** tag methods
|
||||
*/
|
||||
const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
|
||||
const TValue *tm = luaH_getstr(events, ename);
|
||||
const TValue *tm = luaH_getshortstr(events, ename);
|
||||
lua_assert(event <= TM_EQ);
|
||||
if (ttisnil(tm)) { /* no tag method? */
|
||||
events->flags |= cast(lu_byte, 1u<<event); /* cache this fact */
|
||||
if (notm(tm)) { /* no tag method? */
|
||||
events->flags |= cast_byte(1u<<event); /* cache this fact */
|
||||
return NULL;
|
||||
}
|
||||
else return tm;
|
||||
@@ -70,6 +80,182 @@ const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {
|
||||
default:
|
||||
mt = G(L)->mt[ttype(o)];
|
||||
}
|
||||
return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : &luaO_nilobject);
|
||||
return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the name of the type of an object. For tables and userdata
|
||||
** with metatable, use their '__name' metafield, if present.
|
||||
*/
|
||||
const char *luaT_objtypename (lua_State *L, const TValue *o) {
|
||||
Table *mt;
|
||||
if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) ||
|
||||
(ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) {
|
||||
const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name"));
|
||||
if (ttisstring(name)) /* is '__name' a string? */
|
||||
return getstr(tsvalue(name)); /* use it as type name */
|
||||
}
|
||||
return ttypename(ttype(o)); /* else use standard type name */
|
||||
}
|
||||
|
||||
|
||||
void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, const TValue *p3) {
|
||||
StkId func = L->top;
|
||||
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
|
||||
setobj2s(L, func + 1, p1); /* 1st argument */
|
||||
setobj2s(L, func + 2, p2); /* 2nd argument */
|
||||
setobj2s(L, func + 3, p3); /* 3rd argument */
|
||||
L->top = func + 4;
|
||||
/* metamethod may yield only when called from Lua code */
|
||||
if (isLuacode(L->ci))
|
||||
luaD_call(L, func, 0);
|
||||
else
|
||||
luaD_callnoyield(L, func, 0);
|
||||
}
|
||||
|
||||
|
||||
void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, StkId res) {
|
||||
ptrdiff_t result = savestack(L, res);
|
||||
StkId func = L->top;
|
||||
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
|
||||
setobj2s(L, func + 1, p1); /* 1st argument */
|
||||
setobj2s(L, func + 2, p2); /* 2nd argument */
|
||||
L->top += 3;
|
||||
/* metamethod may yield only when called from Lua code */
|
||||
if (isLuacode(L->ci))
|
||||
luaD_call(L, func, 1);
|
||||
else
|
||||
luaD_callnoyield(L, func, 1);
|
||||
res = restorestack(L, result);
|
||||
setobjs2s(L, res, --L->top); /* move result to its place */
|
||||
}
|
||||
|
||||
|
||||
static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
StkId res, TMS event) {
|
||||
const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
|
||||
if (notm(tm))
|
||||
tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
|
||||
if (notm(tm)) return 0;
|
||||
luaT_callTMres(L, tm, p1, p2, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
StkId res, TMS event) {
|
||||
if (!callbinTM(L, p1, p2, res, event)) {
|
||||
switch (event) {
|
||||
case TM_BAND: case TM_BOR: case TM_BXOR:
|
||||
case TM_SHL: case TM_SHR: case TM_BNOT: {
|
||||
if (ttisnumber(p1) && ttisnumber(p2))
|
||||
luaG_tointerror(L, p1, p2);
|
||||
else
|
||||
luaG_opinterror(L, p1, p2, "perform bitwise operation on");
|
||||
}
|
||||
/* calls never return, but to avoid warnings: *//* FALLTHROUGH */
|
||||
default:
|
||||
luaG_opinterror(L, p1, p2, "perform arithmetic on");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void luaT_tryconcatTM (lua_State *L) {
|
||||
StkId top = L->top;
|
||||
if (!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))
|
||||
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
|
||||
}
|
||||
|
||||
|
||||
void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
int flip, StkId res, TMS event) {
|
||||
if (flip)
|
||||
luaT_trybinTM(L, p2, p1, res, event);
|
||||
else
|
||||
luaT_trybinTM(L, p1, p2, res, event);
|
||||
}
|
||||
|
||||
|
||||
void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
|
||||
int flip, StkId res, TMS event) {
|
||||
TValue aux;
|
||||
setivalue(&aux, i2);
|
||||
luaT_trybinassocTM(L, p1, &aux, flip, res, event);
|
||||
}
|
||||
|
||||
|
||||
int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
TMS event) {
|
||||
if (callbinTM(L, p1, p2, L->top, event)) /* try original event */
|
||||
return !l_isfalse(s2v(L->top));
|
||||
#if defined(LUA_COMPAT_LT_LE)
|
||||
else if (event == TM_LE) {
|
||||
/* try '!(p2 < p1)' for '(p1 <= p2)' */
|
||||
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
|
||||
if (callbinTM(L, p2, p1, L->top, TM_LT)) {
|
||||
L->ci->callstatus ^= CIST_LEQ; /* clear mark */
|
||||
return l_isfalse(s2v(L->top));
|
||||
}
|
||||
/* else error will remove this 'ci'; no need to clear mark */
|
||||
}
|
||||
#endif
|
||||
luaG_ordererror(L, p1, p2); /* no metamethod found */
|
||||
return 0; /* to avoid warnings */
|
||||
}
|
||||
|
||||
|
||||
int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
|
||||
int flip, int isfloat, TMS event) {
|
||||
TValue aux; const TValue *p2;
|
||||
if (isfloat) {
|
||||
setfltvalue(&aux, cast_num(v2));
|
||||
}
|
||||
else
|
||||
setivalue(&aux, v2);
|
||||
if (flip) { /* arguments were exchanged? */
|
||||
p2 = p1; p1 = &aux; /* correct them */
|
||||
}
|
||||
else
|
||||
p2 = &aux;
|
||||
return luaT_callorderTM(L, p1, p2, event);
|
||||
}
|
||||
|
||||
|
||||
void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
|
||||
const Proto *p) {
|
||||
int i;
|
||||
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
|
||||
int nextra = actual - nfixparams; /* number of extra arguments */
|
||||
ci->u.l.nextraargs = nextra;
|
||||
checkstackGC(L, p->maxstacksize + 1);
|
||||
/* copy function to the top of the stack */
|
||||
setobjs2s(L, L->top++, ci->func);
|
||||
/* move fixed parameters to the top of the stack */
|
||||
for (i = 1; i <= nfixparams; i++) {
|
||||
setobjs2s(L, L->top++, ci->func + i);
|
||||
setnilvalue(s2v(ci->func + i)); /* erase original parameter (for GC) */
|
||||
}
|
||||
ci->func += actual + 1;
|
||||
ci->top += actual + 1;
|
||||
lua_assert(L->top <= ci->top && ci->top <= L->stack_last);
|
||||
}
|
||||
|
||||
|
||||
void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
|
||||
int i;
|
||||
int nextra = ci->u.l.nextraargs;
|
||||
if (wanted < 0) {
|
||||
wanted = nextra; /* get all extra arguments available */
|
||||
checkstackp(L, nextra, where); /* ensure stack space */
|
||||
L->top = where + nextra; /* next instruction will need top */
|
||||
}
|
||||
for (i = 0; i < wanted && i < nextra; i++)
|
||||
setobjs2s(L, where + i, ci->func - nextra + i);
|
||||
for (; i < wanted; i++) /* complete required results with nil */
|
||||
setnilvalue(s2v(where + i));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: ltm.h,v 2.5 2005/05/20 15:53:42 roberto Exp roberto $
|
||||
** $Id: ltm.h $
|
||||
** Tag methods
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -13,42 +13,82 @@
|
||||
|
||||
/*
|
||||
* WARNING: if you change the order of this enumeration,
|
||||
* grep "ORDER TM"
|
||||
* grep "ORDER TM" and "ORDER OP"
|
||||
*/
|
||||
typedef enum {
|
||||
TM_INDEX,
|
||||
TM_NEWINDEX,
|
||||
TM_GC,
|
||||
TM_MODE,
|
||||
TM_EQ, /* last tag method with `fast' access */
|
||||
TM_LEN,
|
||||
TM_EQ, /* last tag method with fast access */
|
||||
TM_ADD,
|
||||
TM_SUB,
|
||||
TM_MUL,
|
||||
TM_DIV,
|
||||
TM_MOD,
|
||||
TM_POW,
|
||||
TM_DIV,
|
||||
TM_IDIV,
|
||||
TM_BAND,
|
||||
TM_BOR,
|
||||
TM_BXOR,
|
||||
TM_SHL,
|
||||
TM_SHR,
|
||||
TM_UNM,
|
||||
TM_LEN,
|
||||
TM_BNOT,
|
||||
TM_LT,
|
||||
TM_LE,
|
||||
TM_CONCAT,
|
||||
TM_CALL,
|
||||
TM_CLOSE,
|
||||
TM_N /* number of elements in the enum */
|
||||
} TMS;
|
||||
|
||||
|
||||
/*
|
||||
** Test whether there is no tagmethod.
|
||||
** (Because tagmethods use raw accesses, the result may be an "empty" nil.)
|
||||
*/
|
||||
#define notm(tm) ttisnil(tm)
|
||||
|
||||
|
||||
#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
|
||||
((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
|
||||
|
||||
#define fasttm(l,et,e) gfasttm(G(l), et, e)
|
||||
|
||||
LUAI_DATA const char *const luaT_typenames[];
|
||||
#define ttypename(x) luaT_typenames_[(x) + 1]
|
||||
|
||||
LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTAGS];)
|
||||
|
||||
|
||||
LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o);
|
||||
|
||||
LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);
|
||||
LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,
|
||||
TMS event);
|
||||
LUAI_FUNC void luaT_init (lua_State *L);
|
||||
|
||||
LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, const TValue *p3);
|
||||
LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f,
|
||||
const TValue *p1, const TValue *p2, StkId p3);
|
||||
LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
StkId res, TMS event);
|
||||
LUAI_FUNC void luaT_tryconcatTM (lua_State *L);
|
||||
LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1,
|
||||
const TValue *p2, int inv, StkId res, TMS event);
|
||||
LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
|
||||
int inv, StkId res, TMS event);
|
||||
LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1,
|
||||
const TValue *p2, TMS event);
|
||||
LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
|
||||
int inv, int isfloat, TMS event);
|
||||
|
||||
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
|
||||
struct CallInfo *ci, const Proto *p);
|
||||
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
|
||||
StkId where, int wanted);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
/*
|
||||
** $Id: lua.c,v 1.149 2005/08/26 17:32:05 roberto Exp roberto $
|
||||
** $Id: lua.c $
|
||||
** Lua stand-alone interpreter
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lua_c
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define lua_c
|
||||
#include <signal.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
@@ -18,146 +21,425 @@
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
#if !defined(LUA_PROGNAME)
|
||||
#define LUA_PROGNAME "lua"
|
||||
#endif
|
||||
|
||||
#if !defined(LUA_INIT_VAR)
|
||||
#define LUA_INIT_VAR "LUA_INIT"
|
||||
#endif
|
||||
|
||||
#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX
|
||||
|
||||
|
||||
static lua_State *globalL = NULL;
|
||||
|
||||
static const char *progname = LUA_PROGNAME;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Hook set by signal function to stop the interpreter.
|
||||
*/
|
||||
static void lstop (lua_State *L, lua_Debug *ar) {
|
||||
(void)ar; /* unused arg. */
|
||||
lua_sethook(L, NULL, 0, 0);
|
||||
lua_sethook(L, NULL, 0, 0); /* reset hook */
|
||||
luaL_error(L, "interrupted!");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Function to be called at a C signal. Because a C signal cannot
|
||||
** just change a Lua state (as there is no proper synchronization),
|
||||
** this function only sets a hook that, when called, will stop the
|
||||
** interpreter.
|
||||
*/
|
||||
static void laction (int i) {
|
||||
signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
|
||||
terminate process (default action) */
|
||||
signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
|
||||
lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
|
||||
}
|
||||
|
||||
|
||||
static void print_usage (void) {
|
||||
fprintf(stderr,
|
||||
"usage: %s [options] [script [args]].\n"
|
||||
static void print_usage (const char *badoption) {
|
||||
lua_writestringerror("%s: ", progname);
|
||||
if (badoption[1] == 'e' || badoption[1] == 'l')
|
||||
lua_writestringerror("'%s' needs argument\n", badoption);
|
||||
else
|
||||
lua_writestringerror("unrecognized option '%s'\n", badoption);
|
||||
lua_writestringerror(
|
||||
"usage: %s [options] [script [args]]\n"
|
||||
"Available options are:\n"
|
||||
" - execute stdin as a file\n"
|
||||
" -e stat execute string " LUA_QL("stat") "\n"
|
||||
" -i enter interactive mode after executing " LUA_QL("script") "\n"
|
||||
" -l name require library " LUA_QL("name") "\n"
|
||||
" -e stat execute string 'stat'\n"
|
||||
" -i enter interactive mode after executing 'script'\n"
|
||||
" -l name require library 'name' into global 'name'\n"
|
||||
" -v show version information\n"
|
||||
" -- stop handling options\n" ,
|
||||
" -E ignore environment variables\n"
|
||||
" -W turn warnings on\n"
|
||||
" -- stop handling options\n"
|
||||
" - stop handling options and execute stdin\n"
|
||||
,
|
||||
progname);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prints an error message, adding the program name in front of it
|
||||
** (if present)
|
||||
*/
|
||||
static void l_message (const char *pname, const char *msg) {
|
||||
if (pname) fprintf(stderr, "%s: ", pname);
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
fflush(stderr);
|
||||
if (pname) lua_writestringerror("%s: ", pname);
|
||||
lua_writestringerror("%s\n", msg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'status' is not OK and, if so, prints the error
|
||||
** message on the top of the stack. It assumes that the error object
|
||||
** is a string, as it was either generated by Lua or by 'msghandler'.
|
||||
*/
|
||||
static int report (lua_State *L, int status) {
|
||||
if (status && !lua_isnil(L, -1)) {
|
||||
if (status != LUA_OK) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
if (msg == NULL) msg = "(error object is not a string)";
|
||||
l_message(progname, msg);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 1); /* remove message */
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static int traceback (lua_State *L) {
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return 1;
|
||||
/*
|
||||
** Message handler used to run all chunks
|
||||
*/
|
||||
static int msghandler (lua_State *L) {
|
||||
const char *msg = lua_tostring(L, 1);
|
||||
if (msg == NULL) { /* is error object not a string? */
|
||||
if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
|
||||
lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
|
||||
return 1; /* that is the message */
|
||||
else
|
||||
msg = lua_pushfstring(L, "(error object is a %s value)",
|
||||
luaL_typename(L, 1));
|
||||
}
|
||||
lua_getfield(L, -1, "traceback");
|
||||
if (!lua_isfunction(L, -1)) {
|
||||
lua_pop(L, 2);
|
||||
return 1;
|
||||
}
|
||||
lua_pushvalue(L, 1); /* pass error message */
|
||||
lua_pushinteger(L, 2); /* skip this function and traceback */
|
||||
lua_call(L, 2, 1); /* call debug.traceback */
|
||||
return 1;
|
||||
luaL_traceback(L, L, msg, 1); /* append a standard traceback */
|
||||
return 1; /* return the traceback */
|
||||
}
|
||||
|
||||
|
||||
static int docall (lua_State *L, int narg, int clear) {
|
||||
/*
|
||||
** Interface to 'lua_pcall', which sets appropriate message function
|
||||
** and C-signal handler. Used to run all chunks.
|
||||
*/
|
||||
static int docall (lua_State *L, int narg, int nres) {
|
||||
int status;
|
||||
int base = lua_gettop(L) - narg; /* function index */
|
||||
lua_pushcfunction(L, traceback); /* push traceback function */
|
||||
lua_insert(L, base); /* put it under chunk and args */
|
||||
signal(SIGINT, laction);
|
||||
status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
|
||||
signal(SIGINT, SIG_DFL);
|
||||
lua_remove(L, base); /* remove traceback function */
|
||||
lua_pushcfunction(L, msghandler); /* push message handler */
|
||||
lua_insert(L, base); /* put it under function and args */
|
||||
globalL = L; /* to be available to 'laction' */
|
||||
signal(SIGINT, laction); /* set C-signal handler */
|
||||
status = lua_pcall(L, narg, nres, base);
|
||||
signal(SIGINT, SIG_DFL); /* reset C-signal handler */
|
||||
lua_remove(L, base); /* remove message handler from the stack */
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void print_version (void) {
|
||||
l_message(NULL, LUA_VERSION " " LUA_COPYRIGHT);
|
||||
lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT));
|
||||
lua_writeline();
|
||||
}
|
||||
|
||||
|
||||
static int getargs (lua_State *L, int argc, char **argv, int n) {
|
||||
int narg = argc - (n + 1); /* number of arguments to the script */
|
||||
int i;
|
||||
luaL_checkstack(L, narg + 3, "too many arguments to script");
|
||||
for (i=n+1; i < argc; i++)
|
||||
/*
|
||||
** Create the 'arg' table, which stores all arguments from the
|
||||
** command line ('argv'). It should be aligned so that, at index 0,
|
||||
** it has 'argv[script]', which is the script name. The arguments
|
||||
** to the script (everything after 'script') go to positive indices;
|
||||
** other arguments (before the script name) go to negative indices.
|
||||
** If there is no script name, assume interpreter's name as base.
|
||||
*/
|
||||
static void createargtable (lua_State *L, char **argv, int argc, int script) {
|
||||
int i, narg;
|
||||
if (script == argc) script = 0; /* no script name? */
|
||||
narg = argc - (script + 1); /* number of positive indices */
|
||||
lua_createtable(L, narg, script + 1);
|
||||
for (i = 0; i < argc; i++) {
|
||||
lua_pushstring(L, argv[i]);
|
||||
lua_newtable(L);
|
||||
for (i=0; i < argc; i++) {
|
||||
lua_pushstring(L, argv[i]);
|
||||
lua_rawseti(L, -2, i - n);
|
||||
lua_rawseti(L, -2, i - script);
|
||||
}
|
||||
return narg;
|
||||
lua_setglobal(L, "arg");
|
||||
}
|
||||
|
||||
|
||||
static int dochunk (lua_State *L, int status) {
|
||||
if (status == LUA_OK) status = docall(L, 0, 0);
|
||||
return report(L, status);
|
||||
}
|
||||
|
||||
|
||||
static int dofile (lua_State *L, const char *name) {
|
||||
int status = luaL_loadfile(L, name) || docall(L, 0, 1);
|
||||
return report(L, status);
|
||||
return dochunk(L, luaL_loadfile(L, name));
|
||||
}
|
||||
|
||||
|
||||
static int dostring (lua_State *L, const char *s, const char *name) {
|
||||
int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
|
||||
return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls 'require(name)' and stores the result in a global variable
|
||||
** with the given name.
|
||||
*/
|
||||
static int dolibrary (lua_State *L, const char *name) {
|
||||
int status;
|
||||
lua_getglobal(L, "require");
|
||||
lua_pushstring(L, name);
|
||||
status = docall(L, 1, 1); /* call 'require(name)' */
|
||||
if (status == LUA_OK)
|
||||
lua_setglobal(L, name); /* global[name] = require return */
|
||||
return report(L, status);
|
||||
}
|
||||
|
||||
|
||||
static int dolibrary (lua_State *L, const char *name) {
|
||||
lua_getglobal(L, "require");
|
||||
lua_pushstring(L, name);
|
||||
return report(L, lua_pcall(L, 1, 0, 0));
|
||||
/*
|
||||
** Push on the stack the contents of table 'arg' from 1 to #arg
|
||||
*/
|
||||
static int pushargs (lua_State *L) {
|
||||
int i, n;
|
||||
if (lua_getglobal(L, "arg") != LUA_TTABLE)
|
||||
luaL_error(L, "'arg' is not a table");
|
||||
n = (int)luaL_len(L, -1);
|
||||
luaL_checkstack(L, n + 3, "too many arguments to script");
|
||||
for (i = 1; i <= n; i++)
|
||||
lua_rawgeti(L, -i, i);
|
||||
lua_remove(L, -i); /* remove table from the stack */
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static int handle_script (lua_State *L, char **argv) {
|
||||
int status;
|
||||
const char *fname = argv[0];
|
||||
if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
|
||||
fname = NULL; /* stdin */
|
||||
status = luaL_loadfile(L, fname);
|
||||
if (status == LUA_OK) {
|
||||
int n = pushargs(L); /* push arguments to script */
|
||||
status = docall(L, n, LUA_MULTRET);
|
||||
}
|
||||
return report(L, status);
|
||||
}
|
||||
|
||||
|
||||
/* bits of various argument indicators in 'args' */
|
||||
#define has_error 1 /* bad option */
|
||||
#define has_i 2 /* -i */
|
||||
#define has_v 4 /* -v */
|
||||
#define has_e 8 /* -e */
|
||||
#define has_E 16 /* -E */
|
||||
|
||||
|
||||
/*
|
||||
** Traverses all arguments from 'argv', returning a mask with those
|
||||
** needed before running any Lua code (or an error code if it finds
|
||||
** any invalid argument). 'first' returns the first not-handled argument
|
||||
** (either the script name or a bad argument in case of error).
|
||||
*/
|
||||
static int collectargs (char **argv, int *first) {
|
||||
int args = 0;
|
||||
int i;
|
||||
for (i = 1; argv[i] != NULL; i++) {
|
||||
*first = i;
|
||||
if (argv[i][0] != '-') /* not an option? */
|
||||
return args; /* stop handling options */
|
||||
switch (argv[i][1]) { /* else check option */
|
||||
case '-': /* '--' */
|
||||
if (argv[i][2] != '\0') /* extra characters after '--'? */
|
||||
return has_error; /* invalid option */
|
||||
*first = i + 1;
|
||||
return args;
|
||||
case '\0': /* '-' */
|
||||
return args; /* script "name" is '-' */
|
||||
case 'E':
|
||||
if (argv[i][2] != '\0') /* extra characters? */
|
||||
return has_error; /* invalid option */
|
||||
args |= has_E;
|
||||
break;
|
||||
case 'W':
|
||||
if (argv[i][2] != '\0') /* extra characters? */
|
||||
return has_error; /* invalid option */
|
||||
break;
|
||||
case 'i':
|
||||
args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */
|
||||
case 'v':
|
||||
if (argv[i][2] != '\0') /* extra characters? */
|
||||
return has_error; /* invalid option */
|
||||
args |= has_v;
|
||||
break;
|
||||
case 'e':
|
||||
args |= has_e; /* FALLTHROUGH */
|
||||
case 'l': /* both options need an argument */
|
||||
if (argv[i][2] == '\0') { /* no concatenated argument? */
|
||||
i++; /* try next 'argv' */
|
||||
if (argv[i] == NULL || argv[i][0] == '-')
|
||||
return has_error; /* no next argument or it is another option */
|
||||
}
|
||||
break;
|
||||
default: /* invalid option */
|
||||
return has_error;
|
||||
}
|
||||
}
|
||||
*first = i; /* no script name */
|
||||
return args;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Processes options 'e' and 'l', which involve running Lua code, and
|
||||
** 'W', which also affects the state.
|
||||
** Returns 0 if some code raises an error.
|
||||
*/
|
||||
static int runargs (lua_State *L, char **argv, int n) {
|
||||
int i;
|
||||
for (i = 1; i < n; i++) {
|
||||
int option = argv[i][1];
|
||||
lua_assert(argv[i][0] == '-'); /* already checked */
|
||||
switch (option) {
|
||||
case 'e': case 'l': {
|
||||
int status;
|
||||
const char *extra = argv[i] + 2; /* both options need an argument */
|
||||
if (*extra == '\0') extra = argv[++i];
|
||||
lua_assert(extra != NULL);
|
||||
status = (option == 'e')
|
||||
? dostring(L, extra, "=(command line)")
|
||||
: dolibrary(L, extra);
|
||||
if (status != LUA_OK) return 0;
|
||||
break;
|
||||
}
|
||||
case 'W':
|
||||
lua_warning(L, "@on", 0); /* warnings on */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int handle_luainit (lua_State *L) {
|
||||
const char *name = "=" LUA_INITVARVERSION;
|
||||
const char *init = getenv(name + 1);
|
||||
if (init == NULL) {
|
||||
name = "=" LUA_INIT_VAR;
|
||||
init = getenv(name + 1); /* try alternative name */
|
||||
}
|
||||
if (init == NULL) return LUA_OK;
|
||||
else if (init[0] == '@')
|
||||
return dofile(L, init+1);
|
||||
else
|
||||
return dostring(L, init, name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Read-Eval-Print Loop (REPL)
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#if !defined(LUA_PROMPT)
|
||||
#define LUA_PROMPT "> "
|
||||
#define LUA_PROMPT2 ">> "
|
||||
#endif
|
||||
|
||||
#if !defined(LUA_MAXINPUT)
|
||||
#define LUA_MAXINPUT 512
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** lua_stdin_is_tty detects whether the standard input is a 'tty' (that
|
||||
** is, whether we're running lua interactively).
|
||||
*/
|
||||
#if !defined(lua_stdin_is_tty) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
#include <unistd.h>
|
||||
#define lua_stdin_is_tty() isatty(0)
|
||||
|
||||
#elif defined(LUA_USE_WINDOWS) /* }{ */
|
||||
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
|
||||
#define lua_stdin_is_tty() _isatty(_fileno(stdin))
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definition */
|
||||
#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** lua_readline defines how to show a prompt and then read a line from
|
||||
** the standard input.
|
||||
** lua_saveline defines how to "save" a read line in a "history".
|
||||
** lua_freeline defines how to free a line read by lua_readline.
|
||||
*/
|
||||
#if !defined(lua_readline) /* { */
|
||||
|
||||
#if defined(LUA_USE_READLINE) /* { */
|
||||
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
|
||||
#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
|
||||
#define lua_saveline(L,line) ((void)L, add_history(line))
|
||||
#define lua_freeline(L,b) ((void)L, free(b))
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#define lua_initreadline(L) ((void)L)
|
||||
#define lua_readline(L,b,p) \
|
||||
((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
|
||||
fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
|
||||
#define lua_saveline(L,line) { (void)L; (void)line; }
|
||||
#define lua_freeline(L,b) { (void)L; (void)b; }
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** Returns the string to be used as a prompt by the interpreter.
|
||||
*/
|
||||
static const char *get_prompt (lua_State *L, int firstline) {
|
||||
const char *p;
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
|
||||
lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2");
|
||||
p = lua_tostring(L, -1);
|
||||
if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
|
||||
lua_pop(L, 1); /* remove global */
|
||||
return p;
|
||||
}
|
||||
|
||||
/* mark in error messages for incomplete statements */
|
||||
#define EOFMARK "<eof>"
|
||||
#define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'status' signals a syntax error and the error
|
||||
** message at the top of the stack ends with the above mark for
|
||||
** incomplete statements.
|
||||
*/
|
||||
static int incomplete (lua_State *L, int status) {
|
||||
if (status == LUA_ERRSYNTAX) {
|
||||
size_t lmsg;
|
||||
const char *msg = lua_tolstring(L, -1, &lmsg);
|
||||
const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
|
||||
if (strstr(msg, LUA_QL("<eof>")) == tp) {
|
||||
if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
|
||||
lua_pop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
@@ -166,202 +448,187 @@ static int incomplete (lua_State *L, int status) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prompt the user, read a line, and push it into the Lua stack.
|
||||
*/
|
||||
static int pushline (lua_State *L, int firstline) {
|
||||
char buffer[LUA_MAXINPUT];
|
||||
char *b = buffer;
|
||||
size_t l;
|
||||
const char *prmt = get_prompt(L, firstline);
|
||||
if (lua_readline(L, b, prmt) == 0)
|
||||
return 0; /* no input */
|
||||
int readstatus = lua_readline(L, b, prmt);
|
||||
if (readstatus == 0)
|
||||
return 0; /* no input (prompt will be popped by caller) */
|
||||
lua_pop(L, 1); /* remove prompt */
|
||||
l = strlen(b);
|
||||
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
|
||||
b[l-1] = '\0'; /* remove it */
|
||||
if (firstline && b[0] == '=') /* first line starts with `=' ? */
|
||||
lua_pushfstring(L, "return %s", b+1); /* change it to `return' */
|
||||
b[--l] = '\0'; /* remove it */
|
||||
if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */
|
||||
lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
|
||||
else
|
||||
lua_pushstring(L, b);
|
||||
lua_pushlstring(L, b, l);
|
||||
lua_freeline(L, b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to compile line on the stack as 'return <line>;'; on return, stack
|
||||
** has either compiled chunk or original line (if compilation failed).
|
||||
*/
|
||||
static int addreturn (lua_State *L) {
|
||||
const char *line = lua_tostring(L, -1); /* original line */
|
||||
const char *retline = lua_pushfstring(L, "return %s;", line);
|
||||
int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
|
||||
if (status == LUA_OK) {
|
||||
lua_remove(L, -2); /* remove modified line */
|
||||
if (line[0] != '\0') /* non empty? */
|
||||
lua_saveline(L, line); /* keep history */
|
||||
}
|
||||
else
|
||||
lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read multiple lines until a complete Lua statement
|
||||
*/
|
||||
static int multiline (lua_State *L) {
|
||||
for (;;) { /* repeat until gets a complete statement */
|
||||
size_t len;
|
||||
const char *line = lua_tolstring(L, 1, &len); /* get what it has */
|
||||
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
|
||||
if (!incomplete(L, status) || !pushline(L, 0)) {
|
||||
lua_saveline(L, line); /* keep history */
|
||||
return status; /* cannot or should not try to add continuation line */
|
||||
}
|
||||
lua_pushliteral(L, "\n"); /* add newline... */
|
||||
lua_insert(L, -2); /* ...between the two lines */
|
||||
lua_concat(L, 3); /* join them */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read a line and try to load (compile) it first as an expression (by
|
||||
** adding "return " in front of it) and second as a statement. Return
|
||||
** the final status of load/call with the resulting function (if any)
|
||||
** in the top of the stack.
|
||||
*/
|
||||
static int loadline (lua_State *L) {
|
||||
int status;
|
||||
lua_settop(L, 0);
|
||||
if (!pushline(L, 1))
|
||||
return -1; /* no input */
|
||||
for (;;) { /* repeat until gets a complete line */
|
||||
status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
|
||||
if (!incomplete(L, status)) break; /* cannot try to add lines? */
|
||||
if (!pushline(L, 0)) /* no more input? */
|
||||
return -1;
|
||||
lua_pushliteral(L, "\n"); /* add a new line... */
|
||||
lua_insert(L, -2); /* ...between the two lines */
|
||||
lua_concat(L, 3); /* join them */
|
||||
}
|
||||
lua_saveline(L, 1);
|
||||
lua_remove(L, 1); /* remove line */
|
||||
if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */
|
||||
status = multiline(L); /* try as command, maybe with continuation lines */
|
||||
lua_remove(L, 1); /* remove line from the stack */
|
||||
lua_assert(lua_gettop(L) == 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void dotty (lua_State *L) {
|
||||
/*
|
||||
** Prints (calling the Lua 'print' function) any values on the stack
|
||||
*/
|
||||
static void l_print (lua_State *L) {
|
||||
int n = lua_gettop(L);
|
||||
if (n > 0) { /* any result to be printed? */
|
||||
luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
|
||||
lua_getglobal(L, "print");
|
||||
lua_insert(L, 1);
|
||||
if (lua_pcall(L, n, 0, 0) != LUA_OK)
|
||||
l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)",
|
||||
lua_tostring(L, -1)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and
|
||||
** print any results.
|
||||
*/
|
||||
static void doREPL (lua_State *L) {
|
||||
int status;
|
||||
const char *oldprogname = progname;
|
||||
progname = NULL;
|
||||
print_version();
|
||||
progname = NULL; /* no 'progname' on errors in interactive mode */
|
||||
lua_initreadline(L);
|
||||
while ((status = loadline(L)) != -1) {
|
||||
if (status == 0) status = docall(L, 0, 0);
|
||||
report(L, status);
|
||||
if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */
|
||||
lua_getglobal(L, "print");
|
||||
lua_insert(L, 1);
|
||||
if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
|
||||
l_message(progname, lua_pushfstring(L,
|
||||
"error calling " LUA_QL("print") " (%s)",
|
||||
lua_tostring(L, -1)));
|
||||
}
|
||||
if (status == LUA_OK)
|
||||
status = docall(L, 0, LUA_MULTRET);
|
||||
if (status == LUA_OK) l_print(L);
|
||||
else report(L, status);
|
||||
}
|
||||
lua_settop(L, 0); /* clear stack */
|
||||
fputs("\n", stdout);
|
||||
fflush(stdout);
|
||||
lua_writeline();
|
||||
progname = oldprogname;
|
||||
}
|
||||
|
||||
|
||||
#define clearinteractive(i) (*i &= 2)
|
||||
|
||||
static int handle_argv (lua_State *L, int argc, char **argv, int *interactive) {
|
||||
if (argv[1] == NULL) { /* no arguments? */
|
||||
*interactive = 0;
|
||||
if (lua_stdin_is_tty())
|
||||
dotty(L);
|
||||
else
|
||||
dofile(L, NULL); /* executes stdin as a file */
|
||||
}
|
||||
else { /* other arguments; loop over them */
|
||||
int i;
|
||||
for (i = 1; argv[i] != NULL; i++) {
|
||||
if (argv[i][0] != '-') break; /* not an option? */
|
||||
switch (argv[i][1]) { /* option */
|
||||
case '-': { /* `--' */
|
||||
if (argv[i][2] != '\0') {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
i++; /* skip this argument */
|
||||
goto endloop; /* stop handling arguments */
|
||||
}
|
||||
case '\0': {
|
||||
clearinteractive(interactive);
|
||||
dofile(L, NULL); /* executes stdin as a file */
|
||||
break;
|
||||
}
|
||||
case 'i': {
|
||||
*interactive = 2; /* force interactive mode after arguments */
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
clearinteractive(interactive);
|
||||
print_version();
|
||||
break;
|
||||
}
|
||||
case 'e': {
|
||||
const char *chunk = argv[i] + 2;
|
||||
clearinteractive(interactive);
|
||||
if (*chunk == '\0') chunk = argv[++i];
|
||||
if (chunk == NULL) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
if (dostring(L, chunk, "=(command line)") != 0)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
const char *filename = argv[i] + 2;
|
||||
if (*filename == '\0') filename = argv[++i];
|
||||
if (filename == NULL) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
if (dolibrary(L, filename))
|
||||
return 1; /* stop if file fails */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
clearinteractive(interactive);
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} endloop:
|
||||
if (argv[i] != NULL) {
|
||||
int status;
|
||||
const char *filename = argv[i];
|
||||
int narg = getargs(L, argc, argv, i); /* collect arguments */
|
||||
lua_setglobal(L, "arg");
|
||||
clearinteractive(interactive);
|
||||
status = luaL_loadfile(L, filename);
|
||||
lua_insert(L, -(narg+1));
|
||||
if (status == 0)
|
||||
status = docall(L, narg, 0);
|
||||
else
|
||||
lua_pop(L, narg);
|
||||
return report(L, status);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int handle_luainit (lua_State *L) {
|
||||
const char *init = getenv("LUA_INIT");
|
||||
if (init == NULL) return 0; /* status OK */
|
||||
else if (init[0] == '@')
|
||||
return dofile(L, init+1);
|
||||
else
|
||||
return dostring(L, init, "=LUA_INIT");
|
||||
}
|
||||
|
||||
|
||||
struct Smain {
|
||||
int argc;
|
||||
char **argv;
|
||||
int status;
|
||||
};
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** Main body of stand-alone interpreter (to be called in protected mode).
|
||||
** Reads the options and handles them all.
|
||||
*/
|
||||
static int pmain (lua_State *L) {
|
||||
struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
|
||||
int status;
|
||||
int interactive = 1;
|
||||
if (s->argv[0] && s->argv[0][0]) progname = s->argv[0];
|
||||
globalL = L;
|
||||
luaL_openlibs(L); /* open libraries */
|
||||
status = handle_luainit(L);
|
||||
if (status == 0) {
|
||||
status = handle_argv(L, s->argc, s->argv, &interactive);
|
||||
if (status == 0 && interactive) dotty(L);
|
||||
int argc = (int)lua_tointeger(L, 1);
|
||||
char **argv = (char **)lua_touserdata(L, 2);
|
||||
int script;
|
||||
int args = collectargs(argv, &script);
|
||||
luaL_checkversion(L); /* check that interpreter has correct version */
|
||||
if (argv[0] && argv[0][0]) progname = argv[0];
|
||||
if (args == has_error) { /* bad arg? */
|
||||
print_usage(argv[script]); /* 'script' has index of bad arg. */
|
||||
return 0;
|
||||
}
|
||||
s->status = status;
|
||||
return 0;
|
||||
if (args & has_v) /* option '-v'? */
|
||||
print_version();
|
||||
if (args & has_E) { /* option '-E'? */
|
||||
lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
|
||||
}
|
||||
luaL_openlibs(L); /* open standard libraries */
|
||||
createargtable(L, argv, argc, script); /* create table 'arg' */
|
||||
lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */
|
||||
if (!(args & has_E)) { /* no option '-E'? */
|
||||
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
|
||||
return 0; /* error running LUA_INIT */
|
||||
}
|
||||
if (!runargs(L, argv, script)) /* execute arguments -e and -l */
|
||||
return 0; /* something failed */
|
||||
if (script < argc && /* execute main script (if there is one) */
|
||||
handle_script(L, argv + script) != LUA_OK)
|
||||
return 0;
|
||||
if (args & has_i) /* -i option? */
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */
|
||||
if (lua_stdin_is_tty()) { /* running in interactive mode? */
|
||||
print_version();
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
}
|
||||
else dofile(L, NULL); /* executes stdin as a file */
|
||||
}
|
||||
lua_pushboolean(L, 1); /* signal no errors */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
int status;
|
||||
struct Smain s;
|
||||
lua_State *L = lua_open(); /* create state */
|
||||
int status, result;
|
||||
lua_State *L = luaL_newstate(); /* create state */
|
||||
if (L == NULL) {
|
||||
l_message(argv[0], "cannot create state: not enough memory");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
s.argc = argc;
|
||||
s.argv = argv;
|
||||
status = lua_cpcall(L, &pmain, &s);
|
||||
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
||||
lua_pushinteger(L, argc); /* 1st argument */
|
||||
lua_pushlightuserdata(L, argv); /* 2nd argument */
|
||||
status = lua_pcall(L, 2, 1, 0); /* do the call */
|
||||
result = lua_toboolean(L, -1); /* get result */
|
||||
report(L, status);
|
||||
lua_close(L);
|
||||
return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
** $Id: lua.h,v 1.211 2005/08/12 13:34:15 roberto Exp roberto $
|
||||
** Lua - An Extensible Extension Language
|
||||
** $Id: lua.h $
|
||||
** Lua - A Scripting Language
|
||||
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
|
||||
** See Copyright Notice at the end of this file
|
||||
*/
|
||||
@@ -16,29 +16,37 @@
|
||||
#include "luaconf.h"
|
||||
|
||||
|
||||
#define LUA_VERSION "Lua 5.1"
|
||||
#define LUA_VERSION_NUM 501
|
||||
#define LUA_COPYRIGHT "Copyright (C) 1994-2005 Lua.org, PUC-Rio"
|
||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
|
||||
#define LUA_VERSION_MAJOR "5"
|
||||
#define LUA_VERSION_MINOR "4"
|
||||
#define LUA_VERSION_RELEASE "0"
|
||||
|
||||
#define LUA_VERSION_NUM 504
|
||||
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
|
||||
|
||||
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
||||
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2019 Lua.org, PUC-Rio"
|
||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
|
||||
|
||||
|
||||
/* mark for precompiled code (`<esc>Lua') */
|
||||
#define LUA_SIGNATURE "\033Lua"
|
||||
/* mark for precompiled code ('<esc>Lua') */
|
||||
#define LUA_SIGNATURE "\x1bLua"
|
||||
|
||||
/* option for multiple returns in `lua_pcall' and `lua_call' */
|
||||
/* option for multiple returns in 'lua_pcall' and 'lua_call' */
|
||||
#define LUA_MULTRET (-1)
|
||||
|
||||
|
||||
/*
|
||||
** pseudo-indices
|
||||
** Pseudo-indices
|
||||
** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty
|
||||
** space after that to help overflow detection)
|
||||
*/
|
||||
#define LUA_REGISTRYINDEX (-10000)
|
||||
#define LUA_ENVIRONINDEX (-10001)
|
||||
#define LUA_GLOBALSINDEX (-10002)
|
||||
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
|
||||
#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000)
|
||||
#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
|
||||
|
||||
|
||||
/* thread status; 0 is OK */
|
||||
/* thread status */
|
||||
#define LUA_OK 0
|
||||
#define LUA_YIELD 1
|
||||
#define LUA_ERRRUN 2
|
||||
#define LUA_ERRSYNTAX 3
|
||||
@@ -48,22 +56,6 @@
|
||||
|
||||
typedef struct lua_State lua_State;
|
||||
|
||||
typedef int (*lua_CFunction) (lua_State *L);
|
||||
|
||||
|
||||
/*
|
||||
** functions that read/write blocks when loading/dumping Lua chunks
|
||||
*/
|
||||
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
|
||||
|
||||
typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
|
||||
|
||||
|
||||
/*
|
||||
** prototype for memory-allocation functions
|
||||
*/
|
||||
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
|
||||
|
||||
/*
|
||||
** basic types
|
||||
@@ -80,12 +72,67 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
#define LUA_TUSERDATA 7
|
||||
#define LUA_TTHREAD 8
|
||||
|
||||
#define LUA_NUMTAGS 9
|
||||
|
||||
|
||||
|
||||
/* minimum Lua stack available to a C function */
|
||||
#define LUA_MINSTACK 20
|
||||
|
||||
|
||||
/* predefined values in the registry */
|
||||
#define LUA_RIDX_MAINTHREAD 1
|
||||
#define LUA_RIDX_GLOBALS 2
|
||||
#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
|
||||
|
||||
|
||||
/* type of numbers in Lua */
|
||||
typedef LUA_NUMBER lua_Number;
|
||||
|
||||
|
||||
/* type for integer functions */
|
||||
typedef LUA_INTEGER lua_Integer;
|
||||
|
||||
/* unsigned integer type */
|
||||
typedef LUA_UNSIGNED lua_Unsigned;
|
||||
|
||||
/* type for continuation-function contexts */
|
||||
typedef LUA_KCONTEXT lua_KContext;
|
||||
|
||||
|
||||
/*
|
||||
** Type for C functions registered with Lua
|
||||
*/
|
||||
typedef int (*lua_CFunction) (lua_State *L);
|
||||
|
||||
/*
|
||||
** Type for continuation functions
|
||||
*/
|
||||
typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);
|
||||
|
||||
|
||||
/*
|
||||
** Type for functions that read/write blocks when loading/dumping Lua chunks
|
||||
*/
|
||||
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
|
||||
|
||||
typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);
|
||||
|
||||
|
||||
/*
|
||||
** Type for memory-allocation functions
|
||||
*/
|
||||
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
|
||||
|
||||
/*
|
||||
** Type for warning functions
|
||||
*/
|
||||
typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** generic extra include file
|
||||
*/
|
||||
@@ -94,13 +141,10 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
#endif
|
||||
|
||||
|
||||
/* type of numbers in Lua */
|
||||
typedef LUA_NUMBER lua_Number;
|
||||
|
||||
|
||||
/* type for integer functions */
|
||||
typedef LUA_INTEGER lua_Integer;
|
||||
|
||||
/*
|
||||
** RCS ident string
|
||||
*/
|
||||
extern const char lua_ident[];
|
||||
|
||||
|
||||
/*
|
||||
@@ -109,20 +153,24 @@ typedef LUA_INTEGER lua_Integer;
|
||||
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
|
||||
LUA_API void (lua_close) (lua_State *L);
|
||||
LUA_API lua_State *(lua_newthread) (lua_State *L);
|
||||
LUA_API int (lua_resetthread) (lua_State *L);
|
||||
|
||||
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
|
||||
|
||||
|
||||
LUA_API lua_Number (lua_version) (lua_State *L);
|
||||
|
||||
|
||||
/*
|
||||
** basic stack manipulation
|
||||
*/
|
||||
LUA_API int (lua_absindex) (lua_State *L, int idx);
|
||||
LUA_API int (lua_gettop) (lua_State *L);
|
||||
LUA_API void (lua_settop) (lua_State *L, int idx);
|
||||
LUA_API void (lua_pushvalue) (lua_State *L, int idx);
|
||||
LUA_API void (lua_remove) (lua_State *L, int idx);
|
||||
LUA_API void (lua_insert) (lua_State *L, int idx);
|
||||
LUA_API void (lua_replace) (lua_State *L, int idx);
|
||||
LUA_API int (lua_checkstack) (lua_State *L, int sz);
|
||||
LUA_API void (lua_rotate) (lua_State *L, int idx, int n);
|
||||
LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx);
|
||||
LUA_API int (lua_checkstack) (lua_State *L, int n);
|
||||
|
||||
LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
|
||||
|
||||
@@ -134,33 +182,59 @@ LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
|
||||
LUA_API int (lua_isnumber) (lua_State *L, int idx);
|
||||
LUA_API int (lua_isstring) (lua_State *L, int idx);
|
||||
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
|
||||
LUA_API int (lua_isinteger) (lua_State *L, int idx);
|
||||
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
|
||||
LUA_API int (lua_type) (lua_State *L, int idx);
|
||||
LUA_API const char *(lua_typename) (lua_State *L, int tp);
|
||||
|
||||
LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
|
||||
LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
|
||||
LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);
|
||||
|
||||
LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
|
||||
LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
|
||||
LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
|
||||
LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum);
|
||||
LUA_API int (lua_toboolean) (lua_State *L, int idx);
|
||||
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
|
||||
LUA_API size_t (lua_objlen) (lua_State *L, int idx);
|
||||
LUA_API lua_Unsigned (lua_rawlen) (lua_State *L, int idx);
|
||||
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
|
||||
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
|
||||
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
|
||||
LUA_API const void *(lua_topointer) (lua_State *L, int idx);
|
||||
|
||||
|
||||
/*
|
||||
** Comparison and arithmetic functions
|
||||
*/
|
||||
|
||||
#define LUA_OPADD 0 /* ORDER TM, ORDER OP */
|
||||
#define LUA_OPSUB 1
|
||||
#define LUA_OPMUL 2
|
||||
#define LUA_OPMOD 3
|
||||
#define LUA_OPPOW 4
|
||||
#define LUA_OPDIV 5
|
||||
#define LUA_OPIDIV 6
|
||||
#define LUA_OPBAND 7
|
||||
#define LUA_OPBOR 8
|
||||
#define LUA_OPBXOR 9
|
||||
#define LUA_OPSHL 10
|
||||
#define LUA_OPSHR 11
|
||||
#define LUA_OPUNM 12
|
||||
#define LUA_OPBNOT 13
|
||||
|
||||
LUA_API void (lua_arith) (lua_State *L, int op);
|
||||
|
||||
#define LUA_OPEQ 0
|
||||
#define LUA_OPLT 1
|
||||
#define LUA_OPLE 2
|
||||
|
||||
LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
|
||||
LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op);
|
||||
|
||||
|
||||
/*
|
||||
** push functions (C -> stack)
|
||||
*/
|
||||
LUA_API void (lua_pushnil) (lua_State *L);
|
||||
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
|
||||
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
|
||||
LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
|
||||
LUA_API void (lua_pushstring) (lua_State *L, const char *s);
|
||||
LUA_API void (lua_pushnil) (lua_State *L);
|
||||
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
|
||||
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
|
||||
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
|
||||
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
|
||||
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
|
||||
va_list argp);
|
||||
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
|
||||
@@ -173,45 +247,70 @@ LUA_API int (lua_pushthread) (lua_State *L);
|
||||
/*
|
||||
** get functions (Lua -> stack)
|
||||
*/
|
||||
LUA_API void (lua_gettable) (lua_State *L, int idx);
|
||||
LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
|
||||
LUA_API void (lua_rawget) (lua_State *L, int idx);
|
||||
LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
|
||||
LUA_API int (lua_getglobal) (lua_State *L, const char *name);
|
||||
LUA_API int (lua_gettable) (lua_State *L, int idx);
|
||||
LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
|
||||
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
|
||||
LUA_API int (lua_rawget) (lua_State *L, int idx);
|
||||
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
|
||||
LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);
|
||||
|
||||
LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
|
||||
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
|
||||
LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue);
|
||||
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
|
||||
LUA_API void (lua_getfenv) (lua_State *L, int idx);
|
||||
LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n);
|
||||
|
||||
|
||||
/*
|
||||
** set functions (stack -> Lua)
|
||||
*/
|
||||
LUA_API void (lua_setglobal) (lua_State *L, const char *name);
|
||||
LUA_API void (lua_settable) (lua_State *L, int idx);
|
||||
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
|
||||
LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
|
||||
LUA_API void (lua_rawset) (lua_State *L, int idx);
|
||||
LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
|
||||
LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
|
||||
LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p);
|
||||
LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
|
||||
LUA_API int (lua_setfenv) (lua_State *L, int idx);
|
||||
LUA_API int (lua_setiuservalue) (lua_State *L, int idx, int n);
|
||||
|
||||
|
||||
/*
|
||||
** `load' and `call' functions (load and run Lua code)
|
||||
** 'load' and 'call' functions (load and run Lua code)
|
||||
*/
|
||||
LUA_API void (lua_call) (lua_State *L, int nargs, int nresults);
|
||||
LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
|
||||
LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);
|
||||
LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
|
||||
const char *chunkname);
|
||||
LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults,
|
||||
lua_KContext ctx, lua_KFunction k);
|
||||
#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL)
|
||||
|
||||
LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
|
||||
LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc,
|
||||
lua_KContext ctx, lua_KFunction k);
|
||||
#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
|
||||
|
||||
LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
|
||||
const char *chunkname, const char *mode);
|
||||
|
||||
LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip);
|
||||
|
||||
|
||||
/*
|
||||
** coroutine functions
|
||||
*/
|
||||
LUA_API int (lua_yield) (lua_State *L, int nresults);
|
||||
LUA_API int (lua_resume) (lua_State *L, int narg);
|
||||
LUA_API int (lua_status) (lua_State *L);
|
||||
LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx,
|
||||
lua_KFunction k);
|
||||
LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg,
|
||||
int *nres);
|
||||
LUA_API int (lua_status) (lua_State *L);
|
||||
LUA_API int (lua_isyieldable) (lua_State *L);
|
||||
|
||||
#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
|
||||
|
||||
|
||||
/*
|
||||
** Warning-related functions
|
||||
*/
|
||||
LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud);
|
||||
LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont);
|
||||
|
||||
|
||||
/*
|
||||
** garbage-collection function and options
|
||||
@@ -221,11 +320,15 @@ LUA_API int (lua_status) (lua_State *L);
|
||||
#define LUA_GCRESTART 1
|
||||
#define LUA_GCCOLLECT 2
|
||||
#define LUA_GCCOUNT 3
|
||||
#define LUA_GCSTEP 4
|
||||
#define LUA_GCSETPAUSE 5
|
||||
#define LUA_GCSETSTEPMUL 6
|
||||
#define LUA_GCCOUNTB 4
|
||||
#define LUA_GCSTEP 5
|
||||
#define LUA_GCSETPAUSE 6
|
||||
#define LUA_GCSETSTEPMUL 7
|
||||
#define LUA_GCISRUNNING 9
|
||||
#define LUA_GCGEN 10
|
||||
#define LUA_GCINC 11
|
||||
|
||||
LUA_API int (lua_gc) (lua_State *L, int what, int data);
|
||||
LUA_API int (lua_gc) (lua_State *L, int what, ...);
|
||||
|
||||
|
||||
/*
|
||||
@@ -237,17 +340,27 @@ LUA_API int (lua_error) (lua_State *L);
|
||||
LUA_API int (lua_next) (lua_State *L, int idx);
|
||||
|
||||
LUA_API void (lua_concat) (lua_State *L, int n);
|
||||
LUA_API void (lua_len) (lua_State *L, int idx);
|
||||
|
||||
LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
|
||||
|
||||
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
||||
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
|
||||
|
||||
LUA_API void (lua_toclose) (lua_State *L, int idx);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** ===============================================================
|
||||
/*
|
||||
** {==============================================================
|
||||
** some useful macros
|
||||
** ===============================================================
|
||||
*/
|
||||
|
||||
#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE))
|
||||
|
||||
#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL)
|
||||
#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL)
|
||||
|
||||
#define lua_pop(L,n) lua_settop(L, -(n)-1)
|
||||
|
||||
#define lua_newtable(L) lua_createtable(L, 0, 0)
|
||||
@@ -256,8 +369,6 @@ LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
||||
|
||||
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
|
||||
|
||||
#define lua_strlen(L,i) lua_objlen(L, (i))
|
||||
|
||||
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
|
||||
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
|
||||
#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
|
||||
@@ -267,30 +378,41 @@ LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
||||
#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
|
||||
#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
|
||||
|
||||
#define lua_pushliteral(L, s) \
|
||||
lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
|
||||
#define lua_pushliteral(L, s) lua_pushstring(L, "" s)
|
||||
|
||||
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
|
||||
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
|
||||
#define lua_pushglobaltable(L) \
|
||||
((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS))
|
||||
|
||||
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
|
||||
|
||||
|
||||
#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
|
||||
|
||||
#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
|
||||
|
||||
#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1))
|
||||
|
||||
/* }============================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** compatibility macros and functions
|
||||
** {==============================================================
|
||||
** compatibility macros
|
||||
** ===============================================================
|
||||
*/
|
||||
#if defined(LUA_COMPAT_APIINTCASTS)
|
||||
|
||||
#define lua_open() luaL_newstate()
|
||||
#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n))
|
||||
#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is))
|
||||
#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL)
|
||||
|
||||
#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX)
|
||||
|
||||
#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0)
|
||||
|
||||
#define lua_Chunkreader lua_Reader
|
||||
#define lua_Chunkwriter lua_Writer
|
||||
#endif
|
||||
|
||||
#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1)
|
||||
#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1)
|
||||
#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1)
|
||||
|
||||
/* }============================================================== */
|
||||
|
||||
/*
|
||||
** {======================================================================
|
||||
@@ -306,7 +428,7 @@ LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
||||
#define LUA_HOOKRET 1
|
||||
#define LUA_HOOKLINE 2
|
||||
#define LUA_HOOKCOUNT 3
|
||||
#define LUA_HOOKTAILRET 4
|
||||
#define LUA_HOOKTAILCALL 4
|
||||
|
||||
|
||||
/*
|
||||
@@ -320,43 +442,54 @@ LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
||||
typedef struct lua_Debug lua_Debug; /* activation record */
|
||||
|
||||
|
||||
/* Functions to be called by the debuger in specific events */
|
||||
/* Functions to be called by the debugger in specific events */
|
||||
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
|
||||
|
||||
|
||||
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
|
||||
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
|
||||
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
|
||||
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
|
||||
LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);
|
||||
LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);
|
||||
LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
|
||||
LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
|
||||
LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n);
|
||||
LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n);
|
||||
LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n);
|
||||
LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n);
|
||||
|
||||
LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
|
||||
LUA_API lua_Hook lua_gethook (lua_State *L);
|
||||
LUA_API int lua_gethookmask (lua_State *L);
|
||||
LUA_API int lua_gethookcount (lua_State *L);
|
||||
LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n);
|
||||
LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1,
|
||||
int fidx2, int n2);
|
||||
|
||||
LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count);
|
||||
LUA_API lua_Hook (lua_gethook) (lua_State *L);
|
||||
LUA_API int (lua_gethookmask) (lua_State *L);
|
||||
LUA_API int (lua_gethookcount) (lua_State *L);
|
||||
|
||||
LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit);
|
||||
|
||||
struct lua_Debug {
|
||||
int event;
|
||||
const char *name; /* (n) */
|
||||
const char *namewhat; /* (n) `global', `local', `field', `method' */
|
||||
const char *what; /* (S) `Lua', `C', `main', `tail' */
|
||||
const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */
|
||||
const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */
|
||||
const char *source; /* (S) */
|
||||
size_t srclen; /* (S) */
|
||||
int currentline; /* (l) */
|
||||
int nups; /* (u) number of upvalues */
|
||||
int linedefined; /* (S) */
|
||||
int lastlinedefined; /* (S) */
|
||||
unsigned char nups; /* (u) number of upvalues */
|
||||
unsigned char nparams;/* (u) number of parameters */
|
||||
char isvararg; /* (u) */
|
||||
char istailcall; /* (t) */
|
||||
unsigned short ftransfer; /* (r) index of first value transferred */
|
||||
unsigned short ntransfer; /* (r) number of transferred values */
|
||||
char short_src[LUA_IDSIZE]; /* (S) */
|
||||
/* private part */
|
||||
int i_ci; /* active function */
|
||||
struct CallInfo *i_ci; /* active function */
|
||||
};
|
||||
|
||||
/* }====================================================================== */
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2005 Lua.org, PUC-Rio. All rights reserved.
|
||||
* Copyright (C) 1994-2019 Lua.org, PUC-Rio.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lualib.h,v 1.34 2005/04/13 17:24:20 roberto Exp roberto $
|
||||
** $Id: lualib.h $
|
||||
** Lua standard libraries
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -11,37 +11,48 @@
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/* Key to file-handle type */
|
||||
#define LUA_FILEHANDLE "FILE*"
|
||||
/* version suffix for environment variable names */
|
||||
#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR
|
||||
|
||||
|
||||
LUAMOD_API int (luaopen_base) (lua_State *L);
|
||||
|
||||
#define LUA_COLIBNAME "coroutine"
|
||||
LUALIB_API int (luaopen_base) (lua_State *L);
|
||||
LUAMOD_API int (luaopen_coroutine) (lua_State *L);
|
||||
|
||||
#define LUA_TABLIBNAME "table"
|
||||
LUALIB_API int (luaopen_table) (lua_State *L);
|
||||
LUAMOD_API int (luaopen_table) (lua_State *L);
|
||||
|
||||
#define LUA_IOLIBNAME "io"
|
||||
LUALIB_API int (luaopen_io) (lua_State *L);
|
||||
LUAMOD_API int (luaopen_io) (lua_State *L);
|
||||
|
||||
#define LUA_OSLIBNAME "os"
|
||||
LUALIB_API int (luaopen_os) (lua_State *L);
|
||||
LUAMOD_API int (luaopen_os) (lua_State *L);
|
||||
|
||||
#define LUA_STRLIBNAME "string"
|
||||
LUALIB_API int (luaopen_string) (lua_State *L);
|
||||
LUAMOD_API int (luaopen_string) (lua_State *L);
|
||||
|
||||
#define LUA_UTF8LIBNAME "utf8"
|
||||
LUAMOD_API int (luaopen_utf8) (lua_State *L);
|
||||
|
||||
#define LUA_MATHLIBNAME "math"
|
||||
LUALIB_API int (luaopen_math) (lua_State *L);
|
||||
LUAMOD_API int (luaopen_math) (lua_State *L);
|
||||
|
||||
#define LUA_DBLIBNAME "debug"
|
||||
LUALIB_API int (luaopen_debug) (lua_State *L);
|
||||
LUAMOD_API int (luaopen_debug) (lua_State *L);
|
||||
|
||||
#define LUA_LOADLIBNAME "package"
|
||||
LUALIB_API int (luaopen_package) (lua_State *L);
|
||||
LUAMOD_API int (luaopen_package) (lua_State *L);
|
||||
|
||||
|
||||
/* open all previous libraries */
|
||||
LUALIB_API void (luaL_openlibs) (lua_State *L);
|
||||
LUALIB_API void (luaL_openlibs) (lua_State *L);
|
||||
|
||||
|
||||
|
||||
#if !defined(lua_assert)
|
||||
#define lua_assert(x) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,296 +1,315 @@
|
||||
/*
|
||||
** $Id: lundump.c,v 2.4 2005/05/05 20:47:02 roberto Exp roberto $
|
||||
** load pre-compiled Lua chunks
|
||||
** $Id: lundump.c $
|
||||
** load precompiled Lua chunks
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lundump_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lfunc.h"
|
||||
#include "lmem.h"
|
||||
#include "lopcodes.h"
|
||||
#include "lobject.h"
|
||||
#include "lstring.h"
|
||||
#include "lundump.h"
|
||||
#include "lzio.h"
|
||||
|
||||
#define LoadByte (lu_byte) ezgetc
|
||||
|
||||
#if !defined(luai_verifycode)
|
||||
#define luai_verifycode(L,b,f) /* empty */
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
lua_State* L;
|
||||
ZIO* Z;
|
||||
Mbuffer* b;
|
||||
int swap;
|
||||
const char* name;
|
||||
lua_State *L;
|
||||
ZIO *Z;
|
||||
const char *name;
|
||||
} LoadState;
|
||||
|
||||
static void unexpectedEOZ (LoadState* S)
|
||||
{
|
||||
luaG_runerror(S->L,"unexpected end of file in %s",S->name);
|
||||
|
||||
static l_noret error (LoadState *S, const char *why) {
|
||||
luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why);
|
||||
luaD_throw(S->L, LUA_ERRSYNTAX);
|
||||
}
|
||||
|
||||
static int ezgetc (LoadState* S)
|
||||
{
|
||||
int c=zgetc(S->Z);
|
||||
if (c==EOZ) unexpectedEOZ(S);
|
||||
return c;
|
||||
|
||||
/*
|
||||
** All high-level loads go through LoadVector; you can change it to
|
||||
** adapt to the endianness of the input
|
||||
*/
|
||||
#define LoadVector(S,b,n) LoadBlock(S,b,(n)*sizeof((b)[0]))
|
||||
|
||||
static void LoadBlock (LoadState *S, void *b, size_t size) {
|
||||
if (luaZ_read(S->Z, b, size) != 0)
|
||||
error(S, "truncated chunk");
|
||||
}
|
||||
|
||||
static void ezread (LoadState* S, void* b, size_t n)
|
||||
{
|
||||
size_t r=luaZ_read(S->Z,b,n);
|
||||
if (r!=0) unexpectedEOZ(S);
|
||||
|
||||
#define LoadVar(S,x) LoadVector(S,&x,1)
|
||||
|
||||
|
||||
static lu_byte LoadByte (LoadState *S) {
|
||||
int b = zgetc(S->Z);
|
||||
if (b == EOZ)
|
||||
error(S, "truncated chunk");
|
||||
return cast_byte(b);
|
||||
}
|
||||
|
||||
static void LoadBlock (LoadState* S, void* b, size_t size)
|
||||
{
|
||||
if (S->swap)
|
||||
{
|
||||
char* p=(char*) b+size-1;
|
||||
size_t n=size;
|
||||
while (n--) *p--=(char)ezgetc(S);
|
||||
}
|
||||
else
|
||||
ezread(S,b,size);
|
||||
|
||||
static size_t LoadUnsigned (LoadState *S, size_t limit) {
|
||||
size_t x = 0;
|
||||
int b;
|
||||
limit >>= 7;
|
||||
do {
|
||||
b = LoadByte(S);
|
||||
if (x >= limit)
|
||||
error(S, "integer overflow");
|
||||
x = (x << 7) | (b & 0x7f);
|
||||
} while ((b & 0x80) == 0);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void LoadVector (LoadState* S, void* b, int m, size_t size)
|
||||
{
|
||||
if (S->swap)
|
||||
{
|
||||
char* q=(char*) b;
|
||||
while (m--)
|
||||
{
|
||||
char* p=q+size-1;
|
||||
size_t n=size;
|
||||
while (n--) *p--=(char)ezgetc(S);
|
||||
q+=size;
|
||||
|
||||
static size_t LoadSize (LoadState *S) {
|
||||
return LoadUnsigned(S, ~(size_t)0);
|
||||
}
|
||||
|
||||
|
||||
static int LoadInt (LoadState *S) {
|
||||
return cast_int(LoadUnsigned(S, INT_MAX));
|
||||
}
|
||||
|
||||
|
||||
static lua_Number LoadNumber (LoadState *S) {
|
||||
lua_Number x;
|
||||
LoadVar(S, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
static lua_Integer LoadInteger (LoadState *S) {
|
||||
lua_Integer x;
|
||||
LoadVar(S, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Load a nullable string
|
||||
*/
|
||||
static TString *LoadStringN (LoadState *S) {
|
||||
size_t size = LoadSize(S);
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */
|
||||
char buff[LUAI_MAXSHORTLEN];
|
||||
LoadVector(S, buff, size);
|
||||
return luaS_newlstr(S->L, buff, size);
|
||||
}
|
||||
else { /* long string */
|
||||
TString *ts = luaS_createlngstrobj(S->L, size);
|
||||
LoadVector(S, getstr(ts), size); /* load directly in final place */
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
else
|
||||
ezread(S,b,m*size);
|
||||
}
|
||||
|
||||
static int LoadInt (LoadState* S)
|
||||
{
|
||||
int x;
|
||||
LoadBlock(S,&x,sizeof(x));
|
||||
if (x<0) luaG_runerror(S->L,"bad integer in %s",S->name);
|
||||
return x;
|
||||
|
||||
/*
|
||||
** Load a non-nullable string.
|
||||
*/
|
||||
static TString *LoadString (LoadState *S) {
|
||||
TString *st = LoadStringN(S);
|
||||
if (st == NULL)
|
||||
error(S, "bad format for constant string");
|
||||
return st;
|
||||
}
|
||||
|
||||
static size_t LoadSize (LoadState* S)
|
||||
{
|
||||
size_t x;
|
||||
LoadBlock(S,&x,sizeof(x));
|
||||
return x;
|
||||
|
||||
static void LoadCode (LoadState *S, Proto *f) {
|
||||
int n = LoadInt(S);
|
||||
f->code = luaM_newvectorchecked(S->L, n, Instruction);
|
||||
f->sizecode = n;
|
||||
LoadVector(S, f->code, n);
|
||||
}
|
||||
|
||||
static lua_Number LoadNumber (LoadState* S)
|
||||
{
|
||||
lua_Number x;
|
||||
LoadBlock(S,&x,sizeof(x));
|
||||
return x;
|
||||
}
|
||||
|
||||
static TString* LoadString (LoadState* S)
|
||||
{
|
||||
size_t size=LoadSize(S);
|
||||
if (size==0)
|
||||
return NULL;
|
||||
else
|
||||
{
|
||||
char* s=luaZ_openspace(S->L,S->b,size);
|
||||
ezread(S,s,size);
|
||||
return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */
|
||||
}
|
||||
}
|
||||
static void LoadFunction(LoadState *S, Proto *f, TString *psource);
|
||||
|
||||
static void LoadCode (LoadState* S, Proto* f)
|
||||
{
|
||||
int size=LoadInt(S);
|
||||
f->code=luaM_newvector(S->L,size,Instruction);
|
||||
f->sizecode=size;
|
||||
LoadVector(S,f->code,size,sizeof(*f->code));
|
||||
}
|
||||
|
||||
static void LoadLocals (LoadState* S, Proto* f)
|
||||
{
|
||||
int i,n;
|
||||
n=LoadInt(S);
|
||||
f->locvars=luaM_newvector(S->L,n,LocVar);
|
||||
f->sizelocvars=n;
|
||||
for (i=0; i<n; i++) f->locvars[i].varname=NULL;
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
f->locvars[i].varname=LoadString(S);
|
||||
f->locvars[i].startpc=LoadInt(S);
|
||||
f->locvars[i].endpc=LoadInt(S);
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadLines (LoadState* S, Proto* f)
|
||||
{
|
||||
int size=LoadInt(S);
|
||||
f->lineinfo=luaM_newvector(S->L,size,int);
|
||||
f->sizelineinfo=size;
|
||||
LoadVector(S,f->lineinfo,size,sizeof(*f->lineinfo));
|
||||
}
|
||||
|
||||
static void LoadUpvalues (LoadState* S, Proto* f)
|
||||
{
|
||||
int i,n;
|
||||
n=LoadInt(S);
|
||||
if (n!=0 && n!=f->nups)
|
||||
luaG_runerror(S->L,"bad nupvalues in %s: read %d; expected %d",
|
||||
S->name,n,f->nups);
|
||||
f->upvalues=luaM_newvector(S->L,n,TString*);
|
||||
f->sizeupvalues=n;
|
||||
for (i=0; i<n; i++) f->upvalues[i]=NULL;
|
||||
for (i=0; i<n; i++) f->upvalues[i]=LoadString(S);
|
||||
}
|
||||
|
||||
static Proto* LoadFunction (LoadState* S, TString* p);
|
||||
|
||||
static void LoadConstants (LoadState* S, Proto* f)
|
||||
{
|
||||
int i,n;
|
||||
lua_State *L=S->L;
|
||||
n=LoadInt(S);
|
||||
f->k=luaM_newvector(L,n,TValue);
|
||||
f->sizek=n;
|
||||
for (i=0; i<n; i++) setnilvalue(&f->k[i]);
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
TValue* o=&f->k[i];
|
||||
int t=LoadByte(S);
|
||||
switch (t)
|
||||
{
|
||||
case LUA_TNUMBER:
|
||||
setnvalue(o,LoadNumber(S));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
setsvalue2n(L, o,LoadString(S));
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
setnilvalue(o);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
static void LoadConstants (LoadState *S, Proto *f) {
|
||||
int i;
|
||||
int n = LoadInt(S);
|
||||
f->k = luaM_newvectorchecked(S->L, n, TValue);
|
||||
f->sizek = n;
|
||||
for (i = 0; i < n; i++)
|
||||
setnilvalue(&f->k[i]);
|
||||
for (i = 0; i < n; i++) {
|
||||
TValue *o = &f->k[i];
|
||||
int t = LoadByte(S);
|
||||
switch (t) {
|
||||
case LUA_TNIL:
|
||||
setnilvalue(o);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
setbvalue(o, LoadByte(S));
|
||||
break;
|
||||
default:
|
||||
luaG_runerror(L,"bad constant type (%d) in %s",t,S->name);
|
||||
break;
|
||||
break;
|
||||
case LUA_TNUMFLT:
|
||||
setfltvalue(o, LoadNumber(S));
|
||||
break;
|
||||
case LUA_TNUMINT:
|
||||
setivalue(o, LoadInteger(S));
|
||||
break;
|
||||
case LUA_TSHRSTR:
|
||||
case LUA_TLNGSTR:
|
||||
setsvalue2n(S->L, o, LoadString(S));
|
||||
break;
|
||||
default: lua_assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
n=LoadInt(S);
|
||||
f->p=luaM_newvector(L,n,Proto*);
|
||||
f->sizep=n;
|
||||
for (i=0; i<n; i++) f->p[i]=NULL;
|
||||
for (i=0; i<n; i++) f->p[i]=LoadFunction(S,f->source);
|
||||
}
|
||||
|
||||
static Proto* LoadFunction (LoadState* S, TString* p)
|
||||
{
|
||||
lua_State *L=S->L;
|
||||
Proto* f=luaF_newproto(L);
|
||||
setptvalue2s(L, L->top, f);
|
||||
incr_top(L);
|
||||
f->source=LoadString(S); if (f->source==NULL) f->source=p;
|
||||
f->linedefined=LoadInt(S);
|
||||
f->lastlinedefined=LoadInt(S);
|
||||
f->nups=LoadByte(S);
|
||||
f->numparams=LoadByte(S);
|
||||
f->is_vararg=LoadByte(S);
|
||||
f->maxstacksize=LoadByte(S);
|
||||
LoadLines(S,f);
|
||||
LoadLocals(S,f);
|
||||
LoadUpvalues(S,f);
|
||||
LoadConstants(S,f);
|
||||
LoadCode(S,f);
|
||||
#ifndef TRUST_BINARIES
|
||||
if (!luaG_checkcode(f)) luaG_runerror(L,"bad code in %s",S->name);
|
||||
#endif
|
||||
L->top--;
|
||||
return f;
|
||||
|
||||
static void LoadProtos (LoadState *S, Proto *f) {
|
||||
int i;
|
||||
int n = LoadInt(S);
|
||||
f->p = luaM_newvectorchecked(S->L, n, Proto *);
|
||||
f->sizep = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->p[i] = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->p[i] = luaF_newproto(S->L);
|
||||
LoadFunction(S, f->p[i], f->source);
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadSignature (LoadState* S)
|
||||
{
|
||||
const char* s=LUA_SIGNATURE;
|
||||
while (*s!=0 && ezgetc(S)==*s)
|
||||
++s;
|
||||
if (*s!=0) luaG_runerror(S->L,"bad signature in %s",S->name);
|
||||
|
||||
static void LoadUpvalues (LoadState *S, Proto *f) {
|
||||
int i, n;
|
||||
n = LoadInt(S);
|
||||
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
|
||||
f->sizeupvalues = n;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->upvalues[i].name = NULL;
|
||||
f->upvalues[i].instack = LoadByte(S);
|
||||
f->upvalues[i].idx = LoadByte(S);
|
||||
f->upvalues[i].kind = LoadByte(S);
|
||||
}
|
||||
}
|
||||
|
||||
static void TestSize (LoadState* S, int s, const char* what)
|
||||
{
|
||||
int r=LoadByte(S);
|
||||
if (r!=s)
|
||||
luaG_runerror(S->L,"virtual machine mismatch in %s: "
|
||||
"size of %s is %d but read %d",S->name,what,s,r);
|
||||
|
||||
static void LoadDebug (LoadState *S, Proto *f) {
|
||||
int i, n;
|
||||
n = LoadInt(S);
|
||||
f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte);
|
||||
f->sizelineinfo = n;
|
||||
LoadVector(S, f->lineinfo, n);
|
||||
n = LoadInt(S);
|
||||
f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo);
|
||||
f->sizeabslineinfo = n;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->abslineinfo[i].pc = LoadInt(S);
|
||||
f->abslineinfo[i].line = LoadInt(S);
|
||||
}
|
||||
n = LoadInt(S);
|
||||
f->locvars = luaM_newvectorchecked(S->L, n, LocVar);
|
||||
f->sizelocvars = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->locvars[i].varname = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->locvars[i].varname = LoadStringN(S);
|
||||
f->locvars[i].startpc = LoadInt(S);
|
||||
f->locvars[i].endpc = LoadInt(S);
|
||||
}
|
||||
n = LoadInt(S);
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = LoadStringN(S);
|
||||
}
|
||||
|
||||
#define TESTSIZE(s,w) TestSize(S,s,w)
|
||||
#define V(v) v/16,v%16
|
||||
|
||||
static void LoadHeader (LoadState* S)
|
||||
{
|
||||
int version;
|
||||
lua_Number x,tx=TEST_NUMBER;
|
||||
LoadSignature(S);
|
||||
version=LoadByte(S);
|
||||
if (version>VERSION)
|
||||
luaG_runerror(S->L,"%s too new: "
|
||||
"read version %d.%d; expected at most %d.%d",
|
||||
S->name,V(version),V(VERSION));
|
||||
if (version<VERSION0) /* check last major change */
|
||||
luaG_runerror(S->L,"%s too old: "
|
||||
"read version %d.%d; expected at least %d.%d",
|
||||
S->name,V(version),V(VERSION0));
|
||||
S->swap=(luaU_endianness()!=LoadByte(S)); /* need to swap bytes? */
|
||||
TESTSIZE(sizeof(int),"int");
|
||||
TESTSIZE(sizeof(size_t), "size_t");
|
||||
TESTSIZE(sizeof(Instruction), "Instruction");
|
||||
TESTSIZE(sizeof(lua_Number), "number");
|
||||
x=LoadNumber(S);
|
||||
if ((long)x!=(long)tx) /* disregard errors in last bits of fraction */
|
||||
luaG_runerror(S->L,"unknown number format in %s",S->name);
|
||||
static void LoadFunction (LoadState *S, Proto *f, TString *psource) {
|
||||
f->source = LoadStringN(S);
|
||||
if (f->source == NULL) /* no source in dump? */
|
||||
f->source = psource; /* reuse parent's source */
|
||||
f->linedefined = LoadInt(S);
|
||||
f->lastlinedefined = LoadInt(S);
|
||||
f->numparams = LoadByte(S);
|
||||
f->is_vararg = LoadByte(S);
|
||||
f->maxstacksize = LoadByte(S);
|
||||
LoadCode(S, f);
|
||||
LoadConstants(S, f);
|
||||
LoadUpvalues(S, f);
|
||||
LoadProtos(S, f);
|
||||
LoadDebug(S, f);
|
||||
}
|
||||
|
||||
static Proto* LoadChunk (LoadState* S)
|
||||
{
|
||||
LoadHeader(S);
|
||||
return LoadFunction(S,NULL);
|
||||
|
||||
static void checkliteral (LoadState *S, const char *s, const char *msg) {
|
||||
char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */
|
||||
size_t len = strlen(s);
|
||||
LoadVector(S, buff, len);
|
||||
if (memcmp(s, buff, len) != 0)
|
||||
error(S, msg);
|
||||
}
|
||||
|
||||
|
||||
static void fchecksize (LoadState *S, size_t size, const char *tname) {
|
||||
if (LoadByte(S) != size)
|
||||
error(S, luaO_pushfstring(S->L, "%s size mismatch", tname));
|
||||
}
|
||||
|
||||
|
||||
#define checksize(S,t) fchecksize(S,sizeof(t),#t)
|
||||
|
||||
static void checkHeader (LoadState *S) {
|
||||
/* skip 1st char (already read and checked) */
|
||||
checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk");
|
||||
if (LoadInt(S) != LUAC_VERSION)
|
||||
error(S, "version mismatch");
|
||||
if (LoadByte(S) != LUAC_FORMAT)
|
||||
error(S, "format mismatch");
|
||||
checkliteral(S, LUAC_DATA, "corrupted chunk");
|
||||
checksize(S, Instruction);
|
||||
checksize(S, lua_Integer);
|
||||
checksize(S, lua_Number);
|
||||
if (LoadInteger(S) != LUAC_INT)
|
||||
error(S, "integer format mismatch");
|
||||
if (LoadNumber(S) != LUAC_NUM)
|
||||
error(S, "float format mismatch");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** load precompiled chunk
|
||||
*/
|
||||
Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char *s)
|
||||
{
|
||||
LoadState S;
|
||||
if (*s=='@' || *s=='=')
|
||||
S.name=s+1;
|
||||
else if (*s==LUA_SIGNATURE[0])
|
||||
S.name="binary string";
|
||||
else
|
||||
S.name=s;
|
||||
S.L=L;
|
||||
S.Z=Z;
|
||||
S.b=buff;
|
||||
return LoadChunk(&S);
|
||||
LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
|
||||
LoadState S;
|
||||
LClosure *cl;
|
||||
if (*name == '@' || *name == '=')
|
||||
S.name = name + 1;
|
||||
else if (*name == LUA_SIGNATURE[0])
|
||||
S.name = "binary string";
|
||||
else
|
||||
S.name = name;
|
||||
S.L = L;
|
||||
S.Z = Z;
|
||||
checkHeader(&S);
|
||||
cl = luaF_newLclosure(L, LoadByte(&S));
|
||||
setclLvalue2s(L, L->top, cl);
|
||||
luaD_inctop(L);
|
||||
cl->p = luaF_newproto(L);
|
||||
LoadFunction(&S, cl->p, NULL);
|
||||
lua_assert(cl->nupvalues == cl->p->sizeupvalues);
|
||||
luai_verifycode(L, buff, cl->p);
|
||||
return cl;
|
||||
}
|
||||
|
||||
/*
|
||||
** find byte order
|
||||
*/
|
||||
int luaU_endianness (void)
|
||||
{
|
||||
int x=1;
|
||||
return *(char*)&x;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
/*
|
||||
** $Id: lundump.h,v 1.35 2005/04/25 19:24:10 roberto Exp roberto $
|
||||
** load pre-compiled Lua chunks
|
||||
** $Id: lundump.h $
|
||||
** load precompiled Lua chunks
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef lundump_h
|
||||
#define lundump_h
|
||||
|
||||
#include "llimits.h"
|
||||
#include "lobject.h"
|
||||
#include "lzio.h"
|
||||
|
||||
/* load one chunk; from lundump.c */
|
||||
LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff,
|
||||
const char *name);
|
||||
|
||||
/* find byte order; from lundump.c */
|
||||
LUAI_FUNC int luaU_endianness (void);
|
||||
/* data to catch conversion errors */
|
||||
#define LUAC_DATA "\x19\x93\r\n\x1a\n"
|
||||
|
||||
#define LUAC_INT 0x5678
|
||||
#define LUAC_NUM cast_num(370.5)
|
||||
|
||||
#define LUAC_VERSION LUA_VERSION_NUM
|
||||
#define LUAC_FORMAT 0 /* this is the official format */
|
||||
|
||||
/* load one chunk; from lundump.c */
|
||||
LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name);
|
||||
|
||||
/* dump one chunk; from ldump.c */
|
||||
LUAI_FUNC int luaU_dump (lua_State* L, const Proto* Main, lua_Writer w,
|
||||
void* data, int strip);
|
||||
|
||||
/* print one chunk; from print.c */
|
||||
LUAI_FUNC void luaU_print (const Proto* Main);
|
||||
|
||||
/* definitions for headers of binary files */
|
||||
#define VERSION 0x50 /* last format change was in 5.0 */
|
||||
#define VERSION0 0x50 /* last major change was in 5.0 */
|
||||
|
||||
/* a multiple of PI for testing native format */
|
||||
/* multiplying by 1E7 gives non-trivial integer values */
|
||||
#define TEST_NUMBER ((lua_Number)3.14159265358979323846E7)
|
||||
LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w,
|
||||
void* data, int strip);
|
||||
|
||||
#endif
|
||||
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
** $Id: lutf8lib.c $
|
||||
** Standard library for UTF-8 manipulation
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lutf8lib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
#define MAXUNICODE 0x10FFFFu
|
||||
|
||||
#define MAXUTF 0x7FFFFFFFu
|
||||
|
||||
/*
|
||||
** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits.
|
||||
*/
|
||||
#if (UINT_MAX >> 30) >= 1
|
||||
typedef unsigned int utfint;
|
||||
#else
|
||||
typedef unsigned long utfint;
|
||||
#endif
|
||||
|
||||
|
||||
#define iscont(p) ((*(p) & 0xC0) == 0x80)
|
||||
|
||||
|
||||
/* from strlib */
|
||||
/* translate a relative string position: negative means back from end */
|
||||
static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
|
||||
if (pos >= 0) return pos;
|
||||
else if (0u - (size_t)pos > len) return 0;
|
||||
else return (lua_Integer)len + pos + 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Decode one UTF-8 sequence, returning NULL if byte sequence is
|
||||
** invalid. The array 'limits' stores the minimum value for each
|
||||
** sequence length, to check for overlong representations. Its first
|
||||
** entry forces an error for non-ascii bytes with no continuation
|
||||
** bytes (count == 0).
|
||||
*/
|
||||
static const char *utf8_decode (const char *s, utfint *val, int strict) {
|
||||
static const utfint limits[] =
|
||||
{~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
|
||||
unsigned int c = (unsigned char)s[0];
|
||||
utfint res = 0; /* final result */
|
||||
if (c < 0x80) /* ascii? */
|
||||
res = c;
|
||||
else {
|
||||
int count = 0; /* to count number of continuation bytes */
|
||||
for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
|
||||
unsigned int cc = (unsigned char)s[++count]; /* read next byte */
|
||||
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
|
||||
return NULL; /* invalid byte sequence */
|
||||
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
|
||||
}
|
||||
res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */
|
||||
if (count > 5 || res > MAXUTF || res < limits[count])
|
||||
return NULL; /* invalid byte sequence */
|
||||
s += count; /* skip continuation bytes read */
|
||||
}
|
||||
if (strict) {
|
||||
/* check for invalid code points; too large or surrogates */
|
||||
if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu))
|
||||
return NULL;
|
||||
}
|
||||
if (val) *val = res;
|
||||
return s + 1; /* +1 to include first byte */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** utf8len(s [, i [, j [, lax]]]) --> number of characters that
|
||||
** start in the range [i,j], or nil + current position if 's' is not
|
||||
** well formed in that interval
|
||||
*/
|
||||
static int utflen (lua_State *L) {
|
||||
lua_Integer n = 0; /* counter for the number of characters */
|
||||
size_t len; /* string length in bytes */
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
|
||||
lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len);
|
||||
int lax = lua_toboolean(L, 4);
|
||||
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2,
|
||||
"initial position out of string");
|
||||
luaL_argcheck(L, --posj < (lua_Integer)len, 3,
|
||||
"final position out of string");
|
||||
while (posi <= posj) {
|
||||
const char *s1 = utf8_decode(s + posi, NULL, !lax);
|
||||
if (s1 == NULL) { /* conversion error? */
|
||||
luaL_pushfail(L); /* return fail ... */
|
||||
lua_pushinteger(L, posi + 1); /* ... and current position */
|
||||
return 2;
|
||||
}
|
||||
posi = s1 - s;
|
||||
n++;
|
||||
}
|
||||
lua_pushinteger(L, n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all
|
||||
** characters that start in the range [i,j]
|
||||
*/
|
||||
static int codepoint (lua_State *L) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
|
||||
lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len);
|
||||
int lax = lua_toboolean(L, 4);
|
||||
int n;
|
||||
const char *se;
|
||||
luaL_argcheck(L, posi >= 1, 2, "out of range");
|
||||
luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of range");
|
||||
if (posi > pose) return 0; /* empty interval; return no values */
|
||||
if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */
|
||||
return luaL_error(L, "string slice too long");
|
||||
n = (int)(pose - posi) + 1; /* upper bound for number of returns */
|
||||
luaL_checkstack(L, n, "string slice too long");
|
||||
n = 0; /* count the number of returns */
|
||||
se = s + pose; /* string end */
|
||||
for (s += posi - 1; s < se;) {
|
||||
utfint code;
|
||||
s = utf8_decode(s, &code, !lax);
|
||||
if (s == NULL)
|
||||
return luaL_error(L, "invalid UTF-8 code");
|
||||
lua_pushinteger(L, code);
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static void pushutfchar (lua_State *L, int arg) {
|
||||
lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg);
|
||||
luaL_argcheck(L, code <= MAXUTF, arg, "value out of range");
|
||||
lua_pushfstring(L, "%U", (long)code);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** utfchar(n1, n2, ...) -> char(n1)..char(n2)...
|
||||
*/
|
||||
static int utfchar (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
if (n == 1) /* optimize common case of single char */
|
||||
pushutfchar(L, 1);
|
||||
else {
|
||||
int i;
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
for (i = 1; i <= n; i++) {
|
||||
pushutfchar(L, i);
|
||||
luaL_addvalue(&b);
|
||||
}
|
||||
luaL_pushresult(&b);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** offset(s, n, [i]) -> index where n-th character counting from
|
||||
** position 'i' starts; 0 means character at 'i'.
|
||||
*/
|
||||
static int byteoffset (lua_State *L) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer n = luaL_checkinteger(L, 2);
|
||||
lua_Integer posi = (n >= 0) ? 1 : len + 1;
|
||||
posi = u_posrelat(luaL_optinteger(L, 3, posi), len);
|
||||
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3,
|
||||
"position out of range");
|
||||
if (n == 0) {
|
||||
/* find beginning of current byte sequence */
|
||||
while (posi > 0 && iscont(s + posi)) posi--;
|
||||
}
|
||||
else {
|
||||
if (iscont(s + posi))
|
||||
return luaL_error(L, "initial position is a continuation byte");
|
||||
if (n < 0) {
|
||||
while (n < 0 && posi > 0) { /* move back */
|
||||
do { /* find beginning of previous character */
|
||||
posi--;
|
||||
} while (posi > 0 && iscont(s + posi));
|
||||
n++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
n--; /* do not move for 1st character */
|
||||
while (n > 0 && posi < (lua_Integer)len) {
|
||||
do { /* find beginning of next character */
|
||||
posi++;
|
||||
} while (iscont(s + posi)); /* (cannot pass final '\0') */
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n == 0) /* did it find given character? */
|
||||
lua_pushinteger(L, posi + 1);
|
||||
else /* no such character */
|
||||
luaL_pushfail(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int iter_aux (lua_State *L, int strict) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer n = lua_tointeger(L, 2) - 1;
|
||||
if (n < 0) /* first iteration? */
|
||||
n = 0; /* start from here */
|
||||
else if (n < (lua_Integer)len) {
|
||||
n++; /* skip current byte */
|
||||
while (iscont(s + n)) n++; /* and its continuations */
|
||||
}
|
||||
if (n >= (lua_Integer)len)
|
||||
return 0; /* no more codepoints */
|
||||
else {
|
||||
utfint code;
|
||||
const char *next = utf8_decode(s + n, &code, strict);
|
||||
if (next == NULL)
|
||||
return luaL_error(L, "invalid UTF-8 code");
|
||||
lua_pushinteger(L, n + 1);
|
||||
lua_pushinteger(L, code);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int iter_auxstrict (lua_State *L) {
|
||||
return iter_aux(L, 1);
|
||||
}
|
||||
|
||||
static int iter_auxlax (lua_State *L) {
|
||||
return iter_aux(L, 0);
|
||||
}
|
||||
|
||||
|
||||
static int iter_codes (lua_State *L) {
|
||||
int lax = lua_toboolean(L, 2);
|
||||
luaL_checkstring(L, 1);
|
||||
lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, 0);
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
/* pattern to match a single UTF-8 character */
|
||||
#define UTF8PATT "[\0-\x7F\xC2-\xFD][\x80-\xBF]*"
|
||||
|
||||
|
||||
static const luaL_Reg funcs[] = {
|
||||
{"offset", byteoffset},
|
||||
{"codepoint", codepoint},
|
||||
{"char", utfchar},
|
||||
{"len", utflen},
|
||||
{"codes", iter_codes},
|
||||
/* placeholders */
|
||||
{"charpattern", NULL},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUAMOD_API int luaopen_utf8 (lua_State *L) {
|
||||
luaL_newlib(L, funcs);
|
||||
lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1);
|
||||
lua_setfield(L, -2, "charpattern");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lvm.h,v 2.4 2005/04/25 19:24:10 roberto Exp roberto $
|
||||
** $Id: lvm.h $
|
||||
** Lua virtual machine
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -13,24 +13,111 @@
|
||||
#include "ltm.h"
|
||||
|
||||
|
||||
#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o)))
|
||||
|
||||
#define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \
|
||||
(((o) = luaV_tonumber(o,n)) != NULL))
|
||||
|
||||
#define equalobj(L,o1,o2) \
|
||||
(ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2))
|
||||
#if !defined(LUA_NOCVTN2S)
|
||||
#define cvt2str(o) ttisnumber(o)
|
||||
#else
|
||||
#define cvt2str(o) 0 /* no conversion from numbers to strings */
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(LUA_NOCVTS2N)
|
||||
#define cvt2num(o) ttisstring(o)
|
||||
#else
|
||||
#define cvt2num(o) 0 /* no conversion from strings to numbers */
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** You can define LUA_FLOORN2I if you want to convert floats to integers
|
||||
** by flooring them (instead of raising an error if they are not
|
||||
** integral values)
|
||||
*/
|
||||
#if !defined(LUA_FLOORN2I)
|
||||
#define LUA_FLOORN2I 0
|
||||
#endif
|
||||
|
||||
|
||||
/* convert an object to a float (including string coercion) */
|
||||
#define tonumber(o,n) \
|
||||
(ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n))
|
||||
|
||||
|
||||
/* convert an object to a float (without string coercion) */
|
||||
#define tonumberns(o,n) \
|
||||
(ttisfloat(o) ? ((n) = fltvalue(o), 1) : \
|
||||
(ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0))
|
||||
|
||||
|
||||
/* convert an object to an integer (including string coercion) */
|
||||
#define tointeger(o,i) \
|
||||
(ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I))
|
||||
|
||||
|
||||
/* convert an object to an integer (without string coercion) */
|
||||
#define tointegerns(o,i) \
|
||||
(ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointegerns(o,i,LUA_FLOORN2I))
|
||||
|
||||
|
||||
#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))
|
||||
|
||||
#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2)
|
||||
|
||||
|
||||
/*
|
||||
** fast track for 'gettable': if 't' is a table and 't[k]' is present,
|
||||
** return 1 with 'slot' pointing to 't[k]' (position of final result).
|
||||
** Otherwise, return 0 (meaning it will have to check metamethod)
|
||||
** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL
|
||||
** (otherwise). 'f' is the raw get function to use.
|
||||
*/
|
||||
#define luaV_fastget(L,t,k,slot,f) \
|
||||
(!ttistable(t) \
|
||||
? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
|
||||
: (slot = f(hvalue(t), k), /* else, do raw access */ \
|
||||
!isempty(slot))) /* result not empty? */
|
||||
|
||||
|
||||
/*
|
||||
** Special case of 'luaV_fastget' for integers, inlining the fast case
|
||||
** of 'luaH_getint'.
|
||||
*/
|
||||
#define luaV_fastgeti(L,t,k,slot) \
|
||||
(!ttistable(t) \
|
||||
? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
|
||||
: (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \
|
||||
? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \
|
||||
!isempty(slot))) /* result not empty? */
|
||||
|
||||
|
||||
/*
|
||||
** Finish a fast set operation (when fast get succeeds). In that case,
|
||||
** 'slot' points to the place to put the value.
|
||||
*/
|
||||
#define luaV_finishfastset(L,t,slot,v) \
|
||||
{ setobj2t(L, cast(TValue *,slot), v); \
|
||||
luaC_barrierback(L, gcvalue(t), v); }
|
||||
|
||||
|
||||
|
||||
|
||||
LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2);
|
||||
LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);
|
||||
LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2);
|
||||
LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n);
|
||||
LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj);
|
||||
LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key,
|
||||
StkId val);
|
||||
LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key,
|
||||
StkId val);
|
||||
LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls);
|
||||
LUAI_FUNC void luaV_concat (lua_State *L, int total, int last);
|
||||
LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r);
|
||||
LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n);
|
||||
LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode);
|
||||
LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p, int mode);
|
||||
LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, int mode);
|
||||
LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key,
|
||||
StkId val, const TValue *slot);
|
||||
LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
|
||||
TValue *val, const TValue *slot);
|
||||
LUAI_FUNC void luaV_finishOp (lua_State *L);
|
||||
LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci);
|
||||
LUAI_FUNC void luaV_concat (lua_State *L, int total);
|
||||
LUAI_FUNC lua_Integer luaV_idiv (lua_State *L, lua_Integer x, lua_Integer y);
|
||||
LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y);
|
||||
LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y);
|
||||
LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y);
|
||||
LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
/*
|
||||
** $Id: lzio.c,v 1.30 2005/05/17 19:49:15 roberto Exp roberto $
|
||||
** a generic input stream interface
|
||||
** $Id: lzio.c $
|
||||
** Buffered streams
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define lzio_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "llimits.h"
|
||||
@@ -25,23 +27,11 @@ int luaZ_fill (ZIO *z) {
|
||||
lua_unlock(L);
|
||||
buff = z->reader(L, z->data, &size);
|
||||
lua_lock(L);
|
||||
if (buff == NULL || size == 0) return EOZ;
|
||||
z->n = size - 1;
|
||||
if (buff == NULL || size == 0)
|
||||
return EOZ;
|
||||
z->n = size - 1; /* discount char being returned */
|
||||
z->p = buff;
|
||||
return char2int(*(z->p++));
|
||||
}
|
||||
|
||||
|
||||
int luaZ_lookahead (ZIO *z) {
|
||||
if (z->n == 0) {
|
||||
if (luaZ_fill(z) == EOZ)
|
||||
return EOZ;
|
||||
else {
|
||||
z->n++; /* luaZ_fill removed first byte; put back it */
|
||||
z->p--;
|
||||
}
|
||||
}
|
||||
return char2int(*z->p);
|
||||
return cast_uchar(*(z->p++));
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +48,14 @@ void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
|
||||
size_t luaZ_read (ZIO *z, void *b, size_t n) {
|
||||
while (n) {
|
||||
size_t m;
|
||||
if (luaZ_lookahead(z) == EOZ)
|
||||
return n; /* return number of missing bytes */
|
||||
if (z->n == 0) { /* no bytes in buffer? */
|
||||
if (luaZ_fill(z) == EOZ) /* try to read more */
|
||||
return n; /* no more input; return number of missing bytes */
|
||||
else {
|
||||
z->n++; /* luaZ_fill consumed first byte; put it back */
|
||||
z->p--;
|
||||
}
|
||||
}
|
||||
m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
|
||||
memcpy(b, z->p, m);
|
||||
z->n -= m;
|
||||
@@ -70,13 +66,3 @@ size_t luaZ_read (ZIO *z, void *b, size_t n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) {
|
||||
if (n > buff->buffsize) {
|
||||
if (n < LUA_MINBUFFER) n = LUA_MINBUFFER;
|
||||
luaZ_resizebuffer(L, buff, n);
|
||||
}
|
||||
return buff->buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lzio.h,v 1.20 2005/04/25 19:24:10 roberto Exp roberto $
|
||||
** $Id: lzio.h $
|
||||
** Buffered streams
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -17,9 +17,8 @@
|
||||
|
||||
typedef struct Zio ZIO;
|
||||
|
||||
#define char2int(c) cast(int, cast(unsigned char, (c)))
|
||||
#define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z))
|
||||
|
||||
#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z))
|
||||
|
||||
typedef struct Mbuffer {
|
||||
char *buffer;
|
||||
@@ -33,21 +32,21 @@ typedef struct Mbuffer {
|
||||
#define luaZ_sizebuffer(buff) ((buff)->buffsize)
|
||||
#define luaZ_bufflen(buff) ((buff)->n)
|
||||
|
||||
#define luaZ_buffremove(buff,i) ((buff)->n -= (i))
|
||||
#define luaZ_resetbuffer(buff) ((buff)->n = 0)
|
||||
|
||||
|
||||
#define luaZ_resizebuffer(L, buff, size) \
|
||||
(luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \
|
||||
((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \
|
||||
(buff)->buffsize, size), \
|
||||
(buff)->buffsize = size)
|
||||
|
||||
#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0)
|
||||
|
||||
|
||||
LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n);
|
||||
LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,
|
||||
void *data);
|
||||
LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */
|
||||
LUAI_FUNC int luaZ_lookahead (ZIO *z);
|
||||
LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */
|
||||
|
||||
|
||||
|
||||
@@ -56,8 +55,8 @@ LUAI_FUNC int luaZ_lookahead (ZIO *z);
|
||||
struct Zio {
|
||||
size_t n; /* bytes still unread */
|
||||
const char *p; /* current position in buffer */
|
||||
lua_Reader reader;
|
||||
void* data; /* additional data */
|
||||
lua_Reader reader; /* reader function */
|
||||
void *data; /* additional data */
|
||||
lua_State *L; /* Lua state (for reader) */
|
||||
};
|
||||
|
||||
|
||||
@@ -4,35 +4,66 @@
|
||||
|
||||
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
|
||||
|
||||
CWARNS= -pedantic -Waggregate-return -Wcast-align \
|
||||
-Wmissing-prototypes -Wpointer-arith -Wshadow \
|
||||
-Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings
|
||||
# -Wcast-qual
|
||||
# Warnings valid for both C and C++
|
||||
CWARNSCPP= \
|
||||
-fmax-errors=5 \
|
||||
-Wextra \
|
||||
-Wshadow \
|
||||
-Wsign-compare \
|
||||
-Wundef \
|
||||
-Wwrite-strings \
|
||||
-Wredundant-decls \
|
||||
-Wdisabled-optimization \
|
||||
-Wdouble-promotion \
|
||||
-Wlogical-op \
|
||||
-Wno-aggressive-loop-optimizations \
|
||||
# the next warnings might be useful sometimes,
|
||||
# but usually they generate too much noise
|
||||
# -Werror \
|
||||
# -pedantic # warns if we use jump tables \
|
||||
# -Wconversion \
|
||||
# -Wsign-conversion \
|
||||
# -Wstrict-overflow=2 \
|
||||
# -Wformat=2 \
|
||||
# -Wcast-qual \
|
||||
|
||||
# -DEXTERNMEMCHECK -DHARDSTACKTESTS
|
||||
# The next warnings are neither valid nor needed for C++
|
||||
CWARNSC= -Wdeclaration-after-statement \
|
||||
-Wmissing-prototypes \
|
||||
-Wnested-externs \
|
||||
-Wstrict-prototypes \
|
||||
-Wc++-compat \
|
||||
-Wold-style-definition \
|
||||
|
||||
|
||||
CWARNS= $(CWARNSCPP) $(CWARNSC)
|
||||
|
||||
|
||||
# -DEXTERNMEMCHECK -DHARDSTACKTESTS -DHARDMEMTESTS -DTRACEMEM='"tempmem"'
|
||||
# -DMAXINDEXRK=1 -DLUA_COMPAT_5_3
|
||||
# -g -DLUA_USER_H='"ltests.h"'
|
||||
# -fomit-frame-pointer #-pg -malign-double
|
||||
TESTS= -g -DLUA_USER_H='"ltests.h"'
|
||||
# -pg -malign-double
|
||||
# -DLUA_USE_CTYPE -DLUA_USE_APICHECK
|
||||
# ('-ftrapv' for runtime checks of integer overflows)
|
||||
# -fsanitize=undefined -ftrapv -fno-inline
|
||||
# TESTS= -DLUA_USER_H='"ltests.h"' -O0 -g
|
||||
|
||||
|
||||
LOCAL = $(TESTS) $(CWARNS)
|
||||
|
||||
|
||||
# enable Linux goodies
|
||||
MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX -DLUA_USE_READLINE
|
||||
MYLDFLAGS= $(LOCAL) -Wl,-E
|
||||
MYLIBS= -ldl -lreadline
|
||||
|
||||
|
||||
CC= gcc
|
||||
CFLAGS= -O2 -Wall $(MYCFLAGS)
|
||||
AR= ar rcu
|
||||
CFLAGS= -Wall -O2 $(MYCFLAGS) -fno-stack-protector -fno-common -march=native
|
||||
AR= ar rc
|
||||
RANLIB= ranlib
|
||||
RM= rm -f
|
||||
|
||||
MYCFLAGS= $(LOCAL)
|
||||
MYLDFLAGS=
|
||||
MYLIBS=
|
||||
|
||||
|
||||
# enable Linux goodies
|
||||
MYCFLAGS= $(LOCAL) -DLUA_USE_DLOPEN -DLUA_USE_READLINE
|
||||
MYLDFLAGS= -Wl,-E
|
||||
MYLIBS= -ldl -lreadline -lhistory -lncurses
|
||||
|
||||
|
||||
|
||||
# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========
|
||||
@@ -41,24 +72,25 @@ MYLIBS= -ldl -lreadline -lhistory -lncurses
|
||||
LIBS = -lm
|
||||
|
||||
CORE_T= liblua.a
|
||||
CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
|
||||
lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \
|
||||
lundump.o lvm.o lzio.o ltests.o
|
||||
CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o \
|
||||
lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o \
|
||||
ltm.o lundump.o lvm.o lzio.o ltests.o
|
||||
AUX_O= lauxlib.o
|
||||
LIB_O= lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o lstrlib.o \
|
||||
loadlib.o linit.o
|
||||
lutf8lib.o loadlib.o lcorolib.o linit.o
|
||||
|
||||
LUA_T= lua
|
||||
LUA_O= lua.o
|
||||
|
||||
LUAC_T= luac
|
||||
LUAC_O= luac.o print.o
|
||||
# LUAC_T= luac
|
||||
# LUAC_O= luac.o print.o
|
||||
|
||||
ALL_T= $(CORE_T) $(LUA_T) $(LUAC_T)
|
||||
ALL_O= $(CORE_O) $(LUA_O) $(LUAC_O) $(AUX_O) $(LIB_O)
|
||||
ALL_A= $(CORE_T)
|
||||
|
||||
all: $(ALL_T)
|
||||
touch all
|
||||
|
||||
o: $(ALL_O)
|
||||
|
||||
@@ -74,6 +106,16 @@ $(LUA_T): $(LUA_O) $(CORE_T)
|
||||
$(LUAC_T): $(LUAC_O) $(CORE_T)
|
||||
$(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(CORE_T) $(LIBS) $(MYLIBS)
|
||||
|
||||
llex.o:
|
||||
$(CC) $(CFLAGS) -Os -c llex.c
|
||||
|
||||
lparser.o:
|
||||
$(CC) $(CFLAGS) -Os -c lparser.c
|
||||
|
||||
lcode.o:
|
||||
$(CC) $(CFLAGS) -Os -c lcode.c
|
||||
|
||||
|
||||
clean:
|
||||
$(RM) $(ALL_T) $(ALL_O)
|
||||
|
||||
@@ -91,64 +133,75 @@ echo:
|
||||
@echo "MYLIBS = $(MYLIBS)"
|
||||
@echo "DL = $(DL)"
|
||||
|
||||
# DO NOT DELETE
|
||||
$(ALL_O): makefile ltests.h
|
||||
|
||||
lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \
|
||||
lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \
|
||||
lundump.h lvm.h
|
||||
lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h
|
||||
lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h
|
||||
lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
|
||||
lzio.h lmem.h lopcodes.h lparser.h ltable.h ldebug.h lstate.h ltm.h \
|
||||
ldo.h lgc.h
|
||||
ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h
|
||||
ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \
|
||||
llex.h lzio.h lmem.h lopcodes.h lparser.h ltable.h ldebug.h lstate.h \
|
||||
ltm.h ldo.h lfunc.h lstring.h lgc.h lvm.h
|
||||
ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
|
||||
lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h ltable.h \
|
||||
lstring.h lundump.h lvm.h
|
||||
ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lopcodes.h lstate.h \
|
||||
ltm.h lzio.h lmem.h lundump.h
|
||||
lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \
|
||||
lstate.h ltm.h lzio.h
|
||||
lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
|
||||
lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
|
||||
linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h
|
||||
liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h
|
||||
llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \
|
||||
lzio.h lmem.h llex.h lparser.h ltable.h lstring.h lgc.h
|
||||
lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h
|
||||
lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
|
||||
ltm.h lzio.h lmem.h ldo.h
|
||||
loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h
|
||||
lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \
|
||||
ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h
|
||||
lopcodes.o: lopcodes.c lua.h luaconf.h lobject.h llimits.h lopcodes.h
|
||||
loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h
|
||||
lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
|
||||
lzio.h lmem.h lopcodes.h lparser.h ltable.h ldebug.h lstate.h ltm.h \
|
||||
ldo.h lfunc.h lstring.h lgc.h
|
||||
lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
|
||||
ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h
|
||||
lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \
|
||||
ltm.h lzio.h lstring.h lgc.h
|
||||
lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h
|
||||
ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
|
||||
ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h
|
||||
ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h
|
||||
ltests.o: ltests.c lua.h luaconf.h lapi.h lobject.h llimits.h lauxlib.h \
|
||||
lcode.h llex.h lzio.h lmem.h lopcodes.h lparser.h ltable.h ldebug.h \
|
||||
lstate.h ltm.h ldo.h lfunc.h lstring.h lgc.h lualib.h
|
||||
ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \
|
||||
lmem.h lstring.h lgc.h ltable.h
|
||||
lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h
|
||||
lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lopcodes.h lstring.h lgc.h \
|
||||
lundump.h
|
||||
lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
|
||||
lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h
|
||||
lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \
|
||||
lzio.h
|
||||
# DO NOT EDIT
|
||||
# automatically made with 'gcc -MM l*.c'
|
||||
|
||||
lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \
|
||||
ltable.h lundump.h lvm.h
|
||||
lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h
|
||||
lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
||||
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
||||
ldo.h lgc.h lstring.h ltable.h lvm.h
|
||||
lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h
|
||||
ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \
|
||||
ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h
|
||||
ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \
|
||||
lparser.h lstring.h ltable.h lundump.h lvm.h
|
||||
ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \
|
||||
ltm.h lzio.h lmem.h lundump.h
|
||||
lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h
|
||||
lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
|
||||
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h
|
||||
liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \
|
||||
lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \
|
||||
lstring.h ltable.h
|
||||
lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h
|
||||
loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \
|
||||
ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \
|
||||
lvm.h
|
||||
lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h
|
||||
loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
||||
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
||||
ldo.h lfunc.h lstring.h lgc.h ltable.h
|
||||
lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \
|
||||
lstring.h ltable.h
|
||||
lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
|
||||
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h
|
||||
lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
|
||||
ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
ltests.o: ltests.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h lauxlib.h lcode.h llex.h lopcodes.h \
|
||||
lparser.h lctype.h ldebug.h ldo.h lfunc.h lopnames.h lstring.h lgc.h \
|
||||
ltable.h lualib.h
|
||||
ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
|
||||
lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
|
||||
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \
|
||||
lundump.h
|
||||
lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \
|
||||
ltable.h lvm.h ljumptab.h
|
||||
lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \
|
||||
lobject.h ltm.h lzio.h
|
||||
|
||||
# (end of Makefile)
|
||||
|
||||
Executable
+519
@@ -0,0 +1,519 @@
|
||||
#!/usr/bin/env lua5.3
|
||||
|
||||
|
||||
-- special marks:
|
||||
-- \1 - paragraph (empty line)
|
||||
-- \4 - remove spaces around it
|
||||
-- \3 - ref (followed by label|)
|
||||
|
||||
---------------------------------------------------------------
|
||||
header = [[
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Lua 5.4 Reference Manual</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
<link rel="stylesheet" href="lua.css">
|
||||
<link rel="stylesheet" href="manual.css">
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF">
|
||||
|
||||
<hr>
|
||||
<h1>
|
||||
<a href="http://www.lua.org/home.html"><img src="logo.gif" alt="[Lua logo]" border="0"></a>
|
||||
Lua 5.4 Reference Manual
|
||||
</h1>
|
||||
|
||||
by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
|
||||
<p>
|
||||
<small>
|
||||
<a href="http://www.lua.org/copyright.html">Copyright</a>
|
||||
© 2019 Lua.org, PUC-Rio. All rights reserved.
|
||||
</small>
|
||||
<hr>
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<p>
|
||||
|
||||
]]
|
||||
|
||||
footer = "\n\n</body></html>\n\n"
|
||||
|
||||
local seefmt = '(see %s)'
|
||||
|
||||
if arg[1] == 'port' then
|
||||
seefmt = '(ver %s)'
|
||||
header = string.gsub(header, "by (.-)\n",
|
||||
"%1\n<p>Tradução: Sérgio Queiroz de Medeiros", 1)
|
||||
header = string.gsub(header, "Lua (%d+.%d+) Reference Manual",
|
||||
"Manual de Referência de Lua %1")
|
||||
header = string.gsub(header, "All rights reserved",
|
||||
"Todos os direitos reservados")
|
||||
end
|
||||
|
||||
|
||||
---------------------------------------------------------------
|
||||
|
||||
local function compose (f,g)
|
||||
assert(f and g)
|
||||
return function (s) return g(f(s)) end
|
||||
end
|
||||
|
||||
local function concat (f, g)
|
||||
assert(f and g)
|
||||
return function (s) return f(s) .. g(s) end
|
||||
end
|
||||
|
||||
|
||||
local Tag = {}
|
||||
|
||||
|
||||
setmetatable(Tag, {
|
||||
__index = function (t, tag)
|
||||
local v = function (n, att)
|
||||
local e = ""
|
||||
if type(att) == "table" then
|
||||
for k,v in pairs(att) do e = string.format('%s %s="%s"', e, k, v) end
|
||||
end
|
||||
if n then
|
||||
return string.format("<%s%s>%s</%s>", tag, e, n, tag)
|
||||
else
|
||||
return string.format("<%s%s>", tag, e)
|
||||
end
|
||||
end
|
||||
t[tag] = v
|
||||
return v
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
|
||||
---------------------------------------------------------------
|
||||
local labels = {}
|
||||
|
||||
|
||||
local function anchor (text, label, link, textlink)
|
||||
if labels[label] then
|
||||
error("label " .. label .. " already defined")
|
||||
end
|
||||
labels[label] = {text = textlink, link = link}
|
||||
return Tag.a(text, {name=link})
|
||||
end
|
||||
|
||||
local function makeref (label)
|
||||
assert(not string.find(label, "|"))
|
||||
return string.format("\3%s\3", label)
|
||||
end
|
||||
|
||||
local function ref (label)
|
||||
local l = labels[label]
|
||||
if not l then
|
||||
io.stderr:write("label ", label, " undefined\n")
|
||||
return "@@@@@@@"
|
||||
else
|
||||
return Tag.a(l.text, {href="#"..l.link})
|
||||
end
|
||||
end
|
||||
|
||||
---------------------------------------------------------------
|
||||
local function nopara (t)
|
||||
t = string.gsub(t, "\1", "\n\n")
|
||||
t = string.gsub(t, "<p>%s*</p>", "")
|
||||
return t
|
||||
end
|
||||
|
||||
local function fixpara (t)
|
||||
t = string.gsub(t, "\1", "\n</p>\n\n<p>\n")
|
||||
t = string.gsub(t, "<p>%s*</p>", "")
|
||||
return t
|
||||
end
|
||||
|
||||
local function antipara (t)
|
||||
return "</p>\n" .. t .. "<p>"
|
||||
end
|
||||
|
||||
|
||||
Tag.pre = compose(Tag.pre, antipara)
|
||||
Tag.ul = compose(Tag.ul, antipara)
|
||||
|
||||
---------------------------------------------------------------
|
||||
local Gfoots = 0
|
||||
local footnotes = {}
|
||||
|
||||
local line = Tag.hr(nil)
|
||||
|
||||
local function dischargefoots ()
|
||||
if #footnotes == 0 then return "" end
|
||||
local fn = table.concat(footnotes)
|
||||
footnotes = {}
|
||||
return line .. Tag.h3"footnotes:" .. fn .. line
|
||||
end
|
||||
|
||||
|
||||
local Glists = 0
|
||||
local listings = {}
|
||||
|
||||
local function dischargelist ()
|
||||
if #listings == 0 then return "" end
|
||||
local l = listings
|
||||
listings = {}
|
||||
return line .. table.concat(l, line..line) .. line
|
||||
end
|
||||
|
||||
---------------------------------------------------------------
|
||||
local counters = {
|
||||
h1 = {val = 1},
|
||||
h2 = {father = "h1", val = 1},
|
||||
h3 = {father = "h2", val = 1},
|
||||
listing = {father = "h1", val = 1},
|
||||
}
|
||||
|
||||
local function inccounter (count)
|
||||
counters[count].val = counters[count].val + 1
|
||||
for c, v in pairs(counters) do
|
||||
if v.father == count then v.val = 1 end
|
||||
end
|
||||
end
|
||||
|
||||
local function getcounter (count)
|
||||
local c = counters[count]
|
||||
if c.father then
|
||||
return getcounter(c.father) .. "." .. c.val
|
||||
else
|
||||
return c.val .. ""
|
||||
end
|
||||
end
|
||||
---------------------------------------------------------------
|
||||
|
||||
|
||||
local function fixed (x)
|
||||
return function () return x end
|
||||
end
|
||||
|
||||
local function id (x) return x end
|
||||
|
||||
|
||||
local function prepos (x, y)
|
||||
assert(x and y)
|
||||
return function (s) return string.format("%s%s%s", x, s, y) end
|
||||
end
|
||||
|
||||
|
||||
local rw = Tag.b
|
||||
|
||||
|
||||
|
||||
|
||||
local function LuaName (name)
|
||||
return Tag.code(name)
|
||||
end
|
||||
|
||||
|
||||
local function getparam (s)
|
||||
local i, e = string.find(s, "^[^%s@|]+|")
|
||||
if not i then return nil, s
|
||||
else return string.sub(s, i, e - 1), string.sub(s, e + 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function gettitle (h)
|
||||
local title, p = assert(string.match(h, "<title>(.-)</title>()"))
|
||||
return title, string.sub(h, p)
|
||||
end
|
||||
|
||||
local function getparamtitle (what, h, nonum)
|
||||
local label, title, c, count
|
||||
label, h = getparam(h)
|
||||
title, h = gettitle(h)
|
||||
if not nonum then
|
||||
count = getcounter(what)
|
||||
inccounter(what)
|
||||
c = string.format("%s – ", count)
|
||||
else
|
||||
c = ""
|
||||
end
|
||||
label = label or count
|
||||
if label then
|
||||
title = anchor(title, label, count, "§"..count)
|
||||
end
|
||||
title = string.format("%s%s", c, title)
|
||||
return title, h
|
||||
end
|
||||
|
||||
local function section (what, nonum)
|
||||
return function (h)
|
||||
local title
|
||||
title, h = getparamtitle(what, h, nonum)
|
||||
local fn = what == "h1" and dischargefoots() or ""
|
||||
h = fixpara(Tag.p(h))
|
||||
return "</p>\n" .. Tag[what](title) .. h .. fn ..
|
||||
dischargelist() .. "<p>"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function verbatim (s)
|
||||
s = nopara(s)
|
||||
s = string.gsub(s, "\n", "\n ")
|
||||
s = string.gsub(s, "\n%s*$", "\n")
|
||||
return Tag.pre(s)
|
||||
end
|
||||
|
||||
|
||||
local function verb (s)
|
||||
return Tag.code(s)
|
||||
end
|
||||
|
||||
|
||||
local function lua2link (e)
|
||||
return string.find(e, "luaL?_") and e or "pdf-"..e
|
||||
end
|
||||
|
||||
|
||||
local verbfixed = verb
|
||||
|
||||
|
||||
local Tex = {
|
||||
|
||||
ANSI = function (func)
|
||||
return "ISO C function " .. Tag.code(func)
|
||||
end,
|
||||
At = fixed"@",
|
||||
B = Tag.b,
|
||||
bigskip = fixed"",
|
||||
bignum = id,
|
||||
C = fixed"",
|
||||
Ci = prepos("<!-- ", " -->"),
|
||||
CId = function (func)
|
||||
return "C function " .. Tag.code(func)
|
||||
end,
|
||||
chapter = section"h1",
|
||||
Char = compose(verbfixed, prepos("'", "'")),
|
||||
Cdots = fixed"···",
|
||||
Close = fixed"}",
|
||||
col = Tag.td,
|
||||
defid = function (name)
|
||||
local l = lua2link(name)
|
||||
local c = Tag.code(name)
|
||||
return anchor(c, l, l, c)
|
||||
end,
|
||||
def = Tag.em,
|
||||
description = compose(nopara, Tag.ul),
|
||||
Em = fixed("\4" .. "—" .. "\4"),
|
||||
emph = Tag.em,
|
||||
emphx = Tag.em, -- emphasis plus index (if there was an index)
|
||||
En = fixed("–"),
|
||||
format = fixed"",
|
||||
["false"] = fixed(Tag.b"false"),
|
||||
id = Tag.code,
|
||||
idx = Tag.code,
|
||||
index = fixed"",
|
||||
Lidx = fixed"", -- Tag.code,
|
||||
ldots = fixed"...",
|
||||
x = id,
|
||||
itemize = compose(nopara, Tag.ul),
|
||||
leq = fixed"≤",
|
||||
Lid = function (s)
|
||||
return makeref(lua2link(s))
|
||||
end,
|
||||
M = Tag.em,
|
||||
N = function (s) return (string.gsub(s, " ", " ")) end,
|
||||
NE = id, -- tag"foreignphrase",
|
||||
num = id,
|
||||
["nil"] = fixed(Tag.b"nil"),
|
||||
fail = fixed(Tag.b"fail"),
|
||||
Open = fixed"{",
|
||||
part = section("h1", true),
|
||||
Pat = compose(verbfixed, prepos("'", "'")),
|
||||
preface = section("h1", true),
|
||||
psect = section("h2", true),
|
||||
Q = prepos('"', '"'),
|
||||
refchp = makeref,
|
||||
refcode = makeref,
|
||||
refsec = makeref,
|
||||
|
||||
pi = fixed"π",
|
||||
rep = Tag.em, -- compose(prepos("<", ">"), Tag.em),
|
||||
Rw = rw,
|
||||
rw = rw,
|
||||
sb = Tag.sub,
|
||||
sp = Tag.sup,
|
||||
St = compose(verbfixed, prepos('"', '"')),
|
||||
sect1 = section"h1",
|
||||
sect2 = section"h2",
|
||||
sect3 = section"h3",
|
||||
sect4 = section("h4", true),
|
||||
simplesect = id,
|
||||
Tab2 = function (s) return Tag.table(s, {border=1}) end,
|
||||
row = Tag.tr,
|
||||
title = Tag.title,
|
||||
todo = Tag.todo,
|
||||
["true"] = fixed(Tag.b"true"),
|
||||
T = verb,
|
||||
|
||||
item = function (s)
|
||||
local t, p = string.match(s, "^([^\n|]+)|()")
|
||||
if t then
|
||||
s = string.sub(s, p)
|
||||
s = Tag.b(t..": ") .. s
|
||||
end
|
||||
return Tag.li(fixpara(s))
|
||||
end,
|
||||
|
||||
verbatim = verbatim,
|
||||
|
||||
manual = id,
|
||||
|
||||
|
||||
-- for the manual
|
||||
|
||||
link =function (s)
|
||||
local l, t = getparam(s)
|
||||
assert(l)
|
||||
return string.format("%s (%s)", t, makeref(l))
|
||||
end,
|
||||
|
||||
see = function (s) return string.format(seefmt, makeref(s)) end,
|
||||
See = makeref,
|
||||
seeC = function (s)
|
||||
return string.format(seefmt, makeref(s))
|
||||
end,
|
||||
|
||||
seeF = function (s)
|
||||
return string.format(seefmt, makeref(lua2link(s)))
|
||||
end,
|
||||
|
||||
APIEntry = function (e)
|
||||
local h, name
|
||||
h, e = string.match(e, "^%s*(.-)%s*|(.*)$")
|
||||
name = string.match(h, "(luaL?_[%w_]+)%)? +%(") or
|
||||
string.match(h, "luaL?_[%w_]+")
|
||||
local a = anchor(Tag.code(name), name, name, Tag.code(name))
|
||||
local apiicmd, ne = string.match(e, "^(.-</span>)(.*)")
|
||||
--io.stderr:write(e)
|
||||
if not apiicmd then
|
||||
return antipara(Tag.hr() .. Tag.h3(a)) .. Tag.pre(h) .. e
|
||||
else
|
||||
return antipara(Tag.hr() .. Tag.h3(a)) .. apiicmd .. Tag.pre(h) .. ne
|
||||
end
|
||||
end,
|
||||
|
||||
LibEntry = function (e)
|
||||
local h, name
|
||||
h, e = string.match(e, "^(.-)|(.*)$")
|
||||
name = string.gsub(h, " (.+", "")
|
||||
local l = lua2link(name)
|
||||
local a = anchor(Tag.code(h), l, l, Tag.code(name))
|
||||
return Tag.hr() .. Tag.h3(a) .. e
|
||||
end,
|
||||
|
||||
Produc = compose(nopara, Tag.pre),
|
||||
producname = prepos("\t", " ::= "),
|
||||
Or = fixed" | ",
|
||||
VerBar = fixed"|", -- vertical bar
|
||||
OrNL = fixed" | \4",
|
||||
bnfNter = prepos("", ""),
|
||||
bnfopt = prepos("[", "]"),
|
||||
bnfrep = prepos("{", "}"),
|
||||
bnfter = compose(Tag.b, prepos("‘", "’")),
|
||||
producbody = function (s)
|
||||
s = string.gsub(s, "%s+", " ")
|
||||
s = string.gsub(s, "\4", "\n\t\t")
|
||||
return s
|
||||
end,
|
||||
|
||||
apii = function (s)
|
||||
local pop,push,err = string.match(s, "^(.-),(.-),(.*)$")
|
||||
if pop ~= "?" and string.find(pop, "%W") then
|
||||
pop = "(" .. pop .. ")"
|
||||
end
|
||||
if push ~= "?" and string.find(push, "%W") then
|
||||
push = "(" .. push .. ")"
|
||||
end
|
||||
err = (err == "-") and "–" or Tag.em(err)
|
||||
return Tag.span(
|
||||
string.format("[-%s, +%s, %s]", pop, push, err),
|
||||
{class="apii"}
|
||||
)
|
||||
end,
|
||||
}
|
||||
|
||||
local others = prepos("?? "," ??")
|
||||
|
||||
local function trata (t)
|
||||
t = string.gsub(t, "@(%w+)(%b{})", function (w, f)
|
||||
f = trata(string.sub(f, 2, -2))
|
||||
if type(Tex[w]) ~= "function" then
|
||||
io.stderr:write(w .. "\n")
|
||||
return others(f)
|
||||
else
|
||||
return Tex[w](f, w)
|
||||
end
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
---------------------------------------------------------------------
|
||||
---------------------------------------------------------------------
|
||||
|
||||
-- read whole book
|
||||
t = io.read"*a"
|
||||
|
||||
t = string.gsub(t, "[<>&\128-\255]",
|
||||
{["<"] = "<",
|
||||
[">"] = ">",
|
||||
["&"] = "&",
|
||||
["\170"] = "ª",
|
||||
["\186"] = "º",
|
||||
["\192"] = "À",
|
||||
["\193"] = "Á",
|
||||
["\194"] = "Â",
|
||||
["\195"] = "Ã",
|
||||
["\199"] = "Ç",
|
||||
["\201"] = "É",
|
||||
["\202"] = "Ê",
|
||||
["\205"] = "Í",
|
||||
["\211"] = "Ó",
|
||||
["\212"] = "Ô",
|
||||
["\218"] = "Ú",
|
||||
["\224"] = "à",
|
||||
["\225"] = "á",
|
||||
["\226"] = "â",
|
||||
["\227"] = "ã",
|
||||
["\231"] = "ç",
|
||||
["\233"] = "é",
|
||||
["\234"] = "ê",
|
||||
["\237"] = "í",
|
||||
["\243"] = "ó",
|
||||
["\244"] = "ô",
|
||||
["\245"] = "õ",
|
||||
["\250"] = "ú",
|
||||
["\252"] = "ü"
|
||||
})
|
||||
|
||||
t = string.gsub(t, "\n\n+", "\1")
|
||||
|
||||
|
||||
|
||||
-- complete macros with no arguments
|
||||
t = string.gsub(t, "(@%w+)([^{%w])", "%1{}%2")
|
||||
|
||||
t = trata(t)
|
||||
|
||||
-- correct references
|
||||
t = string.gsub(t, "\3(.-)\3", ref)
|
||||
|
||||
-- remove extra space (??)
|
||||
t = string.gsub(t, "%s*\4%s*", "")
|
||||
|
||||
t = nopara(t)
|
||||
|
||||
-- HTML 3.2 does not need </p> (but complains when it is in wrong places :)
|
||||
t = string.gsub(t, "</p>", "")
|
||||
|
||||
io.write(header, t, footer)
|
||||
|
||||
+9165
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* one.c -- Lua core, libraries, and interpreter in a single file
|
||||
*/
|
||||
|
||||
/* default is to build the full interpreter */
|
||||
#ifndef MAKE_LIB
|
||||
#ifndef MAKE_LUAC
|
||||
#ifndef MAKE_LUA
|
||||
#define MAKE_LUA
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* choose suitable platform-specific features */
|
||||
/* some of these may need extra libraries such as -ldl -lreadline -lncurses */
|
||||
#if 0
|
||||
#define LUA_USE_LINUX
|
||||
#define LUA_USE_MACOSX
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_ANSI
|
||||
#endif
|
||||
|
||||
/* no need to change anything below this line ----------------------------- */
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <math.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/* setup for luaconf.h */
|
||||
#define LUA_CORE
|
||||
#define LUA_LIB
|
||||
#define ltable_c
|
||||
#define lvm_c
|
||||
#include "luaconf.h"
|
||||
|
||||
/* do not export internal symbols */
|
||||
#undef LUAI_FUNC
|
||||
#undef LUAI_DDEC
|
||||
#undef LUAI_DDEF
|
||||
#define LUAI_FUNC static
|
||||
#define LUAI_DDEC(def) /* empty */
|
||||
#define LUAI_DDEF static
|
||||
|
||||
/* core -- used by all */
|
||||
#include "lzio.c"
|
||||
#include "lctype.c"
|
||||
#include "lopcodes.c"
|
||||
#include "lmem.c"
|
||||
#include "lundump.c"
|
||||
#include "ldump.c"
|
||||
#include "lstate.c"
|
||||
#include "lgc.c"
|
||||
#include "llex.c"
|
||||
#include "lcode.c"
|
||||
#include "lparser.c"
|
||||
#include "ldebug.c"
|
||||
#include "lfunc.c"
|
||||
#include "lobject.c"
|
||||
#include "ltm.c"
|
||||
#include "lstring.c"
|
||||
#include "ltable.c"
|
||||
#include "ldo.c"
|
||||
#include "lvm.c"
|
||||
#include "lapi.c"
|
||||
|
||||
/* auxiliary library -- used by all */
|
||||
#include "lauxlib.c"
|
||||
|
||||
/* standard library -- not used by luac */
|
||||
#ifndef MAKE_LUAC
|
||||
#include "lbaselib.c"
|
||||
#include "lcorolib.c"
|
||||
#include "ldblib.c"
|
||||
#include "liolib.c"
|
||||
#include "lmathlib.c"
|
||||
#include "loadlib.c"
|
||||
#include "loslib.c"
|
||||
#include "lstrlib.c"
|
||||
#include "ltablib.c"
|
||||
#include "lutf8lib.c"
|
||||
#include "linit.c"
|
||||
#endif
|
||||
|
||||
/* lua */
|
||||
#ifdef MAKE_LUA
|
||||
#include "lua.c"
|
||||
#endif
|
||||
|
||||
/* luac */
|
||||
#ifdef MAKE_LUAC
|
||||
#include "luac.c"
|
||||
#endif
|
||||
+321
@@ -0,0 +1,321 @@
|
||||
#!../lua
|
||||
-- $Id: testes/all.lua $
|
||||
-- See Copyright Notice at the end of this file
|
||||
|
||||
|
||||
local version = "Lua 5.4"
|
||||
if _VERSION ~= version then
|
||||
io.stderr:write("This test suite is for ", version,
|
||||
", not for ", _VERSION, "\nExiting tests")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
_G.ARG = arg -- save arg for other tests
|
||||
|
||||
|
||||
-- next variables control the execution of some tests
|
||||
-- true means no test (so an undefined variable does not skip a test)
|
||||
-- defaults are for Linux; test everything.
|
||||
-- Make true to avoid long or memory consuming tests
|
||||
_soft = rawget(_G, "_soft") or false
|
||||
-- Make true to avoid non-portable tests
|
||||
_port = rawget(_G, "_port") or false
|
||||
-- Make true to avoid messages about tests not performed
|
||||
_nomsg = rawget(_G, "_nomsg") or false
|
||||
|
||||
|
||||
local usertests = rawget(_G, "_U")
|
||||
|
||||
if usertests then
|
||||
-- tests for sissies ;) Avoid problems
|
||||
_soft = true
|
||||
_port = true
|
||||
_nomsg = true
|
||||
end
|
||||
|
||||
-- tests should require debug when needed
|
||||
debug = nil
|
||||
|
||||
|
||||
if usertests then
|
||||
T = nil -- no "internal" tests for user tests
|
||||
else
|
||||
T = rawget(_G, "T") -- avoid problems with 'strict' module
|
||||
end
|
||||
|
||||
|
||||
--[=[
|
||||
example of a long [comment],
|
||||
[[spanning several [lines]]]
|
||||
|
||||
]=]
|
||||
|
||||
print("\n\tStarting Tests")
|
||||
|
||||
do
|
||||
-- set random seed
|
||||
local random_x, random_y = math.randomseed()
|
||||
print(string.format("random seeds: %d, %d", random_x, random_y))
|
||||
end
|
||||
|
||||
print("current path:\n****" .. package.path .. "****\n")
|
||||
|
||||
|
||||
local initclock = os.clock()
|
||||
local lastclock = initclock
|
||||
local walltime = os.time()
|
||||
|
||||
local collectgarbage = collectgarbage
|
||||
|
||||
do -- (
|
||||
|
||||
-- track messages for tests not performed
|
||||
local msgs = {}
|
||||
function Message (m)
|
||||
if not _nomsg then
|
||||
print(m)
|
||||
msgs[#msgs+1] = string.sub(m, 3, -3)
|
||||
end
|
||||
end
|
||||
|
||||
assert(os.setlocale"C")
|
||||
|
||||
local T,print,format,write,assert,type,unpack,floor =
|
||||
T,print,string.format,io.write,assert,type,table.unpack,math.floor
|
||||
|
||||
-- use K for 1000 and M for 1000000 (not 2^10 -- 2^20)
|
||||
local function F (m)
|
||||
local function round (m)
|
||||
m = m + 0.04999
|
||||
return format("%.1f", m) -- keep one decimal digit
|
||||
end
|
||||
if m < 1000 then return m
|
||||
else
|
||||
m = m / 1000
|
||||
if m < 1000 then return round(m).."K"
|
||||
else
|
||||
return round(m/1000).."M"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local Cstacklevel
|
||||
|
||||
local showmem
|
||||
if not T then
|
||||
local max = 0
|
||||
showmem = function ()
|
||||
local m = collectgarbage("count") * 1024
|
||||
max = (m > max) and m or max
|
||||
print(format(" ---- total memory: %s, max memory: %s ----\n",
|
||||
F(m), F(max)))
|
||||
end
|
||||
Cstacklevel = function () return 0 end -- no info about stack level
|
||||
else
|
||||
showmem = function ()
|
||||
T.checkmemory()
|
||||
local total, numblocks, maxmem = T.totalmem()
|
||||
local count = collectgarbage("count")
|
||||
print(format(
|
||||
"\n ---- total memory: %s (%.0fK), max use: %s, blocks: %d\n",
|
||||
F(total), count, F(maxmem), numblocks))
|
||||
print(format("\t(strings: %d, tables: %d, functions: %d, "..
|
||||
"\n\tudata: %d, threads: %d)",
|
||||
T.totalmem"string", T.totalmem"table", T.totalmem"function",
|
||||
T.totalmem"userdata", T.totalmem"thread"))
|
||||
end
|
||||
|
||||
Cstacklevel = function ()
|
||||
local _, _, ncalls, nci = T.stacklevel()
|
||||
return ncalls + nci -- number of free slots in the C stack
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local Cstack = Cstacklevel()
|
||||
|
||||
--
|
||||
-- redefine dofile to run files through dump/undump
|
||||
--
|
||||
local function report (n) print("\n***** FILE '"..n.."'*****") end
|
||||
local olddofile = dofile
|
||||
local dofile = function (n, strip)
|
||||
showmem()
|
||||
local c = os.clock()
|
||||
print(string.format("time: %g (+%g)", c - initclock, c - lastclock))
|
||||
lastclock = c
|
||||
report(n)
|
||||
local f = assert(loadfile(n))
|
||||
local b = string.dump(f, strip)
|
||||
f = assert(load(b))
|
||||
return f()
|
||||
end
|
||||
|
||||
dofile('main.lua')
|
||||
|
||||
do
|
||||
local next, setmetatable, stderr = next, setmetatable, io.stderr
|
||||
-- track collections
|
||||
local mt = {}
|
||||
-- each time a table is collected, remark it for finalization
|
||||
-- on next cycle
|
||||
mt.__gc = function (o)
|
||||
stderr:write'.' -- mark progress
|
||||
local n = setmetatable(o, mt) -- remark it
|
||||
end
|
||||
local n = setmetatable({}, mt) -- create object
|
||||
end
|
||||
|
||||
report"gc.lua"
|
||||
local f = assert(loadfile('gc.lua'))
|
||||
f()
|
||||
|
||||
dofile('db.lua')
|
||||
assert(dofile('calls.lua') == deep and deep)
|
||||
olddofile('strings.lua')
|
||||
olddofile('literals.lua')
|
||||
dofile('tpack.lua')
|
||||
assert(dofile('attrib.lua') == 27)
|
||||
dofile('gengc.lua')
|
||||
assert(dofile('locals.lua') == 5)
|
||||
dofile('constructs.lua')
|
||||
dofile('code.lua', true)
|
||||
if not _G._soft then
|
||||
report('big.lua')
|
||||
local f = coroutine.wrap(assert(loadfile('big.lua')))
|
||||
assert(f() == 'b')
|
||||
assert(f() == 'a')
|
||||
end
|
||||
dofile('cstack.lua')
|
||||
dofile('nextvar.lua')
|
||||
dofile('pm.lua')
|
||||
dofile('utf8.lua')
|
||||
dofile('api.lua')
|
||||
assert(dofile('events.lua') == 12)
|
||||
dofile('vararg.lua')
|
||||
dofile('closure.lua')
|
||||
dofile('coroutine.lua')
|
||||
dofile('goto.lua', true)
|
||||
dofile('errors.lua')
|
||||
dofile('math.lua')
|
||||
dofile('sort.lua', true)
|
||||
dofile('bitwise.lua')
|
||||
assert(dofile('verybig.lua', true) == 10); collectgarbage()
|
||||
dofile('files.lua')
|
||||
|
||||
if #msgs > 0 then
|
||||
local m = table.concat(msgs, "\n ")
|
||||
warn("#tests not performed:\n ", m, "\n")
|
||||
end
|
||||
|
||||
print("(there should be two warnings now)")
|
||||
warn("@on")
|
||||
warn("#This is ", "an expected", " warning")
|
||||
warn("@off")
|
||||
warn("******** THIS WARNING SHOULD NOT APPEAR **********")
|
||||
warn("******** THIS WARNING ALSO SHOULD NOT APPEAR **********")
|
||||
warn("@on")
|
||||
warn("#This is", " another one")
|
||||
|
||||
-- no test module should define 'debug'
|
||||
assert(debug == nil)
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
print(string.format("%d-bit integers, %d-bit floats",
|
||||
string.packsize("j") * 8, string.packsize("n") * 8))
|
||||
|
||||
debug.sethook(function (a) assert(type(a) == 'string') end, "cr")
|
||||
|
||||
-- to survive outside block
|
||||
_G.showmem = showmem
|
||||
|
||||
|
||||
assert(Cstack == Cstacklevel(),
|
||||
"should be at the same C-stack level it was when started the tests")
|
||||
|
||||
end --)
|
||||
|
||||
local _G, showmem, print, format, clock, time, difftime,
|
||||
assert, open, warn =
|
||||
_G, showmem, print, string.format, os.clock, os.time, os.difftime,
|
||||
assert, io.open, warn
|
||||
|
||||
-- file with time of last performed test
|
||||
local fname = T and "time-debug.txt" or "time.txt"
|
||||
local lasttime
|
||||
|
||||
if not usertests then
|
||||
-- open file with time of last performed test
|
||||
local f = io.open(fname)
|
||||
if f then
|
||||
lasttime = assert(tonumber(f:read'a'))
|
||||
f:close();
|
||||
else -- no such file; assume it is recording time for first time
|
||||
lasttime = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- erase (almost) all globals
|
||||
print('cleaning all!!!!')
|
||||
for n in pairs(_G) do
|
||||
if not ({___Glob = 1, tostring = 1})[n] then
|
||||
_G[n] = undef
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
collectgarbage();showmem()
|
||||
|
||||
local clocktime = clock() - initclock
|
||||
walltime = difftime(time(), walltime)
|
||||
|
||||
print(format("\n\ntotal time: %.2fs (wall time: %gs)\n", clocktime, walltime))
|
||||
|
||||
if not usertests then
|
||||
lasttime = lasttime or clocktime -- if no last time, ignore difference
|
||||
-- check whether current test time differs more than 5% from last time
|
||||
local diff = (clocktime - lasttime) / lasttime
|
||||
local tolerance = 0.05 -- 5%
|
||||
if (diff >= tolerance or diff <= -tolerance) then
|
||||
warn(format("#time difference from previous test: %+.1f%%",
|
||||
diff * 100))
|
||||
end
|
||||
assert(open(fname, "w")):write(clocktime):close()
|
||||
end
|
||||
|
||||
print("final OK !!!")
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
*****************************************************************************
|
||||
* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************
|
||||
]]
|
||||
|
||||
+1460
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,492 @@
|
||||
-- $Id: testes/attrib.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print "testing require"
|
||||
|
||||
assert(require"string" == string)
|
||||
assert(require"math" == math)
|
||||
assert(require"table" == table)
|
||||
assert(require"io" == io)
|
||||
assert(require"os" == os)
|
||||
assert(require"coroutine" == coroutine)
|
||||
|
||||
assert(type(package.path) == "string")
|
||||
assert(type(package.cpath) == "string")
|
||||
assert(type(package.loaded) == "table")
|
||||
assert(type(package.preload) == "table")
|
||||
|
||||
assert(type(package.config) == "string")
|
||||
print("package config: "..string.gsub(package.config, "\n", "|"))
|
||||
|
||||
do
|
||||
-- create a path with 'max' templates,
|
||||
-- each with 1-10 repetitions of '?'
|
||||
local max = _soft and 100 or 2000
|
||||
local t = {}
|
||||
for i = 1,max do t[i] = string.rep("?", i%10 + 1) end
|
||||
t[#t + 1] = ";" -- empty template
|
||||
local path = table.concat(t, ";")
|
||||
-- use that path in a search
|
||||
local s, err = package.searchpath("xuxu", path)
|
||||
-- search fails; check that message has an occurence of
|
||||
-- '??????????' with ? replaced by xuxu and at least 'max' lines
|
||||
assert(not s and
|
||||
string.find(err, string.rep("xuxu", 10)) and
|
||||
#string.gsub(err, "[^\n]", "") >= max)
|
||||
-- path with one very long template
|
||||
local path = string.rep("?", max)
|
||||
local s, err = package.searchpath("xuxu", path)
|
||||
assert(not s and string.find(err, string.rep('xuxu', max)))
|
||||
end
|
||||
|
||||
do
|
||||
local oldpath = package.path
|
||||
package.path = {}
|
||||
local s, err = pcall(require, "no-such-file")
|
||||
assert(not s and string.find(err, "package.path"))
|
||||
package.path = oldpath
|
||||
end
|
||||
|
||||
print('+')
|
||||
|
||||
|
||||
-- The next tests for 'require' assume some specific directories and
|
||||
-- libraries.
|
||||
|
||||
if not _port then --[
|
||||
|
||||
local dirsep = string.match(package.config, "^([^\n]+)\n")
|
||||
|
||||
-- auxiliary directory with C modules and temporary files
|
||||
local DIR = "libs" .. dirsep
|
||||
|
||||
-- prepend DIR to a name and correct directory separators
|
||||
local function D (x)
|
||||
x = string.gsub(x, "/", dirsep)
|
||||
return DIR .. x
|
||||
end
|
||||
|
||||
-- prepend DIR and pospend proper C lib. extension to a name
|
||||
local function DC (x)
|
||||
local ext = (dirsep == '\\') and ".dll" or ".so"
|
||||
return D(x .. ext)
|
||||
end
|
||||
|
||||
|
||||
local function createfiles (files, preextras, posextras)
|
||||
for n,c in pairs(files) do
|
||||
io.output(D(n))
|
||||
io.write(string.format(preextras, n))
|
||||
io.write(c)
|
||||
io.write(string.format(posextras, n))
|
||||
io.close(io.output())
|
||||
end
|
||||
end
|
||||
|
||||
function removefiles (files)
|
||||
for n in pairs(files) do
|
||||
os.remove(D(n))
|
||||
end
|
||||
end
|
||||
|
||||
local files = {
|
||||
["names.lua"] = "do return {...} end\n",
|
||||
["err.lua"] = "B = 15; a = a + 1;",
|
||||
["synerr.lua"] = "B =",
|
||||
["A.lua"] = "",
|
||||
["B.lua"] = "assert(...=='B');require 'A'",
|
||||
["A.lc"] = "",
|
||||
["A"] = "",
|
||||
["L"] = "",
|
||||
["XXxX"] = "",
|
||||
["C.lua"] = "package.loaded[...] = 25; require'C'",
|
||||
}
|
||||
|
||||
AA = nil
|
||||
local extras = [[
|
||||
NAME = '%s'
|
||||
REQUIRED = ...
|
||||
return AA]]
|
||||
|
||||
createfiles(files, "", extras)
|
||||
|
||||
-- testing explicit "dir" separator in 'searchpath'
|
||||
assert(package.searchpath("C.lua", D"?", "", "") == D"C.lua")
|
||||
assert(package.searchpath("C.lua", D"?", ".", ".") == D"C.lua")
|
||||
assert(package.searchpath("--x-", D"?", "-", "X") == D"XXxX")
|
||||
assert(package.searchpath("---xX", D"?", "---", "XX") == D"XXxX")
|
||||
assert(package.searchpath(D"C.lua", "?", dirsep) == D"C.lua")
|
||||
assert(package.searchpath(".\\C.lua", D"?", "\\") == D"./C.lua")
|
||||
|
||||
local oldpath = package.path
|
||||
|
||||
package.path = string.gsub("D/?.lua;D/?.lc;D/?;D/??x?;D/L", "D/", DIR)
|
||||
|
||||
local try = function (p, n, r, ext)
|
||||
NAME = nil
|
||||
local rr, x = require(p)
|
||||
assert(NAME == n)
|
||||
assert(REQUIRED == p)
|
||||
assert(rr == r)
|
||||
assert(ext == x)
|
||||
end
|
||||
|
||||
a = require"names"
|
||||
assert(a[1] == "names" and a[2] == D"names.lua")
|
||||
|
||||
_G.a = nil
|
||||
local st, msg = pcall(require, "err")
|
||||
assert(not st and string.find(msg, "arithmetic") and B == 15)
|
||||
st, msg = pcall(require, "synerr")
|
||||
assert(not st and string.find(msg, "error loading module"))
|
||||
|
||||
assert(package.searchpath("C", package.path) == D"C.lua")
|
||||
assert(require"C" == 25)
|
||||
assert(require"C" == 25)
|
||||
AA = nil
|
||||
try('B', 'B.lua', true, "libs/B.lua")
|
||||
assert(package.loaded.B)
|
||||
assert(require"B" == true)
|
||||
assert(package.loaded.A)
|
||||
assert(require"C" == 25)
|
||||
package.loaded.A = nil
|
||||
try('B', nil, true, nil) -- should not reload package
|
||||
try('A', 'A.lua', true, "libs/A.lua")
|
||||
package.loaded.A = nil
|
||||
os.remove(D'A.lua')
|
||||
AA = {}
|
||||
try('A', 'A.lc', AA, "libs/A.lc") -- now must find second option
|
||||
assert(package.searchpath("A", package.path) == D"A.lc")
|
||||
assert(require("A") == AA)
|
||||
AA = false
|
||||
try('K', 'L', false, "libs/L") -- default option
|
||||
try('K', 'L', false, "libs/L") -- default option (should reload it)
|
||||
assert(rawget(_G, "_REQUIREDNAME") == nil)
|
||||
|
||||
AA = "x"
|
||||
try("X", "XXxX", AA, "libs/XXxX")
|
||||
|
||||
|
||||
removefiles(files)
|
||||
|
||||
|
||||
-- testing require of sub-packages
|
||||
|
||||
local _G = _G
|
||||
|
||||
package.path = string.gsub("D/?.lua;D/?/init.lua", "D/", DIR)
|
||||
|
||||
files = {
|
||||
["P1/init.lua"] = "AA = 10",
|
||||
["P1/xuxu.lua"] = "AA = 20",
|
||||
}
|
||||
|
||||
createfiles(files, "_ENV = {}\n", "\nreturn _ENV\n")
|
||||
AA = 0
|
||||
|
||||
local m, ext = assert(require"P1")
|
||||
assert(ext == "libs/P1/init.lua")
|
||||
assert(AA == 0 and m.AA == 10)
|
||||
assert(require"P1" == m)
|
||||
assert(require"P1" == m)
|
||||
|
||||
assert(package.searchpath("P1.xuxu", package.path) == D"P1/xuxu.lua")
|
||||
m.xuxu, ext = assert(require"P1.xuxu")
|
||||
assert(AA == 0 and m.xuxu.AA == 20)
|
||||
assert(ext == "libs/P1/xuxu.lua")
|
||||
assert(require"P1.xuxu" == m.xuxu)
|
||||
assert(require"P1.xuxu" == m.xuxu)
|
||||
assert(require"P1" == m and m.AA == 10)
|
||||
|
||||
|
||||
removefiles(files)
|
||||
|
||||
|
||||
package.path = ""
|
||||
assert(not pcall(require, "file_does_not_exist"))
|
||||
package.path = "??\0?"
|
||||
assert(not pcall(require, "file_does_not_exist1"))
|
||||
|
||||
package.path = oldpath
|
||||
|
||||
-- check 'require' error message
|
||||
local fname = "file_does_not_exist2"
|
||||
local m, err = pcall(require, fname)
|
||||
for t in string.gmatch(package.path..";"..package.cpath, "[^;]+") do
|
||||
t = string.gsub(t, "?", fname)
|
||||
assert(string.find(err, t, 1, true))
|
||||
end
|
||||
|
||||
do -- testing 'package.searchers' not being a table
|
||||
local searchers = package.searchers
|
||||
package.searchers = 3
|
||||
local st, msg = pcall(require, 'a')
|
||||
assert(not st and string.find(msg, "must be a table"))
|
||||
package.searchers = searchers
|
||||
end
|
||||
|
||||
local function import(...)
|
||||
local f = {...}
|
||||
return function (m)
|
||||
for i=1, #f do m[f[i]] = _G[f[i]] end
|
||||
end
|
||||
end
|
||||
|
||||
-- cannot change environment of a C function
|
||||
assert(not pcall(module, 'XUXU'))
|
||||
|
||||
|
||||
|
||||
-- testing require of C libraries
|
||||
|
||||
|
||||
local p = "" -- On Mac OS X, redefine this to "_"
|
||||
|
||||
-- check whether loadlib works in this system
|
||||
local st, err, when = package.loadlib(DC"lib1", "*")
|
||||
if not st then
|
||||
local f, err, when = package.loadlib("donotexist", p.."xuxu")
|
||||
assert(not f and type(err) == "string" and when == "absent")
|
||||
;(Message or print)('\n >>> cannot load dynamic library <<<\n')
|
||||
print(err, when)
|
||||
else
|
||||
-- tests for loadlib
|
||||
local f = assert(package.loadlib(DC"lib1", p.."onefunction"))
|
||||
local a, b = f(15, 25)
|
||||
assert(a == 25 and b == 15)
|
||||
|
||||
f = assert(package.loadlib(DC"lib1", p.."anotherfunc"))
|
||||
assert(f(10, 20) == "10%20\n")
|
||||
|
||||
-- check error messages
|
||||
local f, err, when = package.loadlib(DC"lib1", p.."xuxu")
|
||||
assert(not f and type(err) == "string" and when == "init")
|
||||
f, err, when = package.loadlib("donotexist", p.."xuxu")
|
||||
assert(not f and type(err) == "string" and when == "open")
|
||||
|
||||
-- symbols from 'lib1' must be visible to other libraries
|
||||
f = assert(package.loadlib(DC"lib11", p.."luaopen_lib11"))
|
||||
assert(f() == "exported")
|
||||
|
||||
-- test C modules with prefixes in names
|
||||
package.cpath = DC"?"
|
||||
local lib2, ext = require"lib2-v2"
|
||||
assert(string.find(ext, "libs/lib2-v2", 1, true))
|
||||
-- check correct access to global environment and correct
|
||||
-- parameters
|
||||
assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2")
|
||||
assert(lib2.id("x") == true) -- a different "id" implementation
|
||||
|
||||
-- test C submodules
|
||||
local fs, ext = require"lib1.sub"
|
||||
assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1")
|
||||
assert(string.find(ext, "libs/lib1", 1, true))
|
||||
assert(fs.id(45) == 45)
|
||||
end
|
||||
|
||||
_ENV = _G
|
||||
|
||||
|
||||
-- testing preload
|
||||
|
||||
do
|
||||
local p = package
|
||||
package = {}
|
||||
p.preload.pl = function (...)
|
||||
local _ENV = {...}
|
||||
function xuxu (x) return x+20 end
|
||||
return _ENV
|
||||
end
|
||||
|
||||
local pl, ext = require"pl"
|
||||
assert(require"pl" == pl)
|
||||
assert(pl.xuxu(10) == 30)
|
||||
assert(pl[1] == "pl" and pl[2] == ":preload:" and ext == ":preload:")
|
||||
|
||||
package = p
|
||||
assert(type(package.path) == "string")
|
||||
end
|
||||
|
||||
print('+')
|
||||
|
||||
end --]
|
||||
|
||||
print("testing assignments, logical operators, and constructors")
|
||||
|
||||
local res, res2 = 27
|
||||
|
||||
a, b = 1, 2+3
|
||||
assert(a==1 and b==5)
|
||||
a={}
|
||||
function f() return 10, 11, 12 end
|
||||
a.x, b, a[1] = 1, 2, f()
|
||||
assert(a.x==1 and b==2 and a[1]==10)
|
||||
a[f()], b, a[f()+3] = f(), a, 'x'
|
||||
assert(a[10] == 10 and b == a and a[13] == 'x')
|
||||
|
||||
do
|
||||
local f = function (n) local x = {}; for i=1,n do x[i]=i end;
|
||||
return table.unpack(x) end;
|
||||
local a,b,c
|
||||
a,b = 0, f(1)
|
||||
assert(a == 0 and b == 1)
|
||||
A,b = 0, f(1)
|
||||
assert(A == 0 and b == 1)
|
||||
a,b,c = 0,5,f(4)
|
||||
assert(a==0 and b==5 and c==1)
|
||||
a,b,c = 0,5,f(0)
|
||||
assert(a==0 and b==5 and c==nil)
|
||||
end
|
||||
|
||||
a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6
|
||||
assert(not a and b and c and d==6)
|
||||
|
||||
d = 20
|
||||
a, b, c, d = f()
|
||||
assert(a==10 and b==11 and c==12 and d==nil)
|
||||
a,b = f(), 1, 2, 3, f()
|
||||
assert(a==10 and b==1)
|
||||
|
||||
assert(a<b == false and a>b == true)
|
||||
assert((10 and 2) == 2)
|
||||
assert((10 or 2) == 10)
|
||||
assert((10 or assert(nil)) == 10)
|
||||
assert(not (nil and assert(nil)))
|
||||
assert((nil or "alo") == "alo")
|
||||
assert((nil and 10) == nil)
|
||||
assert((false and 10) == false)
|
||||
assert((true or 10) == true)
|
||||
assert((false or 10) == 10)
|
||||
assert(false ~= nil)
|
||||
assert(nil ~= false)
|
||||
assert(not nil == true)
|
||||
assert(not not nil == false)
|
||||
assert(not not 1 == true)
|
||||
assert(not not a == true)
|
||||
assert(not not (6 or nil) == true)
|
||||
assert(not not (nil and 56) == false)
|
||||
assert(not not (nil and true) == false)
|
||||
assert(not 10 == false)
|
||||
assert(not {} == false)
|
||||
assert(not 0.5 == false)
|
||||
assert(not "x" == false)
|
||||
|
||||
assert({} ~= {})
|
||||
print('+')
|
||||
|
||||
a = {}
|
||||
a[true] = 20
|
||||
a[false] = 10
|
||||
assert(a[1<2] == 20 and a[1>2] == 10)
|
||||
|
||||
function f(a) return a end
|
||||
|
||||
local a = {}
|
||||
for i=3000,-3000,-1 do a[i + 0.0] = i; end
|
||||
a[10e30] = "alo"; a[true] = 10; a[false] = 20
|
||||
assert(a[10e30] == 'alo' and a[not 1] == 20 and a[10<20] == 10)
|
||||
for i=3000,-3000,-1 do assert(a[i] == i); end
|
||||
a[print] = assert
|
||||
a[f] = print
|
||||
a[a] = a
|
||||
assert(a[a][a][a][a][print] == assert)
|
||||
a[print](a[a[f]] == a[print])
|
||||
assert(not pcall(function () local a = {}; a[nil] = 10 end))
|
||||
assert(not pcall(function () local a = {[nil] = 10} end))
|
||||
assert(a[nil] == undef)
|
||||
a = nil
|
||||
|
||||
a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'}
|
||||
a, a.x, a.y = a, a[-3]
|
||||
assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y)
|
||||
a[1], f(a)[2], b, c = {['alo']=assert}, 10, a[1], a[f], 6, 10, 23, f(a), 2
|
||||
a[1].alo(a[2]==10 and b==10 and c==print)
|
||||
|
||||
a.aVeryLongName012345678901234567890123456789012345678901234567890123456789 = 10
|
||||
local function foo ()
|
||||
return a.aVeryLongName012345678901234567890123456789012345678901234567890123456789
|
||||
end
|
||||
assert(foo() == 10 and
|
||||
a.aVeryLongName012345678901234567890123456789012345678901234567890123456789 ==
|
||||
10)
|
||||
|
||||
|
||||
|
||||
-- test of large float/integer indices
|
||||
|
||||
-- compute maximum integer where all bits fit in a float
|
||||
local maxint = math.maxinteger
|
||||
|
||||
-- trim (if needed) to fit in a float
|
||||
while maxint ~= (maxint + 0.0) or (maxint - 1) ~= (maxint - 1.0) do
|
||||
maxint = maxint // 2
|
||||
end
|
||||
|
||||
maxintF = maxint + 0.0 -- float version
|
||||
|
||||
assert(maxintF == maxint and math.type(maxintF) == "float" and
|
||||
maxintF >= 2.0^14)
|
||||
|
||||
-- floats and integers must index the same places
|
||||
a[maxintF] = 10; a[maxintF - 1.0] = 11;
|
||||
a[-maxintF] = 12; a[-maxintF + 1.0] = 13;
|
||||
|
||||
assert(a[maxint] == 10 and a[maxint - 1] == 11 and
|
||||
a[-maxint] == 12 and a[-maxint + 1] == 13)
|
||||
|
||||
a[maxint] = 20
|
||||
a[-maxint] = 22
|
||||
|
||||
assert(a[maxintF] == 20 and a[maxintF - 1.0] == 11 and
|
||||
a[-maxintF] == 22 and a[-maxintF + 1.0] == 13)
|
||||
|
||||
a = nil
|
||||
|
||||
|
||||
-- test conflicts in multiple assignment
|
||||
do
|
||||
local a,i,j,b
|
||||
a = {'a', 'b'}; i=1; j=2; b=a
|
||||
i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
|
||||
assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
|
||||
b[3] == 1)
|
||||
a = {}
|
||||
local function foo () -- assigining to upvalues
|
||||
b, a.x, a = a, 10, 20
|
||||
end
|
||||
foo()
|
||||
assert(a == 20 and b.x == 10)
|
||||
end
|
||||
|
||||
-- repeat test with upvalues
|
||||
do
|
||||
local a,i,j,b
|
||||
a = {'a', 'b'}; i=1; j=2; b=a
|
||||
local function foo ()
|
||||
i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
|
||||
end
|
||||
foo()
|
||||
assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
|
||||
b[3] == 1)
|
||||
local t = {}
|
||||
(function (a) t[a], a = 10, 20 end)(1);
|
||||
assert(t[1] == 10)
|
||||
end
|
||||
|
||||
-- bug in 5.2 beta
|
||||
local function foo ()
|
||||
local a
|
||||
return function ()
|
||||
local b
|
||||
a, b = 3, 14 -- local and upvalue have same index
|
||||
return a, b
|
||||
end
|
||||
end
|
||||
|
||||
local a, b = foo()()
|
||||
assert(a == 3 and b == 14)
|
||||
|
||||
print('OK')
|
||||
|
||||
return res
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
-- $Id: testes/big.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
if _soft then
|
||||
return 'a'
|
||||
end
|
||||
|
||||
print "testing large tables"
|
||||
|
||||
local debug = require"debug"
|
||||
|
||||
local lim = 2^18 + 1000
|
||||
local prog = { "local y = {0" }
|
||||
for i = 1, lim do prog[#prog + 1] = i end
|
||||
prog[#prog + 1] = "}\n"
|
||||
prog[#prog + 1] = "X = y\n"
|
||||
prog[#prog + 1] = ("assert(X[%d] == %d)"):format(lim - 1, lim - 2)
|
||||
prog[#prog + 1] = "return 0"
|
||||
prog = table.concat(prog, ";")
|
||||
|
||||
local env = {string = string, assert = assert}
|
||||
local f = assert(load(prog, nil, nil, env))
|
||||
|
||||
f()
|
||||
assert(env.X[lim] == lim - 1 and env.X[lim + 1] == lim)
|
||||
for k in pairs(env) do env[k] = undef end
|
||||
|
||||
-- yields during accesses larger than K (in RK)
|
||||
setmetatable(env, {
|
||||
__index = function (t, n) coroutine.yield('g'); return _G[n] end,
|
||||
__newindex = function (t, n, v) coroutine.yield('s'); _G[n] = v end,
|
||||
})
|
||||
|
||||
X = nil
|
||||
co = coroutine.wrap(f)
|
||||
assert(co() == 's')
|
||||
assert(co() == 'g')
|
||||
assert(co() == 'g')
|
||||
assert(co() == 0)
|
||||
|
||||
assert(X[lim] == lim - 1 and X[lim + 1] == lim)
|
||||
|
||||
-- errors in accesses larger than K (in RK)
|
||||
getmetatable(env).__index = function () end
|
||||
getmetatable(env).__newindex = function () end
|
||||
local e, m = pcall(f)
|
||||
assert(not e and m:find("global 'X'"))
|
||||
|
||||
-- errors in metamethods
|
||||
getmetatable(env).__newindex = function () error("hi") end
|
||||
local e, m = xpcall(f, debug.traceback)
|
||||
assert(not e and m:find("'newindex'"))
|
||||
|
||||
f, X = nil
|
||||
|
||||
coroutine.yield'b'
|
||||
|
||||
if 2^32 == 0 then -- (small integers) {
|
||||
|
||||
print "testing string length overflow"
|
||||
|
||||
local repstrings = 192 -- number of strings to be concatenated
|
||||
local ssize = math.ceil(2.0^32 / repstrings) + 1 -- size of each string
|
||||
|
||||
assert(repstrings * ssize > 2.0^32) -- it should be larger than maximum size
|
||||
|
||||
local longs = string.rep("\0", ssize) -- create one long string
|
||||
|
||||
-- create function to concatentate 'repstrings' copies of its argument
|
||||
local rep = assert(load(
|
||||
"local a = ...; return " .. string.rep("a", repstrings, "..")))
|
||||
|
||||
local a, b = pcall(rep, longs) -- call that function
|
||||
|
||||
-- it should fail without creating string (result would be too large)
|
||||
assert(not a and string.find(b, "overflow"))
|
||||
|
||||
end -- }
|
||||
|
||||
print'OK'
|
||||
|
||||
return 'a'
|
||||
@@ -0,0 +1,346 @@
|
||||
-- $Id: testes/bitwise.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print("testing bitwise operations")
|
||||
|
||||
require "bwcoercion"
|
||||
|
||||
local numbits = string.packsize('j') * 8
|
||||
|
||||
assert(~0 == -1)
|
||||
|
||||
assert((1 << (numbits - 1)) == math.mininteger)
|
||||
|
||||
-- basic tests for bitwise operators;
|
||||
-- use variables to avoid constant folding
|
||||
local a, b, c, d
|
||||
a = 0xFFFFFFFFFFFFFFFF
|
||||
assert(a == -1 and a & -1 == a and a & 35 == 35)
|
||||
a = 0xF0F0F0F0F0F0F0F0
|
||||
assert(a | -1 == -1)
|
||||
assert(a ~ a == 0 and a ~ 0 == a and a ~ ~a == -1)
|
||||
assert(a >> 4 == ~a)
|
||||
a = 0xF0; b = 0xCC; c = 0xAA; d = 0xFD
|
||||
assert(a | b ~ c & d == 0xF4)
|
||||
|
||||
a = 0xF0.0; b = 0xCC.0; c = "0xAA.0"; d = "0xFD.0"
|
||||
assert(a | b ~ c & d == 0xF4)
|
||||
|
||||
a = 0xF0000000; b = 0xCC000000;
|
||||
c = 0xAA000000; d = 0xFD000000
|
||||
assert(a | b ~ c & d == 0xF4000000)
|
||||
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
|
||||
|
||||
a = a << 32
|
||||
b = b << 32
|
||||
c = c << 32
|
||||
d = d << 32
|
||||
assert(a | b ~ c & d == 0xF4000000 << 32)
|
||||
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
|
||||
|
||||
assert(-1 >> 1 == (1 << (numbits - 1)) - 1 and 1 << 31 == 0x80000000)
|
||||
assert(-1 >> (numbits - 1) == 1)
|
||||
assert(-1 >> numbits == 0 and
|
||||
-1 >> -numbits == 0 and
|
||||
-1 << numbits == 0 and
|
||||
-1 << -numbits == 0)
|
||||
|
||||
assert((2^30 - 1) << 2^30 == 0)
|
||||
assert((2^30 - 1) >> 2^30 == 0)
|
||||
|
||||
assert(1 >> -3 == 1 << 3 and 1000 >> 5 == 1000 << -5)
|
||||
|
||||
|
||||
-- coercion from strings to integers
|
||||
assert("0xffffffffffffffff" | 0 == -1)
|
||||
assert("0xfffffffffffffffe" & "-1" == -2)
|
||||
assert(" \t-0xfffffffffffffffe\n\t" & "-1" == 2)
|
||||
assert(" \n -45 \t " >> " -2 " == -45 * 4)
|
||||
assert("1234.0" << "5.0" == 1234 * 32)
|
||||
assert("0xffff.0" ~ "0xAAAA" == 0x5555)
|
||||
assert(~"0x0.000p4" == -1)
|
||||
|
||||
assert(("7" .. 3) << 1 == 146)
|
||||
assert(0xffffffff >> (1 .. "9") == 0x1fff)
|
||||
assert(10 | (1 .. "9") == 27)
|
||||
|
||||
do
|
||||
local st, msg = pcall(function () return 4 & "a" end)
|
||||
assert(string.find(msg, "'band'"))
|
||||
|
||||
local st, msg = pcall(function () return ~"a" end)
|
||||
assert(string.find(msg, "'bnot'"))
|
||||
end
|
||||
|
||||
|
||||
-- out of range number
|
||||
assert(not pcall(function () return "0xffffffffffffffff.0" | 0 end))
|
||||
|
||||
-- embedded zeros
|
||||
assert(not pcall(function () return "0xffffffffffffffff\0" | 0 end))
|
||||
|
||||
print'+'
|
||||
|
||||
|
||||
package.preload.bit32 = function () --{
|
||||
|
||||
-- no built-in 'bit32' library: implement it using bitwise operators
|
||||
|
||||
local bit = {}
|
||||
|
||||
function bit.bnot (a)
|
||||
return ~a & 0xFFFFFFFF
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- in all vararg functions, avoid creating 'arg' table when there are
|
||||
-- only 2 (or less) parameters, as 2 parameters is the common case
|
||||
--
|
||||
|
||||
function bit.band (x, y, z, ...)
|
||||
if not z then
|
||||
return ((x or -1) & (y or -1)) & 0xFFFFFFFF
|
||||
else
|
||||
local arg = {...}
|
||||
local res = x & y & z
|
||||
for i = 1, #arg do res = res & arg[i] end
|
||||
return res & 0xFFFFFFFF
|
||||
end
|
||||
end
|
||||
|
||||
function bit.bor (x, y, z, ...)
|
||||
if not z then
|
||||
return ((x or 0) | (y or 0)) & 0xFFFFFFFF
|
||||
else
|
||||
local arg = {...}
|
||||
local res = x | y | z
|
||||
for i = 1, #arg do res = res | arg[i] end
|
||||
return res & 0xFFFFFFFF
|
||||
end
|
||||
end
|
||||
|
||||
function bit.bxor (x, y, z, ...)
|
||||
if not z then
|
||||
return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF
|
||||
else
|
||||
local arg = {...}
|
||||
local res = x ~ y ~ z
|
||||
for i = 1, #arg do res = res ~ arg[i] end
|
||||
return res & 0xFFFFFFFF
|
||||
end
|
||||
end
|
||||
|
||||
function bit.btest (...)
|
||||
return bit.band(...) ~= 0
|
||||
end
|
||||
|
||||
function bit.lshift (a, b)
|
||||
return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF
|
||||
end
|
||||
|
||||
function bit.rshift (a, b)
|
||||
return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF
|
||||
end
|
||||
|
||||
function bit.arshift (a, b)
|
||||
a = a & 0xFFFFFFFF
|
||||
if b <= 0 or (a & 0x80000000) == 0 then
|
||||
return (a >> b) & 0xFFFFFFFF
|
||||
else
|
||||
return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF
|
||||
end
|
||||
end
|
||||
|
||||
function bit.lrotate (a ,b)
|
||||
b = b & 31
|
||||
a = a & 0xFFFFFFFF
|
||||
a = (a << b) | (a >> (32 - b))
|
||||
return a & 0xFFFFFFFF
|
||||
end
|
||||
|
||||
function bit.rrotate (a, b)
|
||||
return bit.lrotate(a, -b)
|
||||
end
|
||||
|
||||
local function checkfield (f, w)
|
||||
w = w or 1
|
||||
assert(f >= 0, "field cannot be negative")
|
||||
assert(w > 0, "width must be positive")
|
||||
assert(f + w <= 32, "trying to access non-existent bits")
|
||||
return f, ~(-1 << w)
|
||||
end
|
||||
|
||||
function bit.extract (a, f, w)
|
||||
local f, mask = checkfield(f, w)
|
||||
return (a >> f) & mask
|
||||
end
|
||||
|
||||
function bit.replace (a, v, f, w)
|
||||
local f, mask = checkfield(f, w)
|
||||
v = v & mask
|
||||
a = (a & ~(mask << f)) | (v << f)
|
||||
return a & 0xFFFFFFFF
|
||||
end
|
||||
|
||||
return bit
|
||||
|
||||
end --}
|
||||
|
||||
|
||||
print("testing bitwise library")
|
||||
|
||||
local bit32 = require'bit32'
|
||||
|
||||
assert(bit32.band() == bit32.bnot(0))
|
||||
assert(bit32.btest() == true)
|
||||
assert(bit32.bor() == 0)
|
||||
assert(bit32.bxor() == 0)
|
||||
|
||||
assert(bit32.band() == bit32.band(0xffffffff))
|
||||
assert(bit32.band(1,2) == 0)
|
||||
|
||||
|
||||
-- out-of-range numbers
|
||||
assert(bit32.band(-1) == 0xffffffff)
|
||||
assert(bit32.band((1 << 33) - 1) == 0xffffffff)
|
||||
assert(bit32.band(-(1 << 33) - 1) == 0xffffffff)
|
||||
assert(bit32.band((1 << 33) + 1) == 1)
|
||||
assert(bit32.band(-(1 << 33) + 1) == 1)
|
||||
assert(bit32.band(-(1 << 40)) == 0)
|
||||
assert(bit32.band(1 << 40) == 0)
|
||||
assert(bit32.band(-(1 << 40) - 2) == 0xfffffffe)
|
||||
assert(bit32.band((1 << 40) - 4) == 0xfffffffc)
|
||||
|
||||
assert(bit32.lrotate(0, -1) == 0)
|
||||
assert(bit32.lrotate(0, 7) == 0)
|
||||
assert(bit32.lrotate(0x12345678, 0) == 0x12345678)
|
||||
assert(bit32.lrotate(0x12345678, 32) == 0x12345678)
|
||||
assert(bit32.lrotate(0x12345678, 4) == 0x23456781)
|
||||
assert(bit32.rrotate(0x12345678, -4) == 0x23456781)
|
||||
assert(bit32.lrotate(0x12345678, -8) == 0x78123456)
|
||||
assert(bit32.rrotate(0x12345678, 8) == 0x78123456)
|
||||
assert(bit32.lrotate(0xaaaaaaaa, 2) == 0xaaaaaaaa)
|
||||
assert(bit32.lrotate(0xaaaaaaaa, -2) == 0xaaaaaaaa)
|
||||
for i = -50, 50 do
|
||||
assert(bit32.lrotate(0x89abcdef, i) == bit32.lrotate(0x89abcdef, i%32))
|
||||
end
|
||||
|
||||
assert(bit32.lshift(0x12345678, 4) == 0x23456780)
|
||||
assert(bit32.lshift(0x12345678, 8) == 0x34567800)
|
||||
assert(bit32.lshift(0x12345678, -4) == 0x01234567)
|
||||
assert(bit32.lshift(0x12345678, -8) == 0x00123456)
|
||||
assert(bit32.lshift(0x12345678, 32) == 0)
|
||||
assert(bit32.lshift(0x12345678, -32) == 0)
|
||||
assert(bit32.rshift(0x12345678, 4) == 0x01234567)
|
||||
assert(bit32.rshift(0x12345678, 8) == 0x00123456)
|
||||
assert(bit32.rshift(0x12345678, 32) == 0)
|
||||
assert(bit32.rshift(0x12345678, -32) == 0)
|
||||
assert(bit32.arshift(0x12345678, 0) == 0x12345678)
|
||||
assert(bit32.arshift(0x12345678, 1) == 0x12345678 // 2)
|
||||
assert(bit32.arshift(0x12345678, -1) == 0x12345678 * 2)
|
||||
assert(bit32.arshift(-1, 1) == 0xffffffff)
|
||||
assert(bit32.arshift(-1, 24) == 0xffffffff)
|
||||
assert(bit32.arshift(-1, 32) == 0xffffffff)
|
||||
assert(bit32.arshift(-1, -1) == bit32.band(-1 * 2, 0xffffffff))
|
||||
|
||||
assert(0x12345678 << 4 == 0x123456780)
|
||||
assert(0x12345678 << 8 == 0x1234567800)
|
||||
assert(0x12345678 << -4 == 0x01234567)
|
||||
assert(0x12345678 << -8 == 0x00123456)
|
||||
assert(0x12345678 << 32 == 0x1234567800000000)
|
||||
assert(0x12345678 << -32 == 0)
|
||||
assert(0x12345678 >> 4 == 0x01234567)
|
||||
assert(0x12345678 >> 8 == 0x00123456)
|
||||
assert(0x12345678 >> 32 == 0)
|
||||
assert(0x12345678 >> -32 == 0x1234567800000000)
|
||||
|
||||
print("+")
|
||||
-- some special cases
|
||||
local c = {0, 1, 2, 3, 10, 0x80000000, 0xaaaaaaaa, 0x55555555,
|
||||
0xffffffff, 0x7fffffff}
|
||||
|
||||
for _, b in pairs(c) do
|
||||
assert(bit32.band(b) == b)
|
||||
assert(bit32.band(b, b) == b)
|
||||
assert(bit32.band(b, b, b, b) == b)
|
||||
assert(bit32.btest(b, b) == (b ~= 0))
|
||||
assert(bit32.band(b, b, b) == b)
|
||||
assert(bit32.band(b, b, b, ~b) == 0)
|
||||
assert(bit32.btest(b, b, b) == (b ~= 0))
|
||||
assert(bit32.band(b, bit32.bnot(b)) == 0)
|
||||
assert(bit32.bor(b, bit32.bnot(b)) == bit32.bnot(0))
|
||||
assert(bit32.bor(b) == b)
|
||||
assert(bit32.bor(b, b) == b)
|
||||
assert(bit32.bor(b, b, b) == b)
|
||||
assert(bit32.bor(b, b, 0, ~b) == 0xffffffff)
|
||||
assert(bit32.bxor(b) == b)
|
||||
assert(bit32.bxor(b, b) == 0)
|
||||
assert(bit32.bxor(b, b, b) == b)
|
||||
assert(bit32.bxor(b, b, b, b) == 0)
|
||||
assert(bit32.bxor(b, 0) == b)
|
||||
assert(bit32.bnot(b) ~= b)
|
||||
assert(bit32.bnot(bit32.bnot(b)) == b)
|
||||
assert(bit32.bnot(b) == (1 << 32) - 1 - b)
|
||||
assert(bit32.lrotate(b, 32) == b)
|
||||
assert(bit32.rrotate(b, 32) == b)
|
||||
assert(bit32.lshift(bit32.lshift(b, -4), 4) == bit32.band(b, bit32.bnot(0xf)))
|
||||
assert(bit32.rshift(bit32.rshift(b, 4), -4) == bit32.band(b, bit32.bnot(0xf)))
|
||||
end
|
||||
|
||||
-- for this test, use at most 24 bits (mantissa of a single float)
|
||||
c = {0, 1, 2, 3, 10, 0x800000, 0xaaaaaa, 0x555555, 0xffffff, 0x7fffff}
|
||||
for _, b in pairs(c) do
|
||||
for i = -40, 40 do
|
||||
local x = bit32.lshift(b, i)
|
||||
local y = math.floor(math.fmod(b * 2.0^i, 2.0^32))
|
||||
assert(math.fmod(x - y, 2.0^32) == 0)
|
||||
end
|
||||
end
|
||||
|
||||
assert(not pcall(bit32.band, {}))
|
||||
assert(not pcall(bit32.bnot, "a"))
|
||||
assert(not pcall(bit32.lshift, 45))
|
||||
assert(not pcall(bit32.lshift, 45, print))
|
||||
assert(not pcall(bit32.rshift, 45, print))
|
||||
|
||||
print("+")
|
||||
|
||||
|
||||
-- testing extract/replace
|
||||
|
||||
assert(bit32.extract(0x12345678, 0, 4) == 8)
|
||||
assert(bit32.extract(0x12345678, 4, 4) == 7)
|
||||
assert(bit32.extract(0xa0001111, 28, 4) == 0xa)
|
||||
assert(bit32.extract(0xa0001111, 31, 1) == 1)
|
||||
assert(bit32.extract(0x50000111, 31, 1) == 0)
|
||||
assert(bit32.extract(0xf2345679, 0, 32) == 0xf2345679)
|
||||
|
||||
assert(not pcall(bit32.extract, 0, -1))
|
||||
assert(not pcall(bit32.extract, 0, 32))
|
||||
assert(not pcall(bit32.extract, 0, 0, 33))
|
||||
assert(not pcall(bit32.extract, 0, 31, 2))
|
||||
|
||||
assert(bit32.replace(0x12345678, 5, 28, 4) == 0x52345678)
|
||||
assert(bit32.replace(0x12345678, 0x87654321, 0, 32) == 0x87654321)
|
||||
assert(bit32.replace(0, 1, 2) == 2^2)
|
||||
assert(bit32.replace(0, -1, 4) == 2^4)
|
||||
assert(bit32.replace(-1, 0, 31) == (1 << 31) - 1)
|
||||
assert(bit32.replace(-1, 0, 1, 2) == (1 << 32) - 7)
|
||||
|
||||
|
||||
-- testing conversion of floats
|
||||
|
||||
assert(bit32.bor(3.0) == 3)
|
||||
assert(bit32.bor(-4.0) == 0xfffffffc)
|
||||
|
||||
-- large floats and large-enough integers?
|
||||
if 2.0^50 < 2.0^50 + 1.0 and 2.0^50 < (-1 >> 1) then
|
||||
assert(bit32.bor(2.0^32 - 5.0) == 0xfffffffb)
|
||||
assert(bit32.bor(-2.0^32 - 6.0) == 0xfffffffa)
|
||||
assert(bit32.bor(2.0^48 - 5.0) == 0xfffffffb)
|
||||
assert(bit32.bor(-2.0^48 - 6.0) == 0xfffffffa)
|
||||
end
|
||||
|
||||
print'OK'
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
local tonumber, tointeger = tonumber, math.tointeger
|
||||
local type, getmetatable, rawget, error = type, getmetatable, rawget, error
|
||||
local strsub = string.sub
|
||||
|
||||
local print = print
|
||||
|
||||
_ENV = nil
|
||||
|
||||
-- Try to convert a value to an integer, without assuming any coercion.
|
||||
local function toint (x)
|
||||
x = tonumber(x) -- handle numerical strings
|
||||
if not x then
|
||||
return false -- not coercible to a number
|
||||
end
|
||||
return tointeger(x)
|
||||
end
|
||||
|
||||
|
||||
-- If operation fails, maybe second operand has a metamethod that should
|
||||
-- have been called if not for this string metamethod, so try to
|
||||
-- call it.
|
||||
local function trymt (x, y, mtname)
|
||||
if type(y) ~= "string" then -- avoid recalling original metamethod
|
||||
local mt = getmetatable(y)
|
||||
local mm = mt and rawget(mt, mtname)
|
||||
if mm then
|
||||
return mm(x, y)
|
||||
end
|
||||
end
|
||||
-- if any test fails, there is no other metamethod to be called
|
||||
error("attempt to '" .. strsub(mtname, 3) ..
|
||||
"' a " .. type(x) .. " with a " .. type(y), 4)
|
||||
end
|
||||
|
||||
|
||||
local function checkargs (x, y, mtname)
|
||||
local xi = toint(x)
|
||||
local yi = toint(y)
|
||||
if xi and yi then
|
||||
return xi, yi
|
||||
else
|
||||
return trymt(x, y, mtname), nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local smt = getmetatable("")
|
||||
|
||||
smt.__band = function (x, y)
|
||||
local x, y = checkargs(x, y, "__band")
|
||||
return y and x & y or x
|
||||
end
|
||||
|
||||
smt.__bor = function (x, y)
|
||||
local x, y = checkargs(x, y, "__bor")
|
||||
return y and x | y or x
|
||||
end
|
||||
|
||||
smt.__bxor = function (x, y)
|
||||
local x, y = checkargs(x, y, "__bxor")
|
||||
return y and x ~ y or x
|
||||
end
|
||||
|
||||
smt.__shl = function (x, y)
|
||||
local x, y = checkargs(x, y, "__shl")
|
||||
return y and x << y or x
|
||||
end
|
||||
|
||||
smt.__shr = function (x, y)
|
||||
local x, y = checkargs(x, y, "__shr")
|
||||
return y and x >> y or x
|
||||
end
|
||||
|
||||
smt.__bnot = function (x)
|
||||
local x, y = checkargs(x, x, "__bnot")
|
||||
return y and ~x or x
|
||||
end
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
-- $Id: testes/calls.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print("testing functions and calls")
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
-- get the opportunity to test 'type' too ;)
|
||||
|
||||
assert(type(1<2) == 'boolean')
|
||||
assert(type(true) == 'boolean' and type(false) == 'boolean')
|
||||
assert(type(nil) == 'nil'
|
||||
and type(-3) == 'number'
|
||||
and type'x' == 'string'
|
||||
and type{} == 'table'
|
||||
and type(type) == 'function')
|
||||
|
||||
assert(type(assert) == type(print))
|
||||
function f (x) return a:x (x) end
|
||||
assert(type(f) == 'function')
|
||||
assert(not pcall(type))
|
||||
|
||||
|
||||
-- testing local-function recursion
|
||||
fact = false
|
||||
do
|
||||
local res = 1
|
||||
local function fact (n)
|
||||
if n==0 then return res
|
||||
else return n*fact(n-1)
|
||||
end
|
||||
end
|
||||
assert(fact(5) == 120)
|
||||
end
|
||||
assert(fact == false)
|
||||
|
||||
-- testing declarations
|
||||
a = {i = 10}
|
||||
self = 20
|
||||
function a:x (x) return x+self.i end
|
||||
function a.y (x) return x+self end
|
||||
|
||||
assert(a:x(1)+10 == a.y(1))
|
||||
|
||||
a.t = {i=-100}
|
||||
a["t"].x = function (self, a,b) return self.i+a+b end
|
||||
|
||||
assert(a.t:x(2,3) == -95)
|
||||
|
||||
do
|
||||
local a = {x=0}
|
||||
function a:add (x) self.x, a.y = self.x+x, 20; return self end
|
||||
assert(a:add(10):add(20):add(30).x == 60 and a.y == 20)
|
||||
end
|
||||
|
||||
local a = {b={c={}}}
|
||||
|
||||
function a.b.c.f1 (x) return x+1 end
|
||||
function a.b.c:f2 (x,y) self[x] = y end
|
||||
assert(a.b.c.f1(4) == 5)
|
||||
a.b.c:f2('k', 12); assert(a.b.c.k == 12)
|
||||
|
||||
print('+')
|
||||
|
||||
t = nil -- 'declare' t
|
||||
function f(a,b,c) local d = 'a'; t={a,b,c,d} end
|
||||
|
||||
f( -- this line change must be valid
|
||||
1,2)
|
||||
assert(t[1] == 1 and t[2] == 2 and t[3] == nil and t[4] == 'a')
|
||||
f(1,2, -- this one too
|
||||
3,4)
|
||||
assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a')
|
||||
|
||||
function fat(x)
|
||||
if x <= 1 then return 1
|
||||
else return x*load("return fat(" .. x-1 .. ")", "")()
|
||||
end
|
||||
end
|
||||
|
||||
assert(load "load 'assert(fat(6)==720)' () ")()
|
||||
a = load('return fat(5), 3')
|
||||
a,b = a()
|
||||
assert(a == 120 and b == 3)
|
||||
print('+')
|
||||
|
||||
function err_on_n (n)
|
||||
if n==0 then error(); exit(1);
|
||||
else err_on_n (n-1); exit(1);
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
function dummy (n)
|
||||
if n > 0 then
|
||||
assert(not pcall(err_on_n, n))
|
||||
dummy(n-1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dummy(10)
|
||||
|
||||
function deep (n)
|
||||
if n>0 then deep(n-1) end
|
||||
end
|
||||
deep(10)
|
||||
deep(180)
|
||||
|
||||
-- testing tail calls
|
||||
function deep (n) if n>0 then return deep(n-1) else return 101 end end
|
||||
assert(deep(30000) == 101)
|
||||
a = {}
|
||||
function a:deep (n) if n>0 then return self:deep(n-1) else return 101 end end
|
||||
assert(a:deep(30000) == 101)
|
||||
|
||||
do -- tail calls x varargs
|
||||
local function foo (x, ...) local a = {...}; return x, a[1], a[2] end
|
||||
|
||||
local function foo1 (x) return foo(10, x, x + 1) end
|
||||
|
||||
local a, b, c = foo1(-2)
|
||||
assert(a == 10 and b == -2 and c == -1)
|
||||
|
||||
-- tail calls x metamethods
|
||||
local t = setmetatable({}, {__call = foo})
|
||||
local function foo2 (x) return t(10, x) end
|
||||
a, b, c = foo2(100)
|
||||
assert(a == t and b == 10 and c == 100)
|
||||
|
||||
a, b = (function () return foo() end)()
|
||||
assert(a == nil and b == nil)
|
||||
|
||||
local X, Y, A
|
||||
local function foo (x, y, ...) X = x; Y = y; A = {...} end
|
||||
local function foo1 (...) return foo(...) end
|
||||
|
||||
local a, b, c = foo1()
|
||||
assert(X == nil and Y == nil and #A == 0)
|
||||
|
||||
a, b, c = foo1(10)
|
||||
assert(X == 10 and Y == nil and #A == 0)
|
||||
|
||||
a, b, c = foo1(10, 20)
|
||||
assert(X == 10 and Y == 20 and #A == 0)
|
||||
|
||||
a, b, c = foo1(10, 20, 30)
|
||||
assert(X == 10 and Y == 20 and #A == 1 and A[1] == 30)
|
||||
end
|
||||
|
||||
print('+')
|
||||
|
||||
|
||||
do -- testing chains of '__call'
|
||||
local N = 20
|
||||
local u = table.pack
|
||||
for i = 1, N do
|
||||
u = setmetatable({i}, {__call = u})
|
||||
end
|
||||
|
||||
local Res = u("a", "b", "c")
|
||||
|
||||
assert(Res.n == N + 3)
|
||||
for i = 1, N do
|
||||
assert(Res[i][1] == i)
|
||||
end
|
||||
assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c")
|
||||
end
|
||||
|
||||
|
||||
a = nil
|
||||
(function (x) a=x end)(23)
|
||||
assert(a == 23 and (function (x) return x*2 end)(20) == 40)
|
||||
|
||||
|
||||
-- testing closures
|
||||
|
||||
-- fixed-point operator
|
||||
Z = function (le)
|
||||
local function a (f)
|
||||
return le(function (x) return f(f)(x) end)
|
||||
end
|
||||
return a(a)
|
||||
end
|
||||
|
||||
|
||||
-- non-recursive factorial
|
||||
|
||||
F = function (f)
|
||||
return function (n)
|
||||
if n == 0 then return 1
|
||||
else return n*f(n-1) end
|
||||
end
|
||||
end
|
||||
|
||||
fat = Z(F)
|
||||
|
||||
assert(fat(0) == 1 and fat(4) == 24 and Z(F)(5)==5*Z(F)(4))
|
||||
|
||||
local function g (z)
|
||||
local function f (a,b,c,d)
|
||||
return function (x,y) return a+b+c+d+a+x+y+z end
|
||||
end
|
||||
return f(z,z+1,z+2,z+3)
|
||||
end
|
||||
|
||||
f = g(10)
|
||||
assert(f(9, 16) == 10+11+12+13+10+9+16+10)
|
||||
|
||||
Z, F, f = nil
|
||||
print('+')
|
||||
|
||||
-- testing multiple returns
|
||||
|
||||
function unlpack (t, i)
|
||||
i = i or 1
|
||||
if (i <= #t) then
|
||||
return t[i], unlpack(t, i+1)
|
||||
end
|
||||
end
|
||||
|
||||
function equaltab (t1, t2)
|
||||
assert(#t1 == #t2)
|
||||
for i = 1, #t1 do
|
||||
assert(t1[i] == t2[i])
|
||||
end
|
||||
end
|
||||
|
||||
local pack = function (...) return (table.pack(...)) end
|
||||
|
||||
function f() return 1,2,30,4 end
|
||||
function ret2 (a,b) return a,b end
|
||||
|
||||
local a,b,c,d = unlpack{1,2,3}
|
||||
assert(a==1 and b==2 and c==3 and d==nil)
|
||||
a = {1,2,3,4,false,10,'alo',false,assert}
|
||||
equaltab(pack(unlpack(a)), a)
|
||||
equaltab(pack(unlpack(a), -1), {1,-1})
|
||||
a,b,c,d = ret2(f()), ret2(f())
|
||||
assert(a==1 and b==1 and c==2 and d==nil)
|
||||
a,b,c,d = unlpack(pack(ret2(f()), ret2(f())))
|
||||
assert(a==1 and b==1 and c==2 and d==nil)
|
||||
a,b,c,d = unlpack(pack(ret2(f()), (ret2(f()))))
|
||||
assert(a==1 and b==1 and c==nil and d==nil)
|
||||
|
||||
a = ret2{ unlpack{1,2,3}, unlpack{3,2,1}, unlpack{"a", "b"}}
|
||||
assert(a[1] == 1 and a[2] == 3 and a[3] == "a" and a[4] == "b")
|
||||
|
||||
|
||||
-- testing calls with 'incorrect' arguments
|
||||
rawget({}, "x", 1)
|
||||
rawset({}, "x", 1, 2)
|
||||
assert(math.sin(1,2) == math.sin(1))
|
||||
table.sort({10,9,8,4,19,23,0,0}, function (a,b) return a<b end, "extra arg")
|
||||
|
||||
|
||||
-- test for generic load
|
||||
local x = "-- a comment\0\0\0\n x = 10 + \n23; \
|
||||
local a = function () x = 'hi' end; \
|
||||
return '\0'"
|
||||
function read1 (x)
|
||||
local i = 0
|
||||
return function ()
|
||||
collectgarbage()
|
||||
i=i+1
|
||||
return string.sub(x, i, i)
|
||||
end
|
||||
end
|
||||
|
||||
function cannotload (msg, a,b)
|
||||
assert(not a and string.find(b, msg))
|
||||
end
|
||||
|
||||
a = assert(load(read1(x), "modname", "t", _G))
|
||||
assert(a() == "\0" and _G.x == 33)
|
||||
assert(debug.getinfo(a).source == "modname")
|
||||
-- cannot read text in binary mode
|
||||
cannotload("attempt to load a text chunk", load(read1(x), "modname", "b", {}))
|
||||
cannotload("attempt to load a text chunk", load(x, "modname", "b"))
|
||||
|
||||
a = assert(load(function () return nil end))
|
||||
a() -- empty chunk
|
||||
|
||||
assert(not load(function () return true end))
|
||||
|
||||
|
||||
-- small bug
|
||||
local t = {nil, "return ", "3"}
|
||||
f, msg = load(function () return table.remove(t, 1) end)
|
||||
assert(f() == nil) -- should read the empty chunk
|
||||
|
||||
-- another small bug (in 5.2.1)
|
||||
f = load(string.dump(function () return 1 end), nil, "b", {})
|
||||
assert(type(f) == "function" and f() == 1)
|
||||
|
||||
|
||||
x = string.dump(load("x = 1; return x"))
|
||||
a = assert(load(read1(x), nil, "b"))
|
||||
assert(a() == 1 and _G.x == 1)
|
||||
cannotload("attempt to load a binary chunk", load(read1(x), nil, "t"))
|
||||
cannotload("attempt to load a binary chunk", load(x, nil, "t"))
|
||||
|
||||
assert(not pcall(string.dump, print)) -- no dump of C functions
|
||||
|
||||
cannotload("unexpected symbol", load(read1("*a = 123")))
|
||||
cannotload("unexpected symbol", load("*a = 123"))
|
||||
cannotload("hhi", load(function () error("hhi") end))
|
||||
|
||||
-- any value is valid for _ENV
|
||||
assert(load("return _ENV", nil, nil, 123)() == 123)
|
||||
|
||||
|
||||
-- load when _ENV is not first upvalue
|
||||
local x; XX = 123
|
||||
local function h ()
|
||||
local y=x -- use 'x', so that it becomes 1st upvalue
|
||||
return XX -- global name
|
||||
end
|
||||
local d = string.dump(h)
|
||||
x = load(d, "", "b")
|
||||
assert(debug.getupvalue(x, 2) == '_ENV')
|
||||
debug.setupvalue(x, 2, _G)
|
||||
assert(x() == 123)
|
||||
|
||||
assert(assert(load("return XX + ...", nil, nil, {XX = 13}))(4) == 17)
|
||||
|
||||
|
||||
-- test generic load with nested functions
|
||||
x = [[
|
||||
return function (x)
|
||||
return function (y)
|
||||
return function (z)
|
||||
return x+y+z
|
||||
end
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
a = assert(load(read1(x)))
|
||||
assert(a()(2)(3)(10) == 15)
|
||||
|
||||
|
||||
-- test for dump/undump with upvalues
|
||||
local a, b = 20, 30
|
||||
x = load(string.dump(function (x)
|
||||
if x == "set" then a = 10+b; b = b+1 else
|
||||
return a
|
||||
end
|
||||
end), "", "b", nil)
|
||||
assert(x() == nil)
|
||||
assert(debug.setupvalue(x, 1, "hi") == "a")
|
||||
assert(x() == "hi")
|
||||
assert(debug.setupvalue(x, 2, 13) == "b")
|
||||
assert(not debug.setupvalue(x, 3, 10)) -- only 2 upvalues
|
||||
x("set")
|
||||
assert(x() == 23)
|
||||
x("set")
|
||||
assert(x() == 24)
|
||||
|
||||
-- test for dump/undump with many upvalues
|
||||
do
|
||||
local nup = 200 -- maximum number of local variables
|
||||
local prog = {"local a1"}
|
||||
for i = 2, nup do prog[#prog + 1] = ", a" .. i end
|
||||
prog[#prog + 1] = " = 1"
|
||||
for i = 2, nup do prog[#prog + 1] = ", " .. i end
|
||||
local sum = 1
|
||||
prog[#prog + 1] = "; return function () return a1"
|
||||
for i = 2, nup do prog[#prog + 1] = " + a" .. i; sum = sum + i end
|
||||
prog[#prog + 1] = " end"
|
||||
prog = table.concat(prog)
|
||||
local f = assert(load(prog))()
|
||||
assert(f() == sum)
|
||||
|
||||
f = load(string.dump(f)) -- main chunk now has many upvalues
|
||||
local a = 10
|
||||
local h = function () return a end
|
||||
for i = 1, nup do
|
||||
debug.upvaluejoin(f, i, h, 1)
|
||||
end
|
||||
assert(f() == 10 * nup)
|
||||
end
|
||||
|
||||
-- test for long method names
|
||||
do
|
||||
local t = {x = 1}
|
||||
function t:_012345678901234567890123456789012345678901234567890123456789 ()
|
||||
return self.x
|
||||
end
|
||||
assert(t:_012345678901234567890123456789012345678901234567890123456789() == 1)
|
||||
end
|
||||
|
||||
|
||||
-- test for bug in parameter adjustment
|
||||
assert((function () return nil end)(4) == nil)
|
||||
assert((function () local a; return a end)(4) == nil)
|
||||
assert((function (a) return a end)() == nil)
|
||||
|
||||
|
||||
print("testing binary chunks")
|
||||
do
|
||||
local header = string.pack("c4BBBc6BBBj",
|
||||
"\27Lua", -- signature
|
||||
(504 >> 7) & 0x7f, (504 & 0x7f) | 0x80, -- version 5.4 (504)
|
||||
0, -- format
|
||||
"\x19\x93\r\n\x1a\n", -- data
|
||||
4, -- size of instruction
|
||||
string.packsize("j"), -- sizeof(lua integer)
|
||||
string.packsize("n"), -- sizeof(lua number)
|
||||
0x5678 -- LUAC_INT
|
||||
-- LUAC_NUM may not have a unique binary representation (padding...)
|
||||
)
|
||||
local c = string.dump(function () local a = 1; local b = 3; return a+b*3 end)
|
||||
|
||||
assert(string.sub(c, 1, #header) == header)
|
||||
|
||||
-- corrupted header
|
||||
for i = 1, #header do
|
||||
local s = string.sub(c, 1, i - 1) ..
|
||||
string.char(string.byte(string.sub(c, i, i)) + 1) ..
|
||||
string.sub(c, i + 1, -1)
|
||||
assert(#s == #c)
|
||||
assert(not load(s))
|
||||
end
|
||||
|
||||
-- loading truncated binary chunks
|
||||
for i = 1, #c - 1 do
|
||||
local st, msg = load(string.sub(c, 1, i))
|
||||
assert(not st and string.find(msg, "truncated"))
|
||||
end
|
||||
assert(assert(load(c))() == 10)
|
||||
end
|
||||
|
||||
print('OK')
|
||||
return deep
|
||||
@@ -0,0 +1,270 @@
|
||||
-- $Id: testes/closure.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print "testing closures"
|
||||
|
||||
local A,B = 0,{g=10}
|
||||
function f(x)
|
||||
local a = {}
|
||||
for i=1,1000 do
|
||||
local y = 0
|
||||
do
|
||||
a[i] = function () B.g = B.g+1; y = y+x; return y+A end
|
||||
end
|
||||
end
|
||||
local dummy = function () return a[A] end
|
||||
collectgarbage()
|
||||
A = 1; assert(dummy() == a[1]); A = 0;
|
||||
assert(a[1]() == x)
|
||||
assert(a[3]() == x)
|
||||
collectgarbage()
|
||||
assert(B.g == 12)
|
||||
return a
|
||||
end
|
||||
|
||||
local a = f(10)
|
||||
-- force a GC in this level
|
||||
local x = {[1] = {}} -- to detect a GC
|
||||
setmetatable(x, {__mode = 'kv'})
|
||||
while x[1] do -- repeat until GC
|
||||
local a = A..A..A..A -- create garbage
|
||||
A = A+1
|
||||
end
|
||||
assert(a[1]() == 20+A)
|
||||
assert(a[1]() == 30+A)
|
||||
assert(a[2]() == 10+A)
|
||||
collectgarbage()
|
||||
assert(a[2]() == 20+A)
|
||||
assert(a[2]() == 30+A)
|
||||
assert(a[3]() == 20+A)
|
||||
assert(a[8]() == 10+A)
|
||||
assert(getmetatable(x).__mode == 'kv')
|
||||
assert(B.g == 19)
|
||||
|
||||
|
||||
-- testing equality
|
||||
a = {}
|
||||
|
||||
for i = 1, 5 do a[i] = function (x) return i + a + _ENV end end
|
||||
assert(a[3] ~= a[4] and a[4] ~= a[5])
|
||||
|
||||
do
|
||||
local a = function (x) return math.sin(_ENV[x]) end
|
||||
local function f()
|
||||
return a
|
||||
end
|
||||
assert(f() == f())
|
||||
end
|
||||
|
||||
|
||||
-- testing closures with 'for' control variable
|
||||
a = {}
|
||||
for i=1,10 do
|
||||
a[i] = {set = function(x) i=x end, get = function () return i end}
|
||||
if i == 3 then break end
|
||||
end
|
||||
assert(a[4] == undef)
|
||||
a[1].set(10)
|
||||
assert(a[2].get() == 2)
|
||||
a[2].set('a')
|
||||
assert(a[3].get() == 3)
|
||||
assert(a[2].get() == 'a')
|
||||
|
||||
a = {}
|
||||
local t = {"a", "b"}
|
||||
for i = 1, #t do
|
||||
local k = t[i]
|
||||
a[i] = {set = function(x, y) i=x; k=y end,
|
||||
get = function () return i, k end}
|
||||
if i == 2 then break end
|
||||
end
|
||||
a[1].set(10, 20)
|
||||
local r,s = a[2].get()
|
||||
assert(r == 2 and s == 'b')
|
||||
r,s = a[1].get()
|
||||
assert(r == 10 and s == 20)
|
||||
a[2].set('a', 'b')
|
||||
r,s = a[2].get()
|
||||
assert(r == "a" and s == "b")
|
||||
|
||||
|
||||
-- testing closures with 'for' control variable x break
|
||||
for i=1,3 do
|
||||
f = function () return i end
|
||||
break
|
||||
end
|
||||
assert(f() == 1)
|
||||
|
||||
for k = 1, #t do
|
||||
local v = t[k]
|
||||
f = function () return k, v end
|
||||
break
|
||||
end
|
||||
assert(({f()})[1] == 1)
|
||||
assert(({f()})[2] == "a")
|
||||
|
||||
|
||||
-- testing closure x break x return x errors
|
||||
|
||||
local b
|
||||
function f(x)
|
||||
local first = 1
|
||||
while 1 do
|
||||
if x == 3 and not first then return end
|
||||
local a = 'xuxu'
|
||||
b = function (op, y)
|
||||
if op == 'set' then
|
||||
a = x+y
|
||||
else
|
||||
return a
|
||||
end
|
||||
end
|
||||
if x == 1 then do break end
|
||||
elseif x == 2 then return
|
||||
else if x ~= 3 then error() end
|
||||
end
|
||||
first = nil
|
||||
end
|
||||
end
|
||||
|
||||
for i=1,3 do
|
||||
f(i)
|
||||
assert(b('get') == 'xuxu')
|
||||
b('set', 10); assert(b('get') == 10+i)
|
||||
b = nil
|
||||
end
|
||||
|
||||
pcall(f, 4);
|
||||
assert(b('get') == 'xuxu')
|
||||
b('set', 10); assert(b('get') == 14)
|
||||
|
||||
|
||||
local w
|
||||
-- testing multi-level closure
|
||||
function f(x)
|
||||
return function (y)
|
||||
return function (z) return w+x+y+z end
|
||||
end
|
||||
end
|
||||
|
||||
y = f(10)
|
||||
w = 1.345
|
||||
assert(y(20)(30) == 60+w)
|
||||
|
||||
|
||||
-- testing closures x break
|
||||
do
|
||||
local X, Y
|
||||
local a = math.sin(0)
|
||||
|
||||
while a do
|
||||
local b = 10
|
||||
X = function () return b end -- closure with upvalue
|
||||
if a then break end
|
||||
end
|
||||
|
||||
do
|
||||
local b = 20
|
||||
Y = function () return b end -- closure with upvalue
|
||||
end
|
||||
|
||||
-- upvalues must be different
|
||||
assert(X() == 10 and Y() == 20)
|
||||
end
|
||||
|
||||
|
||||
-- testing closures x repeat-until
|
||||
|
||||
local a = {}
|
||||
local i = 1
|
||||
repeat
|
||||
local x = i
|
||||
a[i] = function () i = x+1; return x end
|
||||
until i > 10 or a[i]() ~= x
|
||||
assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4)
|
||||
|
||||
|
||||
-- testing closures created in 'then' and 'else' parts of 'if's
|
||||
a = {}
|
||||
for i = 1, 10 do
|
||||
if i % 3 == 0 then
|
||||
local y = 0
|
||||
a[i] = function (x) local t = y; y = x; return t end
|
||||
elseif i % 3 == 1 then
|
||||
goto L1
|
||||
error'not here'
|
||||
::L1::
|
||||
local y = 1
|
||||
a[i] = function (x) local t = y; y = x; return t end
|
||||
elseif i % 3 == 2 then
|
||||
local t
|
||||
goto l4
|
||||
::l4a:: a[i] = t; goto l4b
|
||||
error("should never be here!")
|
||||
::l4::
|
||||
local y = 2
|
||||
t = function (x) local t = y; y = x; return t end
|
||||
goto l4a
|
||||
error("should never be here!")
|
||||
::l4b::
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, 10 do
|
||||
assert(a[i](i * 10) == i % 3 and a[i]() == i * 10)
|
||||
end
|
||||
|
||||
print'+'
|
||||
|
||||
|
||||
-- test for correctly closing upvalues in tail calls of vararg functions
|
||||
local function t ()
|
||||
local function c(a,b) assert(a=="test" and b=="OK") end
|
||||
local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end
|
||||
local x = 1
|
||||
return v(function() return x end)
|
||||
end
|
||||
t()
|
||||
|
||||
|
||||
-- test for debug manipulation of upvalues
|
||||
local debug = require'debug'
|
||||
|
||||
do
|
||||
local a , b, c = 3, 5, 7
|
||||
foo1 = function () return a+b end;
|
||||
foo2 = function () return b+a end;
|
||||
do
|
||||
local a = 10
|
||||
foo3 = function () return a+b end;
|
||||
end
|
||||
end
|
||||
|
||||
assert(debug.upvalueid(foo1, 1))
|
||||
assert(debug.upvalueid(foo1, 2))
|
||||
assert(not pcall(debug.upvalueid, foo1, 3))
|
||||
assert(debug.upvalueid(foo1, 1) == debug.upvalueid(foo2, 2))
|
||||
assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo2, 1))
|
||||
assert(debug.upvalueid(foo3, 1))
|
||||
assert(debug.upvalueid(foo1, 1) ~= debug.upvalueid(foo3, 1))
|
||||
assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo3, 2))
|
||||
|
||||
assert(debug.upvalueid(string.gmatch("x", "x"), 1) ~= nil)
|
||||
|
||||
assert(foo1() == 3 + 5 and foo2() == 5 + 3)
|
||||
debug.upvaluejoin(foo1, 2, foo2, 2)
|
||||
assert(foo1() == 3 + 3 and foo2() == 5 + 3)
|
||||
assert(foo3() == 10 + 5)
|
||||
debug.upvaluejoin(foo3, 2, foo2, 1)
|
||||
assert(foo3() == 10 + 5)
|
||||
debug.upvaluejoin(foo3, 2, foo2, 2)
|
||||
assert(foo3() == 10 + 3)
|
||||
|
||||
assert(not pcall(debug.upvaluejoin, foo1, 3, foo2, 1))
|
||||
assert(not pcall(debug.upvaluejoin, foo1, 1, foo2, 3))
|
||||
assert(not pcall(debug.upvaluejoin, foo1, 0, foo2, 1))
|
||||
assert(not pcall(debug.upvaluejoin, print, 1, foo2, 1))
|
||||
assert(not pcall(debug.upvaluejoin, {}, 1, foo2, 1))
|
||||
assert(not pcall(debug.upvaluejoin, foo1, 1, print, 1))
|
||||
|
||||
print'OK'
|
||||
+443
@@ -0,0 +1,443 @@
|
||||
-- $Id: testes/code.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
if T==nil then
|
||||
(Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')
|
||||
return
|
||||
end
|
||||
print "testing code generation and optimizations"
|
||||
|
||||
-- to test constant propagation
|
||||
local k0aux <const> = 0
|
||||
local k0 <const> = k0aux
|
||||
local k1 <const> = 1
|
||||
local k3 <const> = 3
|
||||
local k6 <const> = k3 + (k3 << k0)
|
||||
local kFF0 <const> = 0xFF0
|
||||
local k3_78 <const> = 3.78
|
||||
local x, k3_78_4 <const> = 10, k3_78 / 4
|
||||
assert(x == 10)
|
||||
|
||||
local kx <const> = "x"
|
||||
|
||||
local kTrue <const> = true
|
||||
local kFalse <const> = false
|
||||
|
||||
local kNil <const> = nil
|
||||
|
||||
-- this code gave an error for the code checker
|
||||
do
|
||||
local function f (a)
|
||||
for k,v,w in a do end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- testing reuse in constant table
|
||||
local function checkKlist (func, list)
|
||||
local k = T.listk(func)
|
||||
assert(#k == #list)
|
||||
for i = 1, #k do
|
||||
assert(k[i] == list[i] and math.type(k[i]) == math.type(list[i]))
|
||||
end
|
||||
end
|
||||
|
||||
local function foo ()
|
||||
local a
|
||||
a = k3;
|
||||
a = 0; a = 0.0; a = -7 + 7
|
||||
a = k3_78/4; a = k3_78_4
|
||||
a = -k3_78/4; a = k3_78/4; a = -3.78/4
|
||||
a = -3.79/4; a = 0.0; a = -0;
|
||||
a = k3; a = 3.0; a = 3; a = 3.0
|
||||
end
|
||||
|
||||
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
|
||||
|
||||
|
||||
-- testing opcodes
|
||||
|
||||
-- check that 'f' opcodes match '...'
|
||||
function check (f, ...)
|
||||
local arg = {...}
|
||||
local c = T.listcode(f)
|
||||
for i=1, #arg do
|
||||
local opcode = string.match(c[i], "%u%w+")
|
||||
-- print(arg[i], opcode)
|
||||
assert(arg[i] == opcode)
|
||||
end
|
||||
assert(c[#arg+2] == undef)
|
||||
end
|
||||
|
||||
|
||||
-- check that 'f' opcodes match '...' and that 'f(p) == r'.
|
||||
function checkR (f, p, r, ...)
|
||||
local r1 = f(p)
|
||||
assert(r == r1 and math.type(r) == math.type(r1))
|
||||
check(f, ...)
|
||||
end
|
||||
|
||||
|
||||
-- check that 'a' and 'b' has the same opcodes
|
||||
function checkequal (a, b)
|
||||
a = T.listcode(a)
|
||||
b = T.listcode(b)
|
||||
assert(#a == #b)
|
||||
for i = 1, #a do
|
||||
a[i] = string.gsub(a[i], '%b()', '') -- remove line number
|
||||
b[i] = string.gsub(b[i], '%b()', '') -- remove line number
|
||||
assert(a[i] == b[i])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- some basic instructions
|
||||
check(function () -- function does not create upvalues
|
||||
(function () end){f()}
|
||||
end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
|
||||
'SETLIST', 'CALL', 'RETURN0')
|
||||
|
||||
check(function (x) -- function creates upvalues
|
||||
(function () return x end){f()}
|
||||
end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
|
||||
'SETLIST', 'CALL', 'RETURN')
|
||||
|
||||
|
||||
-- sequence of LOADNILs
|
||||
check(function ()
|
||||
local kNil <const> = nil
|
||||
local a,b,c
|
||||
local d; local e;
|
||||
local f,g,h;
|
||||
d = nil; d=nil; b=nil; a=kNil; c=nil;
|
||||
end, 'LOADNIL', 'RETURN0')
|
||||
|
||||
check(function ()
|
||||
local a,b,c,d = 1,1,1,1
|
||||
d=nil;c=nil;b=nil;a=nil
|
||||
end, 'LOADI', 'LOADI', 'LOADI', 'LOADI', 'LOADNIL', 'RETURN0')
|
||||
|
||||
do
|
||||
local a,b,c,d = 1,1,1,1
|
||||
d=nil;c=nil;b=nil;a=nil
|
||||
assert(a == nil and b == nil and c == nil and d == nil)
|
||||
end
|
||||
|
||||
|
||||
-- single return
|
||||
check (function (a,b,c) return a end, 'RETURN1')
|
||||
|
||||
|
||||
-- infinite loops
|
||||
check(function () while kTrue do local a = -1 end end,
|
||||
'LOADI', 'JMP', 'RETURN0')
|
||||
|
||||
check(function () while 1 do local a = -1 end end,
|
||||
'LOADI', 'JMP', 'RETURN0')
|
||||
|
||||
check(function () repeat local x = 1 until true end,
|
||||
'LOADI', 'RETURN0')
|
||||
|
||||
|
||||
-- concat optimization
|
||||
check(function (a,b,c,d) return a..b..c..d end,
|
||||
'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN1')
|
||||
|
||||
-- not
|
||||
check(function () return not not nil end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not kFalse end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not true end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not k3 end, 'LOADBOOL', 'RETURN1')
|
||||
|
||||
-- direct access to locals
|
||||
check(function ()
|
||||
local a,b,c,d
|
||||
a = b*a
|
||||
c.x, a[b] = -((a + d/b - a[b]) ^ a.x), b
|
||||
end,
|
||||
'LOADNIL',
|
||||
'MUL', 'MMBIN',
|
||||
'DIV', 'MMBIN', 'ADD', 'MMBIN', 'GETTABLE', 'SUB', 'MMBIN',
|
||||
'GETFIELD', 'POW', 'MMBIN', 'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0')
|
||||
|
||||
|
||||
-- direct access to constants
|
||||
check(function ()
|
||||
local a,b
|
||||
local c = kNil
|
||||
a[kx] = 3.2
|
||||
a.x = b
|
||||
a[b] = 'x'
|
||||
end,
|
||||
'LOADNIL', 'SETFIELD', 'SETFIELD', 'SETTABLE', 'RETURN0')
|
||||
|
||||
-- "get/set table" with numeric indices
|
||||
check(function (a)
|
||||
local k255 <const> = 255
|
||||
a[1] = a[100]
|
||||
a[k255] = a[256]
|
||||
a[256] = 5
|
||||
end,
|
||||
'GETI', 'SETI',
|
||||
'LOADI', 'GETTABLE', 'SETI',
|
||||
'LOADI', 'SETTABLE', 'RETURN0')
|
||||
|
||||
check(function ()
|
||||
local a,b
|
||||
a = a - a
|
||||
b = a/a
|
||||
b = 5-4
|
||||
end,
|
||||
'LOADNIL', 'SUB', 'MMBIN', 'DIV', 'MMBIN', 'LOADI', 'RETURN0')
|
||||
|
||||
check(function ()
|
||||
local a,b
|
||||
a[kTrue] = false
|
||||
end,
|
||||
'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
|
||||
|
||||
|
||||
-- equalities
|
||||
checkR(function (a) if a == 1 then return 2 end end, 1, 2,
|
||||
'EQI', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if -4.0 == a then return 2 end end, -4, 2,
|
||||
'EQI', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if a == "hi" then return 2 end end, 10, nil,
|
||||
'EQK', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if a == 10000 then return 2 end end, 1, nil,
|
||||
'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
|
||||
|
||||
checkR(function (a) if -10000 == a then return 2 end end, -10000, 2,
|
||||
'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
|
||||
|
||||
-- comparisons
|
||||
|
||||
checkR(function (a) if -10 <= a then return 2 end end, -10, 2,
|
||||
'GEI', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if 128.0 > a then return 2 end end, 129, nil,
|
||||
'LTI', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if -127.0 < a then return 2 end end, -127, nil,
|
||||
'GTI', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if 10 < a then return 2 end end, 11, 2,
|
||||
'GTI', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if 129 < a then return 2 end end, 130, 2,
|
||||
'LOADI', 'LT', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if a >= 23.0 then return 2 end end, 25, 2,
|
||||
'GEI', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if a >= 23.1 then return 2 end end, 0, nil,
|
||||
'LOADK', 'LE', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
checkR(function (a) if a > 2300.0 then return 2 end end, 0, nil,
|
||||
'LOADF', 'LT', 'JMP', 'LOADI', 'RETURN1')
|
||||
|
||||
|
||||
-- constant folding
|
||||
local function checkK (func, val)
|
||||
check(func, 'LOADK', 'RETURN1')
|
||||
checkKlist(func, {val})
|
||||
assert(func() == val)
|
||||
end
|
||||
|
||||
local function checkI (func, val)
|
||||
check(func, 'LOADI', 'RETURN1')
|
||||
checkKlist(func, {})
|
||||
assert(func() == val)
|
||||
end
|
||||
|
||||
local function checkF (func, val)
|
||||
check(func, 'LOADF', 'RETURN1')
|
||||
checkKlist(func, {})
|
||||
assert(func() == val)
|
||||
end
|
||||
|
||||
checkF(function () return 0.0 end, 0.0)
|
||||
checkI(function () return k0 end, 0)
|
||||
checkI(function () return -k0//1 end, 0)
|
||||
checkK(function () return 3^-1 end, 1/3)
|
||||
checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
|
||||
checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
|
||||
checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0)
|
||||
checkI(function () return -k3 % 5 end, 2)
|
||||
checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
|
||||
checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
|
||||
checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
|
||||
checkI(function () return ~(~kFF0 | kFF0) end, 0)
|
||||
checkI(function () return ~~-1024.0 end, -1024)
|
||||
checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
|
||||
|
||||
-- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
|
||||
local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding
|
||||
local border <const> = 65535
|
||||
checkI(function () return border end, sbx)
|
||||
checkI(function () return -border end, -sbx)
|
||||
checkI(function () return border + 1 end, sbx + 1)
|
||||
checkK(function () return border + 2 end, sbx + 2)
|
||||
checkK(function () return -(border + 1) end, -(sbx + 1))
|
||||
|
||||
local border <const> = 65535.0
|
||||
checkF(function () return border end, sbx + 0.0)
|
||||
checkF(function () return -border end, -sbx + 0.0)
|
||||
checkF(function () return border + 1 end, (sbx + 1.0))
|
||||
checkK(function () return border + 2 end, (sbx + 2.0))
|
||||
checkK(function () return -(border + 1) end, -(sbx + 1.0))
|
||||
|
||||
|
||||
-- immediate operands
|
||||
checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x - 127 end, 10, -117, 'ADDI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return 128 + x end, 0.0, 128.0,
|
||||
'ADDI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x * -127 end, -1.0, 127.0,
|
||||
'MULK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return 20 * x end, 2, 40, 'MULK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x / 40 end, 40, 1.0, 'DIVK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x // 1 end, 10.0, 10.0,
|
||||
'IDIVK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x % (100 - 10) end, 91, 1,
|
||||
'MODK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x << 127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x << -127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x >> 128 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x >> -127 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return 10 | x end, 1, 11, 'BORK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return -10 ~ x end, -1, 9, 'BXORK', 'MMBINK', 'RETURN1')
|
||||
|
||||
-- K operands in arithmetic operations
|
||||
checkR(function (x) return x + 0.0 end, 1, 1.0, 'ADDK', 'MMBINK', 'RETURN1')
|
||||
-- check(function (x) return 128 + x end, 'ADDK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x * -10000 end, 2, -20000,
|
||||
'MULK', 'MMBINK', 'RETURN1')
|
||||
-- check(function (x) return 20 * x end, 'MULK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x ^ 0.5 end, 4, 2.0, 'POWK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x / 2.0 end, 4, 2.0, 'DIVK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x // 10000 end, 10000, 1,
|
||||
'IDIVK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x % (100.0 - 10) end, 91, 1.0,
|
||||
'MODK', 'MMBINK', 'RETURN1')
|
||||
|
||||
-- no foldings (and immediate operands)
|
||||
check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
|
||||
check(function () return k3/0 end, 'LOADI', 'DIVK', 'MMBINK', 'RETURN1')
|
||||
check(function () return 0%0 end, 'LOADI', 'MODK', 'MMBINK', 'RETURN1')
|
||||
check(function () return -4//0 end, 'LOADI', 'IDIVK', 'MMBINK', 'RETURN1')
|
||||
check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'MMBIN', 'RETURN1')
|
||||
check(function (x) return x << 128 end, 'LOADI', 'SHL', 'MMBIN', 'RETURN1')
|
||||
check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'MMBIN', 'RETURN1')
|
||||
|
||||
-- basic 'for' loops
|
||||
check(function () for i = -10, 10.5 do end end,
|
||||
'LOADI', 'LOADK', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
|
||||
check(function () for i = 0xfffffff, 10.0, 1 do end end,
|
||||
'LOADK', 'LOADF', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
|
||||
|
||||
-- bug in constant folding for 5.1
|
||||
check(function () return -nil end, 'LOADNIL', 'UNM', 'RETURN1')
|
||||
|
||||
|
||||
check(function ()
|
||||
local a,b,c
|
||||
b[c], a = c, b
|
||||
b[a], a = c, b
|
||||
a, b = c, a
|
||||
a = a
|
||||
end,
|
||||
'LOADNIL',
|
||||
'MOVE', 'MOVE', 'SETTABLE',
|
||||
'MOVE', 'MOVE', 'MOVE', 'SETTABLE',
|
||||
'MOVE', 'MOVE', 'MOVE',
|
||||
-- no code for a = a
|
||||
'RETURN0')
|
||||
|
||||
|
||||
-- x == nil , x ~= nil
|
||||
-- checkequal(function (b) if (a==nil) then a=1 end; if a~=nil then a=1 end end,
|
||||
-- function () if (a==9) then a=1 end; if a~=9 then a=1 end end)
|
||||
|
||||
-- check(function () if a==nil then a='a' end end,
|
||||
-- 'GETTABUP', 'EQ', 'JMP', 'SETTABUP', 'RETURN')
|
||||
|
||||
do -- tests for table access in upvalues
|
||||
local t
|
||||
check(function () t[kx] = t.y end, 'GETTABUP', 'SETTABUP')
|
||||
check(function (a) t[a()] = t[a()] end,
|
||||
'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
|
||||
'GETUPVAL', 'GETTABLE', 'SETTABLE')
|
||||
end
|
||||
|
||||
-- de morgan
|
||||
checkequal(function () local a; if not (a or b) then b=a end end,
|
||||
function () local a; if (not a and not b) then b=a end end)
|
||||
|
||||
checkequal(function (l) local a; return 0 <= a and a <= l end,
|
||||
function (l) local a; return not (not(a >= 0) or not(a <= l)) end)
|
||||
|
||||
|
||||
-- if-break optimizations
|
||||
check(function (a, b)
|
||||
while a do
|
||||
if b then break else a = a + 1 end
|
||||
end
|
||||
end,
|
||||
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
|
||||
|
||||
checkequal(
|
||||
function (a) while a < 10 do a = a + 1 end end,
|
||||
function (a)
|
||||
::loop::
|
||||
if not (a < 10) then goto exit end
|
||||
a = a + 1
|
||||
goto loop
|
||||
::exit::
|
||||
end
|
||||
)
|
||||
|
||||
checkequal(
|
||||
function (a) repeat local x = a + 1; a = x until a > 0 end,
|
||||
function (a)
|
||||
::loop:: do
|
||||
local x = a + 1
|
||||
a = x
|
||||
end
|
||||
if not (a > 0) then goto loop end
|
||||
end
|
||||
)
|
||||
|
||||
checkequal(function () return 6 or true or nil end,
|
||||
function () return k6 or kTrue or kNil end)
|
||||
|
||||
checkequal(function () return 6 and true or nil end,
|
||||
function () return k6 and kTrue or kNil end)
|
||||
|
||||
|
||||
do -- string constants
|
||||
local k0 <const> = "00000000000000000000000000000000000000000000000000"
|
||||
local function f1 ()
|
||||
local k <const> = k0
|
||||
return function ()
|
||||
return function () return k end
|
||||
end
|
||||
end
|
||||
|
||||
local f2 = f1()
|
||||
local f3 = f2()
|
||||
assert(f3() == k0)
|
||||
checkK(f3, k0)
|
||||
-- string is not needed by other functions
|
||||
assert(T.listk(f1)[1] == nil)
|
||||
assert(T.listk(f2)[1] == nil)
|
||||
end
|
||||
|
||||
print 'OK'
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
-- $Id: testes/constructs.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
;;print "testing syntax";;
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
|
||||
local function checkload (s, msg)
|
||||
assert(string.find(select(2, load(s)), msg))
|
||||
end
|
||||
|
||||
-- testing semicollons
|
||||
do ;;; end
|
||||
; do ; a = 3; assert(a == 3) end;
|
||||
;
|
||||
|
||||
|
||||
-- invalid operations should not raise errors when not executed
|
||||
if false then a = 3 // 0; a = 0 % 0 end
|
||||
|
||||
|
||||
-- testing priorities
|
||||
|
||||
assert(2^3^2 == 2^(3^2));
|
||||
assert(2^3*4 == (2^3)*4);
|
||||
assert(2.0^-2 == 1/4 and -2^- -2 == - - -4);
|
||||
assert(not nil and 2 and not(2>3 or 3<2));
|
||||
assert(-3-1-5 == 0+0-9);
|
||||
assert(-2^2 == -4 and (-2)^2 == 4 and 2*2-3-1 == 0);
|
||||
assert(-3%5 == 2 and -3+5 == 2)
|
||||
assert(2*1+3/3 == 3 and 1+2 .. 3*1 == "33");
|
||||
assert(not(2+1 > 3*1) and "a".."b" > "a");
|
||||
|
||||
assert(0xF0 | 0xCC ~ 0xAA & 0xFD == 0xF4)
|
||||
assert(0xFD & 0xAA ~ 0xCC | 0xF0 == 0xF4)
|
||||
assert(0xF0 & 0x0F + 1 == 0x10)
|
||||
|
||||
assert(3^4//2^3//5 == 2)
|
||||
|
||||
assert(-3+4*5//2^3^2//9+4%10/3 == (-3)+(((4*5)//(2^(3^2)))//9)+((4%10)/3))
|
||||
|
||||
assert(not ((true or false) and nil))
|
||||
assert( true or false and nil)
|
||||
|
||||
-- old bug
|
||||
assert((((1 or false) and true) or false) == true)
|
||||
assert((((nil and true) or false) and true) == false)
|
||||
|
||||
local a,b = 1,nil;
|
||||
assert(-(1 or 2) == -1 and (1 and 2)+(-1.25 or -4) == 0.75);
|
||||
x = ((b or a)+1 == 2 and (10 or a)+1 == 11); assert(x);
|
||||
x = (((2<3) or 1) == true and (2<3 and 4) == 4); assert(x);
|
||||
|
||||
x,y=1,2;
|
||||
assert((x>y) and x or y == 2);
|
||||
x,y=2,1;
|
||||
assert((x>y) and x or y == 2);
|
||||
|
||||
assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891)
|
||||
|
||||
do -- testing operators with diffent kinds of constants
|
||||
-- operands to consider:
|
||||
-- * fit in register
|
||||
-- * constant doesn't fit in register
|
||||
-- * floats with integral values
|
||||
local operand = {3, 100, 5.0, -10, -5.0, 10000, -10000}
|
||||
local operator = {"+", "-", "*", "/", "//", "%", "^",
|
||||
"&", "|", "^", "<<", ">>",
|
||||
"==", "~=", "<", ">", "<=", ">=",}
|
||||
for _, op in ipairs(operator) do
|
||||
local f = assert(load(string.format([[return function (x,y)
|
||||
return x %s y
|
||||
end]], op)))();
|
||||
for _, o1 in ipairs(operand) do
|
||||
for _, o2 in ipairs(operand) do
|
||||
local gab = f(o1, o2)
|
||||
|
||||
_ENV.XX = o1
|
||||
code = string.format("return XX %s %s", op, o2)
|
||||
res = assert(load(code))()
|
||||
assert(res == gab)
|
||||
|
||||
_ENV.XX = o2
|
||||
local code = string.format("return (%s) %s XX", o1, op)
|
||||
local res = assert(load(code))()
|
||||
assert(res == gab)
|
||||
|
||||
code = string.format("return (%s) %s %s", o1, op, o2)
|
||||
res = assert(load(code))()
|
||||
assert(res == gab)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- silly loops
|
||||
repeat until 1; repeat until true;
|
||||
while false do end; while nil do end;
|
||||
|
||||
do -- test old bug (first name could not be an `upvalue')
|
||||
local a; function f(x) x={a=1}; x={x=1}; x={G=1} end
|
||||
end
|
||||
|
||||
function f (i)
|
||||
if type(i) ~= 'number' then return i,'jojo'; end;
|
||||
if i > 0 then return i, f(i-1); end;
|
||||
end
|
||||
|
||||
x = {f(3), f(5), f(10);};
|
||||
assert(x[1] == 3 and x[2] == 5 and x[3] == 10 and x[4] == 9 and x[12] == 1);
|
||||
assert(x[nil] == nil)
|
||||
x = {f'alo', f'xixi', nil};
|
||||
assert(x[1] == 'alo' and x[2] == 'xixi' and x[3] == nil);
|
||||
x = {f'alo'..'xixi'};
|
||||
assert(x[1] == 'aloxixi')
|
||||
x = {f{}}
|
||||
assert(x[2] == 'jojo' and type(x[1]) == 'table')
|
||||
|
||||
|
||||
local f = function (i)
|
||||
if i < 10 then return 'a';
|
||||
elseif i < 20 then return 'b';
|
||||
elseif i < 30 then return 'c';
|
||||
end;
|
||||
end
|
||||
|
||||
assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == nil)
|
||||
|
||||
for i=1,1000 do break; end;
|
||||
n=100;
|
||||
i=3;
|
||||
t = {};
|
||||
a=nil
|
||||
while not a do
|
||||
a=0; for i=1,n do for i=i,1,-1 do a=a+1; t[i]=1; end; end;
|
||||
end
|
||||
assert(a == n*(n+1)/2 and i==3);
|
||||
assert(t[1] and t[n] and not t[0] and not t[n+1])
|
||||
|
||||
function f(b)
|
||||
local x = 1;
|
||||
repeat
|
||||
local a;
|
||||
if b==1 then local b=1; x=10; break
|
||||
elseif b==2 then x=20; break;
|
||||
elseif b==3 then x=30;
|
||||
else local a,b,c,d=math.sin(1); x=x+1;
|
||||
end
|
||||
until x>=12;
|
||||
return x;
|
||||
end;
|
||||
|
||||
assert(f(1) == 10 and f(2) == 20 and f(3) == 30 and f(4)==12)
|
||||
|
||||
|
||||
local f = function (i)
|
||||
if i < 10 then return 'a'
|
||||
elseif i < 20 then return 'b'
|
||||
elseif i < 30 then return 'c'
|
||||
else return 8
|
||||
end
|
||||
end
|
||||
|
||||
assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == 8)
|
||||
|
||||
local a, b = nil, 23
|
||||
x = {f(100)*2+3 or a, a or b+2}
|
||||
assert(x[1] == 19 and x[2] == 25)
|
||||
x = {f=2+3 or a, a = b+2}
|
||||
assert(x.f == 5 and x.a == 25)
|
||||
|
||||
a={y=1}
|
||||
x = {a.y}
|
||||
assert(x[1] == 1)
|
||||
|
||||
function f(i)
|
||||
while 1 do
|
||||
if i>0 then i=i-1;
|
||||
else return; end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function g(i)
|
||||
while 1 do
|
||||
if i>0 then i=i-1
|
||||
else return end
|
||||
end
|
||||
end
|
||||
|
||||
f(10); g(10);
|
||||
|
||||
do
|
||||
function f () return 1,2,3; end
|
||||
local a, b, c = f();
|
||||
assert(a==1 and b==2 and c==3)
|
||||
a, b, c = (f());
|
||||
assert(a==1 and b==nil and c==nil)
|
||||
end
|
||||
|
||||
local a,b = 3 and f();
|
||||
assert(a==1 and b==nil)
|
||||
|
||||
function g() f(); return; end;
|
||||
assert(g() == nil)
|
||||
function g() return nil or f() end
|
||||
a,b = g()
|
||||
assert(a==1 and b==nil)
|
||||
|
||||
print'+';
|
||||
|
||||
do -- testing constants
|
||||
local prog <const> = [[local x <XXX> = 10]]
|
||||
checkload(prog, "unknown attribute 'XXX'")
|
||||
|
||||
checkload([[local xxx <const> = 20; xxx = 10]],
|
||||
":1: attempt to assign to const variable 'xxx'")
|
||||
|
||||
checkload([[
|
||||
local xx;
|
||||
local xxx <const> = 20;
|
||||
local yyy;
|
||||
local function foo ()
|
||||
local abc = xx + yyy + xxx;
|
||||
return function () return function () xxx = yyy end end
|
||||
end
|
||||
]], ":6: attempt to assign to const variable 'xxx'")
|
||||
|
||||
checkload([[
|
||||
local x <close> = nil
|
||||
x = io.open()
|
||||
]], ":2: attempt to assign to const variable 'x'")
|
||||
end
|
||||
|
||||
f = [[
|
||||
return function ( a , b , c , d , e )
|
||||
local x = a >= b or c or ( d and e ) or nil
|
||||
return x
|
||||
end , { a = 1 , b = 2 >= 1 , } or { 1 };
|
||||
]]
|
||||
f = string.gsub(f, "%s+", "\n"); -- force a SETLINE between opcodes
|
||||
f,a = load(f)();
|
||||
assert(a.a == 1 and a.b)
|
||||
|
||||
function g (a,b,c,d,e)
|
||||
if not (a>=b or c or d and e or nil) then return 0; else return 1; end;
|
||||
end
|
||||
|
||||
function h (a,b,c,d,e)
|
||||
while (a>=b or c or (d and e) or nil) do return 1; end;
|
||||
return 0;
|
||||
end;
|
||||
|
||||
assert(f(2,1) == true and g(2,1) == 1 and h(2,1) == 1)
|
||||
assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1)
|
||||
assert(f(1,2,'a')
|
||||
~= -- force SETLINE before nil
|
||||
nil, "")
|
||||
assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1)
|
||||
assert(f(1,2,nil,1,'x') == 'x' and g(1,2,nil,1,'x') == 1 and
|
||||
h(1,2,nil,1,'x') == 1)
|
||||
assert(f(1,2,nil,nil,'x') == nil and g(1,2,nil,nil,'x') == 0 and
|
||||
h(1,2,nil,nil,'x') == 0)
|
||||
assert(f(1,2,nil,1,nil) == nil and g(1,2,nil,1,nil) == 0 and
|
||||
h(1,2,nil,1,nil) == 0)
|
||||
|
||||
assert(1 and 2<3 == true and 2<3 and 'a'<'b' == true)
|
||||
x = 2<3 and not 3; assert(x==false)
|
||||
x = 2<1 or (2>1 and 'a'); assert(x=='a')
|
||||
|
||||
|
||||
do
|
||||
local a; if nil then a=1; else a=2; end; -- this nil comes as PUSHNIL 2
|
||||
assert(a==2)
|
||||
end
|
||||
|
||||
function F(a)
|
||||
assert(debug.getinfo(1, "n").name == 'F')
|
||||
return a,2,3
|
||||
end
|
||||
|
||||
a,b = F(1)~=nil; assert(a == true and b == nil);
|
||||
a,b = F(nil)==nil; assert(a == true and b == nil)
|
||||
|
||||
----------------------------------------------------------------
|
||||
------------------------------------------------------------------
|
||||
|
||||
-- sometimes will be 0, sometimes will not...
|
||||
_ENV.GLOB1 = math.random(0, 1)
|
||||
|
||||
-- basic expressions with their respective values
|
||||
local basiccases = {
|
||||
{"nil", nil},
|
||||
{"false", false},
|
||||
{"true", true},
|
||||
{"10", 10},
|
||||
{"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1},
|
||||
}
|
||||
|
||||
local prog
|
||||
|
||||
if _ENV.GLOB1 == 0 then
|
||||
basiccases[2][1] = "F" -- constant false
|
||||
|
||||
prog = [[
|
||||
local F <const> = false
|
||||
if %s then IX = true end
|
||||
return %s
|
||||
]]
|
||||
else
|
||||
basiccases[4][1] = "k10" -- constant 10
|
||||
|
||||
prog = [[
|
||||
local k10 <const> = 10
|
||||
if %s then IX = true end
|
||||
return %s
|
||||
]]
|
||||
end
|
||||
|
||||
print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')')
|
||||
|
||||
|
||||
-- operators with their respective values
|
||||
local binops <const> = {
|
||||
{" and ", function (a,b) if not a then return a else return b end end},
|
||||
{" or ", function (a,b) if a then return a else return b end end},
|
||||
}
|
||||
|
||||
local cases <const> = {}
|
||||
|
||||
-- creates all combinations of '(cases[i] op cases[n-i])' plus
|
||||
-- 'not(cases[i] op cases[n-i])' (syntax + value)
|
||||
local function createcases (n)
|
||||
local res = {}
|
||||
for i = 1, n - 1 do
|
||||
for _, v1 in ipairs(cases[i]) do
|
||||
for _, v2 in ipairs(cases[n - i]) do
|
||||
for _, op in ipairs(binops) do
|
||||
local t = {
|
||||
"(" .. v1[1] .. op[1] .. v2[1] .. ")",
|
||||
op[2](v1[2], v2[2])
|
||||
}
|
||||
res[#res + 1] = t
|
||||
res[#res + 1] = {"not" .. t[1], not t[2]}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
-- do not do too many combinations for soft tests
|
||||
local level = _soft and 3 or 4
|
||||
|
||||
cases[1] = basiccases
|
||||
for i = 2, level do cases[i] = createcases(i) end
|
||||
print("+")
|
||||
|
||||
local i = 0
|
||||
for n = 1, level do
|
||||
for _, v in pairs(cases[n]) do
|
||||
local s = v[1]
|
||||
local p = load(string.format(prog, s, s), "")
|
||||
IX = false
|
||||
assert(p() == v[2] and IX == not not v[2])
|
||||
i = i + 1
|
||||
if i % 60000 == 0 then print('+') end
|
||||
end
|
||||
end
|
||||
------------------------------------------------------------------
|
||||
|
||||
-- testing some syntax errors (chosen through 'gcov')
|
||||
checkload("for x do", "expected")
|
||||
checkload("x:call", "expected")
|
||||
|
||||
print'OK'
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,144 @@
|
||||
-- $Id: testes/cstack.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
print"testing C-stack overflow detection"
|
||||
print"If this test craches, see its file ('cstack.lua')"
|
||||
|
||||
-- Segmentation faults in these tests probably result from a C-stack
|
||||
-- overflow. To avoid these errors, you can use the function
|
||||
-- 'debug.setcstacklimit' to set a smaller limit for the use of
|
||||
-- C stack by Lua. After finding a reliable limit, you might want
|
||||
-- to recompile Lua with this limit as the value for
|
||||
-- the constant 'LUAI_MAXCCALLS', which defines the default limit.
|
||||
-- (The default limit is printed by this test.)
|
||||
-- Alternatively, you can ensure a larger stack for the program.
|
||||
|
||||
-- For Linux, a limit up to 30_000 seems Ok. Windows cannot go much
|
||||
-- higher than 2_000.
|
||||
|
||||
|
||||
local origlimit = debug.setcstacklimit(400)
|
||||
print("default stack limit: " .. origlimit)
|
||||
|
||||
-- change this value for different limits for this test suite
|
||||
local currentlimit = origlimit
|
||||
debug.setcstacklimit(currentlimit)
|
||||
print("current stack limit: " .. currentlimit)
|
||||
|
||||
|
||||
local function checkerror (msg, f, ...)
|
||||
local s, err = pcall(f, ...)
|
||||
assert(not s and string.find(err, msg))
|
||||
end
|
||||
|
||||
local count
|
||||
local back = string.rep("\b", 8)
|
||||
local function progress ()
|
||||
count = count + 1
|
||||
local n = string.format("%-8d", count)
|
||||
io.stderr:write(back, n)
|
||||
end
|
||||
|
||||
|
||||
do print("testing simple recursion:")
|
||||
count = 0
|
||||
local function foo ()
|
||||
progress()
|
||||
foo()
|
||||
end
|
||||
checkerror("stack overflow", foo)
|
||||
print("\tfinal count: ", count)
|
||||
end
|
||||
|
||||
|
||||
do print("testing stack overflow in message handling")
|
||||
count = 0
|
||||
local function loop (x, y, z)
|
||||
progress()
|
||||
return 1 + loop(x, y, z)
|
||||
end
|
||||
local res, msg = xpcall(loop, loop)
|
||||
assert(msg == "error in error handling")
|
||||
print("\tfinal count: ", count)
|
||||
end
|
||||
|
||||
|
||||
-- bug since 2.5 (C-stack overflow in recursion inside pattern matching)
|
||||
do print("testing recursion inside pattern matching")
|
||||
local function f (size)
|
||||
local s = string.rep("a", size)
|
||||
local p = string.rep(".?", size)
|
||||
return string.match(s, p)
|
||||
end
|
||||
local m = f(80)
|
||||
assert(#m == 80)
|
||||
checkerror("too complex", f, 200000)
|
||||
end
|
||||
|
||||
|
||||
do print("testing stack-overflow in recursive 'gsub'")
|
||||
count = 0
|
||||
local function foo ()
|
||||
progress()
|
||||
string.gsub("a", ".", foo)
|
||||
end
|
||||
checkerror("stack overflow", foo)
|
||||
print("\tfinal count: ", count)
|
||||
|
||||
print("testing stack-overflow in recursive 'gsub' with metatables")
|
||||
count = 0
|
||||
local t = setmetatable({}, {__index = foo})
|
||||
foo = function ()
|
||||
count = count + 1
|
||||
progress(count)
|
||||
string.gsub("a", ".", t)
|
||||
end
|
||||
checkerror("stack overflow", foo)
|
||||
print("\tfinal count: ", count)
|
||||
end
|
||||
|
||||
|
||||
do print("testing changes in C-stack limit")
|
||||
|
||||
assert(not debug.setcstacklimit(0)) -- limit too small
|
||||
assert(not debug.setcstacklimit(50000)) -- limit too large
|
||||
local co = coroutine.wrap (function ()
|
||||
return debug.setcstacklimit(400)
|
||||
end)
|
||||
assert(co() == false) -- cannot change C stack inside coroutine
|
||||
|
||||
local n
|
||||
local function foo () n = n + 1; foo () end
|
||||
|
||||
local function check ()
|
||||
n = 0
|
||||
pcall(foo)
|
||||
return n
|
||||
end
|
||||
|
||||
assert(debug.setcstacklimit(400) == currentlimit)
|
||||
local lim400 = check()
|
||||
-- a very low limit (given that the several calls to arive here)
|
||||
local lowlimit = 38
|
||||
assert(debug.setcstacklimit(lowlimit) == 400)
|
||||
assert(check() < lowlimit - 30)
|
||||
assert(debug.setcstacklimit(600) == lowlimit)
|
||||
local lim600 = check()
|
||||
assert(lim600 == lim400 + 200)
|
||||
|
||||
|
||||
-- 'setcstacklimit' works inside protected calls. (The new stack
|
||||
-- limit is kept when 'pcall' returns.)
|
||||
assert(pcall(function ()
|
||||
assert(debug.setcstacklimit(400) == 600)
|
||||
assert(check() <= lim400)
|
||||
end))
|
||||
|
||||
assert(check() == lim400)
|
||||
assert(debug.setcstacklimit(origlimit) == 400) -- restore original limit
|
||||
end
|
||||
|
||||
|
||||
print'OK'
|
||||
+958
@@ -0,0 +1,958 @@
|
||||
-- $Id: testes/db.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
-- testing debug library
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
local function dostring(s) return assert(load(s))() end
|
||||
|
||||
print"testing debug library and debug information"
|
||||
|
||||
do
|
||||
local a=1
|
||||
end
|
||||
|
||||
assert(not debug.gethook())
|
||||
|
||||
local testline = 19 -- line where 'test' is defined
|
||||
function test (s, l, p) -- this must be line 19
|
||||
collectgarbage() -- avoid gc during trace
|
||||
local function f (event, line)
|
||||
assert(event == 'line')
|
||||
local l = table.remove(l, 1)
|
||||
if p then print(l, line) end
|
||||
assert(l == line, "wrong trace!!")
|
||||
end
|
||||
debug.sethook(f,"l"); load(s)(); debug.sethook()
|
||||
assert(#l == 0)
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
assert(not pcall(debug.getinfo, print, "X")) -- invalid option
|
||||
assert(not debug.getinfo(1000)) -- out of range level
|
||||
assert(not debug.getinfo(-1)) -- out of range level
|
||||
local a = debug.getinfo(print)
|
||||
assert(a.what == "C" and a.short_src == "[C]")
|
||||
a = debug.getinfo(print, "L")
|
||||
assert(a.activelines == nil)
|
||||
local b = debug.getinfo(test, "SfL")
|
||||
assert(b.name == nil and b.what == "Lua" and b.linedefined == testline and
|
||||
b.lastlinedefined == b.linedefined + 10 and
|
||||
b.func == test and not string.find(b.short_src, "%["))
|
||||
assert(b.activelines[b.linedefined + 1] and
|
||||
b.activelines[b.lastlinedefined])
|
||||
assert(not b.activelines[b.linedefined] and
|
||||
not b.activelines[b.lastlinedefined + 1])
|
||||
end
|
||||
|
||||
|
||||
-- test file and string names truncation
|
||||
a = "function f () end"
|
||||
local function dostring (s, x) return load(s, x)() end
|
||||
dostring(a)
|
||||
assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a))
|
||||
dostring(a..string.format("; %s\n=1", string.rep('p', 400)))
|
||||
assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$'))
|
||||
dostring(a..string.format("; %s=1", string.rep('p', 400)))
|
||||
assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$'))
|
||||
dostring("\n"..a)
|
||||
assert(debug.getinfo(f).short_src == '[string "..."]')
|
||||
dostring(a, "")
|
||||
assert(debug.getinfo(f).short_src == '[string ""]')
|
||||
dostring(a, "@xuxu")
|
||||
assert(debug.getinfo(f).short_src == "xuxu")
|
||||
dostring(a, "@"..string.rep('p', 1000)..'t')
|
||||
assert(string.find(debug.getinfo(f).short_src, "^%.%.%.p*t$"))
|
||||
dostring(a, "=xuxu")
|
||||
assert(debug.getinfo(f).short_src == "xuxu")
|
||||
dostring(a, string.format("=%s", string.rep('x', 500)))
|
||||
assert(string.find(debug.getinfo(f).short_src, "^x*$"))
|
||||
dostring(a, "=")
|
||||
assert(debug.getinfo(f).short_src == "")
|
||||
a = nil; f = nil;
|
||||
|
||||
|
||||
repeat
|
||||
local g = {x = function ()
|
||||
local a = debug.getinfo(2)
|
||||
assert(a.name == 'f' and a.namewhat == 'local')
|
||||
a = debug.getinfo(1)
|
||||
assert(a.name == 'x' and a.namewhat == 'field')
|
||||
return 'xixi'
|
||||
end}
|
||||
local f = function () return 1+1 and (not 1 or g.x()) end
|
||||
assert(f() == 'xixi')
|
||||
g = debug.getinfo(f)
|
||||
assert(g.what == "Lua" and g.func == f and g.namewhat == "" and not g.name)
|
||||
|
||||
function f (x, name) -- local!
|
||||
name = name or 'f'
|
||||
local a = debug.getinfo(1)
|
||||
assert(a.name == name and a.namewhat == 'local')
|
||||
return x
|
||||
end
|
||||
|
||||
-- breaks in different conditions
|
||||
if 3>4 then break end; f()
|
||||
if 3<4 then a=1 else break end; f()
|
||||
while 1 do local x=10; break end; f()
|
||||
local b = 1
|
||||
if 3>4 then return math.sin(1) end; f()
|
||||
a = 3<4; f()
|
||||
a = 3<4 or 1; f()
|
||||
repeat local x=20; if 4>3 then f() else break end; f() until 1
|
||||
g = {}
|
||||
f(g).x = f(2) and f(10)+f(9)
|
||||
assert(g.x == f(19))
|
||||
function g(x) if not x then return 3 end return (x('a', 'x')) end
|
||||
assert(g(f) == 'a')
|
||||
until 1
|
||||
|
||||
test([[if
|
||||
math.sin(1)
|
||||
then
|
||||
a=1
|
||||
else
|
||||
a=2
|
||||
end
|
||||
]], {2,3,4,7})
|
||||
|
||||
test([[--
|
||||
if nil then
|
||||
a=1
|
||||
else
|
||||
a=2
|
||||
end
|
||||
]], {2,5,6})
|
||||
|
||||
test([[a=1
|
||||
repeat
|
||||
a=a+1
|
||||
until a==3
|
||||
]], {1,3,4,3,4})
|
||||
|
||||
test([[ do
|
||||
return
|
||||
end
|
||||
]], {2})
|
||||
|
||||
test([[local a
|
||||
a=1
|
||||
while a<=3 do
|
||||
a=a+1
|
||||
end
|
||||
]], {1,2,3,4,3,4,3,4,3,5})
|
||||
|
||||
test([[while math.sin(1) do
|
||||
if math.sin(1)
|
||||
then break
|
||||
end
|
||||
end
|
||||
a=1]], {1,2,3,6})
|
||||
|
||||
test([[for i=1,3 do
|
||||
a=i
|
||||
end
|
||||
]], {1,2,1,2,1,2,1,3})
|
||||
|
||||
test([[for i,v in pairs{'a','b'} do
|
||||
a=tostring(i) .. v
|
||||
end
|
||||
]], {1,2,1,2,1,3})
|
||||
|
||||
test([[for i=1,4 do a=1 end]], {1,1,1,1})
|
||||
|
||||
|
||||
do -- testing line info/trace with large gaps in source
|
||||
|
||||
local a = {1, 2, 3, 10, 124, 125, 126, 127, 128, 129, 130,
|
||||
255, 256, 257, 500, 1000}
|
||||
local s = [[
|
||||
local b = {10}
|
||||
a = b[1] X + Y b[1]
|
||||
b = 4
|
||||
]]
|
||||
for _, i in ipairs(a) do
|
||||
local subs = {X = string.rep("\n", i)}
|
||||
for _, j in ipairs(a) do
|
||||
subs.Y = string.rep("\n", j)
|
||||
local s = string.gsub(s, "[XY]", subs)
|
||||
test(s, {1, 2 + i, 2 + i + j, 2 + i, 2 + i + j, 3 + i + j})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print'+'
|
||||
|
||||
-- invalid levels in [gs]etlocal
|
||||
assert(not pcall(debug.getlocal, 20, 1))
|
||||
assert(not pcall(debug.setlocal, -1, 1, 10))
|
||||
|
||||
|
||||
-- parameter names
|
||||
local function foo (a,b,...) local d, e end
|
||||
local co = coroutine.create(foo)
|
||||
|
||||
assert(debug.getlocal(foo, 1) == 'a')
|
||||
assert(debug.getlocal(foo, 2) == 'b')
|
||||
assert(not debug.getlocal(foo, 3))
|
||||
assert(debug.getlocal(co, foo, 1) == 'a')
|
||||
assert(debug.getlocal(co, foo, 2) == 'b')
|
||||
assert(not debug.getlocal(co, foo, 3))
|
||||
|
||||
assert(not debug.getlocal(print, 1))
|
||||
|
||||
|
||||
local function foo () return (debug.getlocal(1, -1)) end
|
||||
assert(not foo(10))
|
||||
|
||||
|
||||
-- varargs
|
||||
local function foo (a, ...)
|
||||
local t = table.pack(...)
|
||||
for i = 1, t.n do
|
||||
local n, v = debug.getlocal(1, -i)
|
||||
assert(n == "(vararg)" and v == t[i])
|
||||
end
|
||||
assert(not debug.getlocal(1, -(t.n + 1)))
|
||||
assert(not debug.setlocal(1, -(t.n + 1), 30))
|
||||
if t.n > 0 then
|
||||
(function (x)
|
||||
assert(debug.setlocal(2, -1, x) == "(vararg)")
|
||||
assert(debug.setlocal(2, -t.n, x) == "(vararg)")
|
||||
end)(430)
|
||||
assert(... == 430)
|
||||
end
|
||||
end
|
||||
|
||||
foo()
|
||||
foo(print)
|
||||
foo(200, 3, 4)
|
||||
local a = {}
|
||||
for i = 1, (_soft and 100 or 1000) do a[i] = i end
|
||||
foo(table.unpack(a))
|
||||
a = nil
|
||||
|
||||
|
||||
|
||||
do -- test hook presence in debug info
|
||||
assert(not debug.gethook())
|
||||
local count = 0
|
||||
local function f ()
|
||||
assert(debug.getinfo(1).namewhat == "hook")
|
||||
local sndline = string.match(debug.traceback(), "\n(.-)\n")
|
||||
assert(string.find(sndline, "hook"))
|
||||
count = count + 1
|
||||
end
|
||||
debug.sethook(f, "l")
|
||||
local a = 0
|
||||
_ENV.a = a
|
||||
a = 1
|
||||
debug.sethook()
|
||||
assert(count == 4)
|
||||
end
|
||||
|
||||
|
||||
-- hook table has weak keys
|
||||
assert(getmetatable(debug.getregistry()._HOOKKEY).__mode == 'k')
|
||||
|
||||
|
||||
a = {}; L = nil
|
||||
local glob = 1
|
||||
local oldglob = glob
|
||||
debug.sethook(function (e,l)
|
||||
collectgarbage() -- force GC during a hook
|
||||
local f, m, c = debug.gethook()
|
||||
assert(m == 'crl' and c == 0)
|
||||
if e == "line" then
|
||||
if glob ~= oldglob then
|
||||
L = l-1 -- get the first line where "glob" has changed
|
||||
oldglob = glob
|
||||
end
|
||||
elseif e == "call" then
|
||||
local f = debug.getinfo(2, "f").func
|
||||
a[f] = 1
|
||||
else assert(e == "return")
|
||||
end
|
||||
end, "crl")
|
||||
|
||||
|
||||
function f(a,b)
|
||||
collectgarbage()
|
||||
local _, x = debug.getlocal(1, 1)
|
||||
local _, y = debug.getlocal(1, 2)
|
||||
assert(x == a and y == b)
|
||||
assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
|
||||
assert(debug.setlocal(2, 4, "maçã") == "B")
|
||||
x = debug.getinfo(2)
|
||||
assert(x.func == g and x.what == "Lua" and x.name == 'g' and
|
||||
x.nups == 2 and string.find(x.source, "^@.*db%.lua$"))
|
||||
glob = glob+1
|
||||
assert(debug.getinfo(1, "l").currentline == L+1)
|
||||
assert(debug.getinfo(1, "l").currentline == L+2)
|
||||
end
|
||||
|
||||
function foo()
|
||||
glob = glob+1
|
||||
assert(debug.getinfo(1, "l").currentline == L+1)
|
||||
end; foo() -- set L
|
||||
-- check line counting inside strings and empty lines
|
||||
|
||||
_ = 'alo\
|
||||
alo' .. [[
|
||||
|
||||
]]
|
||||
--[[
|
||||
]]
|
||||
assert(debug.getinfo(1, "l").currentline == L+11) -- check count of lines
|
||||
|
||||
|
||||
function g (...)
|
||||
local arg = {...}
|
||||
do local a,b,c; a=math.sin(40); end
|
||||
local feijao
|
||||
local AAAA,B = "xuxu", "mamão"
|
||||
f(AAAA,B)
|
||||
assert(AAAA == "pera" and B == "maçã")
|
||||
do
|
||||
local B = 13
|
||||
local x,y = debug.getlocal(1,5)
|
||||
assert(x == 'B' and y == 13)
|
||||
end
|
||||
end
|
||||
|
||||
g()
|
||||
|
||||
|
||||
assert(a[f] and a[g] and a[assert] and a[debug.getlocal] and not a[print])
|
||||
|
||||
|
||||
-- tests for manipulating non-registered locals (C and Lua temporaries)
|
||||
|
||||
local n, v = debug.getlocal(0, 1)
|
||||
assert(v == 0 and n == "(C temporary)")
|
||||
local n, v = debug.getlocal(0, 2)
|
||||
assert(v == 2 and n == "(C temporary)")
|
||||
assert(not debug.getlocal(0, 3))
|
||||
assert(not debug.getlocal(0, 0))
|
||||
|
||||
function f()
|
||||
assert(select(2, debug.getlocal(2,3)) == 1)
|
||||
assert(not debug.getlocal(2,4))
|
||||
debug.setlocal(2, 3, 10)
|
||||
return 20
|
||||
end
|
||||
|
||||
function g(a,b) return (a+1) + f() end
|
||||
|
||||
assert(g(0,0) == 30)
|
||||
|
||||
|
||||
debug.sethook(nil);
|
||||
assert(not debug.gethook())
|
||||
|
||||
|
||||
-- minimal tests for setuservalue/getuservalue
|
||||
do
|
||||
assert(not debug.setuservalue(io.stdin, 10))
|
||||
local a, b = debug.getuservalue(io.stdin, 10)
|
||||
assert(a == nil and not b)
|
||||
end
|
||||
|
||||
-- testing iteraction between multiple values x hooks
|
||||
do
|
||||
local function f(...) return 3, ... end
|
||||
local count = 0
|
||||
local a = {}
|
||||
for i = 1, 100 do a[i] = i end
|
||||
debug.sethook(function () count = count + 1 end, "", 1)
|
||||
local t = {table.unpack(a)}
|
||||
assert(#t == 100)
|
||||
t = {table.unpack(a, 1, 3)}
|
||||
assert(#t == 3)
|
||||
t = {f(table.unpack(a, 1, 30))}
|
||||
assert(#t == 31)
|
||||
end
|
||||
|
||||
|
||||
-- testing access to function arguments
|
||||
|
||||
local function collectlocals (level)
|
||||
local tab = {}
|
||||
for i = 1, math.huge do
|
||||
local n, v = debug.getlocal(level + 1, i)
|
||||
if not (n and string.find(n, "^[a-zA-Z0-9_]+$")) then
|
||||
break -- consider only real variables
|
||||
end
|
||||
tab[n] = v
|
||||
end
|
||||
return tab
|
||||
end
|
||||
|
||||
|
||||
X = nil
|
||||
a = {}
|
||||
function a:f (a, b, ...) local arg = {...}; local c = 13 end
|
||||
debug.sethook(function (e)
|
||||
assert(e == "call")
|
||||
dostring("XX = 12") -- test dostring inside hooks
|
||||
-- testing errors inside hooks
|
||||
assert(not pcall(load("a='joao'+1")))
|
||||
debug.sethook(function (e, l)
|
||||
assert(debug.getinfo(2, "l").currentline == l)
|
||||
local f,m,c = debug.gethook()
|
||||
assert(e == "line")
|
||||
assert(m == 'l' and c == 0)
|
||||
debug.sethook(nil) -- hook is called only once
|
||||
assert(not X) -- check that
|
||||
X = collectlocals(2)
|
||||
end, "l")
|
||||
end, "c")
|
||||
|
||||
a:f(1,2,3,4,5)
|
||||
assert(X.self == a and X.a == 1 and X.b == 2 and X.c == nil)
|
||||
assert(XX == 12)
|
||||
assert(not debug.gethook())
|
||||
|
||||
|
||||
-- testing access to local variables in return hook (bug in 5.2)
|
||||
do
|
||||
local X = false
|
||||
|
||||
local function foo (a, b, ...)
|
||||
do local x,y,z end
|
||||
local c, d = 10, 20
|
||||
return
|
||||
end
|
||||
|
||||
local function aux ()
|
||||
if debug.getinfo(2).name == "foo" then
|
||||
X = true -- to signal that it found 'foo'
|
||||
local tab = {a = 100, b = 200, c = 10, d = 20}
|
||||
for n, v in pairs(collectlocals(2)) do
|
||||
assert(tab[n] == v)
|
||||
tab[n] = undef
|
||||
end
|
||||
assert(next(tab) == nil) -- 'tab' must be empty
|
||||
end
|
||||
end
|
||||
|
||||
debug.sethook(aux, "r"); foo(100, 200); debug.sethook()
|
||||
assert(X)
|
||||
|
||||
end
|
||||
|
||||
|
||||
local function eqseq (t1, t2)
|
||||
assert(#t1 == #t2)
|
||||
for i = 1, #t1 do
|
||||
assert(t1[i] == t2[i])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do print("testing inspection of parameters/returned values")
|
||||
local on = false
|
||||
local inp, out
|
||||
|
||||
local function hook (event)
|
||||
if not on then return end
|
||||
local ar = debug.getinfo(2, "ruS")
|
||||
local t = {}
|
||||
for i = ar.ftransfer, ar.ftransfer + ar.ntransfer - 1 do
|
||||
local _, v = debug.getlocal(2, i)
|
||||
t[#t + 1] = v
|
||||
end
|
||||
if event == "return" then
|
||||
out = t
|
||||
else
|
||||
inp = t
|
||||
end
|
||||
end
|
||||
|
||||
debug.sethook(hook, "cr")
|
||||
|
||||
on = true; math.sin(3); on = false
|
||||
eqseq(inp, {3}); eqseq(out, {math.sin(3)})
|
||||
|
||||
on = true; select(2, 10, 20, 30, 40); on = false
|
||||
eqseq(inp, {2, 10, 20, 30, 40}); eqseq(out, {20, 30, 40})
|
||||
|
||||
local function foo (a, ...) return ... end
|
||||
local function foo1 () on = not on; return foo(20, 10, 0) end
|
||||
foo1(); on = false
|
||||
eqseq(inp, {20}); eqseq(out, {10, 0})
|
||||
|
||||
debug.sethook()
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- testing upvalue access
|
||||
local function getupvalues (f)
|
||||
local t = {}
|
||||
local i = 1
|
||||
while true do
|
||||
local name, value = debug.getupvalue(f, i)
|
||||
if not name then break end
|
||||
assert(not t[name])
|
||||
t[name] = value
|
||||
i = i + 1
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local a,b,c = 1,2,3
|
||||
local function foo1 (a) b = a; return c end
|
||||
local function foo2 (x) a = x; return c+b end
|
||||
assert(not debug.getupvalue(foo1, 3))
|
||||
assert(not debug.getupvalue(foo1, 0))
|
||||
assert(not debug.setupvalue(foo1, 3, "xuxu"))
|
||||
local t = getupvalues(foo1)
|
||||
assert(t.a == nil and t.b == 2 and t.c == 3)
|
||||
t = getupvalues(foo2)
|
||||
assert(t.a == 1 and t.b == 2 and t.c == 3)
|
||||
assert(debug.setupvalue(foo1, 1, "xuxu") == "b")
|
||||
assert(({debug.getupvalue(foo2, 3)})[2] == "xuxu")
|
||||
-- upvalues of C functions are allways "called" "" (the empty string)
|
||||
assert(debug.getupvalue(string.gmatch("x", "x"), 1) == "")
|
||||
|
||||
|
||||
-- testing count hooks
|
||||
local a=0
|
||||
debug.sethook(function (e) a=a+1 end, "", 1)
|
||||
a=0; for i=1,1000 do end; assert(1000 < a and a < 1012)
|
||||
debug.sethook(function (e) a=a+1 end, "", 4)
|
||||
a=0; for i=1,1000 do end; assert(250 < a and a < 255)
|
||||
local f,m,c = debug.gethook()
|
||||
assert(m == "" and c == 4)
|
||||
debug.sethook(function (e) a=a+1 end, "", 4000)
|
||||
a=0; for i=1,1000 do end; assert(a == 0)
|
||||
|
||||
do
|
||||
debug.sethook(print, "", 2^24 - 1) -- count upperbound
|
||||
local f,m,c = debug.gethook()
|
||||
assert(({debug.gethook()})[3] == 2^24 - 1)
|
||||
end
|
||||
|
||||
debug.sethook()
|
||||
|
||||
|
||||
-- tests for tail calls
|
||||
local function f (x)
|
||||
if x then
|
||||
assert(debug.getinfo(1, "S").what == "Lua")
|
||||
assert(debug.getinfo(1, "t").istailcall == true)
|
||||
local tail = debug.getinfo(2)
|
||||
assert(tail.func == g1 and tail.istailcall == true)
|
||||
assert(debug.getinfo(3, "S").what == "main")
|
||||
print"+"
|
||||
end
|
||||
end
|
||||
|
||||
function g(x) return f(x) end
|
||||
|
||||
function g1(x) g(x) end
|
||||
|
||||
local function h (x) local f=g1; return f(x) end
|
||||
|
||||
h(true)
|
||||
|
||||
local b = {}
|
||||
debug.sethook(function (e) table.insert(b, e) end, "cr")
|
||||
h(false)
|
||||
debug.sethook()
|
||||
local res = {"return", -- first return (from sethook)
|
||||
"call", "tail call", "call", "tail call",
|
||||
"return", "return",
|
||||
"call", -- last call (to sethook)
|
||||
}
|
||||
for i = 1, #res do assert(res[i] == table.remove(b, 1)) end
|
||||
|
||||
b = 0
|
||||
debug.sethook(function (e)
|
||||
if e == "tail call" then
|
||||
b = b + 1
|
||||
assert(debug.getinfo(2, "t").istailcall == true)
|
||||
else
|
||||
assert(debug.getinfo(2, "t").istailcall == false)
|
||||
end
|
||||
end, "c")
|
||||
h(false)
|
||||
debug.sethook()
|
||||
assert(b == 2) -- two tail calls
|
||||
|
||||
lim = _soft and 3000 or 30000
|
||||
local function foo (x)
|
||||
if x==0 then
|
||||
assert(debug.getinfo(2).what == "main")
|
||||
local info = debug.getinfo(1)
|
||||
assert(info.istailcall == true and info.func == foo)
|
||||
else return foo(x-1)
|
||||
end
|
||||
end
|
||||
|
||||
foo(lim)
|
||||
|
||||
|
||||
print"+"
|
||||
|
||||
|
||||
-- testing local function information
|
||||
co = load[[
|
||||
local A = function ()
|
||||
return x
|
||||
end
|
||||
return
|
||||
]]
|
||||
|
||||
local a = 0
|
||||
-- 'A' should be visible to debugger only after its complete definition
|
||||
debug.sethook(function (e, l)
|
||||
if l == 3 then a = a + 1; assert(debug.getlocal(2, 1) == "(temporary)")
|
||||
elseif l == 4 then a = a + 1; assert(debug.getlocal(2, 1) == "A")
|
||||
end
|
||||
end, "l")
|
||||
co() -- run local function definition
|
||||
debug.sethook() -- turn off hook
|
||||
assert(a == 2) -- ensure all two lines where hooked
|
||||
|
||||
-- testing traceback
|
||||
|
||||
assert(debug.traceback(print) == print)
|
||||
assert(debug.traceback(print, 4) == print)
|
||||
assert(string.find(debug.traceback("hi", 4), "^hi\n"))
|
||||
assert(string.find(debug.traceback("hi"), "^hi\n"))
|
||||
assert(not string.find(debug.traceback("hi"), "'debug.traceback'"))
|
||||
assert(string.find(debug.traceback("hi", 0), "'debug.traceback'"))
|
||||
assert(string.find(debug.traceback(), "^stack traceback:\n"))
|
||||
|
||||
do -- C-function names in traceback
|
||||
local st, msg = (function () return pcall end)()(debug.traceback)
|
||||
assert(st == true and string.find(msg, "pcall"))
|
||||
end
|
||||
|
||||
|
||||
-- testing nparams, nups e isvararg
|
||||
local t = debug.getinfo(print, "u")
|
||||
assert(t.isvararg == true and t.nparams == 0 and t.nups == 0)
|
||||
|
||||
t = debug.getinfo(function (a,b,c) end, "u")
|
||||
assert(t.isvararg == false and t.nparams == 3 and t.nups == 0)
|
||||
|
||||
t = debug.getinfo(function (a,b,...) return t[a] end, "u")
|
||||
assert(t.isvararg == true and t.nparams == 2 and t.nups == 1)
|
||||
|
||||
t = debug.getinfo(1) -- main
|
||||
assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and
|
||||
debug.getupvalue(t.func, 1) == "_ENV")
|
||||
|
||||
|
||||
|
||||
|
||||
-- testing debugging of coroutines
|
||||
|
||||
local function checktraceback (co, p, level)
|
||||
local tb = debug.traceback(co, nil, level)
|
||||
local i = 0
|
||||
for l in string.gmatch(tb, "[^\n]+\n?") do
|
||||
assert(i == 0 or string.find(l, p[i]))
|
||||
i = i+1
|
||||
end
|
||||
assert(p[i] == undef)
|
||||
end
|
||||
|
||||
|
||||
local function f (n)
|
||||
if n > 0 then f(n-1)
|
||||
else coroutine.yield() end
|
||||
end
|
||||
|
||||
local co = coroutine.create(f)
|
||||
coroutine.resume(co, 3)
|
||||
checktraceback(co, {"yield", "db.lua", "db.lua", "db.lua", "db.lua"})
|
||||
checktraceback(co, {"db.lua", "db.lua", "db.lua", "db.lua"}, 1)
|
||||
checktraceback(co, {"db.lua", "db.lua", "db.lua"}, 2)
|
||||
checktraceback(co, {"db.lua"}, 4)
|
||||
checktraceback(co, {}, 40)
|
||||
|
||||
|
||||
co = coroutine.create(function (x)
|
||||
local a = 1
|
||||
coroutine.yield(debug.getinfo(1, "l"))
|
||||
coroutine.yield(debug.getinfo(1, "l").currentline)
|
||||
return a
|
||||
end)
|
||||
|
||||
local tr = {}
|
||||
local foo = function (e, l) if l then table.insert(tr, l) end end
|
||||
debug.sethook(co, foo, "lcr")
|
||||
|
||||
local _, l = coroutine.resume(co, 10)
|
||||
local x = debug.getinfo(co, 1, "lfLS")
|
||||
assert(x.currentline == l.currentline and x.activelines[x.currentline])
|
||||
assert(type(x.func) == "function")
|
||||
for i=x.linedefined + 1, x.lastlinedefined do
|
||||
assert(x.activelines[i])
|
||||
x.activelines[i] = undef
|
||||
end
|
||||
assert(next(x.activelines) == nil) -- no 'extra' elements
|
||||
assert(not debug.getinfo(co, 2))
|
||||
local a,b = debug.getlocal(co, 1, 1)
|
||||
assert(a == "x" and b == 10)
|
||||
a,b = debug.getlocal(co, 1, 2)
|
||||
assert(a == "a" and b == 1)
|
||||
debug.setlocal(co, 1, 2, "hi")
|
||||
assert(debug.gethook(co) == foo)
|
||||
assert(#tr == 2 and
|
||||
tr[1] == l.currentline-1 and tr[2] == l.currentline)
|
||||
|
||||
a,b,c = pcall(coroutine.resume, co)
|
||||
assert(a and b and c == l.currentline+1)
|
||||
checktraceback(co, {"yield", "in function <"})
|
||||
|
||||
a,b = coroutine.resume(co)
|
||||
assert(a and b == "hi")
|
||||
assert(#tr == 4 and tr[4] == l.currentline+2)
|
||||
assert(debug.gethook(co) == foo)
|
||||
assert(not debug.gethook())
|
||||
checktraceback(co, {})
|
||||
|
||||
|
||||
-- check get/setlocal in coroutines
|
||||
co = coroutine.create(function (x)
|
||||
local a, b = coroutine.yield(x)
|
||||
assert(a == 100 and b == nil)
|
||||
return x
|
||||
end)
|
||||
a, b = coroutine.resume(co, 10)
|
||||
assert(a and b == 10)
|
||||
a, b = debug.getlocal(co, 1, 1)
|
||||
assert(a == "x" and b == 10)
|
||||
assert(not debug.getlocal(co, 1, 5))
|
||||
assert(debug.setlocal(co, 1, 1, 30) == "x")
|
||||
assert(not debug.setlocal(co, 1, 5, 40))
|
||||
a, b = coroutine.resume(co, 100)
|
||||
assert(a and b == 30)
|
||||
|
||||
|
||||
-- check traceback of suspended (or dead with error) coroutines
|
||||
|
||||
function f(i)
|
||||
if i == 0 then error(i)
|
||||
else coroutine.yield(); f(i-1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
co = coroutine.create(function (x) f(x) end)
|
||||
a, b = coroutine.resume(co, 3)
|
||||
t = {"'coroutine.yield'", "'f'", "in function <"}
|
||||
while coroutine.status(co) == "suspended" do
|
||||
checktraceback(co, t)
|
||||
a, b = coroutine.resume(co)
|
||||
table.insert(t, 2, "'f'") -- one more recursive call to 'f'
|
||||
end
|
||||
t[1] = "'error'"
|
||||
checktraceback(co, t)
|
||||
|
||||
|
||||
-- test acessing line numbers of a coroutine from a resume inside
|
||||
-- a C function (this is a known bug in Lua 5.0)
|
||||
|
||||
local function g(x)
|
||||
coroutine.yield(x)
|
||||
end
|
||||
|
||||
local function f (i)
|
||||
debug.sethook(function () end, "l")
|
||||
for j=1,1000 do
|
||||
g(i+j)
|
||||
end
|
||||
end
|
||||
|
||||
local co = coroutine.wrap(f)
|
||||
co(10)
|
||||
pcall(co)
|
||||
pcall(co)
|
||||
|
||||
|
||||
assert(type(debug.getregistry()) == "table")
|
||||
|
||||
|
||||
-- test tagmethod information
|
||||
local a = {}
|
||||
local function f (t)
|
||||
local info = debug.getinfo(1);
|
||||
assert(info.namewhat == "metamethod")
|
||||
a.op = info.name
|
||||
return info.name
|
||||
end
|
||||
setmetatable(a, {
|
||||
__index = f; __add = f; __div = f; __mod = f; __concat = f; __pow = f;
|
||||
__mul = f; __idiv = f; __unm = f; __len = f; __sub = f;
|
||||
__shl = f; __shr = f; __bor = f; __bxor = f;
|
||||
__eq = f; __le = f; __lt = f; __unm = f; __len = f; __band = f;
|
||||
__bnot = f;
|
||||
})
|
||||
|
||||
local b = setmetatable({}, getmetatable(a))
|
||||
|
||||
assert(a[3] == "index" and a^3 == "pow" and a..a == "concat")
|
||||
assert(a/3 == "div" and 3%a == "mod")
|
||||
assert(a+3 == "add" and 3-a == "sub" and a*3 == "mul" and
|
||||
-a == "unm" and #a == "len" and a&3 == "band")
|
||||
assert(a + 30000 == "add" and a - 3.0 == "sub" and a * 3.0 == "mul" and
|
||||
-a == "unm" and #a == "len" and a & 3 == "band")
|
||||
assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shl" and a>>1 == "shr")
|
||||
assert (a==b and a.op == "eq")
|
||||
assert (a>=b and a.op == "order")
|
||||
assert (a>b and a.op == "order")
|
||||
assert(~a == "bnot")
|
||||
|
||||
do -- testing for-iterator name
|
||||
local function f()
|
||||
assert(debug.getinfo(1).name == "for iterator")
|
||||
end
|
||||
|
||||
for i in f do end
|
||||
end
|
||||
|
||||
|
||||
do -- testing debug info for finalizers
|
||||
local name = nil
|
||||
|
||||
-- create a piece of garbage with a finalizer
|
||||
setmetatable({}, {__gc = function ()
|
||||
local t = debug.getinfo(2) -- get callee information
|
||||
assert(t.namewhat == "metamethod")
|
||||
name = t.name
|
||||
end})
|
||||
|
||||
-- repeat until previous finalizer runs (setting 'name')
|
||||
repeat local a = {} until name
|
||||
assert(name == "__gc")
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
print("testing traceback sizes")
|
||||
|
||||
local function countlines (s)
|
||||
return select(2, string.gsub(s, "\n", ""))
|
||||
end
|
||||
|
||||
local function deep (lvl, n)
|
||||
if lvl == 0 then
|
||||
return (debug.traceback("message", n))
|
||||
else
|
||||
return (deep(lvl-1, n))
|
||||
end
|
||||
end
|
||||
|
||||
local function checkdeep (total, start)
|
||||
local s = deep(total, start)
|
||||
local rest = string.match(s, "^message\nstack traceback:\n(.*)$")
|
||||
local cl = countlines(rest)
|
||||
-- at most 10 lines in first part, 11 in second, plus '...'
|
||||
assert(cl <= 10 + 11 + 1)
|
||||
local brk = string.find(rest, "%.%.%.")
|
||||
if brk then -- does message have '...'?
|
||||
local rest1 = string.sub(rest, 1, brk)
|
||||
local rest2 = string.sub(rest, brk, #rest)
|
||||
assert(countlines(rest1) == 10 and countlines(rest2) == 11)
|
||||
else
|
||||
assert(cl == total - start + 2)
|
||||
end
|
||||
end
|
||||
|
||||
for d = 1, 51, 10 do
|
||||
for l = 1, d do
|
||||
-- use coroutines to ensure complete control of the stack
|
||||
coroutine.wrap(checkdeep)(d, l)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
print("testing debug functions on chunk without debug info")
|
||||
prog = [[-- program to be loaded without debug information
|
||||
local debug = require'debug'
|
||||
local a = 12 -- a local variable
|
||||
|
||||
local n, v = debug.getlocal(1, 1)
|
||||
assert(n == "(temporary)" and v == debug) -- unkown name but known value
|
||||
n, v = debug.getlocal(1, 2)
|
||||
assert(n == "(temporary)" and v == 12) -- unkown name but known value
|
||||
|
||||
-- a function with an upvalue
|
||||
local f = function () local x; return a end
|
||||
n, v = debug.getupvalue(f, 1)
|
||||
assert(n == "(no name)" and v == 12)
|
||||
assert(debug.setupvalue(f, 1, 13) == "(no name)")
|
||||
assert(a == 13)
|
||||
|
||||
local t = debug.getinfo(f)
|
||||
assert(t.name == nil and t.linedefined > 0 and
|
||||
t.lastlinedefined == t.linedefined and
|
||||
t.short_src == "?")
|
||||
assert(debug.getinfo(1).currentline == -1)
|
||||
|
||||
t = debug.getinfo(f, "L").activelines
|
||||
assert(next(t) == nil) -- active lines are empty
|
||||
|
||||
-- dump/load a function without debug info
|
||||
f = load(string.dump(f))
|
||||
|
||||
t = debug.getinfo(f)
|
||||
assert(t.name == nil and t.linedefined > 0 and
|
||||
t.lastlinedefined == t.linedefined and
|
||||
t.short_src == "?")
|
||||
assert(debug.getinfo(1).currentline == -1)
|
||||
|
||||
return a
|
||||
]]
|
||||
|
||||
|
||||
-- load 'prog' without debug info
|
||||
local f = assert(load(string.dump(load(prog), true)))
|
||||
|
||||
assert(f() == 13)
|
||||
|
||||
do -- tests for 'source' in binary dumps
|
||||
local prog = [[
|
||||
return function (x)
|
||||
return function (y)
|
||||
return x + y
|
||||
end
|
||||
end
|
||||
]]
|
||||
local name = string.rep("x", 1000)
|
||||
local p = assert(load(prog, name))
|
||||
-- load 'p' as a binary chunk with debug information
|
||||
local c = string.dump(p)
|
||||
assert(#c > 1000 and #c < 2000) -- no repetition of 'source' in dump
|
||||
local f = assert(load(c))
|
||||
local g = f()
|
||||
local h = g(3)
|
||||
assert(h(5) == 8)
|
||||
assert(debug.getinfo(f).source == name and -- all functions have 'source'
|
||||
debug.getinfo(g).source == name and
|
||||
debug.getinfo(h).source == name)
|
||||
-- again, without debug info
|
||||
local c = string.dump(p, true)
|
||||
assert(#c < 500) -- no 'source' in dump
|
||||
local f = assert(load(c))
|
||||
local g = f()
|
||||
local h = g(30)
|
||||
assert(h(50) == 80)
|
||||
assert(debug.getinfo(f).source == '=?' and -- no function has 'source'
|
||||
debug.getinfo(g).source == '=?' and
|
||||
debug.getinfo(h).source == '=?')
|
||||
end
|
||||
|
||||
print"OK"
|
||||
|
||||
@@ -0,0 +1,589 @@
|
||||
-- $Id: testes/errors.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print("testing errors")
|
||||
|
||||
local debug = require"debug"
|
||||
|
||||
-- avoid problems with 'strict' module (which may generate other error messages)
|
||||
local mt = getmetatable(_G) or {}
|
||||
local oldmm = mt.__index
|
||||
mt.__index = nil
|
||||
|
||||
local function checkerr (msg, f, ...)
|
||||
local st, err = pcall(f, ...)
|
||||
assert(not st and string.find(err, msg))
|
||||
end
|
||||
|
||||
|
||||
local function doit (s)
|
||||
local f, msg = load(s)
|
||||
if not f then return msg end
|
||||
local cond, msg = pcall(f)
|
||||
return (not cond) and msg
|
||||
end
|
||||
|
||||
|
||||
local function checkmessage (prog, msg)
|
||||
local m = doit(prog)
|
||||
assert(string.find(m, msg, 1, true))
|
||||
end
|
||||
|
||||
local function checksyntax (prog, extra, token, line)
|
||||
local msg = doit(prog)
|
||||
if not string.find(token, "^<%a") and not string.find(token, "^char%(")
|
||||
then token = "'"..token.."'" end
|
||||
token = string.gsub(token, "(%p)", "%%%1")
|
||||
local pt = string.format([[^%%[string ".*"%%]:%d: .- near %s$]],
|
||||
line, token)
|
||||
assert(string.find(msg, pt))
|
||||
assert(string.find(msg, msg, 1, true))
|
||||
end
|
||||
|
||||
|
||||
-- test error message with no extra info
|
||||
assert(doit("error('hi', 0)") == 'hi')
|
||||
|
||||
-- test error message with no info
|
||||
assert(doit("error()") == nil)
|
||||
|
||||
|
||||
-- test common errors/errors that crashed in the past
|
||||
assert(doit("table.unpack({}, 1, n=2^30)"))
|
||||
assert(doit("a=math.sin()"))
|
||||
assert(not doit("tostring(1)") and doit("tostring()"))
|
||||
assert(doit"tonumber()")
|
||||
assert(doit"repeat until 1; a")
|
||||
assert(doit"return;;")
|
||||
assert(doit"assert(false)")
|
||||
assert(doit"assert(nil)")
|
||||
assert(doit("function a (... , ...) end"))
|
||||
assert(doit("function a (, ...) end"))
|
||||
assert(doit("local t={}; t = t[#t] + 1"))
|
||||
|
||||
checksyntax([[
|
||||
local a = {4
|
||||
|
||||
]], "'}' expected (to close '{' at line 1)", "<eof>", 3)
|
||||
|
||||
|
||||
if not T then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping memory message test <<<\n')
|
||||
else
|
||||
print "testing memory error message"
|
||||
local a = {}
|
||||
for i = 1, 10000 do a[i] = true end -- preallocate array
|
||||
collectgarbage()
|
||||
T.totalmem(T.totalmem() + 10000)
|
||||
-- force a memory error (by a small margin)
|
||||
local st, msg = pcall(function()
|
||||
for i = 1, 100000 do a[i] = tostring(i) end
|
||||
end)
|
||||
T.totalmem(0)
|
||||
assert(not st and msg == "not enough" .. " memory")
|
||||
end
|
||||
|
||||
|
||||
-- tests for better error messages
|
||||
|
||||
checkmessage("a = {} + 1", "arithmetic")
|
||||
checkmessage("a = {} | 1", "bitwise operation")
|
||||
checkmessage("a = {} < 1", "attempt to compare")
|
||||
checkmessage("a = {} <= 1", "attempt to compare")
|
||||
|
||||
checkmessage("a=1; bbbb=2; a=math.sin(3)+bbbb(3)", "global 'bbbb'")
|
||||
checkmessage("a={}; do local a=1 end a:bbbb(3)", "method 'bbbb'")
|
||||
checkmessage("local a={}; a.bbbb(3)", "field 'bbbb'")
|
||||
assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'"))
|
||||
checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number")
|
||||
checkmessage("a=(1)..{}", "a table value")
|
||||
|
||||
-- tail calls
|
||||
checkmessage("local a={}; return a.bbbb(3)", "field 'bbbb'")
|
||||
checkmessage("a={}; do local a=1 end; return a:bbbb(3)", "method 'bbbb'")
|
||||
|
||||
checkmessage("a = #print", "length of a function value")
|
||||
checkmessage("a = #3", "length of a number value")
|
||||
|
||||
aaa = nil
|
||||
checkmessage("aaa.bbb:ddd(9)", "global 'aaa'")
|
||||
checkmessage("local aaa={bbb=1}; aaa.bbb:ddd(9)", "field 'bbb'")
|
||||
checkmessage("local aaa={bbb={}}; aaa.bbb:ddd(9)", "method 'ddd'")
|
||||
checkmessage("local a,b,c; (function () a = b+1.1 end)()", "upvalue 'b'")
|
||||
assert(not doit"local aaa={bbb={ddd=next}}; aaa.bbb:ddd(nil)")
|
||||
|
||||
-- upvalues being indexed do not go to the stack
|
||||
checkmessage("local a,b,cc; (function () a = cc[1] end)()", "upvalue 'cc'")
|
||||
checkmessage("local a,b,cc; (function () a.x = 1 end)()", "upvalue 'a'")
|
||||
|
||||
checkmessage("local _ENV = {x={}}; a = a + 1", "global 'a'")
|
||||
|
||||
checkmessage("b=1; local aaa={}; x=aaa+b", "local 'aaa'")
|
||||
checkmessage("aaa={}; x=3.3/aaa", "global 'aaa'")
|
||||
checkmessage("aaa=2; b=nil;x=aaa*b", "global 'b'")
|
||||
checkmessage("aaa={}; x=-aaa", "global 'aaa'")
|
||||
|
||||
-- short circuit
|
||||
checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)",
|
||||
"local 'bbbb'")
|
||||
checkmessage("a=1; local a,bbbb=2,3; a = bbbb(1) or a(3)", "local 'bbbb'")
|
||||
checkmessage("local a,b,c,f = 1,1,1; f((a and b) or c)", "local 'f'")
|
||||
checkmessage("local a,b,c = 1,1,1; ((a and b) or c)()", "call a number value")
|
||||
assert(not string.find(doit"aaa={}; x=(aaa or aaa)+(aaa and aaa)", "'aaa'"))
|
||||
assert(not string.find(doit"aaa={}; (aaa or aaa)()", "'aaa'"))
|
||||
|
||||
checkmessage("print(print < 10)", "function with number")
|
||||
checkmessage("print(print < print)", "two function values")
|
||||
checkmessage("print('10' < 10)", "string with number")
|
||||
checkmessage("print(10 < '23')", "number with string")
|
||||
|
||||
-- float->integer conversions
|
||||
checkmessage("local a = 2.0^100; x = a << 2", "local a")
|
||||
checkmessage("local a = 1 >> 2.0^100", "has no integer representation")
|
||||
checkmessage("local a = 10.1 << 2.0^100", "has no integer representation")
|
||||
checkmessage("local a = 2.0^100 & 1", "has no integer representation")
|
||||
checkmessage("local a = 2.0^100 & 1e100", "has no integer representation")
|
||||
checkmessage("local a = 2.0 | 1e40", "has no integer representation")
|
||||
checkmessage("local a = 2e100 ~ 1", "has no integer representation")
|
||||
checkmessage("string.sub('a', 2.0^100)", "has no integer representation")
|
||||
checkmessage("string.rep('a', 3.3)", "has no integer representation")
|
||||
checkmessage("return 6e40 & 7", "has no integer representation")
|
||||
checkmessage("return 34 << 7e30", "has no integer representation")
|
||||
checkmessage("return ~-3e40", "has no integer representation")
|
||||
checkmessage("return ~-3.009", "has no integer representation")
|
||||
checkmessage("return 3.009 & 1", "has no integer representation")
|
||||
checkmessage("return 34 >> {}", "table value")
|
||||
checkmessage("a = 24 // 0", "divide by zero")
|
||||
checkmessage("a = 1 % 0", "'n%0'")
|
||||
|
||||
|
||||
-- numeric for loops
|
||||
checkmessage("for i = {}, 10 do end", "table")
|
||||
checkmessage("for i = io.stdin, 10 do end", "FILE")
|
||||
checkmessage("for i = {}, 10 do end", "initial value")
|
||||
checkmessage("for i = 1, 'x', 10 do end", "string")
|
||||
checkmessage("for i = 1, {}, 10 do end", "limit")
|
||||
checkmessage("for i = 1, {} do end", "limit")
|
||||
checkmessage("for i = 1, 10, print do end", "step")
|
||||
checkmessage("for i = 1, 10, print do end", "function")
|
||||
|
||||
-- passing light userdata instead of full userdata
|
||||
_G.D = debug
|
||||
checkmessage([[
|
||||
-- create light udata
|
||||
local x = D.upvalueid(function () return debug end, 1)
|
||||
D.setuservalue(x, {})
|
||||
]], "light userdata")
|
||||
_G.D = nil
|
||||
|
||||
do -- named objects (field '__name')
|
||||
checkmessage("math.sin(io.input())", "(number expected, got FILE*)")
|
||||
_G.XX = setmetatable({}, {__name = "My Type"})
|
||||
assert(string.find(tostring(XX), "^My Type"))
|
||||
checkmessage("io.input(XX)", "(FILE* expected, got My Type)")
|
||||
checkmessage("return XX + 1", "on a My Type value")
|
||||
checkmessage("return ~io.stdin", "on a FILE* value")
|
||||
checkmessage("return XX < XX", "two My Type values")
|
||||
checkmessage("return {} < XX", "table with My Type")
|
||||
checkmessage("return XX < io.stdin", "My Type with FILE*")
|
||||
_G.XX = nil
|
||||
end
|
||||
|
||||
-- global functions
|
||||
checkmessage("(io.write or print){}", "io.write")
|
||||
checkmessage("(collectgarbage or print){}", "collectgarbage")
|
||||
|
||||
-- errors in functions without debug info
|
||||
do
|
||||
local f = function (a) return a + 1 end
|
||||
f = assert(load(string.dump(f, true)))
|
||||
assert(f(3) == 4)
|
||||
checkerr("^%?:%-1:", f, {})
|
||||
|
||||
-- code with a move to a local var ('OP_MOV A B' with A<B)
|
||||
f = function () local a; a = {}; return a + 2 end
|
||||
-- no debug info (so that 'a' is unknown)
|
||||
f = assert(load(string.dump(f, true)))
|
||||
-- symbolic execution should not get lost
|
||||
checkerr("^%?:%-1:.*table value", f)
|
||||
end
|
||||
|
||||
|
||||
-- tests for field accesses after RK limit
|
||||
local t = {}
|
||||
for i = 1, 1000 do
|
||||
t[i] = "a = x" .. i
|
||||
end
|
||||
local s = table.concat(t, "; ")
|
||||
t = nil
|
||||
checkmessage(s.."; a = bbb + 1", "global 'bbb'")
|
||||
checkmessage("local _ENV=_ENV;"..s.."; a = bbb + 1", "global 'bbb'")
|
||||
checkmessage(s.."; local t = {}; a = t.bbb + 1", "field 'bbb'")
|
||||
checkmessage(s.."; local t = {}; t:bbb()", "method 'bbb'")
|
||||
|
||||
checkmessage([[aaa=9
|
||||
repeat until 3==3
|
||||
local x=math.sin(math.cos(3))
|
||||
if math.sin(1) == x then return math.sin(1) end -- tail call
|
||||
local a,b = 1, {
|
||||
{x='a'..'b'..'c', y='b', z=x},
|
||||
{1,2,3,4,5} or 3+3<=3+3,
|
||||
3+1>3+1,
|
||||
{d = x and aaa[x or y]}}
|
||||
]], "global 'aaa'")
|
||||
|
||||
checkmessage([[
|
||||
local x,y = {},1
|
||||
if math.sin(1) == 0 then return 3 end -- return
|
||||
x.a()]], "field 'a'")
|
||||
|
||||
checkmessage([[
|
||||
prefix = nil
|
||||
insert = nil
|
||||
while 1 do
|
||||
local a
|
||||
if nil then break end
|
||||
insert(prefix, a)
|
||||
end]], "global 'insert'")
|
||||
|
||||
checkmessage([[ -- tail call
|
||||
return math.sin("a")
|
||||
]], "'sin'")
|
||||
|
||||
checkmessage([[collectgarbage("nooption")]], "invalid option")
|
||||
|
||||
checkmessage([[x = print .. "a"]], "concatenate")
|
||||
checkmessage([[x = "a" .. false]], "concatenate")
|
||||
checkmessage([[x = {} .. 2]], "concatenate")
|
||||
|
||||
checkmessage("getmetatable(io.stdin).__gc()", "no value")
|
||||
|
||||
checkmessage([[
|
||||
local Var
|
||||
local function main()
|
||||
NoSuchName (function() Var=0 end)
|
||||
end
|
||||
main()
|
||||
]], "global 'NoSuchName'")
|
||||
print'+'
|
||||
|
||||
a = {}; setmetatable(a, {__index = string})
|
||||
checkmessage("a:sub()", "bad self")
|
||||
checkmessage("string.sub('a', {})", "#2")
|
||||
checkmessage("('a'):sub{}", "#1")
|
||||
|
||||
checkmessage("table.sort({1,2,3}, table.sort)", "'table.sort'")
|
||||
checkmessage("string.gsub('s', 's', setmetatable)", "'setmetatable'")
|
||||
|
||||
-- tests for errors in coroutines
|
||||
|
||||
local function f (n)
|
||||
local c = coroutine.create(f)
|
||||
local a,b = coroutine.resume(c)
|
||||
return b
|
||||
end
|
||||
assert(string.find(f(), "C stack overflow"))
|
||||
|
||||
checkmessage("coroutine.yield()", "outside a coroutine")
|
||||
|
||||
f = coroutine.wrap(function () table.sort({1,2,3}, coroutine.yield) end)
|
||||
checkerr("yield across", f)
|
||||
|
||||
|
||||
-- testing size of 'source' info; size of buffer for that info is
|
||||
-- LUA_IDSIZE, declared as 60 in luaconf. Get one position for '\0'.
|
||||
idsize = 60 - 1
|
||||
local function checksize (source)
|
||||
-- syntax error
|
||||
local _, msg = load("x", source)
|
||||
msg = string.match(msg, "^([^:]*):") -- get source (1st part before ':')
|
||||
assert(msg:len() <= idsize)
|
||||
end
|
||||
|
||||
for i = 60 - 10, 60 + 10 do -- check border cases around 60
|
||||
checksize("@" .. string.rep("x", i)) -- file names
|
||||
checksize(string.rep("x", i - 10)) -- string sources
|
||||
checksize("=" .. string.rep("x", i)) -- exact sources
|
||||
end
|
||||
|
||||
|
||||
-- testing line error
|
||||
|
||||
local function lineerror (s, l)
|
||||
local err,msg = pcall(load(s))
|
||||
local line = tonumber(string.match(msg, ":(%d+):"))
|
||||
assert(line == l or (not line and not l))
|
||||
end
|
||||
|
||||
lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2)
|
||||
lineerror("\n local a \n for k,v in 3 \n do \n print(k) \n end", 3)
|
||||
lineerror("\n\n for k,v in \n 3 \n do \n print(k) \n end", 4)
|
||||
lineerror("function a.x.y ()\na=a+1\nend", 1)
|
||||
|
||||
lineerror("a = \na\n+\n{}", 3)
|
||||
lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6)
|
||||
lineerror("a = \nprint\n+\n(\n4\n/\n7)", 3)
|
||||
|
||||
lineerror("a\n=\n-\n\nprint\n;", 3)
|
||||
|
||||
lineerror([[
|
||||
a
|
||||
(
|
||||
23)
|
||||
]], 1)
|
||||
|
||||
lineerror([[
|
||||
local a = {x = 13}
|
||||
a
|
||||
.
|
||||
x
|
||||
(
|
||||
23
|
||||
)
|
||||
]], 2)
|
||||
|
||||
lineerror([[
|
||||
local a = {x = 13}
|
||||
a
|
||||
.
|
||||
x
|
||||
(
|
||||
23 + a
|
||||
)
|
||||
]], 6)
|
||||
|
||||
local p = [[
|
||||
function g() f() end
|
||||
function f(x) error('a', X) end
|
||||
g()
|
||||
]]
|
||||
X=3;lineerror((p), 3)
|
||||
X=0;lineerror((p), false)
|
||||
X=1;lineerror((p), 2)
|
||||
X=2;lineerror((p), 1)
|
||||
|
||||
|
||||
lineerror([[
|
||||
local b = false
|
||||
if not b then
|
||||
error 'test'
|
||||
end]], 3)
|
||||
|
||||
lineerror([[
|
||||
local b = false
|
||||
if not b then
|
||||
if not b then
|
||||
if not b then
|
||||
error 'test'
|
||||
end
|
||||
end
|
||||
end]], 5)
|
||||
|
||||
|
||||
if not _soft then
|
||||
-- several tests that exaust the Lua stack
|
||||
collectgarbage()
|
||||
print"testing stack overflow"
|
||||
C = 0
|
||||
local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end
|
||||
|
||||
local function checkstackmessage (m)
|
||||
return (string.find(m, "stack overflow"))
|
||||
end
|
||||
-- repeated stack overflows (to check stack recovery)
|
||||
assert(checkstackmessage(doit('y()')))
|
||||
print('+')
|
||||
assert(checkstackmessage(doit('y()')))
|
||||
print('+')
|
||||
assert(checkstackmessage(doit('y()')))
|
||||
print('+')
|
||||
|
||||
|
||||
-- error lines in stack overflow
|
||||
C = 0
|
||||
local l1
|
||||
local function g(x)
|
||||
l1 = debug.getinfo(x, "l").currentline; y()
|
||||
end
|
||||
local _, stackmsg = xpcall(g, debug.traceback, 1)
|
||||
print('+')
|
||||
local stack = {}
|
||||
for line in string.gmatch(stackmsg, "[^\n]*") do
|
||||
local curr = string.match(line, ":(%d+):")
|
||||
if curr then table.insert(stack, tonumber(curr)) end
|
||||
end
|
||||
local i=1
|
||||
while stack[i] ~= l1 do
|
||||
assert(stack[i] == l)
|
||||
i = i+1
|
||||
end
|
||||
assert(i > 15)
|
||||
|
||||
|
||||
-- error in error handling
|
||||
local res, msg = xpcall(error, error)
|
||||
assert(not res and type(msg) == 'string')
|
||||
print('+')
|
||||
|
||||
local function f (x)
|
||||
if x==0 then error('a\n')
|
||||
else
|
||||
local aux = function () return f(x-1) end
|
||||
local a,b = xpcall(aux, aux)
|
||||
return a,b
|
||||
end
|
||||
end
|
||||
f(3)
|
||||
|
||||
local function loop (x,y,z) return 1 + loop(x, y, z) end
|
||||
|
||||
local res, msg = xpcall(loop, function (m)
|
||||
assert(string.find(m, "stack overflow"))
|
||||
checkerr("error handling", loop)
|
||||
assert(math.sin(0) == 0)
|
||||
return 15
|
||||
end)
|
||||
assert(msg == 15)
|
||||
|
||||
local f = function ()
|
||||
for i = 999900, 1000000, 1 do table.unpack({}, 1, i) end
|
||||
end
|
||||
checkerr("too many results", f)
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- non string messages
|
||||
local t = {}
|
||||
local res, msg = pcall(function () error(t) end)
|
||||
assert(not res and msg == t)
|
||||
|
||||
res, msg = pcall(function () error(nil) end)
|
||||
assert(not res and msg == nil)
|
||||
|
||||
local function f() error{msg='x'} end
|
||||
res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end)
|
||||
assert(msg.msg == 'xy')
|
||||
|
||||
-- 'assert' with extra arguments
|
||||
res, msg = pcall(assert, false, "X", t)
|
||||
assert(not res and msg == "X")
|
||||
|
||||
-- 'assert' with no message
|
||||
res, msg = pcall(function () assert(false) end)
|
||||
local line = string.match(msg, "%w+%.lua:(%d+): assertion failed!$")
|
||||
assert(tonumber(line) == debug.getinfo(1, "l").currentline - 2)
|
||||
|
||||
-- 'assert' with non-string messages
|
||||
res, msg = pcall(assert, false, t)
|
||||
assert(not res and msg == t)
|
||||
|
||||
res, msg = pcall(assert, nil, nil)
|
||||
assert(not res and msg == nil)
|
||||
|
||||
-- 'assert' without arguments
|
||||
res, msg = pcall(assert)
|
||||
assert(not res and string.find(msg, "value expected"))
|
||||
end
|
||||
|
||||
-- xpcall with arguments
|
||||
a, b, c = xpcall(string.find, error, "alo", "al")
|
||||
assert(a and b == 1 and c == 2)
|
||||
a, b, c = xpcall(string.find, function (x) return {} end, true, "al")
|
||||
assert(not a and type(b) == "table" and c == nil)
|
||||
|
||||
|
||||
print("testing tokens in error messages")
|
||||
checksyntax("syntax error", "", "error", 1)
|
||||
checksyntax("1.000", "", "1.000", 1)
|
||||
checksyntax("[[a]]", "", "[[a]]", 1)
|
||||
checksyntax("'aa'", "", "'aa'", 1)
|
||||
checksyntax("while << do end", "", "<<", 1)
|
||||
checksyntax("for >> do end", "", ">>", 1)
|
||||
|
||||
-- test invalid non-printable char in a chunk
|
||||
checksyntax("a\1a = 1", "", "<\\1>", 1)
|
||||
|
||||
-- test 255 as first char in a chunk
|
||||
checksyntax("\255a = 1", "", "<\\255>", 1)
|
||||
|
||||
doit('I = load("a=9+"); a=3')
|
||||
assert(a==3 and not I)
|
||||
print('+')
|
||||
|
||||
lim = 1000
|
||||
if _soft then lim = 100 end
|
||||
for i=1,lim do
|
||||
doit('a = ')
|
||||
doit('a = 4+nil')
|
||||
end
|
||||
|
||||
|
||||
-- testing syntax limits
|
||||
|
||||
local function testrep (init, rep, close, repc, finalresult)
|
||||
local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100)
|
||||
local res, msg = load(s)
|
||||
assert(res) -- 100 levels is OK
|
||||
if (finalresult) then
|
||||
assert(res() == finalresult)
|
||||
end
|
||||
s = init .. string.rep(rep, 10000)
|
||||
local res, msg = load(s) -- 10000 levels not ok
|
||||
assert(not res and (string.find(msg, "too many registers") or
|
||||
string.find(msg, "stack overflow")))
|
||||
end
|
||||
|
||||
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
|
||||
testrep("local a; a=", "{", "0", "}")
|
||||
testrep("return ", "(", "2", ")", 2)
|
||||
testrep("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2)
|
||||
testrep("", "do ", "", " end")
|
||||
testrep("", "while a do ", "", " end")
|
||||
testrep("local a; ", "if a then else ", "", " end")
|
||||
testrep("", "function foo () ", "", " end")
|
||||
testrep("local a = ''; return ", "a..", "'a'", "", "a")
|
||||
testrep("local a = 1; return ", "a^", "a", "", 1)
|
||||
|
||||
checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers")
|
||||
|
||||
|
||||
-- testing other limits
|
||||
|
||||
-- upvalues
|
||||
local lim = 127
|
||||
local s = "local function fooA ()\n local "
|
||||
for j = 1,lim do
|
||||
s = s.."a"..j..", "
|
||||
end
|
||||
s = s.."b,c\n"
|
||||
s = s.."local function fooB ()\n local "
|
||||
for j = 1,lim do
|
||||
s = s.."b"..j..", "
|
||||
end
|
||||
s = s.."b\n"
|
||||
s = s.."function fooC () return b+c"
|
||||
local c = 1+2
|
||||
for j = 1,lim do
|
||||
s = s.."+a"..j.."+b"..j
|
||||
c = c + 2
|
||||
end
|
||||
s = s.."\nend end end"
|
||||
local a,b = load(s)
|
||||
assert(c > 255 and string.find(b, "too many upvalues") and
|
||||
string.find(b, "line 5"))
|
||||
|
||||
-- local variables
|
||||
s = "\nfunction foo ()\n local "
|
||||
for j = 1,300 do
|
||||
s = s.."a"..j..", "
|
||||
end
|
||||
s = s.."b\n"
|
||||
local a,b = load(s)
|
||||
assert(string.find(b, "line 2") and string.find(b, "too many local variables"))
|
||||
|
||||
mt.__index = oldmm
|
||||
|
||||
print('OK')
|
||||
@@ -0,0 +1,476 @@
|
||||
-- $Id: testes/events.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print('testing metatables')
|
||||
|
||||
local debug = require'debug'
|
||||
|
||||
X = 20; B = 30
|
||||
|
||||
_ENV = setmetatable({}, {__index=_G})
|
||||
|
||||
collectgarbage()
|
||||
|
||||
X = X+10
|
||||
assert(X == 30 and _G.X == 20)
|
||||
B = false
|
||||
assert(B == false)
|
||||
_ENV["B"] = undef
|
||||
assert(B == 30)
|
||||
|
||||
assert(getmetatable{} == nil)
|
||||
assert(getmetatable(4) == nil)
|
||||
assert(getmetatable(nil) == nil)
|
||||
a={name = "NAME"}; setmetatable(a, {__metatable = "xuxu",
|
||||
__tostring=function(x) return x.name end})
|
||||
assert(getmetatable(a) == "xuxu")
|
||||
assert(tostring(a) == "NAME")
|
||||
-- cannot change a protected metatable
|
||||
assert(pcall(setmetatable, a, {}) == false)
|
||||
a.name = "gororoba"
|
||||
assert(tostring(a) == "gororoba")
|
||||
|
||||
local a, t = {10,20,30; x="10", y="20"}, {}
|
||||
assert(setmetatable(a,t) == a)
|
||||
assert(getmetatable(a) == t)
|
||||
assert(setmetatable(a,nil) == a)
|
||||
assert(getmetatable(a) == nil)
|
||||
assert(setmetatable(a,t) == a)
|
||||
|
||||
|
||||
function f (t, i, e)
|
||||
assert(not e)
|
||||
local p = rawget(t, "parent")
|
||||
return (p and p[i]+3), "dummy return"
|
||||
end
|
||||
|
||||
t.__index = f
|
||||
|
||||
a.parent = {z=25, x=12, [4] = 24}
|
||||
assert(a[1] == 10 and a.z == 28 and a[4] == 27 and a.x == "10")
|
||||
|
||||
collectgarbage()
|
||||
|
||||
a = setmetatable({}, t)
|
||||
function f(t, i, v) rawset(t, i, v-3) end
|
||||
setmetatable(t, t) -- causes a bug in 5.1 !
|
||||
t.__newindex = f
|
||||
a[1] = 30; a.x = "101"; a[5] = 200
|
||||
assert(a[1] == 27 and a.x == 98 and a[5] == 197)
|
||||
|
||||
do -- bug in Lua 5.3.2
|
||||
local mt = {}
|
||||
mt.__newindex = mt
|
||||
local t = setmetatable({}, mt)
|
||||
t[1] = 10 -- will segfault on some machines
|
||||
assert(mt[1] == 10)
|
||||
end
|
||||
|
||||
|
||||
local c = {}
|
||||
a = setmetatable({}, t)
|
||||
t.__newindex = c
|
||||
t.__index = c
|
||||
a[1] = 10; a[2] = 20; a[3] = 90;
|
||||
for i = 4, 20 do a[i] = i * 10 end
|
||||
assert(a[1] == 10 and a[2] == 20 and a[3] == 90)
|
||||
for i = 4, 20 do assert(a[i] == i * 10) end
|
||||
assert(next(a) == nil)
|
||||
|
||||
|
||||
do
|
||||
local a;
|
||||
a = setmetatable({}, {__index = setmetatable({},
|
||||
{__index = setmetatable({},
|
||||
{__index = function (_,n) return a[n-3]+4, "lixo" end})})})
|
||||
a[0] = 20
|
||||
for i=0,10 do
|
||||
assert(a[i*3] == 20 + i*4)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do -- newindex
|
||||
local foi
|
||||
local a = {}
|
||||
for i=1,10 do a[i] = 0; a['a'..i] = 0; end
|
||||
setmetatable(a, {__newindex = function (t,k,v) foi=true; rawset(t,k,v) end})
|
||||
foi = false; a[1]=0; assert(not foi)
|
||||
foi = false; a['a1']=0; assert(not foi)
|
||||
foi = false; a['a11']=0; assert(foi)
|
||||
foi = false; a[11]=0; assert(foi)
|
||||
foi = false; a[1]=undef; assert(not foi)
|
||||
a[1] = undef
|
||||
foi = false; a[1]=nil; assert(foi)
|
||||
end
|
||||
|
||||
|
||||
setmetatable(t, nil)
|
||||
function f (t, ...) return t, {...} end
|
||||
t.__call = f
|
||||
|
||||
do
|
||||
local x,y = a(table.unpack{'a', 1})
|
||||
assert(x==a and y[1]=='a' and y[2]==1 and y[3]==undef)
|
||||
x,y = a()
|
||||
assert(x==a and y[1]==undef)
|
||||
end
|
||||
|
||||
|
||||
local b = setmetatable({}, t)
|
||||
setmetatable(b,t)
|
||||
|
||||
function f(op)
|
||||
return function (...) cap = {[0] = op, ...} ; return (...) end
|
||||
end
|
||||
t.__add = f("add")
|
||||
t.__sub = f("sub")
|
||||
t.__mul = f("mul")
|
||||
t.__div = f("div")
|
||||
t.__idiv = f("idiv")
|
||||
t.__mod = f("mod")
|
||||
t.__unm = f("unm")
|
||||
t.__pow = f("pow")
|
||||
t.__len = f("len")
|
||||
t.__band = f("band")
|
||||
t.__bor = f("bor")
|
||||
t.__bxor = f("bxor")
|
||||
t.__shl = f("shl")
|
||||
t.__shr = f("shr")
|
||||
t.__bnot = f("bnot")
|
||||
t.__lt = f("lt")
|
||||
t.__le = f("le")
|
||||
|
||||
|
||||
local function checkcap (t)
|
||||
assert(#cap + 1 == #t)
|
||||
for i = 1, #t do
|
||||
assert(cap[i - 1] == t[i])
|
||||
assert(math.type(cap[i - 1]) == math.type(t[i]))
|
||||
end
|
||||
end
|
||||
|
||||
-- Some tests are done inside small anonymous functions to ensure
|
||||
-- that constants go to constant table even in debug compilation,
|
||||
-- when the constant table is very small.
|
||||
assert(b+5 == b); checkcap{"add", b, 5}
|
||||
assert(5.2 + b == 5.2); checkcap{"add", 5.2, b}
|
||||
assert(b+'5' == b); checkcap{"add", b, '5'}
|
||||
assert(5+b == 5); checkcap{"add", 5, b}
|
||||
assert('5'+b == '5'); checkcap{"add", '5', b}
|
||||
b=b-3; assert(getmetatable(b) == t); checkcap{"sub", b, 3}
|
||||
assert(5-a == 5); checkcap{"sub", 5, a}
|
||||
assert('5'-a == '5'); checkcap{"sub", '5', a}
|
||||
assert(a*a == a); checkcap{"mul", a, a}
|
||||
assert(a/0 == a); checkcap{"div", a, 0}
|
||||
assert(a/0.0 == a); checkcap{"div", a, 0.0}
|
||||
assert(a%2 == a); checkcap{"mod", a, 2}
|
||||
assert(a // (1/0) == a); checkcap{"idiv", a, 1/0}
|
||||
;(function () assert(a & "hi" == a) end)(); checkcap{"band", a, "hi"}
|
||||
;(function () assert(10 & a == 10) end)(); checkcap{"band", 10, a}
|
||||
;(function () assert(a | 10 == a) end)(); checkcap{"bor", a, 10}
|
||||
assert(a | "hi" == a); checkcap{"bor", a, "hi"}
|
||||
assert("hi" ~ a == "hi"); checkcap{"bxor", "hi", a}
|
||||
;(function () assert(10 ~ a == 10) end)(); checkcap{"bxor", 10, a}
|
||||
assert(-a == a); checkcap{"unm", a, a}
|
||||
assert(a^4.0 == a); checkcap{"pow", a, 4.0}
|
||||
assert(a^'4' == a); checkcap{"pow", a, '4'}
|
||||
assert(4^a == 4); checkcap{"pow", 4, a}
|
||||
assert('4'^a == '4'); checkcap{"pow", '4', a}
|
||||
assert(#a == a); checkcap{"len", a, a}
|
||||
assert(~a == a); checkcap{"bnot", a, a}
|
||||
assert(a << 3 == a); checkcap{"shl", a, 3}
|
||||
assert(1.5 >> a == 1.5); checkcap{"shr", 1.5, a}
|
||||
|
||||
-- for comparsion operators, all results are true
|
||||
assert(5.0 > a); checkcap{"lt", a, 5.0}
|
||||
assert(a >= 10); checkcap{"le", 10, a}
|
||||
assert(a <= -10.0); checkcap{"le", a, -10.0}
|
||||
assert(a < -10); checkcap{"lt", a, -10}
|
||||
|
||||
|
||||
-- test for rawlen
|
||||
t = setmetatable({1,2,3}, {__len = function () return 10 end})
|
||||
assert(#t == 10 and rawlen(t) == 3)
|
||||
assert(rawlen"abc" == 3)
|
||||
assert(not pcall(rawlen, io.stdin))
|
||||
assert(not pcall(rawlen, 34))
|
||||
assert(not pcall(rawlen))
|
||||
|
||||
-- rawlen for long strings
|
||||
assert(rawlen(string.rep('a', 1000)) == 1000)
|
||||
|
||||
|
||||
t = {}
|
||||
t.__lt = function (a,b,c)
|
||||
collectgarbage()
|
||||
assert(c == nil)
|
||||
if type(a) == 'table' then a = a.x end
|
||||
if type(b) == 'table' then b = b.x end
|
||||
return a<b, "dummy"
|
||||
end
|
||||
|
||||
t.__le = function (a,b,c)
|
||||
assert(c == nil)
|
||||
if type(a) == 'table' then a = a.x end
|
||||
if type(b) == 'table' then b = b.x end
|
||||
return a<=b, "dummy"
|
||||
end
|
||||
|
||||
t.__eq = function (a,b,c)
|
||||
assert(c == nil)
|
||||
if type(a) == 'table' then a = a.x end
|
||||
if type(b) == 'table' then b = b.x end
|
||||
return a == b, "dummy"
|
||||
end
|
||||
|
||||
function Op(x) return setmetatable({x=x}, t) end
|
||||
|
||||
local function test (a, b, c)
|
||||
assert(not(Op(1)<Op(1)) and (Op(1)<Op(2)) and not(Op(2)<Op(1)))
|
||||
assert(not(1 < Op(1)) and (Op(1) < 2) and not(2 < Op(1)))
|
||||
assert(not(Op('a')<Op('a')) and (Op('a')<Op('b')) and not(Op('b')<Op('a')))
|
||||
assert(not('a' < Op('a')) and (Op('a') < 'b') and not(Op('b') < Op('a')))
|
||||
assert((Op(1)<=Op(1)) and (Op(1)<=Op(2)) and not(Op(2)<=Op(1)))
|
||||
assert((Op('a')<=Op('a')) and (Op('a')<=Op('b')) and not(Op('b')<=Op('a')))
|
||||
assert(not(Op(1)>Op(1)) and not(Op(1)>Op(2)) and (Op(2)>Op(1)))
|
||||
assert(not(Op('a')>Op('a')) and not(Op('a')>Op('b')) and (Op('b')>Op('a')))
|
||||
assert((Op(1)>=Op(1)) and not(Op(1)>=Op(2)) and (Op(2)>=Op(1)))
|
||||
assert((1 >= Op(1)) and not(1 >= Op(2)) and (Op(2) >= 1))
|
||||
assert((Op('a')>=Op('a')) and not(Op('a')>=Op('b')) and (Op('b')>=Op('a')))
|
||||
assert(('a' >= Op('a')) and not(Op('a') >= 'b') and (Op('b') >= Op('a')))
|
||||
assert(Op(1) == Op(1) and Op(1) ~= Op(2))
|
||||
assert(Op('a') == Op('a') and Op('a') ~= Op('b'))
|
||||
assert(a == a and a ~= b)
|
||||
assert(Op(3) == c)
|
||||
end
|
||||
|
||||
test(Op(1), Op(2), Op(3))
|
||||
|
||||
|
||||
-- test `partial order'
|
||||
|
||||
local function rawSet(x)
|
||||
local y = {}
|
||||
for _,k in pairs(x) do y[k] = 1 end
|
||||
return y
|
||||
end
|
||||
|
||||
local function Set(x)
|
||||
return setmetatable(rawSet(x), t)
|
||||
end
|
||||
|
||||
t.__lt = function (a,b)
|
||||
for k in pairs(a) do
|
||||
if not b[k] then return false end
|
||||
b[k] = undef
|
||||
end
|
||||
return next(b) ~= nil
|
||||
end
|
||||
|
||||
t.__le = function (a,b)
|
||||
for k in pairs(a) do
|
||||
if not b[k] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
assert(Set{1,2,3} < Set{1,2,3,4})
|
||||
assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
|
||||
assert((Set{1,2,3,4} <= Set{1,2,3,4}))
|
||||
assert((Set{1,2,3,4} >= Set{1,2,3,4}))
|
||||
assert(not (Set{1,3} <= Set{3,5}))
|
||||
assert(not(Set{1,3} <= Set{3,5}))
|
||||
assert(not(Set{1,3} >= Set{3,5}))
|
||||
|
||||
|
||||
t.__eq = function (a,b)
|
||||
for k in pairs(a) do
|
||||
if not b[k] then return false end
|
||||
b[k] = undef
|
||||
end
|
||||
return next(b) == nil
|
||||
end
|
||||
|
||||
local s = Set{1,3,5}
|
||||
assert(s == Set{3,5,1})
|
||||
assert(not rawequal(s, Set{3,5,1}))
|
||||
assert(rawequal(s, s))
|
||||
assert(Set{1,3,5,1} == rawSet{3,5,1})
|
||||
assert(rawSet{1,3,5,1} == Set{3,5,1})
|
||||
assert(Set{1,3,5} ~= Set{3,5,1,6})
|
||||
|
||||
-- '__eq' is not used for table accesses
|
||||
t[Set{1,3,5}] = 1
|
||||
assert(t[Set{1,3,5}] == undef)
|
||||
|
||||
|
||||
if not T then
|
||||
(Message or print)('\n >>> testC not active: skipping tests for \z
|
||||
userdata <<<\n')
|
||||
else
|
||||
local u1 = T.newuserdata(0, 1)
|
||||
local u2 = T.newuserdata(0, 1)
|
||||
local u3 = T.newuserdata(0, 1)
|
||||
assert(u1 ~= u2 and u1 ~= u3)
|
||||
debug.setuservalue(u1, 1);
|
||||
debug.setuservalue(u2, 2);
|
||||
debug.setuservalue(u3, 1);
|
||||
debug.setmetatable(u1, {__eq = function (a, b)
|
||||
return debug.getuservalue(a) == debug.getuservalue(b)
|
||||
end})
|
||||
debug.setmetatable(u2, {__eq = function (a, b)
|
||||
return true
|
||||
end})
|
||||
assert(u1 == u3 and u3 == u1 and u1 ~= u2)
|
||||
assert(u2 == u1 and u2 == u3 and u3 == u2)
|
||||
assert(u2 ~= {}) -- different types cannot be equal
|
||||
|
||||
local mirror = {}
|
||||
debug.setmetatable(u3, {__index = mirror, __newindex = mirror})
|
||||
for i = 1, 10 do u3[i] = i end
|
||||
for i = 1, 10 do assert(u3[i] == i) end
|
||||
end
|
||||
|
||||
|
||||
t.__concat = function (a,b,c)
|
||||
assert(c == nil)
|
||||
if type(a) == 'table' then a = a.val end
|
||||
if type(b) == 'table' then b = b.val end
|
||||
if A then return a..b
|
||||
else
|
||||
return setmetatable({val=a..b}, t)
|
||||
end
|
||||
end
|
||||
|
||||
c = {val="c"}; setmetatable(c, t)
|
||||
d = {val="d"}; setmetatable(d, t)
|
||||
|
||||
A = true
|
||||
assert(c..d == 'cd')
|
||||
assert(0 .."a".."b"..c..d.."e".."f"..(5+3).."g" == "0abcdef8g")
|
||||
|
||||
A = false
|
||||
assert((c..d..c..d).val == 'cdcd')
|
||||
x = c..d
|
||||
assert(getmetatable(x) == t and x.val == 'cd')
|
||||
x = 0 .."a".."b"..c..d.."e".."f".."g"
|
||||
assert(x.val == "0abcdefg")
|
||||
|
||||
|
||||
-- concat metamethod x numbers (bug in 5.1.1)
|
||||
c = {}
|
||||
local x
|
||||
setmetatable(c, {__concat = function (a,b)
|
||||
assert(type(a) == "number" and b == c or type(b) == "number" and a == c)
|
||||
return c
|
||||
end})
|
||||
assert(c..5 == c and 5 .. c == c)
|
||||
assert(4 .. c .. 5 == c and 4 .. 5 .. 6 .. 7 .. c == c)
|
||||
|
||||
|
||||
-- test comparison compatibilities
|
||||
local t1, t2, c, d
|
||||
t1 = {}; c = {}; setmetatable(c, t1)
|
||||
d = {}
|
||||
t1.__eq = function () return true end
|
||||
t1.__lt = function () return true end
|
||||
t1.__le = function () return false end
|
||||
setmetatable(d, t1)
|
||||
assert(c == d and c < d and not(d <= c))
|
||||
t2 = {}
|
||||
t2.__eq = t1.__eq
|
||||
t2.__lt = t1.__lt
|
||||
setmetatable(d, t2)
|
||||
assert(c == d and c < d and not(d <= c))
|
||||
|
||||
|
||||
|
||||
-- test for several levels of calls
|
||||
local i
|
||||
local tt = {
|
||||
__call = function (t, ...)
|
||||
i = i+1
|
||||
if t.f then return t.f(...)
|
||||
else return {...}
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
local a = setmetatable({}, tt)
|
||||
local b = setmetatable({f=a}, tt)
|
||||
local c = setmetatable({f=b}, tt)
|
||||
|
||||
i = 0
|
||||
x = c(3,4,5)
|
||||
assert(i == 3 and x[1] == 3 and x[3] == 5)
|
||||
|
||||
|
||||
assert(_G.X == 20)
|
||||
|
||||
print'+'
|
||||
|
||||
local _g = _G
|
||||
_ENV = setmetatable({}, {__index=function (_,k) return _g[k] end})
|
||||
|
||||
|
||||
a = {}
|
||||
rawset(a, "x", 1, 2, 3)
|
||||
assert(a.x == 1 and rawget(a, "x", 3) == 1)
|
||||
|
||||
print '+'
|
||||
|
||||
-- testing metatables for basic types
|
||||
mt = {__index = function (a,b) return a+b end,
|
||||
__len = function (x) return math.floor(x) end}
|
||||
debug.setmetatable(10, mt)
|
||||
assert(getmetatable(-2) == mt)
|
||||
assert((10)[3] == 13)
|
||||
assert((10)["3"] == 13)
|
||||
assert(#3.45 == 3)
|
||||
debug.setmetatable(23, nil)
|
||||
assert(getmetatable(-2) == nil)
|
||||
|
||||
debug.setmetatable(true, mt)
|
||||
assert(getmetatable(false) == mt)
|
||||
mt.__index = function (a,b) return a or b end
|
||||
assert((true)[false] == true)
|
||||
assert((false)[false] == false)
|
||||
debug.setmetatable(false, nil)
|
||||
assert(getmetatable(true) == nil)
|
||||
|
||||
debug.setmetatable(nil, mt)
|
||||
assert(getmetatable(nil) == mt)
|
||||
mt.__add = function (a,b) return (a or 1) + (b or 2) end
|
||||
assert(10 + nil == 12)
|
||||
assert(nil + 23 == 24)
|
||||
assert(nil + nil == 3)
|
||||
debug.setmetatable(nil, nil)
|
||||
assert(getmetatable(nil) == nil)
|
||||
|
||||
debug.setmetatable(nil, {})
|
||||
|
||||
|
||||
-- loops in delegation
|
||||
a = {}; setmetatable(a, a); a.__index = a; a.__newindex = a
|
||||
assert(not pcall(function (a,b) return a[b] end, a, 10))
|
||||
assert(not pcall(function (a,b,c) a[b] = c end, a, 10, true))
|
||||
|
||||
-- bug in 5.1
|
||||
T, K, V = nil
|
||||
grandparent = {}
|
||||
grandparent.__newindex = function(t,k,v) T=t; K=k; V=v end
|
||||
|
||||
parent = {}
|
||||
parent.__newindex = parent
|
||||
setmetatable(parent, grandparent)
|
||||
|
||||
child = setmetatable({}, parent)
|
||||
child.foo = 10 --> CRASH (on some machines)
|
||||
assert(T == parent and K == "foo" and V == 10)
|
||||
|
||||
print 'OK'
|
||||
|
||||
return 12
|
||||
|
||||
|
||||
@@ -0,0 +1,925 @@
|
||||
-- $Id: testes/files.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
local maxint = math.maxinteger
|
||||
|
||||
assert(type(os.getenv"PATH") == "string")
|
||||
|
||||
assert(io.input(io.stdin) == io.stdin)
|
||||
assert(not pcall(io.input, "non-existent-file"))
|
||||
assert(io.output(io.stdout) == io.stdout)
|
||||
|
||||
|
||||
local function testerr (msg, f, ...)
|
||||
local stat, err = pcall(f, ...)
|
||||
return (not stat and string.find(err, msg, 1, true))
|
||||
end
|
||||
|
||||
|
||||
local function checkerr (msg, f, ...)
|
||||
assert(testerr(msg, f, ...))
|
||||
end
|
||||
|
||||
|
||||
-- cannot close standard files
|
||||
assert(not io.close(io.stdin) and
|
||||
not io.stdout:close() and
|
||||
not io.stderr:close())
|
||||
|
||||
-- cannot call close method without an argument (new in 5.3.5)
|
||||
checkerr("got no value", io.stdin.close)
|
||||
|
||||
|
||||
assert(type(io.input()) == "userdata" and io.type(io.output()) == "file")
|
||||
assert(type(io.stdin) == "userdata" and io.type(io.stderr) == "file")
|
||||
assert(not io.type(8))
|
||||
local a = {}; setmetatable(a, {})
|
||||
assert(not io.type(a))
|
||||
|
||||
assert(getmetatable(io.input()).__name == "FILE*")
|
||||
|
||||
local a,b,c = io.open('xuxu_nao_existe')
|
||||
assert(not a and type(b) == "string" and type(c) == "number")
|
||||
|
||||
a,b,c = io.open('/a/b/c/d', 'w')
|
||||
assert(not a and type(b) == "string" and type(c) == "number")
|
||||
|
||||
local file = os.tmpname()
|
||||
local f, msg = io.open(file, "w")
|
||||
if not f then
|
||||
(Message or print)("'os.tmpname' file cannot be open; skipping file tests")
|
||||
|
||||
else --{ most tests here need tmpname
|
||||
f:close()
|
||||
|
||||
print('testing i/o')
|
||||
|
||||
local otherfile = os.tmpname()
|
||||
|
||||
checkerr("invalid mode", io.open, file, "rw")
|
||||
checkerr("invalid mode", io.open, file, "rb+")
|
||||
checkerr("invalid mode", io.open, file, "r+bk")
|
||||
checkerr("invalid mode", io.open, file, "")
|
||||
checkerr("invalid mode", io.open, file, "+")
|
||||
checkerr("invalid mode", io.open, file, "b")
|
||||
assert(io.open(file, "r+b")):close()
|
||||
assert(io.open(file, "r+")):close()
|
||||
assert(io.open(file, "rb")):close()
|
||||
|
||||
assert(os.setlocale('C', 'all'))
|
||||
|
||||
io.input(io.stdin); io.output(io.stdout);
|
||||
|
||||
os.remove(file)
|
||||
assert(not loadfile(file))
|
||||
checkerr("", dofile, file)
|
||||
assert(not io.open(file))
|
||||
io.output(file)
|
||||
assert(io.output() ~= io.stdout)
|
||||
|
||||
if not _port then -- invalid seek
|
||||
local status, msg, code = io.stdin:seek("set", 1000)
|
||||
assert(not status and type(msg) == "string" and type(code) == "number")
|
||||
end
|
||||
|
||||
assert(io.output():seek() == 0)
|
||||
assert(io.write("alo alo"):seek() == string.len("alo alo"))
|
||||
assert(io.output():seek("cur", -3) == string.len("alo alo")-3)
|
||||
assert(io.write("joao"))
|
||||
assert(io.output():seek("end") == string.len("alo joao"))
|
||||
|
||||
assert(io.output():seek("set") == 0)
|
||||
|
||||
assert(io.write('"álo"', "{a}\n", "second line\n", "third line \n"))
|
||||
assert(io.write('çfourth_line'))
|
||||
io.output(io.stdout)
|
||||
collectgarbage() -- file should be closed by GC
|
||||
assert(io.input() == io.stdin and rawequal(io.output(), io.stdout))
|
||||
print('+')
|
||||
|
||||
-- test GC for files
|
||||
collectgarbage()
|
||||
for i=1,120 do
|
||||
for i=1,5 do
|
||||
io.input(file)
|
||||
assert(io.open(file, 'r'))
|
||||
io.lines(file)
|
||||
end
|
||||
collectgarbage()
|
||||
end
|
||||
|
||||
io.input():close()
|
||||
io.close()
|
||||
|
||||
assert(os.rename(file, otherfile))
|
||||
assert(not os.rename(file, otherfile))
|
||||
|
||||
io.output(io.open(otherfile, "ab"))
|
||||
assert(io.write("\n\n\t\t ", 3450, "\n"));
|
||||
io.close()
|
||||
|
||||
|
||||
do
|
||||
-- closing file by scope
|
||||
local F = nil
|
||||
do
|
||||
local f <close> = assert(io.open(file, "w"))
|
||||
F = f
|
||||
end
|
||||
assert(tostring(F) == "file (closed)")
|
||||
end
|
||||
assert(os.remove(file))
|
||||
|
||||
|
||||
do
|
||||
-- test writing/reading numbers
|
||||
local f <close> = assert(io.open(file, "w"))
|
||||
f:write(maxint, '\n')
|
||||
f:write(string.format("0X%x\n", maxint))
|
||||
f:write("0xABCp-3", '\n')
|
||||
f:write(0, '\n')
|
||||
f:write(-maxint, '\n')
|
||||
f:write(string.format("0x%X\n", -maxint))
|
||||
f:write("-0xABCp-3", '\n')
|
||||
assert(f:close())
|
||||
local f <close> = assert(io.open(file, "r"))
|
||||
assert(f:read("n") == maxint)
|
||||
assert(f:read("n") == maxint)
|
||||
assert(f:read("n") == 0xABCp-3)
|
||||
assert(f:read("n") == 0)
|
||||
assert(f:read("*n") == -maxint) -- test old format (with '*')
|
||||
assert(f:read("n") == -maxint)
|
||||
assert(f:read("*n") == -0xABCp-3) -- test old format (with '*')
|
||||
end
|
||||
assert(os.remove(file))
|
||||
|
||||
|
||||
-- testing multiple arguments to io.read
|
||||
do
|
||||
local f <close> = assert(io.open(file, "w"))
|
||||
f:write[[
|
||||
a line
|
||||
another line
|
||||
1234
|
||||
3.45
|
||||
one
|
||||
two
|
||||
three
|
||||
]]
|
||||
local l1, l2, l3, l4, n1, n2, c, dummy
|
||||
assert(f:close())
|
||||
local f <close> = assert(io.open(file, "r"))
|
||||
l1, l2, n1, n2, dummy = f:read("l", "L", "n", "n")
|
||||
assert(l1 == "a line" and l2 == "another line\n" and
|
||||
n1 == 1234 and n2 == 3.45 and dummy == nil)
|
||||
assert(f:close())
|
||||
local f <close> = assert(io.open(file, "r"))
|
||||
l1, l2, n1, n2, c, l3, l4, dummy = f:read(7, "l", "n", "n", 1, "l", "l")
|
||||
assert(l1 == "a line\n" and l2 == "another line" and c == '\n' and
|
||||
n1 == 1234 and n2 == 3.45 and l3 == "one" and l4 == "two"
|
||||
and dummy == nil)
|
||||
assert(f:close())
|
||||
local f <close> = assert(io.open(file, "r"))
|
||||
-- second item failing
|
||||
l1, n1, n2, dummy = f:read("l", "n", "n", "l")
|
||||
assert(l1 == "a line" and not n1)
|
||||
end
|
||||
assert(os.remove(file))
|
||||
|
||||
|
||||
|
||||
-- test yielding during 'dofile'
|
||||
f = assert(io.open(file, "w"))
|
||||
f:write[[
|
||||
local x, z = coroutine.yield(10)
|
||||
local y = coroutine.yield(20)
|
||||
return x + y * z
|
||||
]]
|
||||
assert(f:close())
|
||||
f = coroutine.wrap(dofile)
|
||||
assert(f(file) == 10)
|
||||
assert(f(100, 101) == 20)
|
||||
assert(f(200) == 100 + 200 * 101)
|
||||
assert(os.remove(file))
|
||||
|
||||
|
||||
f = assert(io.open(file, "w"))
|
||||
-- test number termination
|
||||
f:write[[
|
||||
-12.3- -0xffff+ .3|5.E-3X +234e+13E 0xDEADBEEFDEADBEEFx
|
||||
0x1.13Ap+3e
|
||||
]]
|
||||
-- very long number
|
||||
f:write("1234"); for i = 1, 1000 do f:write("0") end; f:write("\n")
|
||||
-- invalid sequences (must read and discard valid prefixes)
|
||||
f:write[[
|
||||
.e+ 0.e; --; 0xX;
|
||||
]]
|
||||
assert(f:close())
|
||||
f = assert(io.open(file, "r"))
|
||||
assert(f:read("n") == -12.3); assert(f:read(1) == "-")
|
||||
assert(f:read("n") == -0xffff); assert(f:read(2) == "+ ")
|
||||
assert(f:read("n") == 0.3); assert(f:read(1) == "|")
|
||||
assert(f:read("n") == 5e-3); assert(f:read(1) == "X")
|
||||
assert(f:read("n") == 234e13); assert(f:read(1) == "E")
|
||||
assert(f:read("n") == 0Xdeadbeefdeadbeef); assert(f:read(2) == "x\n")
|
||||
assert(f:read("n") == 0x1.13aP3); assert(f:read(1) == "e")
|
||||
|
||||
do -- attempt to read too long number
|
||||
assert(not f:read("n")) -- fails
|
||||
local s = f:read("L") -- read rest of line
|
||||
assert(string.find(s, "^00*\n$")) -- lots of 0's left
|
||||
end
|
||||
|
||||
assert(not f:read("n")); assert(f:read(2) == "e+")
|
||||
assert(not f:read("n")); assert(f:read(1) == ";")
|
||||
assert(not f:read("n")); assert(f:read(2) == "-;")
|
||||
assert(not f:read("n")); assert(f:read(1) == "X")
|
||||
assert(not f:read("n")); assert(f:read(1) == ";")
|
||||
assert(not f:read("n")); assert(not f:read(0)) -- end of file
|
||||
assert(f:close())
|
||||
assert(os.remove(file))
|
||||
|
||||
|
||||
-- test line generators
|
||||
assert(not pcall(io.lines, "non-existent-file"))
|
||||
assert(os.rename(otherfile, file))
|
||||
io.output(otherfile)
|
||||
local n = 0
|
||||
local f = io.lines(file)
|
||||
while f() do n = n + 1 end;
|
||||
assert(n == 6) -- number of lines in the file
|
||||
checkerr("file is already closed", f)
|
||||
checkerr("file is already closed", f)
|
||||
-- copy from file to otherfile
|
||||
n = 0
|
||||
for l in io.lines(file) do io.write(l, "\n"); n = n + 1 end
|
||||
io.close()
|
||||
assert(n == 6)
|
||||
-- copy from otherfile back to file
|
||||
local f = assert(io.open(otherfile))
|
||||
assert(io.type(f) == "file")
|
||||
io.output(file)
|
||||
assert(not io.output():read())
|
||||
n = 0
|
||||
for l in f:lines() do io.write(l, "\n"); n = n + 1 end
|
||||
assert(tostring(f):sub(1, 5) == "file ")
|
||||
assert(f:close()); io.close()
|
||||
assert(n == 6)
|
||||
checkerr("closed file", io.close, f)
|
||||
assert(tostring(f) == "file (closed)")
|
||||
assert(io.type(f) == "closed file")
|
||||
io.input(file)
|
||||
f = io.open(otherfile):lines()
|
||||
n = 0
|
||||
for l in io.lines() do assert(l == f()); n = n + 1 end
|
||||
f = nil; collectgarbage()
|
||||
assert(n == 6)
|
||||
assert(os.remove(otherfile))
|
||||
|
||||
do -- bug in 5.3.1
|
||||
io.output(otherfile)
|
||||
io.write(string.rep("a", 300), "\n")
|
||||
io.close()
|
||||
local t ={}; for i = 1, 250 do t[i] = 1 end
|
||||
t = {io.lines(otherfile, table.unpack(t))()}
|
||||
-- everything ok here
|
||||
assert(#t == 250 and t[1] == 'a' and t[#t] == 'a')
|
||||
t[#t + 1] = 1 -- one too many
|
||||
checkerr("too many arguments", io.lines, otherfile, table.unpack(t))
|
||||
collectgarbage() -- ensure 'otherfile' is closed
|
||||
assert(os.remove(otherfile))
|
||||
end
|
||||
|
||||
io.input(file)
|
||||
do -- test error returns
|
||||
local a,b,c = io.input():write("xuxu")
|
||||
assert(not a and type(b) == "string" and type(c) == "number")
|
||||
end
|
||||
checkerr("invalid format", io.read, "x")
|
||||
assert(io.read(0) == "") -- not eof
|
||||
assert(io.read(5, 'l') == '"álo"')
|
||||
assert(io.read(0) == "")
|
||||
assert(io.read() == "second line")
|
||||
local x = io.input():seek()
|
||||
assert(io.read() == "third line ")
|
||||
assert(io.input():seek("set", x))
|
||||
assert(io.read('L') == "third line \n")
|
||||
assert(io.read(1) == "ç")
|
||||
assert(io.read(string.len"fourth_line") == "fourth_line")
|
||||
assert(io.input():seek("cur", -string.len"fourth_line"))
|
||||
assert(io.read() == "fourth_line")
|
||||
assert(io.read() == "") -- empty line
|
||||
assert(io.read('n') == 3450)
|
||||
assert(io.read(1) == '\n')
|
||||
assert(not io.read(0)) -- end of file
|
||||
assert(not io.read(1)) -- end of file
|
||||
assert(not io.read(30000)) -- end of file
|
||||
assert(({io.read(1)})[2] == undef)
|
||||
assert(not io.read()) -- end of file
|
||||
assert(({io.read()})[2] == undef)
|
||||
assert(not io.read('n')) -- end of file
|
||||
assert(({io.read('n')})[2] == undef)
|
||||
assert(io.read('a') == '') -- end of file (OK for 'a')
|
||||
assert(io.read('a') == '') -- end of file (OK for 'a')
|
||||
collectgarbage()
|
||||
print('+')
|
||||
io.close(io.input())
|
||||
checkerr(" input file is closed", io.read)
|
||||
|
||||
assert(os.remove(file))
|
||||
|
||||
local t = '0123456789'
|
||||
for i=1,10 do t = t..t; end
|
||||
assert(string.len(t) == 10*2^10)
|
||||
|
||||
io.output(file)
|
||||
io.write("alo"):write("\n")
|
||||
io.close()
|
||||
checkerr(" output file is closed", io.write)
|
||||
local f = io.open(file, "a+b")
|
||||
io.output(f)
|
||||
collectgarbage()
|
||||
|
||||
assert(io.write(' ' .. t .. ' '))
|
||||
assert(io.write(';', 'end of file\n'))
|
||||
f:flush(); io.flush()
|
||||
f:close()
|
||||
print('+')
|
||||
|
||||
io.input(file)
|
||||
assert(io.read() == "alo")
|
||||
assert(io.read(1) == ' ')
|
||||
assert(io.read(string.len(t)) == t)
|
||||
assert(io.read(1) == ' ')
|
||||
assert(io.read(0))
|
||||
assert(io.read('a') == ';end of file\n')
|
||||
assert(not io.read(0))
|
||||
assert(io.close(io.input()))
|
||||
|
||||
|
||||
-- test errors in read/write
|
||||
do
|
||||
local function ismsg (m)
|
||||
-- error message is not a code number
|
||||
return (type(m) == "string" and not tonumber(m))
|
||||
end
|
||||
|
||||
-- read
|
||||
local f = io.open(file, "w")
|
||||
local r, m, c = f:read()
|
||||
assert(not r and ismsg(m) and type(c) == "number")
|
||||
assert(f:close())
|
||||
-- write
|
||||
f = io.open(file, "r")
|
||||
r, m, c = f:write("whatever")
|
||||
assert(not r and ismsg(m) and type(c) == "number")
|
||||
assert(f:close())
|
||||
-- lines
|
||||
f = io.open(file, "w")
|
||||
r, m = pcall(f:lines())
|
||||
assert(r == false and ismsg(m))
|
||||
assert(f:close())
|
||||
end
|
||||
|
||||
assert(os.remove(file))
|
||||
|
||||
-- test for L format
|
||||
io.output(file); io.write"\n\nline\nother":close()
|
||||
io.input(file)
|
||||
assert(io.read"L" == "\n")
|
||||
assert(io.read"L" == "\n")
|
||||
assert(io.read"L" == "line\n")
|
||||
assert(io.read"L" == "other")
|
||||
assert(not io.read"L")
|
||||
io.input():close()
|
||||
|
||||
local f = assert(io.open(file))
|
||||
local s = ""
|
||||
for l in f:lines("L") do s = s .. l end
|
||||
assert(s == "\n\nline\nother")
|
||||
f:close()
|
||||
|
||||
io.input(file)
|
||||
s = ""
|
||||
for l in io.lines(nil, "L") do s = s .. l end
|
||||
assert(s == "\n\nline\nother")
|
||||
io.input():close()
|
||||
|
||||
s = ""
|
||||
for l in io.lines(file, "L") do s = s .. l end
|
||||
assert(s == "\n\nline\nother")
|
||||
|
||||
s = ""
|
||||
for l in io.lines(file, "l") do s = s .. l end
|
||||
assert(s == "lineother")
|
||||
|
||||
io.output(file); io.write"a = 10 + 34\na = 2*a\na = -a\n":close()
|
||||
local t = {}
|
||||
assert(load(io.lines(file, "L"), nil, nil, t))()
|
||||
assert(t.a == -((10 + 34) * 2))
|
||||
|
||||
|
||||
do -- testing closing file in line iteration
|
||||
|
||||
-- get the to-be-closed variable from a loop
|
||||
local function gettoclose (lv)
|
||||
lv = lv + 1
|
||||
local stvar = 0 -- to-be-closed is 4th state variable in the loop
|
||||
for i = 1, 1000 do
|
||||
local n, v = debug.getlocal(lv, i)
|
||||
if n == "(for state)" then
|
||||
stvar = stvar + 1
|
||||
if stvar == 4 then return v end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local f
|
||||
for l in io.lines(file) do
|
||||
f = gettoclose(1)
|
||||
assert(io.type(f) == "file")
|
||||
break
|
||||
end
|
||||
assert(io.type(f) == "closed file")
|
||||
|
||||
f = nil
|
||||
local function foo (name)
|
||||
for l in io.lines(name) do
|
||||
f = gettoclose(1)
|
||||
assert(io.type(f) == "file")
|
||||
error(f) -- exit loop with an error
|
||||
end
|
||||
end
|
||||
local st, msg = pcall(foo, file)
|
||||
assert(st == false and io.type(msg) == "closed file")
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- test for multipe arguments in 'lines'
|
||||
io.output(file); io.write"0123456789\n":close()
|
||||
for a,b in io.lines(file, 1, 1) do
|
||||
if a == "\n" then assert(not b)
|
||||
else assert(tonumber(a) == tonumber(b) - 1)
|
||||
end
|
||||
end
|
||||
|
||||
for a,b,c in io.lines(file, 1, 2, "a") do
|
||||
assert(a == "0" and b == "12" and c == "3456789\n")
|
||||
end
|
||||
|
||||
for a,b,c in io.lines(file, "a", 0, 1) do
|
||||
if a == "" then break end
|
||||
assert(a == "0123456789\n" and not b and not c)
|
||||
end
|
||||
collectgarbage() -- to close file in previous iteration
|
||||
|
||||
io.output(file); io.write"00\n10\n20\n30\n40\n":close()
|
||||
for a, b in io.lines(file, "n", "n") do
|
||||
if a == 40 then assert(not b)
|
||||
else assert(a == b - 10)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- test load x lines
|
||||
io.output(file);
|
||||
io.write[[
|
||||
local y
|
||||
= X
|
||||
X =
|
||||
X *
|
||||
2 +
|
||||
X;
|
||||
X =
|
||||
X
|
||||
- y;
|
||||
]]:close()
|
||||
_G.X = 1
|
||||
assert(not load((io.lines(file))))
|
||||
collectgarbage() -- to close file in previous iteration
|
||||
load((io.lines(file, "L")))()
|
||||
assert(_G.X == 2)
|
||||
load((io.lines(file, 1)))()
|
||||
assert(_G.X == 4)
|
||||
load((io.lines(file, 3)))()
|
||||
assert(_G.X == 8)
|
||||
|
||||
print('+')
|
||||
|
||||
local x1 = "string\n\n\\com \"\"''coisas [[estranhas]] ]]'"
|
||||
io.output(file)
|
||||
assert(io.write(string.format("x2 = %q\n-- comment without ending EOS", x1)))
|
||||
io.close()
|
||||
assert(loadfile(file))()
|
||||
assert(x1 == x2)
|
||||
print('+')
|
||||
assert(os.remove(file))
|
||||
assert(not os.remove(file))
|
||||
assert(not os.remove(otherfile))
|
||||
|
||||
-- testing loadfile
|
||||
local function testloadfile (s, expres)
|
||||
io.output(file)
|
||||
if s then io.write(s) end
|
||||
io.close()
|
||||
local res = assert(loadfile(file))()
|
||||
assert(os.remove(file))
|
||||
assert(res == expres)
|
||||
end
|
||||
|
||||
-- loading empty file
|
||||
testloadfile(nil, nil)
|
||||
|
||||
-- loading file with initial comment without end of line
|
||||
testloadfile("# a non-ending comment", nil)
|
||||
|
||||
|
||||
-- checking Unicode BOM in files
|
||||
testloadfile("\xEF\xBB\xBF# some comment\nreturn 234", 234)
|
||||
testloadfile("\xEF\xBB\xBFreturn 239", 239)
|
||||
testloadfile("\xEF\xBB\xBF", nil) -- empty file with a BOM
|
||||
|
||||
|
||||
-- checking line numbers in files with initial comments
|
||||
testloadfile("# a comment\nreturn require'debug'.getinfo(1).currentline", 2)
|
||||
|
||||
|
||||
-- loading binary file
|
||||
io.output(io.open(file, "wb"))
|
||||
assert(io.write(string.dump(function () return 10, '\0alo\255', 'hi' end)))
|
||||
io.close()
|
||||
a, b, c = assert(loadfile(file))()
|
||||
assert(a == 10 and b == "\0alo\255" and c == "hi")
|
||||
assert(os.remove(file))
|
||||
|
||||
-- bug in 5.2.1
|
||||
do
|
||||
io.output(io.open(file, "wb"))
|
||||
-- save function with no upvalues
|
||||
assert(io.write(string.dump(function () return 1 end)))
|
||||
io.close()
|
||||
f = assert(loadfile(file, "b", {}))
|
||||
assert(type(f) == "function" and f() == 1)
|
||||
assert(os.remove(file))
|
||||
end
|
||||
|
||||
-- loading binary file with initial comment
|
||||
io.output(io.open(file, "wb"))
|
||||
assert(io.write("#this is a comment for a binary file\0\n",
|
||||
string.dump(function () return 20, '\0\0\0' end)))
|
||||
io.close()
|
||||
a, b, c = assert(loadfile(file))()
|
||||
assert(a == 20 and b == "\0\0\0" and c == nil)
|
||||
assert(os.remove(file))
|
||||
|
||||
|
||||
-- 'loadfile' with 'env'
|
||||
do
|
||||
local f = io.open(file, 'w')
|
||||
f:write[[
|
||||
if (...) then a = 15; return b, c, d
|
||||
else return _ENV
|
||||
end
|
||||
]]
|
||||
f:close()
|
||||
local t = {b = 12, c = "xuxu", d = print}
|
||||
local f = assert(loadfile(file, 't', t))
|
||||
local b, c, d = f(1)
|
||||
assert(t.a == 15 and b == 12 and c == t.c and d == print)
|
||||
assert(f() == t)
|
||||
f = assert(loadfile(file, 't', nil))
|
||||
assert(f() == nil)
|
||||
f = assert(loadfile(file))
|
||||
assert(f() == _G)
|
||||
assert(os.remove(file))
|
||||
end
|
||||
|
||||
|
||||
-- 'loadfile' x modes
|
||||
do
|
||||
io.open(file, 'w'):write("return 10"):close()
|
||||
local s, m = loadfile(file, 'b')
|
||||
assert(not s and string.find(m, "a text chunk"))
|
||||
io.open(file, 'w'):write("\27 return 10"):close()
|
||||
local s, m = loadfile(file, 't')
|
||||
assert(not s and string.find(m, "a binary chunk"))
|
||||
assert(os.remove(file))
|
||||
end
|
||||
|
||||
|
||||
io.output(file)
|
||||
assert(io.write("qualquer coisa\n"))
|
||||
assert(io.write("mais qualquer coisa"))
|
||||
io.close()
|
||||
assert(io.output(assert(io.open(otherfile, 'wb')))
|
||||
:write("outra coisa\0\1\3\0\0\0\0\255\0")
|
||||
:close())
|
||||
|
||||
local filehandle = assert(io.open(file, 'r+'))
|
||||
local otherfilehandle = assert(io.open(otherfile, 'rb'))
|
||||
assert(filehandle ~= otherfilehandle)
|
||||
assert(type(filehandle) == "userdata")
|
||||
assert(filehandle:read('l') == "qualquer coisa")
|
||||
io.input(otherfilehandle)
|
||||
assert(io.read(string.len"outra coisa") == "outra coisa")
|
||||
assert(filehandle:read('l') == "mais qualquer coisa")
|
||||
filehandle:close();
|
||||
assert(type(filehandle) == "userdata")
|
||||
io.input(otherfilehandle)
|
||||
assert(io.read(4) == "\0\1\3\0")
|
||||
assert(io.read(3) == "\0\0\0")
|
||||
assert(io.read(0) == "") -- 255 is not eof
|
||||
assert(io.read(1) == "\255")
|
||||
assert(io.read('a') == "\0")
|
||||
assert(not io.read(0))
|
||||
assert(otherfilehandle == io.input())
|
||||
otherfilehandle:close()
|
||||
assert(os.remove(file))
|
||||
assert(os.remove(otherfile))
|
||||
collectgarbage()
|
||||
|
||||
io.output(file)
|
||||
:write[[
|
||||
123.4 -56e-2 not a number
|
||||
second line
|
||||
third line
|
||||
|
||||
and the rest of the file
|
||||
]]
|
||||
:close()
|
||||
io.input(file)
|
||||
local _,a,b,c,d,e,h,__ = io.read(1, 'n', 'n', 'l', 'l', 'l', 'a', 10)
|
||||
assert(io.close(io.input()))
|
||||
assert(_ == ' ' and not __)
|
||||
assert(type(a) == 'number' and a==123.4 and b==-56e-2)
|
||||
assert(d=='second line' and e=='third line')
|
||||
assert(h==[[
|
||||
|
||||
and the rest of the file
|
||||
]])
|
||||
assert(os.remove(file))
|
||||
collectgarbage()
|
||||
|
||||
-- testing buffers
|
||||
do
|
||||
local f = assert(io.open(file, "w"))
|
||||
local fr = assert(io.open(file, "r"))
|
||||
assert(f:setvbuf("full", 2000))
|
||||
f:write("x")
|
||||
assert(fr:read("all") == "") -- full buffer; output not written yet
|
||||
f:close()
|
||||
fr:seek("set")
|
||||
assert(fr:read("all") == "x") -- `close' flushes it
|
||||
f = assert(io.open(file), "w")
|
||||
assert(f:setvbuf("no"))
|
||||
f:write("x")
|
||||
fr:seek("set")
|
||||
assert(fr:read("all") == "x") -- no buffer; output is ready
|
||||
f:close()
|
||||
f = assert(io.open(file, "a"))
|
||||
assert(f:setvbuf("line"))
|
||||
f:write("x")
|
||||
fr:seek("set", 1)
|
||||
assert(fr:read("all") == "") -- line buffer; no output without `\n'
|
||||
f:write("a\n"):seek("set", 1)
|
||||
assert(fr:read("all") == "xa\n") -- now we have a whole line
|
||||
f:close(); fr:close()
|
||||
assert(os.remove(file))
|
||||
end
|
||||
|
||||
|
||||
if not _soft then
|
||||
print("testing large files (> BUFSIZ)")
|
||||
io.output(file)
|
||||
for i=1,5001 do io.write('0123456789123') end
|
||||
io.write('\n12346'):close()
|
||||
io.input(file)
|
||||
local x = io.read('a')
|
||||
io.input():seek('set', 0)
|
||||
local y = io.read(30001)..io.read(1005)..io.read(0)..
|
||||
io.read(1)..io.read(100003)
|
||||
assert(x == y and string.len(x) == 5001*13 + 6)
|
||||
io.input():seek('set', 0)
|
||||
y = io.read() -- huge line
|
||||
assert(x == y..'\n'..io.read())
|
||||
assert(not io.read())
|
||||
io.close(io.input())
|
||||
assert(os.remove(file))
|
||||
x = nil; y = nil
|
||||
end
|
||||
|
||||
if not _port then
|
||||
local progname
|
||||
do -- get name of running executable
|
||||
local arg = arg or ARG
|
||||
local i = 0
|
||||
while arg[i] do i = i - 1 end
|
||||
progname = '"' .. arg[i + 1] .. '"'
|
||||
end
|
||||
print("testing popen/pclose and execute")
|
||||
local tests = {
|
||||
-- command, what, code
|
||||
{"ls > /dev/null", "ok"},
|
||||
{"not-to-be-found-command", "exit"},
|
||||
{"exit 3", "exit", 3},
|
||||
{"exit 129", "exit", 129},
|
||||
{"kill -s HUP $$", "signal", 1},
|
||||
{"kill -s KILL $$", "signal", 9},
|
||||
{"sh -c 'kill -s HUP $$'", "exit"},
|
||||
{progname .. ' -e " "', "ok"},
|
||||
{progname .. ' -e "os.exit(0, true)"', "ok"},
|
||||
{progname .. ' -e "os.exit(20, true)"', "exit", 20},
|
||||
}
|
||||
print("\n(some error messages are expected now)")
|
||||
for _, v in ipairs(tests) do
|
||||
local x, y, z = io.popen(v[1]):close()
|
||||
local x1, y1, z1 = os.execute(v[1])
|
||||
assert(x == x1 and y == y1 and z == z1)
|
||||
if v[2] == "ok" then
|
||||
assert(x and y == 'exit' and z == 0)
|
||||
else
|
||||
assert(not x and y == v[2]) -- correct status and 'what'
|
||||
-- correct code if known (but always different from 0)
|
||||
assert((v[3] == nil and z > 0) or v[3] == z)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- testing tmpfile
|
||||
f = io.tmpfile()
|
||||
assert(io.type(f) == "file")
|
||||
f:write("alo")
|
||||
f:seek("set")
|
||||
assert(f:read"a" == "alo")
|
||||
|
||||
end --}
|
||||
|
||||
print'+'
|
||||
|
||||
print("testing date/time")
|
||||
|
||||
assert(os.date("") == "")
|
||||
assert(os.date("!") == "")
|
||||
assert(os.date("\0\0") == "\0\0")
|
||||
assert(os.date("!\0\0") == "\0\0")
|
||||
local x = string.rep("a", 10000)
|
||||
assert(os.date(x) == x)
|
||||
local t = os.time()
|
||||
D = os.date("*t", t)
|
||||
assert(os.date(string.rep("%d", 1000), t) ==
|
||||
string.rep(os.date("%d", t), 1000))
|
||||
assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
|
||||
|
||||
local function checkDateTable (t)
|
||||
_G.D = os.date("*t", t)
|
||||
assert(os.time(D) == t)
|
||||
load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and
|
||||
D.hour==%H and D.min==%M and D.sec==%S and
|
||||
D.wday==%w+1 and D.yday==%j)]], t))()
|
||||
_G.D = nil
|
||||
end
|
||||
|
||||
checkDateTable(os.time())
|
||||
if not _port then
|
||||
-- assume that time_t can represent these values
|
||||
checkDateTable(0)
|
||||
checkDateTable(1)
|
||||
checkDateTable(1000)
|
||||
checkDateTable(0x7fffffff)
|
||||
checkDateTable(0x80000000)
|
||||
end
|
||||
|
||||
checkerr("invalid conversion specifier", os.date, "%")
|
||||
checkerr("invalid conversion specifier", os.date, "%9")
|
||||
checkerr("invalid conversion specifier", os.date, "%")
|
||||
checkerr("invalid conversion specifier", os.date, "%O")
|
||||
checkerr("invalid conversion specifier", os.date, "%E")
|
||||
checkerr("invalid conversion specifier", os.date, "%Ea")
|
||||
|
||||
checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour='x'})
|
||||
checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour=1.5})
|
||||
|
||||
checkerr("missing", os.time, {hour = 12}) -- missing date
|
||||
|
||||
|
||||
if string.packsize("i") == 4 then -- 4-byte ints
|
||||
checkerr("field 'year' is out-of-bound", os.time,
|
||||
{year = -(1 << 31) + 1899, month = 1, day = 1})
|
||||
end
|
||||
|
||||
if not _port then
|
||||
-- test Posix-specific modifiers
|
||||
assert(type(os.date("%Ex")) == 'string')
|
||||
assert(type(os.date("%Oy")) == 'string')
|
||||
|
||||
-- test large dates (assume at least 4-byte ints and time_t)
|
||||
local t0 = os.time{year = 1970, month = 1, day = 0}
|
||||
local t1 = os.time{year = 1970, month = 1, day = 0, sec = (1 << 31) - 1}
|
||||
assert(t1 - t0 == (1 << 31) - 1)
|
||||
t0 = os.time{year = 1970, month = 1, day = 1}
|
||||
t1 = os.time{year = 1970, month = 1, day = 1, sec = -(1 << 31)}
|
||||
assert(t1 - t0 == -(1 << 31))
|
||||
|
||||
-- test out-of-range dates (at least for Unix)
|
||||
if maxint >= 2^62 then -- cannot do these tests in Small Lua
|
||||
-- no arith overflows
|
||||
checkerr("out-of-bound", os.time, {year = -maxint, month = 1, day = 1})
|
||||
if string.packsize("i") == 4 then -- 4-byte ints
|
||||
if testerr("out-of-bound", os.date, "%Y", 2^40) then
|
||||
-- time_t has 4 bytes and therefore cannot represent year 4000
|
||||
print(" 4-byte time_t")
|
||||
checkerr("cannot be represented", os.time, {year=4000, month=1, day=1})
|
||||
else
|
||||
-- time_t has 8 bytes; an int year cannot represent a huge time
|
||||
print(" 8-byte time_t")
|
||||
checkerr("cannot be represented", os.date, "%Y", 2^60)
|
||||
|
||||
-- this is the maximum year
|
||||
assert(tonumber(os.time
|
||||
{year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=59}))
|
||||
|
||||
-- this is too much
|
||||
checkerr("represented", os.time,
|
||||
{year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=60})
|
||||
end
|
||||
|
||||
-- internal 'int' fields cannot hold these values
|
||||
checkerr("field 'day' is out-of-bound", os.time,
|
||||
{year = 0, month = 1, day = 2^32})
|
||||
|
||||
checkerr("field 'month' is out-of-bound", os.time,
|
||||
{year = 0, month = -((1 << 31) + 1), day = 1})
|
||||
|
||||
checkerr("field 'year' is out-of-bound", os.time,
|
||||
{year = (1 << 31) + 1900, month = 1, day = 1})
|
||||
|
||||
else -- 8-byte ints
|
||||
-- assume time_t has 8 bytes too
|
||||
print(" 8-byte time_t")
|
||||
assert(tonumber(os.date("%Y", 2^60)))
|
||||
|
||||
-- but still cannot represent a huge year
|
||||
checkerr("cannot be represented", os.time, {year=2^60, month=1, day=1})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local D = os.date("*t")
|
||||
local t = os.time(D)
|
||||
if D.isdst == nil then
|
||||
print("no daylight saving information")
|
||||
else
|
||||
assert(type(D.isdst) == 'boolean')
|
||||
end
|
||||
D.isdst = nil
|
||||
local t1 = os.time(D)
|
||||
assert(t == t1) -- if isdst is absent uses correct default
|
||||
end
|
||||
|
||||
local D = os.date("*t")
|
||||
t = os.time(D)
|
||||
D.year = D.year-1;
|
||||
local t1 = os.time(D)
|
||||
-- allow for leap years
|
||||
assert(math.abs(os.difftime(t,t1)/(24*3600) - 365) < 2)
|
||||
|
||||
-- should not take more than 1 second to execute these two lines
|
||||
t = os.time()
|
||||
t1 = os.time(os.date("*t"))
|
||||
local diff = os.difftime(t1,t)
|
||||
assert(0 <= diff and diff <= 1)
|
||||
diff = os.difftime(t,t1)
|
||||
assert(-1 <= diff and diff <= 0)
|
||||
|
||||
local t1 = os.time{year=2000, month=10, day=1, hour=23, min=12}
|
||||
local t2 = os.time{year=2000, month=10, day=1, hour=23, min=10, sec=19}
|
||||
assert(os.difftime(t1,t2) == 60*2-19)
|
||||
|
||||
-- since 5.3.3, 'os.time' normalizes table fields
|
||||
t1 = {year = 2005, month = 1, day = 1, hour = 1, min = 0, sec = -3602}
|
||||
os.time(t1)
|
||||
assert(t1.day == 31 and t1.month == 12 and t1.year == 2004 and
|
||||
t1.hour == 23 and t1.min == 59 and t1.sec == 58 and
|
||||
t1.yday == 366)
|
||||
|
||||
io.output(io.stdout)
|
||||
local t = os.date('%d %m %Y %H %M %S')
|
||||
local d, m, a, h, min, s = string.match(t,
|
||||
"(%d+) (%d+) (%d+) (%d+) (%d+) (%d+)")
|
||||
d = tonumber(d)
|
||||
m = tonumber(m)
|
||||
a = tonumber(a)
|
||||
h = tonumber(h)
|
||||
min = tonumber(min)
|
||||
s = tonumber(s)
|
||||
io.write(string.format('test done on %2.2d/%2.2d/%d', d, m, a))
|
||||
io.write(string.format(', at %2.2d:%2.2d:%2.2d\n', h, min, s))
|
||||
io.write(string.format('%s\n', _VERSION))
|
||||
|
||||
|
||||
+681
@@ -0,0 +1,681 @@
|
||||
-- $Id: testes/gc.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print('testing incremental garbage collection')
|
||||
|
||||
local debug = require"debug"
|
||||
|
||||
assert(collectgarbage("isrunning"))
|
||||
|
||||
collectgarbage()
|
||||
|
||||
local oldmode = collectgarbage("incremental")
|
||||
|
||||
-- changing modes should return previous mode
|
||||
assert(collectgarbage("generational") == "incremental")
|
||||
assert(collectgarbage("generational") == "generational")
|
||||
assert(collectgarbage("incremental") == "generational")
|
||||
assert(collectgarbage("incremental") == "incremental")
|
||||
|
||||
|
||||
local function nop () end
|
||||
|
||||
local function gcinfo ()
|
||||
return collectgarbage"count" * 1024
|
||||
end
|
||||
|
||||
|
||||
-- test weird parameters to 'collectgarbage'
|
||||
do
|
||||
-- save original parameters
|
||||
local a = collectgarbage("setpause", 200)
|
||||
local b = collectgarbage("setstepmul", 200)
|
||||
local t = {0, 2, 10, 90, 500, 5000, 30000, 0x7ffffffe}
|
||||
for i = 1, #t do
|
||||
local p = t[i]
|
||||
for j = 1, #t do
|
||||
local m = t[j]
|
||||
collectgarbage("setpause", p)
|
||||
collectgarbage("setstepmul", m)
|
||||
collectgarbage("step", 0)
|
||||
collectgarbage("step", 10000)
|
||||
end
|
||||
end
|
||||
-- restore original parameters
|
||||
collectgarbage("setpause", a)
|
||||
collectgarbage("setstepmul", b)
|
||||
collectgarbage()
|
||||
end
|
||||
|
||||
|
||||
_G["while"] = 234
|
||||
|
||||
|
||||
--
|
||||
-- tests for GC activation when creating different kinds of objects
|
||||
--
|
||||
local function GC1 ()
|
||||
local u
|
||||
local b -- (above 'u' it in the stack)
|
||||
local finish = false
|
||||
u = setmetatable({}, {__gc = function () finish = true end})
|
||||
b = {34}
|
||||
repeat u = {} until finish
|
||||
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
|
||||
|
||||
finish = false; local i = 1
|
||||
u = setmetatable({}, {__gc = function () finish = true end})
|
||||
repeat i = i + 1; u = tostring(i) .. tostring(i) until finish
|
||||
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
|
||||
|
||||
finish = false
|
||||
u = setmetatable({}, {__gc = function () finish = true end})
|
||||
repeat local i; u = function () return i end until finish
|
||||
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
|
||||
end
|
||||
|
||||
local function GC2 ()
|
||||
local u
|
||||
local finish = false
|
||||
u = {setmetatable({}, {__gc = function () finish = true end})}
|
||||
local b = {34}
|
||||
repeat u = {{}} until finish
|
||||
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
|
||||
|
||||
finish = false; local i = 1
|
||||
u = {setmetatable({}, {__gc = function () finish = true end})}
|
||||
repeat i = i + 1; u = {tostring(i) .. tostring(i)} until finish
|
||||
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
|
||||
|
||||
finish = false
|
||||
u = {setmetatable({}, {__gc = function () finish = true end})}
|
||||
repeat local i; u = {function () return i end} until finish
|
||||
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
|
||||
end
|
||||
|
||||
local function GC() GC1(); GC2() end
|
||||
|
||||
|
||||
do
|
||||
print("creating many objects")
|
||||
|
||||
local limit = 5000
|
||||
|
||||
for i = 1, limit do
|
||||
local a = {}; a = nil
|
||||
end
|
||||
|
||||
local a = "a"
|
||||
|
||||
for i = 1, limit do
|
||||
a = i .. "b";
|
||||
a = string.gsub(a, '(%d%d*)', "%1 %1")
|
||||
a = "a"
|
||||
end
|
||||
|
||||
|
||||
|
||||
a = {}
|
||||
|
||||
function a:test ()
|
||||
for i = 1, limit do
|
||||
load(string.format("function temp(a) return 'a%d' end", i), "")()
|
||||
assert(temp() == string.format('a%d', i))
|
||||
end
|
||||
end
|
||||
|
||||
a:test()
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- collection of functions without locals, globals, etc.
|
||||
do local f = function () end end
|
||||
|
||||
|
||||
print("functions with errors")
|
||||
prog = [[
|
||||
do
|
||||
a = 10;
|
||||
function foo(x,y)
|
||||
a = sin(a+0.456-0.23e-12);
|
||||
return function (z) return sin(%x+z) end
|
||||
end
|
||||
local x = function (w) a=a+w; end
|
||||
end
|
||||
]]
|
||||
do
|
||||
local step = 1
|
||||
if _soft then step = 13 end
|
||||
for i=1, string.len(prog), step do
|
||||
for j=i, string.len(prog), step do
|
||||
pcall(load(string.sub(prog, i, j), ""))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
foo = nil
|
||||
print('long strings')
|
||||
x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
assert(string.len(x)==80)
|
||||
s = ''
|
||||
k = math.min(300, (math.maxinteger // 80) // 2)
|
||||
for n = 1, k do s = s..x; j=tostring(n) end
|
||||
assert(string.len(s) == k*80)
|
||||
s = string.sub(s, 1, 10000)
|
||||
s, i = string.gsub(s, '(%d%d%d%d)', '')
|
||||
assert(i==10000 // 4)
|
||||
s = nil
|
||||
x = nil
|
||||
|
||||
assert(_G["while"] == 234)
|
||||
|
||||
|
||||
--
|
||||
-- test the "size" of basic GC steps (whatever they mean...)
|
||||
--
|
||||
do
|
||||
print("steps")
|
||||
|
||||
print("steps (2)")
|
||||
|
||||
local function dosteps (siz)
|
||||
collectgarbage()
|
||||
local a = {}
|
||||
for i=1,100 do a[i] = {{}}; local b = {} end
|
||||
local x = gcinfo()
|
||||
local i = 0
|
||||
repeat -- do steps until it completes a collection cycle
|
||||
i = i+1
|
||||
until collectgarbage("step", siz)
|
||||
assert(gcinfo() < x)
|
||||
return i -- number of steps
|
||||
end
|
||||
|
||||
collectgarbage"stop"
|
||||
|
||||
if not _port then
|
||||
assert(dosteps(10) < dosteps(2))
|
||||
end
|
||||
|
||||
-- collector should do a full collection with so many steps
|
||||
assert(dosteps(20000) == 1)
|
||||
assert(collectgarbage("step", 20000) == true)
|
||||
assert(collectgarbage("step", 20000) == true)
|
||||
|
||||
assert(not collectgarbage("isrunning"))
|
||||
collectgarbage"restart"
|
||||
assert(collectgarbage("isrunning"))
|
||||
|
||||
end
|
||||
|
||||
|
||||
if not _port then
|
||||
-- test the pace of the collector
|
||||
collectgarbage(); collectgarbage()
|
||||
local x = gcinfo()
|
||||
collectgarbage"stop"
|
||||
repeat
|
||||
local a = {}
|
||||
until gcinfo() > 3 * x
|
||||
collectgarbage"restart"
|
||||
assert(collectgarbage("isrunning"))
|
||||
repeat
|
||||
local a = {}
|
||||
until gcinfo() <= x * 2
|
||||
end
|
||||
|
||||
|
||||
print("clearing tables")
|
||||
lim = 15
|
||||
a = {}
|
||||
-- fill a with `collectable' indices
|
||||
for i=1,lim do a[{}] = i end
|
||||
b = {}
|
||||
for k,v in pairs(a) do b[k]=v end
|
||||
-- remove all indices and collect them
|
||||
for n in pairs(b) do
|
||||
a[n] = undef
|
||||
assert(type(n) == 'table' and next(n) == nil)
|
||||
collectgarbage()
|
||||
end
|
||||
b = nil
|
||||
collectgarbage()
|
||||
for n in pairs(a) do error'cannot be here' end
|
||||
for i=1,lim do a[i] = i end
|
||||
for i=1,lim do assert(a[i] == i) end
|
||||
|
||||
|
||||
print('weak tables')
|
||||
a = {}; setmetatable(a, {__mode = 'k'});
|
||||
-- fill a with some `collectable' indices
|
||||
for i=1,lim do a[{}] = i end
|
||||
-- and some non-collectable ones
|
||||
for i=1,lim do a[i] = i end
|
||||
for i=1,lim do local s=string.rep('@', i); a[s] = s..'#' end
|
||||
collectgarbage()
|
||||
local i = 0
|
||||
for k,v in pairs(a) do assert(k==v or k..'#'==v); i=i+1 end
|
||||
assert(i == 2*lim)
|
||||
|
||||
a = {}; setmetatable(a, {__mode = 'v'});
|
||||
a[1] = string.rep('b', 21)
|
||||
collectgarbage()
|
||||
assert(a[1]) -- strings are *values*
|
||||
a[1] = undef
|
||||
-- fill a with some `collectable' values (in both parts of the table)
|
||||
for i=1,lim do a[i] = {} end
|
||||
for i=1,lim do a[i..'x'] = {} end
|
||||
-- and some non-collectable ones
|
||||
for i=1,lim do local t={}; a[t]=t end
|
||||
for i=1,lim do a[i+lim]=i..'x' end
|
||||
collectgarbage()
|
||||
local i = 0
|
||||
for k,v in pairs(a) do assert(k==v or k-lim..'x' == v); i=i+1 end
|
||||
assert(i == 2*lim)
|
||||
|
||||
a = {}; setmetatable(a, {__mode = 'kv'});
|
||||
local x, y, z = {}, {}, {}
|
||||
-- keep only some items
|
||||
a[1], a[2], a[3] = x, y, z
|
||||
a[string.rep('$', 11)] = string.rep('$', 11)
|
||||
-- fill a with some `collectable' values
|
||||
for i=4,lim do a[i] = {} end
|
||||
for i=1,lim do a[{}] = i end
|
||||
for i=1,lim do local t={}; a[t]=t end
|
||||
collectgarbage()
|
||||
assert(next(a) ~= nil)
|
||||
local i = 0
|
||||
for k,v in pairs(a) do
|
||||
assert((k == 1 and v == x) or
|
||||
(k == 2 and v == y) or
|
||||
(k == 3 and v == z) or k==v);
|
||||
i = i+1
|
||||
end
|
||||
assert(i == 4)
|
||||
x,y,z=nil
|
||||
collectgarbage()
|
||||
assert(next(a) == string.rep('$', 11))
|
||||
|
||||
|
||||
-- 'bug' in 5.1
|
||||
a = {}
|
||||
local t = {x = 10}
|
||||
local C = setmetatable({key = t}, {__mode = 'v'})
|
||||
local C1 = setmetatable({[t] = 1}, {__mode = 'k'})
|
||||
a.x = t -- this should not prevent 't' from being removed from
|
||||
-- weak table 'C' by the time 'a' is finalized
|
||||
|
||||
setmetatable(a, {__gc = function (u)
|
||||
assert(C.key == nil)
|
||||
assert(type(next(C1)) == 'table')
|
||||
end})
|
||||
|
||||
a, t = nil
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
assert(next(C) == nil and next(C1) == nil)
|
||||
C, C1 = nil
|
||||
|
||||
|
||||
-- ephemerons
|
||||
local mt = {__mode = 'k'}
|
||||
a = {{10},{20},{30},{40}}; setmetatable(a, mt)
|
||||
x = nil
|
||||
for i = 1, 100 do local n = {}; a[n] = {k = {x}}; x = n end
|
||||
GC()
|
||||
local n = x
|
||||
local i = 0
|
||||
while n do n = a[n].k[1]; i = i + 1 end
|
||||
assert(i == 100)
|
||||
x = nil
|
||||
GC()
|
||||
for i = 1, 4 do assert(a[i][1] == i * 10); a[i] = undef end
|
||||
assert(next(a) == nil)
|
||||
|
||||
local K = {}
|
||||
a[K] = {}
|
||||
for i=1,10 do a[K][i] = {}; a[a[K][i]] = setmetatable({}, mt) end
|
||||
x = nil
|
||||
local k = 1
|
||||
for j = 1,100 do
|
||||
local n = {}; local nk = k%10 + 1
|
||||
a[a[K][nk]][n] = {x, k = k}; x = n; k = nk
|
||||
end
|
||||
GC()
|
||||
local n = x
|
||||
local i = 0
|
||||
while n do local t = a[a[K][k]][n]; n = t[1]; k = t.k; i = i + 1 end
|
||||
assert(i == 100)
|
||||
K = nil
|
||||
GC()
|
||||
-- assert(next(a) == nil)
|
||||
|
||||
|
||||
-- testing errors during GC
|
||||
if T then
|
||||
collectgarbage("stop") -- stop collection
|
||||
local u = {}
|
||||
local s = {}; setmetatable(s, {__mode = 'k'})
|
||||
setmetatable(u, {__gc = function (o)
|
||||
local i = s[o]
|
||||
s[i] = true
|
||||
assert(not s[i - 1]) -- check proper finalization order
|
||||
if i == 8 then error("@expected@") end -- error during GC
|
||||
end})
|
||||
|
||||
for i = 6, 10 do
|
||||
local n = setmetatable({}, getmetatable(u))
|
||||
s[n] = i
|
||||
end
|
||||
|
||||
warn("@on"); warn("@store")
|
||||
collectgarbage()
|
||||
assert(string.find(_WARN, "error in __gc metamethod"))
|
||||
assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = nil
|
||||
for i = 8, 10 do assert(s[i]) end
|
||||
|
||||
for i = 1, 5 do
|
||||
local n = setmetatable({}, getmetatable(u))
|
||||
s[n] = i
|
||||
end
|
||||
|
||||
collectgarbage()
|
||||
for i = 1, 10 do assert(s[i]) end
|
||||
|
||||
getmetatable(u).__gc = nil
|
||||
warn("@normal")
|
||||
|
||||
end
|
||||
print '+'
|
||||
|
||||
|
||||
-- testing userdata
|
||||
if T==nil then
|
||||
(Message or print)('\n >>> testC not active: skipping userdata GC tests <<<\n')
|
||||
|
||||
else
|
||||
|
||||
local function newproxy(u)
|
||||
return debug.setmetatable(T.newuserdata(0), debug.getmetatable(u))
|
||||
end
|
||||
|
||||
collectgarbage("stop") -- stop collection
|
||||
local u = newproxy(nil)
|
||||
debug.setmetatable(u, {__gc = true})
|
||||
local s = 0
|
||||
local a = {[u] = 0}; setmetatable(a, {__mode = 'vk'})
|
||||
for i=1,10 do a[newproxy(u)] = i end
|
||||
for k in pairs(a) do assert(getmetatable(k) == getmetatable(u)) end
|
||||
local a1 = {}; for k,v in pairs(a) do a1[k] = v end
|
||||
for k,v in pairs(a1) do a[v] = k end
|
||||
for i =1,10 do assert(a[i]) end
|
||||
getmetatable(u).a = a1
|
||||
getmetatable(u).u = u
|
||||
do
|
||||
local u = u
|
||||
getmetatable(u).__gc = function (o)
|
||||
assert(a[o] == 10-s)
|
||||
assert(a[10-s] == undef) -- udata already removed from weak table
|
||||
assert(getmetatable(o) == getmetatable(u))
|
||||
assert(getmetatable(o).a[o] == 10-s)
|
||||
s=s+1
|
||||
end
|
||||
end
|
||||
a1, u = nil
|
||||
assert(next(a) ~= nil)
|
||||
collectgarbage()
|
||||
assert(s==11)
|
||||
collectgarbage()
|
||||
assert(next(a) == nil) -- finalized keys are removed in two cycles
|
||||
end
|
||||
|
||||
|
||||
-- __gc x weak tables
|
||||
local u = setmetatable({}, {__gc = true})
|
||||
-- __gc metamethod should be collected before running
|
||||
setmetatable(getmetatable(u), {__mode = "v"})
|
||||
getmetatable(u).__gc = function (o) os.exit(1) end -- cannot happen
|
||||
u = nil
|
||||
collectgarbage()
|
||||
|
||||
local u = setmetatable({}, {__gc = true})
|
||||
local m = getmetatable(u)
|
||||
m.x = {[{0}] = 1; [0] = {1}}; setmetatable(m.x, {__mode = "kv"});
|
||||
m.__gc = function (o)
|
||||
assert(next(getmetatable(o).x) == nil)
|
||||
m = 10
|
||||
end
|
||||
u, m = nil
|
||||
collectgarbage()
|
||||
assert(m==10)
|
||||
|
||||
do -- tests for string keys in weak tables
|
||||
collectgarbage(); collectgarbage()
|
||||
local m = collectgarbage("count") -- current memory
|
||||
local a = setmetatable({}, {__mode = "kv"})
|
||||
a[string.rep("a", 2^22)] = 25 -- long string key -> number value
|
||||
a[string.rep("b", 2^22)] = {} -- long string key -> colectable value
|
||||
a[{}] = 14 -- colectable key
|
||||
assert(collectgarbage("count") > m + 2^13) -- 2^13 == 2 * 2^22 in KB
|
||||
collectgarbage()
|
||||
assert(collectgarbage("count") >= m + 2^12 and
|
||||
collectgarbage("count") < m + 2^13) -- one key was collected
|
||||
local k, v = next(a) -- string key with number value preserved
|
||||
assert(k == string.rep("a", 2^22) and v == 25)
|
||||
assert(next(a, k) == nil) -- everything else cleared
|
||||
assert(a[string.rep("b", 2^22)] == undef)
|
||||
a[k] = undef -- erase this last entry
|
||||
k = nil
|
||||
collectgarbage()
|
||||
assert(next(a) == nil)
|
||||
-- make sure will not try to compare with dead key
|
||||
assert(a[string.rep("b", 100)] == undef)
|
||||
assert(collectgarbage("count") <= m + 1) -- eveything collected
|
||||
end
|
||||
|
||||
|
||||
-- errors during collection
|
||||
if T then
|
||||
warn("@store")
|
||||
u = setmetatable({}, {__gc = function () error "@expected error" end})
|
||||
u = nil
|
||||
collectgarbage()
|
||||
assert(string.find(_WARN, "@expected error")); _WARN = nil
|
||||
warn("@normal")
|
||||
end
|
||||
|
||||
|
||||
if not _soft then
|
||||
print("long list")
|
||||
local a = {}
|
||||
for i = 1,200000 do
|
||||
a = {next = a}
|
||||
end
|
||||
a = nil
|
||||
collectgarbage()
|
||||
end
|
||||
|
||||
-- create many threads with self-references and open upvalues
|
||||
print("self-referenced threads")
|
||||
local thread_id = 0
|
||||
local threads = {}
|
||||
|
||||
local function fn (thread)
|
||||
local x = {}
|
||||
threads[thread_id] = function()
|
||||
thread = x
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
while thread_id < 1000 do
|
||||
local thread = coroutine.create(fn)
|
||||
coroutine.resume(thread, thread)
|
||||
thread_id = thread_id + 1
|
||||
end
|
||||
|
||||
|
||||
-- Create a closure (function inside 'f') with an upvalue ('param') that
|
||||
-- points (through a table) to the closure itself and to the thread
|
||||
-- ('co' and the initial value of 'param') where closure is running.
|
||||
-- Then, assert that table (and therefore everything else) will be
|
||||
-- collected.
|
||||
do
|
||||
local collected = false -- to detect collection
|
||||
collectgarbage(); collectgarbage("stop")
|
||||
do
|
||||
local function f (param)
|
||||
;(function ()
|
||||
assert(type(f) == 'function' and type(param) == 'thread')
|
||||
param = {param, f}
|
||||
setmetatable(param, {__gc = function () collected = true end})
|
||||
coroutine.yield(100)
|
||||
end)()
|
||||
end
|
||||
local co = coroutine.create(f)
|
||||
assert(coroutine.resume(co, co))
|
||||
end
|
||||
-- Now, thread and closure are not reacheable any more.
|
||||
collectgarbage()
|
||||
assert(collected)
|
||||
collectgarbage("restart")
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
collectgarbage()
|
||||
collectgarbage"stop"
|
||||
collectgarbage("step", 0) -- steps should not unblock the collector
|
||||
local x = gcinfo()
|
||||
repeat
|
||||
for i=1,1000 do _ENV.a = {} end -- no collection during the loop
|
||||
until gcinfo() > 2 * x
|
||||
collectgarbage"restart"
|
||||
end
|
||||
|
||||
|
||||
if T then -- tests for weird cases collecting upvalues
|
||||
|
||||
local function foo ()
|
||||
local a = {x = 20}
|
||||
coroutine.yield(function () return a.x end) -- will run collector
|
||||
assert(a.x == 20) -- 'a' is 'ok'
|
||||
a = {x = 30} -- create a new object
|
||||
assert(T.gccolor(a) == "white") -- of course it is new...
|
||||
coroutine.yield(100) -- 'a' is still local to this thread
|
||||
end
|
||||
|
||||
local t = setmetatable({}, {__mode = "kv"})
|
||||
collectgarbage(); collectgarbage('stop')
|
||||
-- create coroutine in a weak table, so it will never be marked
|
||||
t.co = coroutine.wrap(foo)
|
||||
local f = t.co() -- create function to access local 'a'
|
||||
T.gcstate("atomic") -- ensure all objects are traversed
|
||||
assert(T.gcstate() == "atomic")
|
||||
assert(t.co() == 100) -- resume coroutine, creating new table for 'a'
|
||||
assert(T.gccolor(t.co) == "white") -- thread was not traversed
|
||||
T.gcstate("pause") -- collect thread, but should mark 'a' before that
|
||||
assert(t.co == nil and f() == 30) -- ensure correct access to 'a'
|
||||
|
||||
collectgarbage("restart")
|
||||
|
||||
-- test barrier in sweep phase (backing userdata to gray)
|
||||
local u = T.newuserdata(0, 1) -- create a userdata
|
||||
collectgarbage()
|
||||
collectgarbage"stop"
|
||||
local a = {} -- avoid 'u' as first element in 'allgc'
|
||||
T.gcstate"atomic"
|
||||
T.gcstate"sweepallgc"
|
||||
local x = {}
|
||||
assert(T.gccolor(u) == "black") -- userdata is "old" (black)
|
||||
assert(T.gccolor(x) == "white") -- table is "new" (white)
|
||||
debug.setuservalue(u, x) -- trigger barrier
|
||||
assert(T.gccolor(u) == "gray") -- userdata changed back to gray
|
||||
collectgarbage"restart"
|
||||
|
||||
print"+"
|
||||
end
|
||||
|
||||
|
||||
if T then
|
||||
local debug = require "debug"
|
||||
collectgarbage("stop")
|
||||
local x = T.newuserdata(0)
|
||||
local y = T.newuserdata(0)
|
||||
debug.setmetatable(y, {__gc = nop}) -- bless the new udata before...
|
||||
debug.setmetatable(x, {__gc = nop}) -- ...the old one
|
||||
assert(T.gccolor(y) == "white")
|
||||
T.checkmemory()
|
||||
collectgarbage("restart")
|
||||
end
|
||||
|
||||
|
||||
if T then
|
||||
print("emergency collections")
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
T.totalmem(T.totalmem() + 200)
|
||||
for i=1,200 do local a = {} end
|
||||
T.totalmem(0)
|
||||
collectgarbage()
|
||||
local t = T.totalmem("table")
|
||||
local a = {{}, {}, {}} -- create 4 new tables
|
||||
assert(T.totalmem("table") == t + 4)
|
||||
t = T.totalmem("function")
|
||||
a = function () end -- create 1 new closure
|
||||
assert(T.totalmem("function") == t + 1)
|
||||
t = T.totalmem("thread")
|
||||
a = coroutine.create(function () end) -- create 1 new coroutine
|
||||
assert(T.totalmem("thread") == t + 1)
|
||||
end
|
||||
|
||||
|
||||
-- create an object to be collected when state is closed
|
||||
do
|
||||
local setmetatable,assert,type,print,getmetatable =
|
||||
setmetatable,assert,type,print,getmetatable
|
||||
local tt = {}
|
||||
tt.__gc = function (o)
|
||||
assert(getmetatable(o) == tt)
|
||||
-- create new objects during GC
|
||||
local a = 'xuxu'..(10+3)..'joao', {}
|
||||
___Glob = o -- ressurect object!
|
||||
setmetatable({}, tt) -- creates a new one with same metatable
|
||||
print(">>> closing state " .. "<<<\n")
|
||||
end
|
||||
local u = setmetatable({}, tt)
|
||||
___Glob = {u} -- avoid object being collected before program end
|
||||
end
|
||||
|
||||
-- create several objects to raise errors when collected while closing state
|
||||
if T then
|
||||
local error, assert, find, warn = error, assert, string.find, warn
|
||||
local n = 0
|
||||
local lastmsg
|
||||
local mt = {__gc = function (o)
|
||||
n = n + 1
|
||||
assert(n == o[1])
|
||||
if n == 1 then
|
||||
_WARN = nil
|
||||
elseif n == 2 then
|
||||
assert(find(_WARN, "@expected warning"))
|
||||
lastmsg = _WARN -- get message from previous error (first 'o')
|
||||
else
|
||||
assert(lastmsg == _WARN) -- subsequent error messages are equal
|
||||
end
|
||||
warn("@store"); _WARN = nil
|
||||
error"@expected warning"
|
||||
end}
|
||||
for i = 10, 1, -1 do
|
||||
-- create object and preserve it until the end
|
||||
table.insert(___Glob, setmetatable({i}, mt))
|
||||
end
|
||||
end
|
||||
|
||||
-- just to make sure
|
||||
assert(collectgarbage'isrunning')
|
||||
|
||||
collectgarbage(oldmode)
|
||||
|
||||
print('OK')
|
||||
@@ -0,0 +1,83 @@
|
||||
-- $Id: testes/gengc.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print('testing generational garbage collection')
|
||||
|
||||
local debug = require"debug"
|
||||
|
||||
assert(collectgarbage("isrunning"))
|
||||
|
||||
collectgarbage()
|
||||
|
||||
local oldmode = collectgarbage("generational")
|
||||
|
||||
|
||||
-- ensure that table barrier evolves correctly
|
||||
do
|
||||
local U = {}
|
||||
-- full collection makes 'U' old
|
||||
collectgarbage()
|
||||
assert(not T or T.gcage(U) == "old")
|
||||
|
||||
-- U refers to a new table, so it becomes 'touched1'
|
||||
U[1] = {x = {234}}
|
||||
assert(not T or (T.gcage(U) == "touched1" and T.gcage(U[1]) == "new"))
|
||||
|
||||
-- both U and the table survive one more collection
|
||||
collectgarbage("step", 0)
|
||||
assert(not T or (T.gcage(U) == "touched2" and T.gcage(U[1]) == "survival"))
|
||||
|
||||
-- both U and the table survive yet another collection
|
||||
-- now everything is old
|
||||
collectgarbage("step", 0)
|
||||
assert(not T or (T.gcage(U) == "old" and T.gcage(U[1]) == "old1"))
|
||||
|
||||
-- data was not corrupted
|
||||
assert(U[1].x[1] == 234)
|
||||
end
|
||||
|
||||
|
||||
if T == nil then
|
||||
(Message or print)('\n >>> testC not active: \z
|
||||
skipping some generational tests <<<\n')
|
||||
print 'OK'
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- ensure that userdata barrier evolves correctly
|
||||
do
|
||||
local U = T.newuserdata(0, 1)
|
||||
-- full collection makes 'U' old
|
||||
collectgarbage()
|
||||
assert(T.gcage(U) == "old")
|
||||
|
||||
-- U refers to a new table, so it becomes 'touched1'
|
||||
debug.setuservalue(U, {x = {234}})
|
||||
assert(T.gcage(U) == "touched1" and
|
||||
T.gcage(debug.getuservalue(U)) == "new")
|
||||
|
||||
-- both U and the table survive one more collection
|
||||
collectgarbage("step", 0)
|
||||
assert(T.gcage(U) == "touched2" and
|
||||
T.gcage(debug.getuservalue(U)) == "survival")
|
||||
|
||||
-- both U and the table survive yet another collection
|
||||
-- now everything is old
|
||||
collectgarbage("step", 0)
|
||||
assert(T.gcage(U) == "old" and
|
||||
T.gcage(debug.getuservalue(U)) == "old1")
|
||||
|
||||
-- data was not corrupted
|
||||
assert(debug.getuservalue(U).x[1] == 234)
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- just to make sure
|
||||
assert(collectgarbage'isrunning')
|
||||
|
||||
collectgarbage(oldmode)
|
||||
|
||||
print('OK')
|
||||
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
-- $Id: testes/goto.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
collectgarbage()
|
||||
|
||||
local function errmsg (code, m)
|
||||
local st, msg = load(code)
|
||||
assert(not st and string.find(msg, m))
|
||||
end
|
||||
|
||||
-- cannot see label inside block
|
||||
errmsg([[ goto l1; do ::l1:: end ]], "label 'l1'")
|
||||
errmsg([[ do ::l1:: end goto l1; ]], "label 'l1'")
|
||||
|
||||
-- repeated label
|
||||
errmsg([[ ::l1:: ::l1:: ]], "label 'l1'")
|
||||
errmsg([[ ::l1:: do ::l1:: end]], "label 'l1'")
|
||||
|
||||
|
||||
-- undefined label
|
||||
errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "local 'aa'")
|
||||
|
||||
-- jumping over variable definition
|
||||
errmsg([[
|
||||
do local bb, cc; goto l1; end
|
||||
local aa
|
||||
::l1:: print(3)
|
||||
]], "local 'aa'")
|
||||
|
||||
-- jumping into a block
|
||||
errmsg([[ do ::l1:: end goto l1 ]], "label 'l1'")
|
||||
errmsg([[ goto l1 do ::l1:: end ]], "label 'l1'")
|
||||
|
||||
-- cannot continue a repeat-until with variables
|
||||
errmsg([[
|
||||
repeat
|
||||
if x then goto cont end
|
||||
local xuxu = 10
|
||||
::cont::
|
||||
until xuxu < x
|
||||
]], "local 'xuxu'")
|
||||
|
||||
-- simple gotos
|
||||
local x
|
||||
do
|
||||
local y = 12
|
||||
goto l1
|
||||
::l2:: x = x + 1; goto l3
|
||||
::l1:: x = y; goto l2
|
||||
end
|
||||
::l3:: ::l3_1:: assert(x == 13)
|
||||
|
||||
|
||||
-- long labels
|
||||
do
|
||||
local prog = [[
|
||||
do
|
||||
local a = 1
|
||||
goto l%sa; a = a + 1
|
||||
::l%sa:: a = a + 10
|
||||
goto l%sb; a = a + 2
|
||||
::l%sb:: a = a + 20
|
||||
return a
|
||||
end
|
||||
]]
|
||||
local label = string.rep("0123456789", 40)
|
||||
prog = string.format(prog, label, label, label, label)
|
||||
assert(assert(load(prog))() == 31)
|
||||
end
|
||||
|
||||
|
||||
-- ok to jump over local dec. to end of block
|
||||
do
|
||||
goto l1
|
||||
local a = 23
|
||||
x = a
|
||||
::l1::;
|
||||
end
|
||||
|
||||
while true do
|
||||
goto l4
|
||||
goto l1 -- ok to jump over local dec. to end of block
|
||||
goto l1 -- multiple uses of same label
|
||||
local x = 45
|
||||
::l1:: ;;;
|
||||
end
|
||||
::l4:: assert(x == 13)
|
||||
|
||||
if print then
|
||||
goto l1 -- ok to jump over local dec. to end of block
|
||||
error("should not be here")
|
||||
goto l2 -- ok to jump over local dec. to end of block
|
||||
local x
|
||||
::l1:: ; ::l2:: ;;
|
||||
else end
|
||||
|
||||
-- to repeat a label in a different function is OK
|
||||
local function foo ()
|
||||
local a = {}
|
||||
goto l3
|
||||
::l1:: a[#a + 1] = 1; goto l2;
|
||||
::l2:: a[#a + 1] = 2; goto l5;
|
||||
::l3::
|
||||
::l3a:: a[#a + 1] = 3; goto l1;
|
||||
::l4:: a[#a + 1] = 4; goto l6;
|
||||
::l5:: a[#a + 1] = 5; goto l4;
|
||||
::l6:: assert(a[1] == 3 and a[2] == 1 and a[3] == 2 and
|
||||
a[4] == 5 and a[5] == 4)
|
||||
if not a[6] then a[6] = true; goto l3a end -- do it twice
|
||||
end
|
||||
|
||||
::l6:: foo()
|
||||
|
||||
|
||||
do -- bug in 5.2 -> 5.3.2
|
||||
local x
|
||||
::L1::
|
||||
local y -- cannot join this SETNIL with previous one
|
||||
assert(y == nil)
|
||||
y = true
|
||||
if x == nil then
|
||||
x = 1
|
||||
goto L1
|
||||
else
|
||||
x = x + 1
|
||||
end
|
||||
assert(x == 2 and y == true)
|
||||
end
|
||||
|
||||
-- bug in 5.3
|
||||
do
|
||||
local first = true
|
||||
local a = false
|
||||
if true then
|
||||
goto LBL
|
||||
::loop::
|
||||
a = true
|
||||
::LBL::
|
||||
if first then
|
||||
first = false
|
||||
goto loop
|
||||
end
|
||||
end
|
||||
assert(a)
|
||||
end
|
||||
|
||||
do -- compiling infinite loops
|
||||
goto escape -- do not run the infinite loops
|
||||
::a:: goto a
|
||||
::b:: goto c
|
||||
::c:: goto b
|
||||
end
|
||||
::escape::
|
||||
--------------------------------------------------------------------------------
|
||||
-- testing closing of upvalues
|
||||
|
||||
local debug = require 'debug'
|
||||
|
||||
local function foo ()
|
||||
local t = {}
|
||||
do
|
||||
local i = 1
|
||||
local a, b, c, d
|
||||
t[1] = function () return a, b, c, d end
|
||||
::l1::
|
||||
local b
|
||||
do
|
||||
local c
|
||||
t[#t + 1] = function () return a, b, c, d end -- t[2], t[4], t[6]
|
||||
if i > 2 then goto l2 end
|
||||
do
|
||||
local d
|
||||
t[#t + 1] = function () return a, b, c, d end -- t[3], t[5]
|
||||
i = i + 1
|
||||
local a
|
||||
goto l1
|
||||
end
|
||||
end
|
||||
end
|
||||
::l2:: return t
|
||||
end
|
||||
|
||||
local a = foo()
|
||||
assert(#a == 6)
|
||||
|
||||
-- all functions share same 'a'
|
||||
for i = 2, 6 do
|
||||
assert(debug.upvalueid(a[1], 1) == debug.upvalueid(a[i], 1))
|
||||
end
|
||||
|
||||
-- 'b' and 'c' are shared among some of them
|
||||
for i = 2, 6 do
|
||||
-- only a[1] uses external 'b'/'b'
|
||||
assert(debug.upvalueid(a[1], 2) ~= debug.upvalueid(a[i], 2))
|
||||
assert(debug.upvalueid(a[1], 3) ~= debug.upvalueid(a[i], 3))
|
||||
end
|
||||
|
||||
for i = 3, 5, 2 do
|
||||
-- inner functions share 'b'/'c' with previous ones
|
||||
assert(debug.upvalueid(a[i], 2) == debug.upvalueid(a[i - 1], 2))
|
||||
assert(debug.upvalueid(a[i], 3) == debug.upvalueid(a[i - 1], 3))
|
||||
-- but not with next ones
|
||||
assert(debug.upvalueid(a[i], 2) ~= debug.upvalueid(a[i + 1], 2))
|
||||
assert(debug.upvalueid(a[i], 3) ~= debug.upvalueid(a[i + 1], 3))
|
||||
end
|
||||
|
||||
-- only external 'd' is shared
|
||||
for i = 2, 6, 2 do
|
||||
assert(debug.upvalueid(a[1], 4) == debug.upvalueid(a[i], 4))
|
||||
end
|
||||
|
||||
-- internal 'd's are all different
|
||||
for i = 3, 5, 2 do
|
||||
for j = 1, 6 do
|
||||
assert((debug.upvalueid(a[i], 4) == debug.upvalueid(a[j], 4))
|
||||
== (i == j))
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- testing if x goto optimizations
|
||||
|
||||
local function testG (a)
|
||||
if a == 1 then
|
||||
goto l1
|
||||
error("should never be here!")
|
||||
elseif a == 2 then goto l2
|
||||
elseif a == 3 then goto l3
|
||||
elseif a == 4 then
|
||||
goto l1 -- go to inside the block
|
||||
error("should never be here!")
|
||||
::l1:: a = a + 1 -- must go to 'if' end
|
||||
else
|
||||
goto l4
|
||||
::l4a:: a = a * 2; goto l4b
|
||||
error("should never be here!")
|
||||
::l4:: goto l4a
|
||||
error("should never be here!")
|
||||
::l4b::
|
||||
end
|
||||
do return a end
|
||||
::l2:: do return "2" end
|
||||
::l3:: do return "3" end
|
||||
::l1:: return "1"
|
||||
end
|
||||
|
||||
assert(testG(1) == "1")
|
||||
assert(testG(2) == "2")
|
||||
assert(testG(3) == "3")
|
||||
assert(testG(4) == 5)
|
||||
assert(testG(5) == 10)
|
||||
|
||||
do
|
||||
-- if x back goto out of scope of upvalue
|
||||
local X
|
||||
goto L1
|
||||
|
||||
::L2:: goto L3
|
||||
|
||||
::L1:: do
|
||||
local a <close> = setmetatable({}, {__close = function () X = true end})
|
||||
assert(X == nil)
|
||||
if a then goto L2 end -- jumping back out of scope of 'a'
|
||||
end
|
||||
|
||||
::L3:: assert(X == true) -- checks that 'a' was correctly closed
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
print'OK'
|
||||
@@ -0,0 +1,173 @@
|
||||
-- $Id: heavy.lua,v 1.7 2017/12/29 15:42:15 roberto Exp $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
local function teststring ()
|
||||
print("creating a string too long")
|
||||
do
|
||||
local a = "x"
|
||||
local st, msg = pcall(function ()
|
||||
while true do
|
||||
a = a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
|
||||
print(string.format("string with %d bytes", #a))
|
||||
end
|
||||
end)
|
||||
assert(not st and
|
||||
(string.find(msg, "string length overflow") or
|
||||
string.find(msg, "not enough memory")))
|
||||
print("string length overflow with " .. #a * 100)
|
||||
end
|
||||
print('+')
|
||||
end
|
||||
|
||||
local function loadrep (x, what)
|
||||
local p = 1<<20
|
||||
local s = string.rep(x, p)
|
||||
local count = 0
|
||||
local function f()
|
||||
count = count + p
|
||||
if count % (0x80*p) == 0 then
|
||||
io.stderr:write("(", count // 2^20, " M)")
|
||||
end
|
||||
return s
|
||||
end
|
||||
local st, msg = load(f, "=big")
|
||||
print("\nmemory: ", collectgarbage'count' * 1024)
|
||||
msg = string.match(msg, "^[^\n]+") -- get only first line
|
||||
print(string.format("total: 0x%x %s ('%s')", count, what, msg))
|
||||
return st, msg
|
||||
end
|
||||
|
||||
|
||||
function controlstruct ()
|
||||
print("control structure too long")
|
||||
local lim = ((1 << 24) - 2) // 3
|
||||
local s = string.rep("a = a + 1\n", lim)
|
||||
s = "while true do " .. s .. "end"
|
||||
assert(load(s))
|
||||
print("ok with " .. lim .. " lines")
|
||||
lim = lim + 3
|
||||
s = string.rep("a = a + 1\n", lim)
|
||||
s = "while true do " .. s .. "end"
|
||||
local st, msg = load(s)
|
||||
assert(not st and string.find(msg, "too long"))
|
||||
print(msg)
|
||||
end
|
||||
|
||||
|
||||
function manylines ()
|
||||
print("loading chunk with too many lines")
|
||||
local st, msg = loadrep("\n", "lines")
|
||||
assert(not st and string.find(msg, "too many lines"))
|
||||
print('+')
|
||||
end
|
||||
|
||||
|
||||
function hugeid ()
|
||||
print("loading chunk with huge identifier")
|
||||
local st, msg = loadrep("a", "chars")
|
||||
assert(not st and
|
||||
(string.find(msg, "lexical element too long") or
|
||||
string.find(msg, "not enough memory")))
|
||||
print('+')
|
||||
end
|
||||
|
||||
function toomanyinst ()
|
||||
print("loading chunk with too many instructions")
|
||||
local st, msg = loadrep("a = 10; ", "instructions")
|
||||
print('+')
|
||||
end
|
||||
|
||||
|
||||
local function loadrepfunc (prefix, f)
|
||||
local count = -1
|
||||
local function aux ()
|
||||
count = count + 1
|
||||
if count == 0 then
|
||||
return prefix
|
||||
else
|
||||
if count % (0x100000) == 0 then
|
||||
io.stderr:write("(", count // 2^20, " M)")
|
||||
end
|
||||
return f(count)
|
||||
end
|
||||
end
|
||||
local st, msg = load(aux, "k")
|
||||
print("\nmemory: ", collectgarbage'count' * 1024)
|
||||
msg = string.match(msg, "^[^\n]+") -- get only first line
|
||||
print("expected error: ", msg)
|
||||
end
|
||||
|
||||
|
||||
function toomanyconst ()
|
||||
print("loading function with too many constants")
|
||||
loadrepfunc("function foo () return {0,",
|
||||
function (n)
|
||||
-- convert 'n' to a string in the format [["...",]],
|
||||
-- where '...' is a kind of number in base 128
|
||||
-- (in a range that does not include either the double quote
|
||||
-- and the escape.)
|
||||
return string.char(34,
|
||||
((n // 128^0) & 127) + 128,
|
||||
((n // 128^1) & 127) + 128,
|
||||
((n // 128^2) & 127) + 128,
|
||||
((n // 128^3) & 127) + 128,
|
||||
((n // 128^4) & 127) + 128,
|
||||
34, 44)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function toomanystr ()
|
||||
local a = {}
|
||||
local st, msg = pcall(function ()
|
||||
for i = 1, math.huge do
|
||||
if i % (0x100000) == 0 then
|
||||
io.stderr:write("(", i // 2^20, " M)")
|
||||
end
|
||||
a[i] = string.pack("I", i)
|
||||
end
|
||||
end)
|
||||
local size = #a
|
||||
a = collectgarbage'count'
|
||||
print("\nmemory:", a * 1024)
|
||||
print("expected error:", msg)
|
||||
print("size:", size)
|
||||
end
|
||||
|
||||
|
||||
function toomanyidx ()
|
||||
local a = {}
|
||||
local st, msg = pcall(function ()
|
||||
for i = 1, math.huge do
|
||||
if i % (0x100000) == 0 then
|
||||
io.stderr:write("(", i // 2^20, " M)")
|
||||
end
|
||||
a[i] = i
|
||||
end
|
||||
end)
|
||||
print("\nmemory: ", collectgarbage'count' * 1024)
|
||||
print("expected error: ", msg)
|
||||
print("size:", #a)
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- teststring()
|
||||
-- controlstruct()
|
||||
-- manylines()
|
||||
-- hugeid()
|
||||
-- toomanyinst()
|
||||
-- toomanyconst()
|
||||
-- toomanystr()
|
||||
toomanyidx()
|
||||
|
||||
print "OK"
|
||||
@@ -0,0 +1,2 @@
|
||||
# This is a dummy file just to make git keep the otherwise empty
|
||||
# directory 'P1' in the repository.
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
static int id (lua_State *L) {
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
|
||||
static const struct luaL_Reg funcs[] = {
|
||||
{"id", id},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
/* function used by lib11.c */
|
||||
LUAMOD_API int lib1_export (lua_State *L) {
|
||||
lua_pushstring(L, "exported");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
LUAMOD_API int onefunction (lua_State *L) {
|
||||
luaL_checkversion(L);
|
||||
lua_settop(L, 2);
|
||||
lua_pushvalue(L, 1);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
LUAMOD_API int anotherfunc (lua_State *L) {
|
||||
luaL_checkversion(L);
|
||||
lua_pushfstring(L, "%d%%%d\n", (int)lua_tointeger(L, 1),
|
||||
(int)lua_tointeger(L, 2));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
LUAMOD_API int luaopen_lib1_sub (lua_State *L) {
|
||||
lua_setglobal(L, "y"); /* 2nd arg: extra value (file name) */
|
||||
lua_setglobal(L, "x"); /* 1st arg: module name */
|
||||
luaL_newlib(L, funcs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "lua.h"
|
||||
|
||||
/* function from lib1.c */
|
||||
int lib1_export (lua_State *L);
|
||||
|
||||
LUAMOD_API int luaopen_lib11 (lua_State *L) {
|
||||
return lib1_export(L);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
static int id (lua_State *L) {
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
|
||||
static const struct luaL_Reg funcs[] = {
|
||||
{"id", id},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUAMOD_API int luaopen_lib2 (lua_State *L) {
|
||||
lua_settop(L, 2);
|
||||
lua_setglobal(L, "y"); /* y gets 2nd parameter */
|
||||
lua_setglobal(L, "x"); /* x gets 1st parameter */
|
||||
luaL_newlib(L, funcs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
int luaopen_lib2 (lua_State *L);
|
||||
|
||||
LUAMOD_API int luaopen_lib21 (lua_State *L) {
|
||||
return luaopen_lib2(L);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
static int id (lua_State *L) {
|
||||
lua_pushboolean(L, 1);
|
||||
lua_insert(L, 1);
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
|
||||
static const struct luaL_Reg funcs[] = {
|
||||
{"id", id},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUAMOD_API int luaopen_lib2 (lua_State *L) {
|
||||
lua_settop(L, 2);
|
||||
lua_setglobal(L, "y"); /* y gets 2nd parameter */
|
||||
lua_setglobal(L, "x"); /* x gets 1st parameter */
|
||||
luaL_newlib(L, funcs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# change this variable to point to the directory with Lua headers
|
||||
# of the version being tested
|
||||
LUA_DIR = ../../
|
||||
|
||||
CC = gcc
|
||||
|
||||
# compilation should generate Dynamic-Link Libraries
|
||||
CFLAGS = -Wall -std=gnu99 -O2 -I$(LUA_DIR) -fPIC -shared
|
||||
|
||||
# libraries used by the tests
|
||||
all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so
|
||||
touch all
|
||||
|
||||
lib1.so: lib1.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib1.so lib1.c
|
||||
|
||||
lib11.so: lib11.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib11.so lib11.c
|
||||
|
||||
lib2.so: lib2.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib2.so lib2.c
|
||||
|
||||
lib21.so: lib21.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib21.so lib21.c
|
||||
|
||||
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib2-v2.so lib22.c
|
||||
@@ -0,0 +1,318 @@
|
||||
-- $Id: testes/literals.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print('testing scanner')
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
|
||||
local function dostring (x) return assert(load(x), "")() end
|
||||
|
||||
dostring("x \v\f = \t\r 'a\0a' \v\f\f")
|
||||
assert(x == 'a\0a' and string.len(x) == 3)
|
||||
|
||||
-- escape sequences
|
||||
assert('\n\"\'\\' == [[
|
||||
|
||||
"'\]])
|
||||
|
||||
assert(string.find("\a\b\f\n\r\t\v", "^%c%c%c%c%c%c%c$"))
|
||||
|
||||
-- assume ASCII just for tests:
|
||||
assert("\09912" == 'c12')
|
||||
assert("\99ab" == 'cab')
|
||||
assert("\099" == '\99')
|
||||
assert("\099\n" == 'c\10')
|
||||
assert('\0\0\0alo' == '\0' .. '\0\0' .. 'alo')
|
||||
|
||||
assert(010 .. 020 .. -030 == "1020-30")
|
||||
|
||||
-- hexadecimal escapes
|
||||
assert("\x00\x05\x10\x1f\x3C\xfF\xe8" == "\0\5\16\31\60\255\232")
|
||||
|
||||
local function lexstring (x, y, n)
|
||||
local f = assert(load('return ' .. x ..
|
||||
', require"debug".getinfo(1).currentline', ''))
|
||||
local s, l = f()
|
||||
assert(s == y and l == n)
|
||||
end
|
||||
|
||||
lexstring("'abc\\z \n efg'", "abcefg", 2)
|
||||
lexstring("'abc\\z \n\n\n'", "abc", 4)
|
||||
lexstring("'\\z \n\t\f\v\n'", "", 3)
|
||||
lexstring("[[\nalo\nalo\n\n]]", "alo\nalo\n\n", 5)
|
||||
lexstring("[[\nalo\ralo\n\n]]", "alo\nalo\n\n", 5)
|
||||
lexstring("[[\nalo\ralo\r\n]]", "alo\nalo\n", 4)
|
||||
lexstring("[[\ralo\n\ralo\r\n]]", "alo\nalo\n", 4)
|
||||
lexstring("[[alo]\n]alo]]", "alo]\n]alo", 2)
|
||||
|
||||
assert("abc\z
|
||||
def\z
|
||||
ghi\z
|
||||
" == 'abcdefghi')
|
||||
|
||||
|
||||
-- UTF-8 sequences
|
||||
assert("\u{0}\u{00000000}\x00\0" == string.char(0, 0, 0, 0))
|
||||
|
||||
-- limits for 1-byte sequences
|
||||
assert("\u{0}\u{7F}" == "\x00\x7F")
|
||||
|
||||
-- limits for 2-byte sequences
|
||||
assert("\u{80}\u{7FF}" == "\xC2\x80\xDF\xBF")
|
||||
|
||||
-- limits for 3-byte sequences
|
||||
assert("\u{800}\u{FFFF}" == "\xE0\xA0\x80\xEF\xBF\xBF")
|
||||
|
||||
-- limits for 4-byte sequences
|
||||
assert("\u{10000}\u{1FFFFF}" == "\xF0\x90\x80\x80\xF7\xBF\xBF\xBF")
|
||||
|
||||
-- limits for 5-byte sequences
|
||||
assert("\u{200000}\u{3FFFFFF}" == "\xF8\x88\x80\x80\x80\xFB\xBF\xBF\xBF\xBF")
|
||||
|
||||
-- limits for 6-byte sequences
|
||||
assert("\u{4000000}\u{7FFFFFFF}" ==
|
||||
"\xFC\x84\x80\x80\x80\x80\xFD\xBF\xBF\xBF\xBF\xBF")
|
||||
|
||||
|
||||
-- Error in escape sequences
|
||||
local function lexerror (s, err)
|
||||
local st, msg = load('return ' .. s, '')
|
||||
if err ~= '<eof>' then err = err .. "'" end
|
||||
assert(not st and string.find(msg, "near .-" .. err))
|
||||
end
|
||||
|
||||
lexerror([["abc\x"]], [[\x"]])
|
||||
lexerror([["abc\x]], [[\x]])
|
||||
lexerror([["\x]], [[\x]])
|
||||
lexerror([["\x5"]], [[\x5"]])
|
||||
lexerror([["\x5]], [[\x5]])
|
||||
lexerror([["\xr"]], [[\xr]])
|
||||
lexerror([["\xr]], [[\xr]])
|
||||
lexerror([["\x.]], [[\x.]])
|
||||
lexerror([["\x8%"]], [[\x8%%]])
|
||||
lexerror([["\xAG]], [[\xAG]])
|
||||
lexerror([["\g"]], [[\g]])
|
||||
lexerror([["\g]], [[\g]])
|
||||
lexerror([["\."]], [[\%.]])
|
||||
|
||||
lexerror([["\999"]], [[\999"]])
|
||||
lexerror([["xyz\300"]], [[\300"]])
|
||||
lexerror([[" \256"]], [[\256"]])
|
||||
|
||||
-- errors in UTF-8 sequences
|
||||
lexerror([["abc\u{100000000}"]], [[abc\u{100000000]]) -- too large
|
||||
lexerror([["abc\u11r"]], [[abc\u1]]) -- missing '{'
|
||||
lexerror([["abc\u"]], [[abc\u"]]) -- missing '{'
|
||||
lexerror([["abc\u{11r"]], [[abc\u{11r]]) -- missing '}'
|
||||
lexerror([["abc\u{11"]], [[abc\u{11"]]) -- missing '}'
|
||||
lexerror([["abc\u{11]], [[abc\u{11]]) -- missing '}'
|
||||
lexerror([["abc\u{r"]], [[abc\u{r]]) -- no digits
|
||||
|
||||
-- unfinished strings
|
||||
lexerror("[=[alo]]", "<eof>")
|
||||
lexerror("[=[alo]=", "<eof>")
|
||||
lexerror("[=[alo]", "<eof>")
|
||||
lexerror("'alo", "<eof>")
|
||||
lexerror("'alo \\z \n\n", "<eof>")
|
||||
lexerror("'alo \\z", "<eof>")
|
||||
lexerror([['alo \98]], "<eof>")
|
||||
|
||||
-- valid characters in variable names
|
||||
for i = 0, 255 do
|
||||
local s = string.char(i)
|
||||
assert(not string.find(s, "[a-zA-Z_]") == not load(s .. "=1", ""))
|
||||
assert(not string.find(s, "[a-zA-Z_0-9]") ==
|
||||
not load("a" .. s .. "1 = 1", ""))
|
||||
end
|
||||
|
||||
|
||||
-- long variable names
|
||||
|
||||
var1 = string.rep('a', 15000) .. '1'
|
||||
var2 = string.rep('a', 15000) .. '2'
|
||||
prog = string.format([[
|
||||
%s = 5
|
||||
%s = %s + 1
|
||||
return function () return %s - %s end
|
||||
]], var1, var2, var1, var1, var2)
|
||||
local f = dostring(prog)
|
||||
assert(_G[var1] == 5 and _G[var2] == 6 and f() == -1)
|
||||
var1, var2, f = nil
|
||||
print('+')
|
||||
|
||||
-- escapes --
|
||||
assert("\n\t" == [[
|
||||
|
||||
]])
|
||||
assert([[
|
||||
|
||||
$debug]] == "\n $debug")
|
||||
assert([[ [ ]] ~= [[ ] ]])
|
||||
-- long strings --
|
||||
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
assert(string.len(b) == 960)
|
||||
prog = [=[
|
||||
print('+')
|
||||
|
||||
a1 = [["this is a 'string' with several 'quotes'"]]
|
||||
a2 = "'quotes'"
|
||||
|
||||
assert(string.find(a1, a2) == 34)
|
||||
print('+')
|
||||
|
||||
a1 = [==[temp = [[an arbitrary value]]; ]==]
|
||||
assert(load(a1))()
|
||||
assert(temp == 'an arbitrary value')
|
||||
-- long strings --
|
||||
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
assert(string.len(b) == 960)
|
||||
print('+')
|
||||
|
||||
a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
]]
|
||||
assert(string.len(a) == 1863)
|
||||
assert(string.sub(a, 1, 40) == string.sub(b, 1, 40))
|
||||
x = 1
|
||||
]=]
|
||||
|
||||
print('+')
|
||||
x = nil
|
||||
dostring(prog)
|
||||
assert(x)
|
||||
|
||||
prog = nil
|
||||
a = nil
|
||||
b = nil
|
||||
|
||||
|
||||
-- testing line ends
|
||||
prog = [[
|
||||
a = 1 -- a comment
|
||||
b = 2
|
||||
|
||||
|
||||
x = [=[
|
||||
hi
|
||||
]=]
|
||||
y = "\
|
||||
hello\r\n\
|
||||
"
|
||||
return require"debug".getinfo(1).currentline
|
||||
]]
|
||||
|
||||
for _, n in pairs{"\n", "\r", "\n\r", "\r\n"} do
|
||||
local prog, nn = string.gsub(prog, "\n", n)
|
||||
assert(dostring(prog) == nn)
|
||||
assert(_G.x == "hi\n" and _G.y == "\nhello\r\n\n")
|
||||
end
|
||||
|
||||
|
||||
-- testing comments and strings with long brackets
|
||||
a = [==[]=]==]
|
||||
assert(a == "]=")
|
||||
|
||||
a = [==[[===[[=[]]=][====[]]===]===]==]
|
||||
assert(a == "[===[[=[]]=][====[]]===]===")
|
||||
|
||||
a = [====[[===[[=[]]=][====[]]===]===]====]
|
||||
assert(a == "[===[[=[]]=][====[]]===]===")
|
||||
|
||||
a = [=[]]]]]]]]]=]
|
||||
assert(a == "]]]]]]]]")
|
||||
|
||||
|
||||
--[===[
|
||||
x y z [==[ blu foo
|
||||
]==
|
||||
]
|
||||
]=]==]
|
||||
error error]=]===]
|
||||
|
||||
-- generate all strings of four of these chars
|
||||
local x = {"=", "[", "]", "\n"}
|
||||
local len = 4
|
||||
local function gen (c, n)
|
||||
if n==0 then coroutine.yield(c)
|
||||
else
|
||||
for _, a in pairs(x) do
|
||||
gen(c..a, n-1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for s in coroutine.wrap(function () gen("", len) end) do
|
||||
assert(s == load("return [====[\n"..s.."]====]", "")())
|
||||
end
|
||||
|
||||
|
||||
-- testing decimal point locale
|
||||
if os.setlocale("pt_BR") or os.setlocale("ptb") then
|
||||
assert(tonumber("3,4") == 3.4 and tonumber"3.4" == 3.4)
|
||||
assert(tonumber(" -.4 ") == -0.4)
|
||||
assert(tonumber(" +0x.41 ") == 0X0.41)
|
||||
assert(not load("a = (3,4)"))
|
||||
assert(assert(load("return 3.4"))() == 3.4)
|
||||
assert(assert(load("return .4,3"))() == .4)
|
||||
assert(assert(load("return 4."))() == 4.)
|
||||
assert(assert(load("return 4.+.5"))() == 4.5)
|
||||
|
||||
assert(" 0x.1 " + " 0x,1" + "-0X.1\t" == 0x0.1)
|
||||
|
||||
assert(not tonumber"inf" and not tonumber"NAN")
|
||||
|
||||
assert(assert(load(string.format("return %q", 4.51)))() == 4.51)
|
||||
|
||||
local a,b = load("return 4.5.")
|
||||
assert(string.find(b, "'4%.5%.'"))
|
||||
|
||||
assert(os.setlocale("C"))
|
||||
else
|
||||
(Message or print)(
|
||||
'\n >>> pt_BR locale not available: skipping decimal point tests <<<\n')
|
||||
end
|
||||
|
||||
|
||||
-- testing %q x line ends
|
||||
local s = "a string with \r and \n and \r\n and \n\r"
|
||||
local c = string.format("return %q", s)
|
||||
assert(assert(load(c))() == s)
|
||||
|
||||
-- testing errors
|
||||
assert(not load"a = 'non-ending string")
|
||||
assert(not load"a = 'non-ending string\n'")
|
||||
assert(not load"a = '\\345'")
|
||||
assert(not load"a = [=x]")
|
||||
|
||||
local function malformednum (n, exp)
|
||||
local s, msg = load("return " .. n)
|
||||
assert(not s and string.find(msg, exp))
|
||||
end
|
||||
|
||||
malformednum("0xe-", "near <eof>")
|
||||
malformednum("0xep-p", "malformed number")
|
||||
malformednum("1print()", "malformed number")
|
||||
|
||||
print('OK')
|
||||
@@ -0,0 +1,699 @@
|
||||
-- $Id: testes/locals.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print('testing local variables and environments')
|
||||
|
||||
local debug = require"debug"
|
||||
|
||||
|
||||
-- bug in 5.1:
|
||||
|
||||
local function f(x) x = nil; return x end
|
||||
assert(f(10) == nil)
|
||||
|
||||
local function f() local x; return x end
|
||||
assert(f(10) == nil)
|
||||
|
||||
local function f(x) x = nil; local y; return x, y end
|
||||
assert(f(10) == nil and select(2, f(20)) == nil)
|
||||
|
||||
do
|
||||
local i = 10
|
||||
do local i = 100; assert(i==100) end
|
||||
do local i = 1000; assert(i==1000) end
|
||||
assert(i == 10)
|
||||
if i ~= 10 then
|
||||
local i = 20
|
||||
else
|
||||
local i = 30
|
||||
assert(i == 30)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
f = nil
|
||||
|
||||
local f
|
||||
x = 1
|
||||
|
||||
a = nil
|
||||
load('local a = {}')()
|
||||
assert(a == nil)
|
||||
|
||||
function f (a)
|
||||
local _1, _2, _3, _4, _5
|
||||
local _6, _7, _8, _9, _10
|
||||
local x = 3
|
||||
local b = a
|
||||
local c,d = a,b
|
||||
if (d == b) then
|
||||
local x = 'q'
|
||||
x = b
|
||||
assert(x == 2)
|
||||
else
|
||||
assert(nil)
|
||||
end
|
||||
assert(x == 3)
|
||||
local f = 10
|
||||
end
|
||||
|
||||
local b=10
|
||||
local a; repeat local b; a,b=1,2; assert(a+1==b); until a+b==3
|
||||
|
||||
|
||||
assert(x == 1)
|
||||
|
||||
f(2)
|
||||
assert(type(f) == 'function')
|
||||
|
||||
|
||||
local function getenv (f)
|
||||
local a,b = debug.getupvalue(f, 1)
|
||||
assert(a == '_ENV')
|
||||
return b
|
||||
end
|
||||
|
||||
-- test for global table of loaded chunks
|
||||
assert(getenv(load"a=3") == _G)
|
||||
local c = {}; local f = load("a = 3", nil, nil, c)
|
||||
assert(getenv(f) == c)
|
||||
assert(c.a == nil)
|
||||
f()
|
||||
assert(c.a == 3)
|
||||
|
||||
-- old test for limits for special instructions
|
||||
do
|
||||
local i = 2
|
||||
local p = 4 -- p == 2^i
|
||||
repeat
|
||||
for j=-3,3 do
|
||||
assert(load(string.format([[local a=%s;
|
||||
a=a+%s;
|
||||
assert(a ==2^%s)]], j, p-j, i), '')) ()
|
||||
assert(load(string.format([[local a=%s;
|
||||
a=a-%s;
|
||||
assert(a==-2^%s)]], -j, p-j, i), '')) ()
|
||||
assert(load(string.format([[local a,b=0,%s;
|
||||
a=b-%s;
|
||||
assert(a==-2^%s)]], -j, p-j, i), '')) ()
|
||||
end
|
||||
p = 2 * p; i = i + 1
|
||||
until p <= 0
|
||||
end
|
||||
|
||||
print'+'
|
||||
|
||||
|
||||
if rawget(_G, "T") then
|
||||
-- testing clearing of dead elements from tables
|
||||
collectgarbage("stop") -- stop GC
|
||||
local a = {[{}] = 4, [3] = 0, alo = 1,
|
||||
a1234567890123456789012345678901234567890 = 10}
|
||||
|
||||
local t = T.querytab(a)
|
||||
|
||||
for k,_ in pairs(a) do a[k] = undef end
|
||||
collectgarbage() -- restore GC and collect dead fiels in `a'
|
||||
for i=0,t-1 do
|
||||
local k = querytab(a, i)
|
||||
assert(k == nil or type(k) == 'number' or k == 'alo')
|
||||
end
|
||||
|
||||
-- testing allocation errors during table insertions
|
||||
local a = {}
|
||||
local function additems ()
|
||||
a.x = true; a.y = true; a.z = true
|
||||
a[1] = true
|
||||
a[2] = true
|
||||
end
|
||||
for i = 1, math.huge do
|
||||
T.alloccount(i)
|
||||
local st, msg = pcall(additems)
|
||||
T.alloccount()
|
||||
local count = 0
|
||||
for k, v in pairs(a) do
|
||||
assert(a[k] == v)
|
||||
count = count + 1
|
||||
end
|
||||
if st then assert(count == 5); break end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- testing lexical environments
|
||||
|
||||
assert(_ENV == _G)
|
||||
|
||||
do
|
||||
local dummy
|
||||
local _ENV = (function (...) return ... end)(_G, dummy) -- {
|
||||
|
||||
do local _ENV = {assert=assert}; assert(true) end
|
||||
mt = {_G = _G}
|
||||
local foo,x
|
||||
A = false -- "declare" A
|
||||
do local _ENV = mt
|
||||
function foo (x)
|
||||
A = x
|
||||
do local _ENV = _G; A = 1000 end
|
||||
return function (x) return A .. x end
|
||||
end
|
||||
end
|
||||
assert(getenv(foo) == mt)
|
||||
x = foo('hi'); assert(mt.A == 'hi' and A == 1000)
|
||||
assert(x('*') == mt.A .. '*')
|
||||
|
||||
do local _ENV = {assert=assert, A=10};
|
||||
do local _ENV = {assert=assert, A=20};
|
||||
assert(A==20);x=A
|
||||
end
|
||||
assert(A==10 and x==20)
|
||||
end
|
||||
assert(x==20)
|
||||
|
||||
|
||||
do -- constants
|
||||
local a<const>, b, c<const> = 10, 20, 30
|
||||
b = a + c + b -- 'b' is not constant
|
||||
assert(a == 10 and b == 60 and c == 30)
|
||||
local function checkro (name, code)
|
||||
local st, msg = load(code)
|
||||
local gab = string.format("attempt to assign to const variable '%s'", name)
|
||||
assert(not st and string.find(msg, gab))
|
||||
end
|
||||
checkro("y", "local x, y <const>, z = 10, 20, 30; x = 11; y = 12")
|
||||
checkro("x", "local x <const>, y, z <const> = 10, 20, 30; x = 11")
|
||||
checkro("z", "local x <const>, y, z <const> = 10, 20, 30; y = 10; z = 11")
|
||||
|
||||
checkro("z", [[
|
||||
local a, z <const>, b = 10;
|
||||
function foo() a = 20; z = 32; end
|
||||
]])
|
||||
|
||||
checkro("var1", [[
|
||||
local a, var1 <const> = 10;
|
||||
function foo() a = 20; z = function () var1 = 12; end end
|
||||
]])
|
||||
end
|
||||
|
||||
|
||||
print"testing to-be-closed variables"
|
||||
|
||||
local function stack(n) n = ((n == 0) or stack(n - 1)) end
|
||||
|
||||
local function func2close (f, x, y)
|
||||
local obj = setmetatable({}, {__close = f})
|
||||
if x then
|
||||
return x, obj, y
|
||||
else
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local a = {}
|
||||
do
|
||||
local b <close> = false -- not to be closed
|
||||
local x <close> = setmetatable({"x"}, {__close = function (self)
|
||||
a[#a + 1] = self[1] end})
|
||||
local w, y <close>, z = func2close(function (self, err)
|
||||
assert(err == nil); a[#a + 1] = "y"
|
||||
end, 10, 20)
|
||||
local c <close> = nil -- not to be closed
|
||||
a[#a + 1] = "in"
|
||||
assert(w == 10 and z == 20)
|
||||
end
|
||||
a[#a + 1] = "out"
|
||||
assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out")
|
||||
end
|
||||
|
||||
do
|
||||
local X = false
|
||||
|
||||
local x, closescope = func2close(function () stack(10); X = true end, 100)
|
||||
assert(x == 100); x = 101; -- 'x' is not read-only
|
||||
|
||||
-- closing functions do not corrupt returning values
|
||||
local function foo (x)
|
||||
local _ <close> = closescope
|
||||
return x, X, 23
|
||||
end
|
||||
|
||||
local a, b, c = foo(1.5)
|
||||
assert(a == 1.5 and b == false and c == 23 and X == true)
|
||||
|
||||
X = false
|
||||
foo = function (x)
|
||||
local _<close> = closescope
|
||||
local y = 15
|
||||
return y
|
||||
end
|
||||
|
||||
assert(foo() == 15 and X == true)
|
||||
|
||||
X = false
|
||||
foo = function ()
|
||||
local x <close> = closescope
|
||||
return x
|
||||
end
|
||||
|
||||
assert(foo() == closescope and X == true)
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- calls cannot be tail in the scope of to-be-closed variables
|
||||
local X, Y
|
||||
local function foo ()
|
||||
local _ <close> = func2close(function () Y = 10 end)
|
||||
assert(X == true and Y == nil) -- 'X' not closed yet
|
||||
return 1,2,3
|
||||
end
|
||||
|
||||
local function bar ()
|
||||
local _ <close> = func2close(function () X = false end)
|
||||
X = true
|
||||
do
|
||||
return foo() -- not a tail call!
|
||||
end
|
||||
end
|
||||
|
||||
local a, b, c, d = bar()
|
||||
assert(a == 1 and b == 2 and c == 3 and X == false and Y == 10 and d == nil)
|
||||
end
|
||||
|
||||
|
||||
-- auxiliary functions for testing warnings in '__close'
|
||||
local function prepwarn ()
|
||||
if not T then -- no test library?
|
||||
warn("@off") -- do not show (lots of) warnings
|
||||
else
|
||||
warn("@store") -- to test the warnings
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function endwarn ()
|
||||
if not T then
|
||||
warn("@on") -- back to normal
|
||||
else
|
||||
assert(_WARN == nil)
|
||||
warn("@normal")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function checkwarn (msg)
|
||||
if T then
|
||||
assert(string.find(_WARN, msg))
|
||||
_WARN = nil -- reset variable to check next warning
|
||||
end
|
||||
end
|
||||
|
||||
warn("@on")
|
||||
|
||||
do print("testing errors in __close")
|
||||
|
||||
prepwarn()
|
||||
|
||||
-- original error is in __close
|
||||
local function foo ()
|
||||
|
||||
local x <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(string.find(msg, "@z"))
|
||||
error("@x")
|
||||
end)
|
||||
|
||||
local x1 <close> =
|
||||
func2close(function (self, msg)
|
||||
checkwarn("@y")
|
||||
assert(string.find(msg, "@z"))
|
||||
end)
|
||||
|
||||
local gc <close> = func2close(function () collectgarbage() end)
|
||||
|
||||
local y <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(string.find(msg, "@z")) -- first error in 'z'
|
||||
checkwarn("@z") -- second error in 'z' generated a warning
|
||||
error("@y")
|
||||
end)
|
||||
|
||||
local first = true
|
||||
local z <close> =
|
||||
-- 'z' close is called twice
|
||||
func2close(function (self, msg)
|
||||
if first then
|
||||
assert(msg == nil)
|
||||
first = false
|
||||
else
|
||||
assert(string.find(msg, "@z")) -- own error
|
||||
end
|
||||
error("@z")
|
||||
end)
|
||||
|
||||
return 200
|
||||
end
|
||||
|
||||
local stat, msg = pcall(foo, false)
|
||||
assert(string.find(msg, "@z"))
|
||||
checkwarn("@x")
|
||||
|
||||
|
||||
-- original error not in __close
|
||||
local function foo ()
|
||||
|
||||
local x <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(msg == 4)
|
||||
end)
|
||||
|
||||
local x1 <close> =
|
||||
func2close(function (self, msg)
|
||||
checkwarn("@y")
|
||||
assert(msg == 4)
|
||||
error("@x1")
|
||||
end)
|
||||
|
||||
local gc <close> = func2close(function () collectgarbage() end)
|
||||
|
||||
local y <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(msg == 4) -- error in body
|
||||
checkwarn("@z")
|
||||
error("@y")
|
||||
end)
|
||||
|
||||
local first = true
|
||||
local z <close> =
|
||||
func2close(function (self, msg)
|
||||
-- 'z' close is called once
|
||||
assert(first and msg == 4)
|
||||
first = false
|
||||
error("@z")
|
||||
end)
|
||||
|
||||
error(4) -- original error
|
||||
end
|
||||
|
||||
local stat, msg = pcall(foo, true)
|
||||
assert(msg == 4)
|
||||
checkwarn("@x1") -- last error
|
||||
|
||||
-- error leaving a block
|
||||
local function foo (...)
|
||||
do
|
||||
local x1 <close> =
|
||||
func2close(function ()
|
||||
checkwarn("@X")
|
||||
error("@Y")
|
||||
end)
|
||||
|
||||
local x123 <close> =
|
||||
func2close(function ()
|
||||
error("@X")
|
||||
end)
|
||||
end
|
||||
os.exit(false) -- should not run
|
||||
end
|
||||
|
||||
local st, msg = xpcall(foo, debug.traceback)
|
||||
assert(string.match(msg, "^[^ ]* @X"))
|
||||
assert(string.find(msg, "in metamethod 'close'"))
|
||||
checkwarn("@Y")
|
||||
|
||||
-- error in toclose in vararg function
|
||||
local function foo (...)
|
||||
local x123 <close> = func2close(function () error("@x123") end)
|
||||
end
|
||||
|
||||
local st, msg = xpcall(foo, debug.traceback)
|
||||
assert(string.match(msg, "^[^ ]* @x123"))
|
||||
assert(string.find(msg, "in metamethod 'close'"))
|
||||
checkwarn("@x123") -- from second call to close 'x123'
|
||||
|
||||
endwarn()
|
||||
end
|
||||
|
||||
|
||||
do -- errors due to non-closable values
|
||||
local function foo ()
|
||||
local x <close> = {}
|
||||
os.exit(false) -- should not run
|
||||
end
|
||||
local stat, msg = pcall(foo)
|
||||
assert(not stat and
|
||||
string.find(msg, "variable 'x' got a non%-closable value"))
|
||||
|
||||
local function foo ()
|
||||
local xyz <close> = setmetatable({}, {__close = print})
|
||||
getmetatable(xyz).__close = nil -- remove metamethod
|
||||
end
|
||||
local stat, msg = pcall(foo)
|
||||
assert(not stat and
|
||||
string.find(msg, "attempt to close non%-closable variable 'xyz'"))
|
||||
end
|
||||
|
||||
|
||||
if rawget(_G, "T") then
|
||||
|
||||
warn("@off")
|
||||
|
||||
-- memory error inside closing function
|
||||
local function foo ()
|
||||
local y <close> = func2close(function () T.alloccount() end)
|
||||
local x <close> = setmetatable({}, {__close = function ()
|
||||
T.alloccount(0); local x = {} -- force a memory error
|
||||
end})
|
||||
error(1000) -- common error inside the function's body
|
||||
end
|
||||
|
||||
stack(5) -- ensure a minimal number of CI structures
|
||||
|
||||
-- despite memory error, 'y' will be executed and
|
||||
-- memory limit will be lifted
|
||||
local _, msg = pcall(foo)
|
||||
assert(msg == 1000)
|
||||
|
||||
local close = func2close(function (self, msg)
|
||||
T.alloccount()
|
||||
assert(msg == "not enough memory")
|
||||
end)
|
||||
|
||||
-- set a memory limit and return a closing object to remove the limit
|
||||
local function enter (count)
|
||||
stack(10) -- reserve some stack space
|
||||
T.alloccount(count)
|
||||
return close
|
||||
end
|
||||
|
||||
local function test ()
|
||||
local x <close> = enter(0) -- set a memory limit
|
||||
-- creation of previous upvalue will raise a memory error
|
||||
assert(false) -- should not run
|
||||
end
|
||||
|
||||
local _, msg = pcall(test)
|
||||
assert(msg == "not enough memory")
|
||||
|
||||
-- now use metamethod for closing
|
||||
close = setmetatable({}, {__close = function ()
|
||||
T.alloccount()
|
||||
end})
|
||||
|
||||
-- repeat test with extra closing upvalues
|
||||
local function test ()
|
||||
local xxx <close> = func2close(function (self, msg)
|
||||
assert(msg == "not enough memory");
|
||||
error(1000) -- raise another error
|
||||
end)
|
||||
local xx <close> = func2close(function (self, msg)
|
||||
assert(msg == "not enough memory");
|
||||
end)
|
||||
local x <close> = enter(0) -- set a memory limit
|
||||
-- creation of previous upvalue will raise a memory error
|
||||
os.exit(false) -- should not run
|
||||
end
|
||||
|
||||
local _, msg = pcall(test)
|
||||
assert(msg == "not enough memory") -- reported error is the first one
|
||||
|
||||
do -- testing 'toclose' in C string buffer
|
||||
collectgarbage()
|
||||
local s = string.rep('a', 10000) -- large string
|
||||
local m = T.totalmem()
|
||||
collectgarbage("stop")
|
||||
s = string.upper(s) -- allocate buffer + new string (10K each)
|
||||
-- ensure buffer was deallocated
|
||||
assert(T.totalmem() - m <= 11000)
|
||||
collectgarbage("restart")
|
||||
end
|
||||
|
||||
do -- now some tests for freeing buffer in case of errors
|
||||
local lim = 10000 -- some size larger than the static buffer
|
||||
local extra = 2000 -- some extra memory (for callinfo, etc.)
|
||||
|
||||
local s = string.rep("a", lim)
|
||||
|
||||
-- concat this table needs two buffer resizes (one for each 's')
|
||||
local a = {s, s}
|
||||
|
||||
collectgarbage()
|
||||
|
||||
m = T.totalmem()
|
||||
collectgarbage("stop")
|
||||
|
||||
-- error in the first buffer allocation
|
||||
T. totalmem(m + extra)
|
||||
assert(not pcall(table.concat, a))
|
||||
-- first buffer was not even allocated
|
||||
assert(T.totalmem() - m <= extra)
|
||||
|
||||
-- error in the second buffer allocation
|
||||
T. totalmem(m + lim + extra)
|
||||
assert(not pcall(table.concat, a))
|
||||
-- first buffer was released by 'toclose'
|
||||
assert(T.totalmem() - m <= extra)
|
||||
|
||||
-- error in creation of final string
|
||||
T.totalmem(m + 2 * lim + extra)
|
||||
assert(not pcall(table.concat, a))
|
||||
-- second buffer was released by 'toclose'
|
||||
assert(T.totalmem() - m <= extra)
|
||||
|
||||
-- userdata, upvalue, buffer, buffer, final string
|
||||
T.totalmem(m + 4*lim + extra)
|
||||
assert(#table.concat(a) == 2*lim)
|
||||
|
||||
T.totalmem(0) -- remove memory limit
|
||||
collectgarbage("restart")
|
||||
|
||||
print'+'
|
||||
end
|
||||
|
||||
warn("@on")
|
||||
end
|
||||
|
||||
|
||||
print "to-be-closed variables in coroutines"
|
||||
|
||||
do
|
||||
-- an error in a wrapped coroutine closes variables
|
||||
local x = false
|
||||
local y = false
|
||||
local co = coroutine.wrap(function ()
|
||||
local xv <close> = func2close(function () x = true end)
|
||||
do
|
||||
local yv <close> = func2close(function () y = true end)
|
||||
coroutine.yield(100) -- yield doesn't close variable
|
||||
end
|
||||
coroutine.yield(200) -- yield doesn't close variable
|
||||
error(23) -- error does
|
||||
end)
|
||||
|
||||
local b = co()
|
||||
assert(b == 100 and not x and not y)
|
||||
b = co()
|
||||
assert(b == 200 and not x and y)
|
||||
local a, b = pcall(co)
|
||||
assert(not a and b == 23 and x and y)
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
prepwarn()
|
||||
|
||||
-- error in a wrapped coroutine raising errors when closing a variable
|
||||
local x = 0
|
||||
local co = coroutine.wrap(function ()
|
||||
local xx <close> = func2close(function () x = x + 1; error("@YYY") end)
|
||||
local xv <close> = func2close(function () x = x + 1; error("@XXX") end)
|
||||
coroutine.yield(100)
|
||||
error(200)
|
||||
end)
|
||||
assert(co() == 100); assert(x == 0)
|
||||
local st, msg = pcall(co); assert(x == 2)
|
||||
assert(not st and msg == 200) -- should get first error raised
|
||||
checkwarn("@YYY")
|
||||
|
||||
local x = 0
|
||||
local y = 0
|
||||
co = coroutine.wrap(function ()
|
||||
local xx <close> = func2close(function () y = y + 1; error("YYY") end)
|
||||
local xv <close> = func2close(function () x = x + 1; error("XXX") end)
|
||||
coroutine.yield(100)
|
||||
return 200
|
||||
end)
|
||||
assert(co() == 100); assert(x == 0)
|
||||
local st, msg = pcall(co)
|
||||
assert(x == 2 and y == 1) -- first close is called twice
|
||||
-- should get first error raised
|
||||
assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
|
||||
checkwarn("YYY")
|
||||
|
||||
endwarn()
|
||||
end
|
||||
|
||||
|
||||
-- a suspended coroutine should not close its variables when collected
|
||||
local co
|
||||
co = coroutine.wrap(function()
|
||||
-- should not run
|
||||
local x <close> = func2close(function () os.exit(false) end)
|
||||
co = nil
|
||||
coroutine.yield()
|
||||
end)
|
||||
co() -- start coroutine
|
||||
assert(co == nil) -- eventually it will be collected
|
||||
collectgarbage()
|
||||
|
||||
|
||||
-- to-be-closed variables in generic for loops
|
||||
do
|
||||
local numopen = 0
|
||||
local function open (x)
|
||||
numopen = numopen + 1
|
||||
return
|
||||
function () -- iteraction function
|
||||
x = x - 1
|
||||
if x > 0 then return x end
|
||||
end,
|
||||
nil, -- state
|
||||
nil, -- control variable
|
||||
func2close(function () numopen = numopen - 1 end) -- closing function
|
||||
end
|
||||
|
||||
local s = 0
|
||||
for i in open(10) do
|
||||
s = s + i
|
||||
end
|
||||
assert(s == 45 and numopen == 0)
|
||||
|
||||
local s = 0
|
||||
for i in open(10) do
|
||||
if i < 5 then break end
|
||||
s = s + i
|
||||
end
|
||||
assert(s == 35 and numopen == 0)
|
||||
|
||||
local s = 0
|
||||
for i in open(10) do
|
||||
for j in open(10) do
|
||||
if i + j < 5 then goto endloop end
|
||||
s = s + i
|
||||
end
|
||||
end
|
||||
::endloop::
|
||||
assert(s == 375 and numopen == 0)
|
||||
end
|
||||
|
||||
print('OK')
|
||||
|
||||
return 5,f
|
||||
|
||||
end -- }
|
||||
|
||||
+468
@@ -0,0 +1,468 @@
|
||||
# testing special comment on first line
|
||||
-- $Id: testes/main.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
-- most (all?) tests here assume a reasonable "Unix-like" shell
|
||||
if _port then return end
|
||||
|
||||
-- use only "double quotes" inside shell scripts (better change to
|
||||
-- run on Windows)
|
||||
|
||||
|
||||
print ("testing stand-alone interpreter")
|
||||
|
||||
assert(os.execute()) -- machine has a system command
|
||||
|
||||
local arg = arg or ARG
|
||||
|
||||
local prog = os.tmpname()
|
||||
local otherprog = os.tmpname()
|
||||
local out = os.tmpname()
|
||||
|
||||
local progname
|
||||
do
|
||||
local i = 0
|
||||
while arg[i] do i=i-1 end
|
||||
progname = arg[i+1]
|
||||
end
|
||||
print("progname: "..progname)
|
||||
|
||||
local prepfile = function (s, p)
|
||||
p = p or prog
|
||||
io.output(p)
|
||||
io.write(s)
|
||||
assert(io.close())
|
||||
end
|
||||
|
||||
local function getoutput ()
|
||||
io.input(out)
|
||||
local t = io.read("a")
|
||||
io.input():close()
|
||||
assert(os.remove(out))
|
||||
return t
|
||||
end
|
||||
|
||||
local function checkprogout (s)
|
||||
-- expected result must end with new line
|
||||
assert(string.sub(s, -1) == "\n")
|
||||
local t = getoutput()
|
||||
for line in string.gmatch(s, ".-\n") do
|
||||
assert(string.find(t, line, 1, true))
|
||||
end
|
||||
end
|
||||
|
||||
local function checkout (s)
|
||||
local t = getoutput()
|
||||
if s ~= t then print(string.format("'%s' - '%s'\n", s, t)) end
|
||||
assert(s == t)
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
local function RUN (p, ...)
|
||||
p = string.gsub(p, "lua", '"'..progname..'"', 1)
|
||||
local s = string.format(p, ...)
|
||||
assert(os.execute(s))
|
||||
end
|
||||
|
||||
local function NoRun (msg, p, ...)
|
||||
p = string.gsub(p, "lua", '"'..progname..'"', 1)
|
||||
local s = string.format(p, ...)
|
||||
s = string.format("%s 2> %s", s, out) -- will send error to 'out'
|
||||
assert(not os.execute(s))
|
||||
assert(string.find(getoutput(), msg, 1, true)) -- check error message
|
||||
end
|
||||
|
||||
RUN('lua -v')
|
||||
|
||||
print(string.format("(temporary program file used in these tests: %s)", prog))
|
||||
|
||||
-- running stdin as a file
|
||||
prepfile""
|
||||
RUN('lua - < %s > %s', prog, out)
|
||||
checkout("")
|
||||
|
||||
prepfile[[
|
||||
print(
|
||||
1, a
|
||||
)
|
||||
]]
|
||||
RUN('lua - < %s > %s', prog, out)
|
||||
checkout("1\tnil\n")
|
||||
|
||||
RUN('echo "print(10)\nprint(2)\n" | lua > %s', out)
|
||||
checkout("10\n2\n")
|
||||
|
||||
|
||||
-- test option '-'
|
||||
RUN('echo "print(arg[1])" | lua - -h > %s', out)
|
||||
checkout("-h\n")
|
||||
|
||||
-- test environment variables used by Lua
|
||||
|
||||
prepfile("print(package.path)")
|
||||
|
||||
-- test LUA_PATH
|
||||
RUN('env LUA_INIT= LUA_PATH=x lua %s > %s', prog, out)
|
||||
checkout("x\n")
|
||||
|
||||
-- test LUA_PATH_version
|
||||
RUN('env LUA_INIT= LUA_PATH_5_4=y LUA_PATH=x lua %s > %s', prog, out)
|
||||
checkout("y\n")
|
||||
|
||||
-- test LUA_CPATH
|
||||
prepfile("print(package.cpath)")
|
||||
RUN('env LUA_INIT= LUA_CPATH=xuxu lua %s > %s', prog, out)
|
||||
checkout("xuxu\n")
|
||||
|
||||
-- test LUA_CPATH_version
|
||||
RUN('env LUA_INIT= LUA_CPATH_5_4=yacc LUA_CPATH=x lua %s > %s', prog, out)
|
||||
checkout("yacc\n")
|
||||
|
||||
-- test LUA_INIT (and its access to 'arg' table)
|
||||
prepfile("print(X)")
|
||||
RUN('env LUA_INIT="X=tonumber(arg[1])" lua %s 3.2 > %s', prog, out)
|
||||
checkout("3.2\n")
|
||||
|
||||
-- test LUA_INIT_version
|
||||
prepfile("print(X)")
|
||||
RUN('env LUA_INIT_5_4="X=10" LUA_INIT="X=3" lua %s > %s', prog, out)
|
||||
checkout("10\n")
|
||||
|
||||
-- test LUA_INIT for files
|
||||
prepfile("x = x or 10; print(x); x = x + 1")
|
||||
RUN('env LUA_INIT="@%s" lua %s > %s', prog, prog, out)
|
||||
checkout("10\n11\n")
|
||||
|
||||
-- test errors in LUA_INIT
|
||||
NoRun('LUA_INIT:1: msg', 'env LUA_INIT="error(\'msg\')" lua')
|
||||
|
||||
-- test option '-E'
|
||||
local defaultpath, defaultCpath
|
||||
|
||||
do
|
||||
prepfile("print(package.path, package.cpath)")
|
||||
RUN('env LUA_INIT="error(10)" LUA_PATH=xxx LUA_CPATH=xxx lua -E %s > %s',
|
||||
prog, out)
|
||||
local output = getoutput()
|
||||
defaultpath = string.match(output, "^(.-)\t")
|
||||
defaultCpath = string.match(output, "\t(.-)$")
|
||||
|
||||
-- running with an empty environment
|
||||
RUN('env -i lua %s > %s', prog, out)
|
||||
local out = getoutput()
|
||||
assert(defaultpath == string.match(output, "^(.-)\t"))
|
||||
assert(defaultCpath == string.match(output, "\t(.-)$"))
|
||||
end
|
||||
|
||||
-- paths did not change
|
||||
assert(not string.find(defaultpath, "xxx") and
|
||||
string.find(defaultpath, "lua") and
|
||||
not string.find(defaultCpath, "xxx") and
|
||||
string.find(defaultCpath, "lua"))
|
||||
|
||||
|
||||
-- test replacement of ';;' to default path
|
||||
local function convert (p)
|
||||
prepfile("print(package.path)")
|
||||
RUN('env LUA_PATH="%s" lua %s > %s', p, prog, out)
|
||||
local expected = getoutput()
|
||||
expected = string.sub(expected, 1, -2) -- cut final end of line
|
||||
if string.find(p, ";;") then
|
||||
p = string.gsub(p, ";;", ";"..defaultpath..";")
|
||||
p = string.gsub(p, "^;", "") -- remove ';' at the beginning
|
||||
p = string.gsub(p, ";$", "") -- remove ';' at the end
|
||||
end
|
||||
assert(p == expected)
|
||||
end
|
||||
|
||||
convert(";")
|
||||
convert(";;")
|
||||
convert("a;;b")
|
||||
convert(";;b")
|
||||
convert("a;;")
|
||||
convert("a;b;;c")
|
||||
|
||||
|
||||
-- test -l over multiple libraries
|
||||
prepfile("print(1); a=2; return {x=15}")
|
||||
prepfile(("print(a); print(_G['%s'].x)"):format(prog), otherprog)
|
||||
RUN('env LUA_PATH="?;;" lua -l %s -l%s -lstring -l io %s > %s', prog, otherprog, otherprog, out)
|
||||
checkout("1\n2\n15\n2\n15\n")
|
||||
|
||||
-- test 'arg' table
|
||||
local a = [[
|
||||
assert(#arg == 3 and arg[1] == 'a' and
|
||||
arg[2] == 'b' and arg[3] == 'c')
|
||||
assert(arg[-1] == '--' and arg[-2] == "-e " and arg[-3] == '%s')
|
||||
assert(arg[4] == undef and arg[-4] == undef)
|
||||
local a, b, c = ...
|
||||
assert(... == 'a' and a == 'a' and b == 'b' and c == 'c')
|
||||
]]
|
||||
a = string.format(a, progname)
|
||||
prepfile(a)
|
||||
RUN('lua "-e " -- %s a b c', prog) -- "-e " runs an empty command
|
||||
|
||||
-- test 'arg' availability in libraries
|
||||
prepfile"assert(arg)"
|
||||
prepfile("assert(arg)", otherprog)
|
||||
RUN('env LUA_PATH="?;;" lua -l%s - < %s', prog, otherprog)
|
||||
|
||||
-- test messing up the 'arg' table
|
||||
RUN('echo "print(...)" | lua -e "arg[1] = 100" - > %s', out)
|
||||
checkout("100\n")
|
||||
NoRun("'arg' is not a table", 'echo "" | lua -e "arg = 1" -')
|
||||
|
||||
-- test error in 'print'
|
||||
RUN('echo 10 | lua -e "print=nil" -i > /dev/null 2> %s', out)
|
||||
assert(string.find(getoutput(), "error calling 'print'"))
|
||||
|
||||
-- test 'debug.debug'
|
||||
RUN('echo "io.stderr:write(1000)\ncont" | lua -e "require\'debug\'.debug()" 2> %s', out)
|
||||
checkout("lua_debug> 1000lua_debug> ")
|
||||
|
||||
|
||||
print("testing warnings")
|
||||
|
||||
-- no warnings by default
|
||||
RUN('echo "io.stderr:write(1); warn[[XXX]]" | lua 2> %s', out)
|
||||
checkout("1")
|
||||
|
||||
prepfile[[
|
||||
warn("@allow") -- unknown control, ignored
|
||||
warn("@off", "XXX", "@off") -- these are not control messages
|
||||
warn("@off") -- this one is
|
||||
warn("@on", "YYY", "@on") -- not control, but warn is off
|
||||
warn("@off") -- keep it off
|
||||
warn("@on") -- restart warnings
|
||||
warn("", "@on") -- again, no control, real warning
|
||||
warn("@on") -- keep it "started"
|
||||
warn("Z", "Z", "Z") -- common warning
|
||||
]]
|
||||
RUN('lua -W %s 2> %s', prog, out)
|
||||
checkout[[
|
||||
Lua warning: @offXXX@off
|
||||
Lua warning: @on
|
||||
Lua warning: ZZZ
|
||||
]]
|
||||
|
||||
prepfile[[
|
||||
warn("@allow")
|
||||
-- create two objects to be finalized when closing state
|
||||
-- the errors in the finalizers must generate warnings
|
||||
u1 = setmetatable({}, {__gc = function () error("XYZ") end})
|
||||
u2 = setmetatable({}, {__gc = function () error("ZYX") end})
|
||||
]]
|
||||
RUN('lua -W %s 2> %s', prog, out)
|
||||
checkprogout("ZYX)\nXYZ)\n")
|
||||
|
||||
|
||||
-- test many arguments
|
||||
prepfile[[print(({...})[30])]]
|
||||
RUN('lua %s %s > %s', prog, string.rep(" a", 30), out)
|
||||
checkout("a\n")
|
||||
|
||||
RUN([[lua "-eprint(1)" -ea=3 -e "print(a)" > %s]], out)
|
||||
checkout("1\n3\n")
|
||||
|
||||
-- test iteractive mode
|
||||
prepfile[[
|
||||
(6*2-6) -- ===
|
||||
a =
|
||||
10
|
||||
print(a)
|
||||
a]]
|
||||
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
|
||||
checkprogout("6\n10\n10\n\n")
|
||||
|
||||
prepfile("a = [[b\nc\nd\ne]]\n=a")
|
||||
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
|
||||
checkprogout("b\nc\nd\ne\n\n")
|
||||
|
||||
prompt = "alo"
|
||||
prepfile[[ --
|
||||
a = 2
|
||||
]]
|
||||
RUN([[lua "-e_PROMPT='%s'" -i < %s > %s]], prompt, prog, out)
|
||||
local t = getoutput()
|
||||
assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
|
||||
|
||||
-- test for error objects
|
||||
prepfile[[
|
||||
debug = require "debug"
|
||||
m = {x=0}
|
||||
setmetatable(m, {__tostring = function(x)
|
||||
return tostring(debug.getinfo(4).currentline + x.x)
|
||||
end})
|
||||
error(m)
|
||||
]]
|
||||
NoRun(progname .. ": 6\n", [[lua %s]], prog)
|
||||
|
||||
prepfile("error{}")
|
||||
NoRun("error object is a table value", [[lua %s]], prog)
|
||||
|
||||
|
||||
-- chunk broken in many lines
|
||||
s = [=[ --
|
||||
function f ( x )
|
||||
local a = [[
|
||||
xuxu
|
||||
]]
|
||||
local b = "\
|
||||
xuxu\n"
|
||||
if x == 11 then return 1 + 12 , 2 + 20 end --[[ test multiple returns ]]
|
||||
return x + 1
|
||||
--\\
|
||||
end
|
||||
return( f( 100 ) )
|
||||
assert( a == b )
|
||||
do return f( 11 ) end ]=]
|
||||
s = string.gsub(s, ' ', '\n\n') -- change all spaces for newlines
|
||||
prepfile(s)
|
||||
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
|
||||
checkprogout("101\n13\t22\n\n")
|
||||
|
||||
prepfile[[#comment in 1st line without \n at the end]]
|
||||
RUN('lua %s', prog)
|
||||
|
||||
prepfile[[#test line number when file starts with comment line
|
||||
debug = require"debug"
|
||||
print(debug.getinfo(1).currentline)
|
||||
]]
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkprogout('3\n')
|
||||
|
||||
-- close Lua with an open file
|
||||
prepfile(string.format([[io.output(%q); io.write('alo')]], out))
|
||||
RUN('lua %s', prog)
|
||||
checkout('alo')
|
||||
|
||||
-- bug in 5.2 beta (extra \0 after version line)
|
||||
RUN([[lua -v -e"print'hello'" > %s]], out)
|
||||
t = getoutput()
|
||||
assert(string.find(t, "PUC%-Rio\nhello"))
|
||||
|
||||
|
||||
-- testing os.exit
|
||||
prepfile("os.exit(nil, true)")
|
||||
RUN('lua %s', prog)
|
||||
prepfile("os.exit(0, true)")
|
||||
RUN('lua %s', prog)
|
||||
prepfile("os.exit(true, true)")
|
||||
RUN('lua %s', prog)
|
||||
prepfile("os.exit(1, true)")
|
||||
NoRun("", "lua %s", prog) -- no message
|
||||
prepfile("os.exit(false, true)")
|
||||
NoRun("", "lua %s", prog) -- no message
|
||||
|
||||
|
||||
-- to-be-closed variables in main chunk
|
||||
prepfile[[
|
||||
local x <close> = setmetatable({},
|
||||
{__close = function (self, err)
|
||||
assert(err == nil)
|
||||
print("Ok")
|
||||
end})
|
||||
local e1 <close> = setmetatable({}, {__close = function () print(120) end})
|
||||
os.exit(true, true)
|
||||
]]
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkprogout("120\nOk\n")
|
||||
|
||||
|
||||
-- remove temporary files
|
||||
assert(os.remove(prog))
|
||||
assert(os.remove(otherprog))
|
||||
assert(not os.remove(out))
|
||||
|
||||
-- invalid options
|
||||
NoRun("unrecognized option '-h'", "lua -h")
|
||||
NoRun("unrecognized option '---'", "lua ---")
|
||||
NoRun("unrecognized option '-Ex'", "lua -Ex")
|
||||
NoRun("unrecognized option '-vv'", "lua -vv")
|
||||
NoRun("unrecognized option '-iv'", "lua -iv")
|
||||
NoRun("'-e' needs argument", "lua -e")
|
||||
NoRun("syntax error", "lua -e a")
|
||||
NoRun("'-l' needs argument", "lua -l")
|
||||
|
||||
|
||||
if T then -- test library?
|
||||
print("testing 'not enough memory' to create a state")
|
||||
NoRun("not enough memory", "env MEMLIMIT=100 lua")
|
||||
|
||||
-- testing 'warn'
|
||||
warn("@store")
|
||||
warn("@123", "456", "789")
|
||||
assert(_WARN == "@123456789"); _WARN = nil
|
||||
|
||||
warn("zip", "", " ", "zap")
|
||||
assert(_WARN == "zip zap"); _WARN = nil
|
||||
warn("ZIP", "", " ", "ZAP")
|
||||
assert(_WARN == "ZIP ZAP"); _WARN = nil
|
||||
warn("@normal")
|
||||
end
|
||||
|
||||
do
|
||||
-- 'warn' must get at least one argument
|
||||
local st, msg = pcall(warn)
|
||||
assert(string.find(msg, "string expected"))
|
||||
|
||||
-- 'warn' does not leave unfinished warning in case of errors
|
||||
-- (message would appear in next warning)
|
||||
st, msg = pcall(warn, "SHOULD NOT APPEAR", {})
|
||||
assert(string.find(msg, "string expected"))
|
||||
end
|
||||
|
||||
print('+')
|
||||
|
||||
print('testing Ctrl C')
|
||||
do
|
||||
-- interrupt a script
|
||||
local function kill (pid)
|
||||
return os.execute(string.format('kill -INT %s 2> /dev/null', pid))
|
||||
end
|
||||
|
||||
-- function to run a script in background, returning its output file
|
||||
-- descriptor and its pid
|
||||
local function runback (luaprg)
|
||||
-- shell script to run 'luaprg' in background and echo its pid
|
||||
local shellprg = string.format('%s -e "%s" & echo $!', progname, luaprg)
|
||||
local f = io.popen(shellprg, "r") -- run shell script
|
||||
local pid = f:read() -- get pid for Lua script
|
||||
print("(if test fails now, it may leave a Lua script running in \z
|
||||
background, pid " .. pid .. ")")
|
||||
return f, pid
|
||||
end
|
||||
|
||||
-- Lua script that runs protected infinite loop and then prints '42'
|
||||
local f, pid = runback[[
|
||||
pcall(function () print(12); while true do end end); print(42)]]
|
||||
-- wait until script is inside 'pcall'
|
||||
assert(f:read() == "12")
|
||||
kill(pid) -- send INT signal to Lua script
|
||||
-- check that 'pcall' captured the exception and script continued running
|
||||
assert(f:read() == "42") -- expected output
|
||||
assert(f:close())
|
||||
print("done")
|
||||
|
||||
-- Lua script in a long unbreakable search
|
||||
local f, pid = runback[[
|
||||
print(15); string.find(string.rep('a', 100000), '.*b')]]
|
||||
-- wait (so script can reach the loop)
|
||||
assert(f:read() == "15")
|
||||
assert(os.execute("sleep 1"))
|
||||
-- must send at least two INT signals to stop this Lua script
|
||||
local n = 100
|
||||
for i = 0, 100 do -- keep sending signals
|
||||
if not kill(pid) then -- until it fails
|
||||
n = i -- number of non-failed kills
|
||||
break
|
||||
end
|
||||
end
|
||||
assert(f:close())
|
||||
assert(n >= 2)
|
||||
print(string.format("done (with %d kills)", n))
|
||||
|
||||
end
|
||||
|
||||
print("OK")
|
||||
+1019
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,733 @@
|
||||
-- $Id: testes/nextvar.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
print('testing tables, next, and for')
|
||||
|
||||
local function checkerror (msg, f, ...)
|
||||
local s, err = pcall(f, ...)
|
||||
assert(not s and string.find(err, msg))
|
||||
end
|
||||
|
||||
|
||||
local a = {}
|
||||
|
||||
-- make sure table has lots of space in hash part
|
||||
for i=1,100 do a[i.."+"] = true end
|
||||
for i=1,100 do a[i.."+"] = undef end
|
||||
-- fill hash part with numeric indices testing size operator
|
||||
for i=1,100 do
|
||||
a[i] = true
|
||||
assert(#a == i)
|
||||
end
|
||||
|
||||
-- testing ipairs
|
||||
local x = 0
|
||||
for k,v in ipairs{10,20,30;x=12} do
|
||||
x = x + 1
|
||||
assert(k == x and v == x * 10)
|
||||
end
|
||||
|
||||
for _ in ipairs{x=12, y=24} do assert(nil) end
|
||||
|
||||
-- test for 'false' x ipair
|
||||
x = false
|
||||
local i = 0
|
||||
for k,v in ipairs{true,false,true,false} do
|
||||
i = i + 1
|
||||
x = not x
|
||||
assert(x == v)
|
||||
end
|
||||
assert(i == 4)
|
||||
|
||||
-- iterator function is always the same
|
||||
assert(type(ipairs{}) == 'function' and ipairs{} == ipairs{})
|
||||
|
||||
|
||||
if not T then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping tests for table sizes <<<\n')
|
||||
else --[
|
||||
-- testing table sizes
|
||||
|
||||
|
||||
local function mp2 (n) -- minimum power of 2 >= n
|
||||
local mp = 2^math.ceil(math.log(n, 2))
|
||||
assert(n == 0 or (mp/2 < n and n <= mp))
|
||||
return mp
|
||||
end
|
||||
|
||||
|
||||
local function check (t, na, nh)
|
||||
local a, h = T.querytab(t)
|
||||
if a ~= na or h ~= nh then
|
||||
print(na, nh, a, h)
|
||||
assert(nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- testing C library sizes
|
||||
do
|
||||
local s = 0
|
||||
for _ in pairs(math) do s = s + 1 end
|
||||
check(math, 0, mp2(s))
|
||||
end
|
||||
|
||||
|
||||
-- testing constructor sizes
|
||||
local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17,
|
||||
30, 31, 32, 33, 34, 500, 1000}
|
||||
|
||||
for _, sa in ipairs(sizes) do -- 'sa' is size of the array part
|
||||
local arr = {"return {"}
|
||||
for i = 1, sa do arr[1 + i] = "1," end -- build array part
|
||||
for _, sh in ipairs(sizes) do -- 'sh' is size of the hash part
|
||||
for j = 1, sh do -- build hash part
|
||||
arr[1 + sa + j] = string.format('k%x=%d,', j, j)
|
||||
end
|
||||
arr[1 + sa + sh + 1] = "}"
|
||||
local prog = table.concat(arr)
|
||||
local f = assert(load(prog))
|
||||
f() -- call once to ensure stack space
|
||||
-- make sure table is not resized after being created
|
||||
if sa == 0 or sh == 0 then
|
||||
T.alloccount(2); -- header + array or hash part
|
||||
else
|
||||
T.alloccount(3); -- header + array part + hash part
|
||||
end
|
||||
local t = f()
|
||||
T.alloccount();
|
||||
assert(#t == sa)
|
||||
check(t, sa, mp2(sh))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- tests with unknown number of elements
|
||||
local a = {}
|
||||
for i=1,sizes[#sizes] do a[i] = i end -- build auxiliary table
|
||||
for k in ipairs(sizes) do
|
||||
local t = {table.unpack(a,1,k)}
|
||||
assert(#t == k)
|
||||
check(t, k, 0)
|
||||
t = {1,2,3,table.unpack(a,1,k)}
|
||||
check(t, k+3, 0)
|
||||
assert(#t == k + 3)
|
||||
end
|
||||
|
||||
|
||||
-- testing tables dynamically built
|
||||
local lim = 130
|
||||
local a = {}; a[2] = 1; check(a, 0, 1)
|
||||
a = {}; a[0] = 1; check(a, 0, 1); a[2] = 1; check(a, 0, 2)
|
||||
a = {}; a[0] = 1; a[1] = 1; check(a, 1, 1)
|
||||
a = {}
|
||||
for i = 1,lim do
|
||||
a[i] = 1
|
||||
assert(#a == i)
|
||||
check(a, mp2(i), 0)
|
||||
end
|
||||
|
||||
a = {}
|
||||
for i = 1,lim do
|
||||
a['a'..i] = 1
|
||||
assert(#a == 0)
|
||||
check(a, 0, mp2(i))
|
||||
end
|
||||
|
||||
a = {}
|
||||
for i=1,16 do a[i] = i end
|
||||
check(a, 16, 0)
|
||||
do
|
||||
for i=1,11 do a[i] = undef end
|
||||
for i=30,50 do a[i] = true; a[i] = undef end -- force a rehash (?)
|
||||
check(a, 0, 8) -- 5 elements in the table
|
||||
a[10] = 1
|
||||
for i=30,50 do a[i] = true; a[i] = undef end -- force a rehash (?)
|
||||
check(a, 0, 8) -- only 6 elements in the table
|
||||
for i=1,14 do a[i] = true; a[i] = undef end
|
||||
for i=18,50 do a[i] = true; a[i] = undef end -- force a rehash (?)
|
||||
check(a, 0, 4) -- only 2 elements ([15] and [16])
|
||||
end
|
||||
|
||||
-- reverse filling
|
||||
for i=1,lim do
|
||||
local a = {}
|
||||
for i=i,1,-1 do a[i] = i end -- fill in reverse
|
||||
check(a, mp2(i), 0)
|
||||
end
|
||||
|
||||
-- size tests for vararg
|
||||
lim = 35
|
||||
function foo (n, ...)
|
||||
local arg = {...}
|
||||
check(arg, n, 0)
|
||||
assert(select('#', ...) == n)
|
||||
arg[n+1] = true
|
||||
check(arg, mp2(n+1), 0)
|
||||
arg.x = true
|
||||
check(arg, mp2(n+1), 1)
|
||||
end
|
||||
local a = {}
|
||||
for i=1,lim do a[i] = true; foo(i, table.unpack(a)) end
|
||||
|
||||
|
||||
-- Table length with limit smaller than maximum value at array
|
||||
local a = {}
|
||||
for i = 1,64 do a[i] = true end -- make its array size 64
|
||||
for i = 1,64 do a[i] = nil end -- erase all elements
|
||||
assert(T.querytab(a) == 64) -- array part has 64 elements
|
||||
a[32] = true; a[48] = true; -- binary search will find these ones
|
||||
a[51] = true -- binary search will miss this one
|
||||
assert(#a == 48) -- this will set the limit
|
||||
assert(select(4, T.querytab(a)) == 48) -- this is the limit now
|
||||
a[50] = true -- this will set a new limit
|
||||
assert(select(4, T.querytab(a)) == 50) -- this is the limit now
|
||||
-- but the size is larger (and still inside the array part)
|
||||
assert(#a == 51)
|
||||
|
||||
end --]
|
||||
|
||||
|
||||
-- test size operation on tables with nils
|
||||
assert(#{} == 0)
|
||||
assert(#{nil} == 0)
|
||||
assert(#{nil, nil} == 0)
|
||||
assert(#{nil, nil, nil} == 0)
|
||||
assert(#{nil, nil, nil, nil} == 0)
|
||||
assert(#{1, 2, 3, nil, nil} == 3)
|
||||
print'+'
|
||||
|
||||
|
||||
local nofind = {}
|
||||
|
||||
a,b,c = 1,2,3
|
||||
a,b,c = nil
|
||||
|
||||
|
||||
-- next uses always the same iteraction function
|
||||
assert(next{} == next{})
|
||||
|
||||
local function find (name)
|
||||
local n,v
|
||||
while 1 do
|
||||
n,v = next(_G, n)
|
||||
if not n then return nofind end
|
||||
assert(_G[n] ~= undef)
|
||||
if n == name then return v end
|
||||
end
|
||||
end
|
||||
|
||||
local function find1 (name)
|
||||
for n,v in pairs(_G) do
|
||||
if n==name then return v end
|
||||
end
|
||||
return nil -- not found
|
||||
end
|
||||
|
||||
|
||||
assert(print==find("print") and print == find1("print"))
|
||||
assert(_G["print"]==find("print"))
|
||||
assert(assert==find1("assert"))
|
||||
assert(nofind==find("return"))
|
||||
assert(not find1("return"))
|
||||
_G["ret" .. "urn"] = undef
|
||||
assert(nofind==find("return"))
|
||||
_G["xxx"] = 1
|
||||
assert(xxx==find("xxx"))
|
||||
|
||||
-- invalid key to 'next'
|
||||
checkerror("invalid key", next, {10,20}, 3)
|
||||
|
||||
-- both 'pairs' and 'ipairs' need an argument
|
||||
checkerror("bad argument", pairs)
|
||||
checkerror("bad argument", ipairs)
|
||||
|
||||
print('+')
|
||||
|
||||
a = {}
|
||||
for i=0,10000 do
|
||||
if math.fmod(i,10) ~= 0 then
|
||||
a['x'..i] = i
|
||||
end
|
||||
end
|
||||
|
||||
n = {n=0}
|
||||
for i,v in pairs(a) do
|
||||
n.n = n.n+1
|
||||
assert(i and v and a[i] == v)
|
||||
end
|
||||
assert(n.n == 9000)
|
||||
a = nil
|
||||
|
||||
do -- clear global table
|
||||
local a = {}
|
||||
for n,v in pairs(_G) do a[n]=v end
|
||||
for n,v in pairs(a) do
|
||||
if not package.loaded[n] and type(v) ~= "function" and
|
||||
not string.find(n, "^[%u_]") then
|
||||
_G[n] = undef
|
||||
end
|
||||
collectgarbage()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
|
||||
local function checknext (a)
|
||||
local b = {}
|
||||
do local k,v = next(a); while k do b[k] = v; k,v = next(a,k) end end
|
||||
for k,v in pairs(b) do assert(a[k] == v) end
|
||||
for k,v in pairs(a) do assert(b[k] == v) end
|
||||
end
|
||||
|
||||
checknext{1,x=1,y=2,z=3}
|
||||
checknext{1,2,x=1,y=2,z=3}
|
||||
checknext{1,2,3,x=1,y=2,z=3}
|
||||
checknext{1,2,3,4,x=1,y=2,z=3}
|
||||
checknext{1,2,3,4,5,x=1,y=2,z=3}
|
||||
|
||||
assert(#{} == 0)
|
||||
assert(#{[-1] = 2} == 0)
|
||||
for i=0,40 do
|
||||
local a = {}
|
||||
for j=1,i do a[j]=j end
|
||||
assert(#a == i)
|
||||
end
|
||||
|
||||
-- 'maxn' is now deprecated, but it is easily defined in Lua
|
||||
function table.maxn (t)
|
||||
local max = 0
|
||||
for k in pairs(t) do
|
||||
max = (type(k) == 'number') and math.max(max, k) or max
|
||||
end
|
||||
return max
|
||||
end
|
||||
|
||||
assert(table.maxn{} == 0)
|
||||
assert(table.maxn{["1000"] = true} == 0)
|
||||
assert(table.maxn{["1000"] = true, [24.5] = 3} == 24.5)
|
||||
assert(table.maxn{[1000] = true} == 1000)
|
||||
assert(table.maxn{[10] = true, [100*math.pi] = print} == 100*math.pi)
|
||||
|
||||
table.maxn = nil
|
||||
|
||||
-- int overflow
|
||||
a = {}
|
||||
for i=0,50 do a[2^i] = true end
|
||||
assert(a[#a])
|
||||
|
||||
print('+')
|
||||
|
||||
|
||||
do -- testing 'next' with all kinds of keys
|
||||
local a = {
|
||||
[1] = 1, -- integer
|
||||
[1.1] = 2, -- float
|
||||
['x'] = 3, -- short string
|
||||
[string.rep('x', 1000)] = 4, -- long string
|
||||
[print] = 5, -- C function
|
||||
[checkerror] = 6, -- Lua function
|
||||
[coroutine.running()] = 7, -- thread
|
||||
[true] = 8, -- boolean
|
||||
[io.stdin] = 9, -- userdata
|
||||
[{}] = 10, -- table
|
||||
}
|
||||
local b = {}; for i = 1, 10 do b[i] = true end
|
||||
for k, v in pairs(a) do
|
||||
assert(b[v]); b[v] = undef
|
||||
end
|
||||
assert(next(b) == nil) -- 'b' now is empty
|
||||
end
|
||||
|
||||
|
||||
-- erasing values
|
||||
local t = {[{1}] = 1, [{2}] = 2, [string.rep("x ", 4)] = 3,
|
||||
[100.3] = 4, [4] = 5}
|
||||
|
||||
local n = 0
|
||||
for k, v in pairs( t ) do
|
||||
n = n+1
|
||||
assert(t[k] == v)
|
||||
t[k] = undef
|
||||
collectgarbage()
|
||||
assert(t[k] == undef)
|
||||
end
|
||||
assert(n == 5)
|
||||
|
||||
|
||||
local function test (a)
|
||||
assert(not pcall(table.insert, a, 2, 20));
|
||||
table.insert(a, 10); table.insert(a, 2, 20);
|
||||
table.insert(a, 1, -1); table.insert(a, 40);
|
||||
table.insert(a, #a+1, 50)
|
||||
table.insert(a, 2, -2)
|
||||
assert(a[2] ~= undef)
|
||||
assert(a["2"] == undef)
|
||||
assert(not pcall(table.insert, a, 0, 20));
|
||||
assert(not pcall(table.insert, a, #a + 2, 20));
|
||||
assert(table.remove(a,1) == -1)
|
||||
assert(table.remove(a,1) == -2)
|
||||
assert(table.remove(a,1) == 10)
|
||||
assert(table.remove(a,1) == 20)
|
||||
assert(table.remove(a,1) == 40)
|
||||
assert(table.remove(a,1) == 50)
|
||||
assert(table.remove(a,1) == nil)
|
||||
assert(table.remove(a) == nil)
|
||||
assert(table.remove(a, #a) == nil)
|
||||
end
|
||||
|
||||
a = {n=0, [-7] = "ban"}
|
||||
test(a)
|
||||
assert(a.n == 0 and a[-7] == "ban")
|
||||
|
||||
a = {[-7] = "ban"};
|
||||
test(a)
|
||||
assert(a.n == nil and #a == 0 and a[-7] == "ban")
|
||||
|
||||
a = {[-1] = "ban"}
|
||||
test(a)
|
||||
assert(#a == 0 and table.remove(a) == nil and a[-1] == "ban")
|
||||
|
||||
a = {[0] = "ban"}
|
||||
assert(#a == 0 and table.remove(a) == "ban" and a[0] == undef)
|
||||
|
||||
table.insert(a, 1, 10); table.insert(a, 1, 20); table.insert(a, 1, -1)
|
||||
assert(table.remove(a) == 10)
|
||||
assert(table.remove(a) == 20)
|
||||
assert(table.remove(a) == -1)
|
||||
assert(table.remove(a) == nil)
|
||||
|
||||
a = {'c', 'd'}
|
||||
table.insert(a, 3, 'a')
|
||||
table.insert(a, 'b')
|
||||
assert(table.remove(a, 1) == 'c')
|
||||
assert(table.remove(a, 1) == 'd')
|
||||
assert(table.remove(a, 1) == 'a')
|
||||
assert(table.remove(a, 1) == 'b')
|
||||
assert(table.remove(a, 1) == nil)
|
||||
assert(#a == 0 and a.n == nil)
|
||||
|
||||
a = {10,20,30,40}
|
||||
assert(table.remove(a, #a + 1) == nil)
|
||||
assert(not pcall(table.remove, a, 0))
|
||||
assert(a[#a] == 40)
|
||||
assert(table.remove(a, #a) == 40)
|
||||
assert(a[#a] == 30)
|
||||
assert(table.remove(a, 2) == 20)
|
||||
assert(a[#a] == 30 and #a == 2)
|
||||
|
||||
do -- testing table library with metamethods
|
||||
local function test (proxy, t)
|
||||
for i = 1, 10 do
|
||||
table.insert(proxy, 1, i)
|
||||
end
|
||||
assert(#proxy == 10 and #t == 10 and proxy[1] ~= undef)
|
||||
for i = 1, 10 do
|
||||
assert(t[i] == 11 - i)
|
||||
end
|
||||
table.sort(proxy)
|
||||
for i = 1, 10 do
|
||||
assert(t[i] == i and proxy[i] == i)
|
||||
end
|
||||
assert(table.concat(proxy, ",") == "1,2,3,4,5,6,7,8,9,10")
|
||||
for i = 1, 8 do
|
||||
assert(table.remove(proxy, 1) == i)
|
||||
end
|
||||
assert(#proxy == 2 and #t == 2)
|
||||
local a, b, c = table.unpack(proxy)
|
||||
assert(a == 9 and b == 10 and c == nil)
|
||||
end
|
||||
|
||||
-- all virtual
|
||||
local t = {}
|
||||
local proxy = setmetatable({}, {
|
||||
__len = function () return #t end,
|
||||
__index = t,
|
||||
__newindex = t,
|
||||
})
|
||||
test(proxy, t)
|
||||
|
||||
-- only __newindex
|
||||
local count = 0
|
||||
t = setmetatable({}, {
|
||||
__newindex = function (t,k,v) count = count + 1; rawset(t,k,v) end})
|
||||
test(t, t)
|
||||
assert(count == 10) -- after first 10, all other sets are not new
|
||||
|
||||
-- no __newindex
|
||||
t = setmetatable({}, {
|
||||
__index = function (_,k) return k + 1 end,
|
||||
__len = function (_) return 5 end})
|
||||
assert(table.concat(t, ";") == "2;3;4;5;6")
|
||||
|
||||
end
|
||||
|
||||
|
||||
if not T then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping tests for table library on non-tables <<<\n')
|
||||
else --[
|
||||
local debug = require'debug'
|
||||
local tab = {10, 20, 30}
|
||||
local mt = {}
|
||||
local u = T.newuserdata(0)
|
||||
checkerror("table expected", table.insert, u, 40)
|
||||
checkerror("table expected", table.remove, u)
|
||||
debug.setmetatable(u, mt)
|
||||
checkerror("table expected", table.insert, u, 40)
|
||||
checkerror("table expected", table.remove, u)
|
||||
mt.__index = tab
|
||||
checkerror("table expected", table.insert, u, 40)
|
||||
checkerror("table expected", table.remove, u)
|
||||
mt.__newindex = tab
|
||||
checkerror("table expected", table.insert, u, 40)
|
||||
checkerror("table expected", table.remove, u)
|
||||
mt.__len = function () return #tab end
|
||||
table.insert(u, 40)
|
||||
assert(#u == 4 and #tab == 4 and u[4] == 40 and tab[4] == 40)
|
||||
assert(table.remove(u) == 40)
|
||||
table.insert(u, 1, 50)
|
||||
assert(#u == 4 and #tab == 4 and u[4] == 30 and tab[1] == 50)
|
||||
|
||||
mt.__newindex = nil
|
||||
mt.__len = nil
|
||||
local tab2 = {}
|
||||
local u2 = T.newuserdata(0)
|
||||
debug.setmetatable(u2, {__newindex = function (_, k, v) tab2[k] = v end})
|
||||
table.move(u, 1, 4, 1, u2)
|
||||
assert(#tab2 == 4 and tab2[1] == tab[1] and tab2[4] == tab[4])
|
||||
|
||||
end -- ]
|
||||
|
||||
print('+')
|
||||
|
||||
a = {}
|
||||
for i=1,1000 do
|
||||
a[i] = i; a[i - 1] = undef
|
||||
end
|
||||
assert(next(a,nil) == 1000 and next(a,1000) == nil)
|
||||
|
||||
assert(next({}) == nil)
|
||||
assert(next({}, nil) == nil)
|
||||
|
||||
for a,b in pairs{} do error"not here" end
|
||||
for i=1,0 do error'not here' end
|
||||
for i=0,1,-1 do error'not here' end
|
||||
a = nil; for i=1,1 do assert(not a); a=1 end; assert(a)
|
||||
a = nil; for i=1,1,-1 do assert(not a); a=1 end; assert(a)
|
||||
|
||||
do
|
||||
print("testing floats in numeric for")
|
||||
local a
|
||||
-- integer count
|
||||
a = 0; for i=1, 1, 1 do a=a+1 end; assert(a==1)
|
||||
a = 0; for i=10000, 1e4, -1 do a=a+1 end; assert(a==1)
|
||||
a = 0; for i=1, 0.99999, 1 do a=a+1 end; assert(a==0)
|
||||
a = 0; for i=9999, 1e4, -1 do a=a+1 end; assert(a==0)
|
||||
a = 0; for i=1, 0.99999, -1 do a=a+1 end; assert(a==1)
|
||||
|
||||
-- float count
|
||||
a = 0; for i=0, 0.999999999, 0.1 do a=a+1 end; assert(a==10)
|
||||
a = 0; for i=1.0, 1, 1 do a=a+1 end; assert(a==1)
|
||||
a = 0; for i=-1.5, -1.5, 1 do a=a+1 end; assert(a==1)
|
||||
a = 0; for i=1e6, 1e6, -1 do a=a+1 end; assert(a==1)
|
||||
a = 0; for i=1.0, 0.99999, 1 do a=a+1 end; assert(a==0)
|
||||
a = 0; for i=99999, 1e5, -1.0 do a=a+1 end; assert(a==0)
|
||||
a = 0; for i=1.0, 0.99999, -1 do a=a+1 end; assert(a==1)
|
||||
end
|
||||
|
||||
do -- changing the control variable
|
||||
local a
|
||||
a = 0; for i = 1, 10 do a = a + 1; i = "x" end; assert(a == 10)
|
||||
a = 0; for i = 10.0, 1, -1 do a = a + 1; i = "x" end; assert(a == 10)
|
||||
end
|
||||
|
||||
-- conversion
|
||||
a = 0; for i="10","1","-2" do a=a+1 end; assert(a==5)
|
||||
|
||||
do -- checking types
|
||||
local c
|
||||
local function checkfloat (i)
|
||||
assert(math.type(i) == "float")
|
||||
c = c + 1
|
||||
end
|
||||
|
||||
c = 0; for i = 1.0, 10 do checkfloat(i) end
|
||||
assert(c == 10)
|
||||
|
||||
c = 0; for i = -1, -10, -1.0 do checkfloat(i) end
|
||||
assert(c == 10)
|
||||
|
||||
local function checkint (i)
|
||||
assert(math.type(i) == "integer")
|
||||
c = c + 1
|
||||
end
|
||||
|
||||
local m = math.maxinteger
|
||||
c = 0; for i = m, m - 10, -1 do checkint(i) end
|
||||
assert(c == 11)
|
||||
|
||||
c = 0; for i = 1, 10.9 do checkint(i) end
|
||||
assert(c == 10)
|
||||
|
||||
c = 0; for i = 10, 0.001, -1 do checkint(i) end
|
||||
assert(c == 10)
|
||||
|
||||
c = 0; for i = 1, "10.8" do checkint(i) end
|
||||
assert(c == 10)
|
||||
|
||||
c = 0; for i = 9, "3.4", -1 do checkint(i) end
|
||||
assert(c == 6)
|
||||
|
||||
c = 0; for i = 0, " -3.4 ", -1 do checkint(i) end
|
||||
assert(c == 4)
|
||||
|
||||
c = 0; for i = 100, "96.3", -2 do checkint(i) end
|
||||
assert(c == 2)
|
||||
|
||||
c = 0; for i = 1, math.huge do if i > 10 then break end; checkint(i) end
|
||||
assert(c == 10)
|
||||
|
||||
c = 0; for i = -1, -math.huge, -1 do
|
||||
if i < -10 then break end; checkint(i)
|
||||
end
|
||||
assert(c == 10)
|
||||
|
||||
|
||||
for i = math.mininteger, -10e100 do assert(false) end
|
||||
for i = math.maxinteger, 10e100, -1 do assert(false) end
|
||||
|
||||
end
|
||||
|
||||
|
||||
do -- testing other strange cases for numeric 'for'
|
||||
|
||||
local function checkfor (from, to, step, t)
|
||||
local c = 0
|
||||
for i = from, to, step do
|
||||
c = c + 1
|
||||
assert(i == t[c])
|
||||
end
|
||||
assert(c == #t)
|
||||
end
|
||||
|
||||
local maxi = math.maxinteger
|
||||
local mini = math.mininteger
|
||||
|
||||
checkfor(mini, maxi, maxi, {mini, -1, maxi - 1})
|
||||
|
||||
checkfor(mini, math.huge, maxi, {mini, -1, maxi - 1})
|
||||
|
||||
checkfor(maxi, mini, mini, {maxi, -1})
|
||||
|
||||
checkfor(maxi, mini, -maxi, {maxi, 0, -maxi})
|
||||
|
||||
checkfor(maxi, -math.huge, mini, {maxi, -1})
|
||||
|
||||
checkfor(maxi, mini, 1, {})
|
||||
checkfor(mini, maxi, -1, {})
|
||||
|
||||
checkfor(maxi - 6, maxi, 3, {maxi - 6, maxi - 3, maxi})
|
||||
checkfor(mini + 4, mini, -2, {mini + 4, mini + 2, mini})
|
||||
|
||||
local step = maxi // 10
|
||||
local c = mini
|
||||
for i = mini, maxi, step do
|
||||
assert(i == c)
|
||||
c = c + step
|
||||
end
|
||||
|
||||
c = maxi
|
||||
for i = maxi, mini, -step do
|
||||
assert(i == c)
|
||||
c = c - step
|
||||
end
|
||||
|
||||
checkfor(maxi, maxi, maxi, {maxi})
|
||||
checkfor(maxi, maxi, mini, {maxi})
|
||||
checkfor(mini, mini, maxi, {mini})
|
||||
checkfor(mini, mini, mini, {mini})
|
||||
end
|
||||
|
||||
|
||||
checkerror("'for' step is zero", function ()
|
||||
for i = 1, 10, 0 do end
|
||||
end)
|
||||
|
||||
checkerror("'for' step is zero", function ()
|
||||
for i = 1, -10, 0 do end
|
||||
end)
|
||||
|
||||
checkerror("'for' step is zero", function ()
|
||||
for i = 1.0, -10, 0.0 do end
|
||||
end)
|
||||
|
||||
collectgarbage()
|
||||
|
||||
|
||||
-- testing generic 'for'
|
||||
|
||||
local function f (n, p)
|
||||
local t = {}; for i=1,p do t[i] = i*10 end
|
||||
return function (_, n, ...)
|
||||
assert(select("#", ...) == 0) -- no extra arguments
|
||||
if n > 0 then
|
||||
n = n-1
|
||||
return n, table.unpack(t)
|
||||
end
|
||||
end, nil, n
|
||||
end
|
||||
|
||||
local x = 0
|
||||
for n,a,b,c,d in f(5,3) do
|
||||
x = x+1
|
||||
assert(a == 10 and b == 20 and c == 30 and d == nil)
|
||||
end
|
||||
assert(x == 5)
|
||||
|
||||
|
||||
|
||||
-- testing __pairs and __ipairs metamethod
|
||||
a = {}
|
||||
do
|
||||
local x,y,z = pairs(a)
|
||||
assert(type(x) == 'function' and y == a and z == nil)
|
||||
end
|
||||
|
||||
local function foo (e,i)
|
||||
assert(e == a)
|
||||
if i <= 10 then return i+1, i+2 end
|
||||
end
|
||||
|
||||
local function foo1 (e,i)
|
||||
i = i + 1
|
||||
assert(e == a)
|
||||
if i <= e.n then return i,a[i] end
|
||||
end
|
||||
|
||||
setmetatable(a, {__pairs = function (x) return foo, x, 0 end})
|
||||
|
||||
local i = 0
|
||||
for k,v in pairs(a) do
|
||||
i = i + 1
|
||||
assert(k == i and v == k+1)
|
||||
end
|
||||
|
||||
a.n = 5
|
||||
a[3] = 30
|
||||
|
||||
-- testing ipairs with metamethods
|
||||
a = {n=10}
|
||||
setmetatable(a, { __index = function (t,k)
|
||||
if k <= t.n then return k * 10 end
|
||||
end})
|
||||
i = 0
|
||||
for k,v in ipairs(a) do
|
||||
i = i + 1
|
||||
assert(k == i and v == i * 10)
|
||||
end
|
||||
assert(i == a.n)
|
||||
|
||||
print"OK"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user