mirror of
https://github.com/lua/lua.git
synced 2026-07-27 08:29:08 +00:00
Compare commits
2542 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f791535cd | ||
|
|
51c29ec0c4 | ||
|
|
fbaf040f5e | ||
|
|
e51564d1be | ||
|
|
98ec799591 | ||
|
|
b6888a158b | ||
|
|
613513d09f | ||
|
|
8496112a18 | ||
|
|
6bc0f13505 | ||
|
|
8a89da07ba | ||
|
|
f849885a4b | ||
|
|
f7ce7e5faa | ||
|
|
65141832d2 | ||
|
|
f13dc59416 | ||
|
|
7c3cb71fa4 | ||
|
|
68109afcdb | ||
|
|
9cf3299faf | ||
|
|
b9b554e0f6 | ||
|
|
0dc5deca1c | ||
|
|
b4c353434f | ||
|
|
71f70df327 | ||
|
|
663f83f647 | ||
|
|
ae5b5ba529 | ||
|
|
a585eae6e7 | ||
|
|
d2c2e32e8a | ||
|
|
8c7c9ea065 | ||
|
|
a6da1472c0 | ||
|
|
34affe7a63 | ||
|
|
a2195644d8 | ||
|
|
1ecfbfa1a1 | ||
|
|
e1d8770f12 | ||
|
|
0f1cd0eba9 | ||
|
|
127e7a6c89 | ||
|
|
6f5bd5072d | ||
|
|
56ec432281 | ||
|
|
31b8c2d438 | ||
|
|
eb41999461 | ||
|
|
314c6057b7 | ||
|
|
d39ea8b3ce | ||
|
|
6298903e35 | ||
|
|
b57574d6fb | ||
|
|
bfcf06d91a | ||
|
|
0280407fc5 | ||
|
|
ae809e9fd1 | ||
|
|
e96385aded | ||
|
|
56a165bf0f | ||
|
|
422ce50d2e | ||
|
|
c33b1728ae | ||
|
|
a304199836 | ||
|
|
6d7cd31fee | ||
|
|
993c58fde3 | ||
|
|
d49b288728 | ||
|
|
69e84805e4 | ||
|
|
364e569945 | ||
|
|
63295f1f7f | ||
|
|
50523b107d | ||
|
|
aa8d4a782d | ||
|
|
efcf24be0c | ||
|
|
17dbaa8639 | ||
|
|
9514abc2da | ||
|
|
0be57b9b6d | ||
|
|
61a4e64a66 | ||
|
|
9a6f47f0ed | ||
|
|
948fb628d9 | ||
|
|
a901c505ab | ||
|
|
0ddc0f47bd | ||
|
|
cac075a122 | ||
|
|
9e0a8475cd | ||
|
|
7ccc6d8290 | ||
|
|
7288528a1e | ||
|
|
513559cc47 | ||
|
|
e460752323 | ||
|
|
92594f0939 | ||
|
|
e8a52281d9 | ||
|
|
6eb53b7526 | ||
|
|
9b7987a9d1 | ||
|
|
28ef7061bb | ||
|
|
46c3587a6f | ||
|
|
69c7139ff8 | ||
|
|
5ff408d218 | ||
|
|
e3c83835e7 | ||
|
|
bd1b87c579 | ||
|
|
d7bb8df841 | ||
|
|
c646e57fd6 | ||
|
|
e0ab13c62f | ||
|
|
e0cbaa50fa | ||
|
|
3747487320 | ||
|
|
1e0ad018ce | ||
|
|
95735bda46 | ||
|
|
d30569c064 | ||
|
|
2d92102dee | ||
|
|
490ecfcaa1 | ||
|
|
e174f43807 | ||
|
|
81f2401c6d | ||
|
|
508a705c1c | ||
|
|
6f1c033d72 | ||
|
|
5f83fb6582 | ||
|
|
679dc72c08 | ||
|
|
1499680f9e | ||
|
|
bdcfae2e1c | ||
|
|
7d526e75a7 | ||
|
|
c12983cf8a | ||
|
|
ba9cd0d25a | ||
|
|
b93f3b00bb | ||
|
|
4c32d9300c | ||
|
|
6e285e5392 | ||
|
|
b8cdea0190 | ||
|
|
e592f94a64 | ||
|
|
6e1aec7a67 | ||
|
|
6055a039b5 | ||
|
|
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 |
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
.gitattributes
|
||||
|
||||
*.so
|
||||
*.o
|
||||
*.a
|
||||
|
||||
manual/manual.html
|
||||
|
||||
testes/time.txt
|
||||
testes/time-debug.txt
|
||||
|
||||
testes/libs/all
|
||||
7
README.md
Normal file
7
README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Lua
|
||||
|
||||
This is the repository of Lua development code, as seen by the Lua team. It contains the full history of all commits but is mirrored irregularly. For complete information about Lua, visit [Lua.org](https://www.lua.org/).
|
||||
|
||||
Please **do not** send pull requests. To report issues and send patches, post a message to the [Lua mailing list](https://www.lua.org/lua-l.html).
|
||||
|
||||
Download official Lua releases from [Lua.org](https://www.lua.org/download.html).
|
||||
9
all
Executable file
9
all
Executable file
@@ -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
|
||||
37
lapi.h
37
lapi.h
@@ -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 accommodate 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
|
||||
|
||||
230
lauxlib.h
230
lauxlib.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lauxlib.h,v 1.87 2005/12/29 15:32:11 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, int szhint);
|
||||
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,14 +122,21 @@ 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)))
|
||||
|
||||
@@ -118,56 +150,126 @@ LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
|
||||
|
||||
#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)
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Buffer manipulation
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
|
||||
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,s) ((B)->n += (s))
|
||||
|
||||
#define luaL_addsize(B,n) ((B)->p += (n))
|
||||
#define luaL_buffsub(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
|
||||
|
||||
|
||||
706
lbaselib.c
706
lbaselib.c
@@ -1,9 +1,13 @@
|
||||
/*
|
||||
** $Id: lbaselib.c,v 1.190 2006/05/31 16:50:40 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,32 +181,59 @@ 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_optint(L, 2, 0);
|
||||
int res = lua_gc(L, optsnum[o], ex);
|
||||
switch (optsnum[o]) {
|
||||
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 b = lua_gc(L, LUA_GCCOUNTB, 0);
|
||||
lua_pushnumber(L, res + ((lua_Number)b/1024));
|
||||
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: {
|
||||
lua_pushnumber(L, res);
|
||||
int res = lua_gc(L, o);
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -217,8 +241,9 @@ static int luaB_collectgarbage (lua_State *L) {
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -236,120 +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, e, n;
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
i = luaL_optint(L, 2, 1);
|
||||
e = luaL_opt(L, luaL_checkint, 3, 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' */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -360,84 +420,63 @@ static int luaB_select (lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
int i = luaL_checkint(L, 1);
|
||||
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 - i;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -447,197 +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) {
|
||||
/* set global _G */
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
lua_setglobal(L, "_G");
|
||||
LUAMOD_API int luaopen_base (lua_State *L) {
|
||||
/* open lib into global table */
|
||||
luaL_register(L, "_G", base_funcs);
|
||||
lua_pushglobaltable(L);
|
||||
luaL_setfuncs(L, base_funcs, 0);
|
||||
/* set global _G */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, LUA_GNAME);
|
||||
/* set global _VERSION */
|
||||
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_createtable(L, 0, 1); /* 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' */
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API int luaopen_base (lua_State *L) {
|
||||
base_open(L);
|
||||
luaL_register(L, LUA_COLIBNAME, co_funcs);
|
||||
return 2;
|
||||
lua_setfield(L, -2, "_VERSION");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
54
lcode.h
54
lcode.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lcode.h,v 1.47 2005/11/08 19:44:31 roberto Exp roberto $
|
||||
** $Id: lcode.h $
|
||||
** Code generator for Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -21,43 +21,66 @@
|
||||
|
||||
|
||||
/*
|
||||
** 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_MUL, 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;
|
||||
|
||||
|
||||
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)->u.s.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)
|
||||
|
||||
#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);
|
||||
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
|
||||
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
|
||||
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
|
||||
LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
|
||||
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
|
||||
@@ -67,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 asize, int hsize);
|
||||
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
|
||||
|
||||
207
lcorolib.c
Normal file
207
lcorolib.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
** $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) { /* error? */
|
||||
int stat = lua_status(co);
|
||||
if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
|
||||
lua_resetthread(co); /* close its tbc variables */
|
||||
if (stat != LUA_ERRMEM && /* not a memory error and ... */
|
||||
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;
|
||||
}
|
||||
|
||||
64
lctype.c
Normal file
64
lctype.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
** $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>
|
||||
|
||||
|
||||
#if defined (LUA_UCID) /* accept UniCode IDentifiers? */
|
||||
/* consider all non-ascii codepoints to be alphabetic */
|
||||
#define NONA 0x01
|
||||
#else
|
||||
#define NONA 0x00 /* default */
|
||||
#endif
|
||||
|
||||
|
||||
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,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
#endif /* } */
|
||||
101
lctype.h
Normal file
101
lctype.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
** $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))
|
||||
|
||||
|
||||
/*
|
||||
** In ASCII, this 'ltolower' is correct for alphabetic characters and
|
||||
** for '.'. That is enough for Lua needs. ('check_exp' ensures that
|
||||
** the character either is an upper-case letter or is unchanged by
|
||||
** the transformation, which holds for lower-case letters and '.'.)
|
||||
*/
|
||||
#define ltolower(c) \
|
||||
check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \
|
||||
(c) | ('A' ^ 'a'))
|
||||
|
||||
|
||||
/* one entry for each character and for -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
|
||||
|
||||
430
ldblib.c
430
ldblib.c
@@ -1,23 +1,42 @@
|
||||
/*
|
||||
** $Id: ldblib.c,v 1.103 2005/11/01 16:08: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);
|
||||
@@ -36,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;
|
||||
@@ -79,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_createtable(L, 0, 2);
|
||||
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);
|
||||
@@ -126,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 */
|
||||
lua_Debug ar;
|
||||
const char *name;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -199,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;
|
||||
@@ -231,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';
|
||||
@@ -241,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_createtable(L, 0, 1);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -283,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;
|
||||
}
|
||||
|
||||
@@ -301,97 +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", luaL_tolstring(L, -1, NULL));
|
||||
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 = (int)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;
|
||||
}
|
||||
|
||||
|
||||
47
ldebug.h
47
ldebug.h
@@ -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,42 @@
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1)
|
||||
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
|
||||
|
||||
|
||||
/* Active Lua function (given call info) */
|
||||
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
|
||||
|
||||
#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0)
|
||||
|
||||
#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
|
||||
|
||||
70
ldo.h
70
ldo.h
@@ -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,65 @@
|
||||
#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.
|
||||
** It also allows the running of one GC step when the stack is
|
||||
** reallocated.
|
||||
** '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 checkstackGCp(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), luaC_checkGC(L), (void)0)
|
||||
|
||||
|
||||
/* 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
|
||||
|
||||
|
||||
322
ldump.c
322
ldump.c
@@ -1,164 +1,226 @@
|
||||
/*
|
||||
** $Id: ldump.c,v 1.15 2006/02/16 15:53:49 lhf Exp $
|
||||
** $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 "lstate.h"
|
||||
#include "lundump.h"
|
||||
|
||||
|
||||
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;
|
||||
|
||||
#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D)
|
||||
#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D)
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
/*
|
||||
** All high-level dumps go through dumpVector; you can change it to
|
||||
** change the endianness of the result
|
||||
*/
|
||||
#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0]))
|
||||
|
||||
static void DumpChar(int y, DumpState* D)
|
||||
{
|
||||
char x=(char)y;
|
||||
DumpVar(x,D);
|
||||
}
|
||||
#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char))
|
||||
|
||||
static void DumpInt(int x, DumpState* D)
|
||||
{
|
||||
DumpVar(x,D);
|
||||
}
|
||||
|
||||
static void DumpNumber(lua_Number x, DumpState* D)
|
||||
{
|
||||
DumpVar(x,D);
|
||||
}
|
||||
|
||||
static void DumpVector(const void* b, int n, size_t size, DumpState* D)
|
||||
{
|
||||
DumpInt(n,D);
|
||||
DumpMem(b,n,size,D);
|
||||
}
|
||||
|
||||
static void DumpString(const TString* s, DumpState* D)
|
||||
{
|
||||
if (s==NULL || getstr(s)==NULL)
|
||||
{
|
||||
size_t size=0;
|
||||
DumpVar(size,D);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t size=s->tsv.len+1; /* include trailing '\0' */
|
||||
DumpVar(size,D);
|
||||
DumpBlock(getstr(s),size,D);
|
||||
}
|
||||
}
|
||||
|
||||
#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D)
|
||||
|
||||
static void DumpFunction(const Proto* f, const TString* p, DumpState* D);
|
||||
|
||||
static void DumpConstants(const Proto* f, DumpState* D)
|
||||
{
|
||||
int i,n=f->sizek;
|
||||
DumpInt(n,D);
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
const TValue* o=&f->k[i];
|
||||
DumpChar(ttype(o),D);
|
||||
switch (ttype(o))
|
||||
{
|
||||
case LUA_TNIL:
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
DumpChar(bvalue(o),D);
|
||||
break;
|
||||
case LUA_TNUMBER:
|
||||
DumpNumber(nvalue(o),D);
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
DumpString(rawtsvalue(o),D);
|
||||
break;
|
||||
default:
|
||||
lua_assert(0); /* cannot happen */
|
||||
break;
|
||||
static void dumpBlock (DumpState *D, const void *b, size_t size) {
|
||||
if (D->status == 0 && size > 0) {
|
||||
lua_unlock(D->L);
|
||||
D->status = (*D->writer)(D->L, b, size, D->data);
|
||||
lua_lock(D->L);
|
||||
}
|
||||
}
|
||||
n=f->sizep;
|
||||
DumpInt(n,D);
|
||||
for (i=0; i<n; i++) DumpFunction(f->p[i],f->source,D);
|
||||
}
|
||||
|
||||
static void DumpDebug(const Proto* f, DumpState* D)
|
||||
{
|
||||
int i,n;
|
||||
n= (D->strip) ? 0 : f->sizelineinfo;
|
||||
DumpVector(f->lineinfo,n,sizeof(int),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],D);
|
||||
|
||||
#define dumpVar(D,x) dumpVector(D,&x,1)
|
||||
|
||||
|
||||
static void dumpByte (DumpState *D, int y) {
|
||||
lu_byte x = (lu_byte)y;
|
||||
dumpVar(D, x);
|
||||
}
|
||||
|
||||
static void DumpFunction(const Proto* f, const TString* p, DumpState* D)
|
||||
{
|
||||
DumpString((f->source==p || D->strip) ? NULL : f->source,D);
|
||||
DumpInt(f->linedefined,D);
|
||||
DumpInt(f->lastlinedefined,D);
|
||||
DumpChar(f->nups,D);
|
||||
DumpChar(f->numparams,D);
|
||||
DumpChar(f->is_vararg,D);
|
||||
DumpChar(f->maxstacksize,D);
|
||||
DumpCode(f,D);
|
||||
DumpConstants(f,D);
|
||||
DumpDebug(f,D);
|
||||
|
||||
/* dumpInt Buff Size */
|
||||
#define DIBS ((sizeof(size_t) * 8 / 7) + 1)
|
||||
|
||||
static void dumpSize (DumpState *D, size_t x) {
|
||||
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(D, buff + DIBS - n, n);
|
||||
}
|
||||
|
||||
static void DumpHeader(DumpState* D)
|
||||
{
|
||||
char h[LUAC_HEADERSIZE];
|
||||
luaU_header(h);
|
||||
DumpBlock(h,LUAC_HEADERSIZE,D);
|
||||
|
||||
static void dumpInt (DumpState *D, int x) {
|
||||
dumpSize(D, x);
|
||||
}
|
||||
|
||||
|
||||
static void dumpNumber (DumpState *D, lua_Number x) {
|
||||
dumpVar(D, x);
|
||||
}
|
||||
|
||||
|
||||
static void dumpInteger (DumpState *D, lua_Integer x) {
|
||||
dumpVar(D, x);
|
||||
}
|
||||
|
||||
|
||||
static void dumpString (DumpState *D, const TString *s) {
|
||||
if (s == NULL)
|
||||
dumpSize(D, 0);
|
||||
else {
|
||||
size_t size = tsslen(s);
|
||||
const char *str = getstr(s);
|
||||
dumpSize(D, size + 1);
|
||||
dumpVector(D, str, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void dumpCode (DumpState *D, const Proto *f) {
|
||||
dumpInt(D, f->sizecode);
|
||||
dumpVector(D, f->code, f->sizecode);
|
||||
}
|
||||
|
||||
|
||||
static void dumpFunction(DumpState *D, const Proto *f, TString *psource);
|
||||
|
||||
static void dumpConstants (DumpState *D, const Proto *f) {
|
||||
int i;
|
||||
int n = f->sizek;
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
const TValue *o = &f->k[i];
|
||||
int tt = ttypetag(o);
|
||||
dumpByte(D, tt);
|
||||
switch (tt) {
|
||||
case LUA_VNUMFLT:
|
||||
dumpNumber(D, fltvalue(o));
|
||||
break;
|
||||
case LUA_VNUMINT:
|
||||
dumpInteger(D, ivalue(o));
|
||||
break;
|
||||
case LUA_VSHRSTR:
|
||||
case LUA_VLNGSTR:
|
||||
dumpString(D, tsvalue(o));
|
||||
break;
|
||||
default:
|
||||
lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void dumpProtos (DumpState *D, const Proto *f) {
|
||||
int i;
|
||||
int n = f->sizep;
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++)
|
||||
dumpFunction(D, f->p[i], f->source);
|
||||
}
|
||||
|
||||
|
||||
static void dumpUpvalues (DumpState *D, const Proto *f) {
|
||||
int i, n = f->sizeupvalues;
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
dumpByte(D, f->upvalues[i].instack);
|
||||
dumpByte(D, f->upvalues[i].idx);
|
||||
dumpByte(D, f->upvalues[i].kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void dumpDebug (DumpState *D, const Proto *f) {
|
||||
int i, n;
|
||||
n = (D->strip) ? 0 : f->sizelineinfo;
|
||||
dumpInt(D, n);
|
||||
dumpVector(D, f->lineinfo, n);
|
||||
n = (D->strip) ? 0 : f->sizeabslineinfo;
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
dumpInt(D, f->abslineinfo[i].pc);
|
||||
dumpInt(D, f->abslineinfo[i].line);
|
||||
}
|
||||
n = (D->strip) ? 0 : f->sizelocvars;
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
dumpString(D, f->locvars[i].varname);
|
||||
dumpInt(D, f->locvars[i].startpc);
|
||||
dumpInt(D, f->locvars[i].endpc);
|
||||
}
|
||||
n = (D->strip) ? 0 : f->sizeupvalues;
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++)
|
||||
dumpString(D, f->upvalues[i].name);
|
||||
}
|
||||
|
||||
|
||||
static void dumpFunction (DumpState *D, const Proto *f, TString *psource) {
|
||||
if (D->strip || f->source == psource)
|
||||
dumpString(D, NULL); /* no debug info or same source as its parent */
|
||||
else
|
||||
dumpString(D, f->source);
|
||||
dumpInt(D, f->linedefined);
|
||||
dumpInt(D, f->lastlinedefined);
|
||||
dumpByte(D, f->numparams);
|
||||
dumpByte(D, f->is_vararg);
|
||||
dumpByte(D, f->maxstacksize);
|
||||
dumpCode(D, f);
|
||||
dumpConstants(D, f);
|
||||
dumpUpvalues(D, f);
|
||||
dumpProtos(D, f);
|
||||
dumpDebug(D, f);
|
||||
}
|
||||
|
||||
|
||||
static void dumpHeader (DumpState *D) {
|
||||
dumpLiteral(D, LUA_SIGNATURE);
|
||||
dumpByte(D, LUAC_VERSION);
|
||||
dumpByte(D, LUAC_FORMAT);
|
||||
dumpLiteral(D, LUAC_DATA);
|
||||
dumpByte(D, sizeof(Instruction));
|
||||
dumpByte(D, sizeof(lua_Integer));
|
||||
dumpByte(D, sizeof(lua_Number));
|
||||
dumpInteger(D, LUAC_INT);
|
||||
dumpNumber(D, LUAC_NUM);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** 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(&D, f->sizeupvalues);
|
||||
dumpFunction(&D, f, NULL);
|
||||
return D.status;
|
||||
}
|
||||
|
||||
|
||||
300
lfunc.c
300
lfunc.c
@@ -1,17 +1,21 @@
|
||||
/*
|
||||
** $Id: lfunc.c,v 2.11 2005/05/05 20:47:02 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,245 @@
|
||||
|
||||
|
||||
|
||||
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_byte(nelems);
|
||||
CClosure *luaF_newCclosure (lua_State *L, int nupvals) {
|
||||
GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals));
|
||||
CClosure *c = gco2ccl(o);
|
||||
c->nupvalues = cast_byte(nupvals);
|
||||
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_byte(nelems);
|
||||
while (nelems--) c->l.upvals[nelems] = NULL;
|
||||
LClosure *luaF_newLclosure (lua_State *L, int nupvals) {
|
||||
GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals));
|
||||
LClosure *c = gco2lcl(o);
|
||||
c->p = NULL;
|
||||
c->nupvalues = cast_byte(nupvals);
|
||||
while (nupvals--) c->upvals[nupvals] = 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_VUPVAL, 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_VUPVAL, 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) /* suppressed 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)) { /* neither white nor dead? */
|
||||
nw2black(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_VPROTO, 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 +271,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) {
|
||||
|
||||
57
lfunc.h
57
lfunc.h
@@ -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 nupvals);
|
||||
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);
|
||||
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);
|
||||
|
||||
|
||||
209
lgc.h
209
lgc.h
@@ -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,181 @@
|
||||
|
||||
|
||||
#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. (Open
|
||||
** upvalues are an exception to this rule.) 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 used
|
||||
** by tests.
|
||||
*/
|
||||
#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 TESTBIT 7
|
||||
|
||||
|
||||
|
||||
#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 nw2black(x) \
|
||||
check_exp(!iswhite(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
|
||||
|
||||
45
linit.c
45
linit.c
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
** $Id: linit.c,v 1.13 2005/08/26 17:36:32 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},
|
||||
{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 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
745
liolib.c
745
liolib.c
@@ -1,18 +1,22 @@
|
||||
/*
|
||||
** $Id: liolib.c,v 2.72 2006/01/28 12:59:13 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,48 +24,143 @@
|
||||
|
||||
|
||||
|
||||
#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) {
|
||||
int en = errno; /* calls to Lua API may change this value */
|
||||
if (i) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
if (filename)
|
||||
lua_pushfstring(L, "%s: %s", filename, strerror(en));
|
||||
else
|
||||
lua_pushfstring(L, "%s", strerror(en));
|
||||
lua_pushinteger(L, en);
|
||||
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_checkmodep)
|
||||
/* By default, Lua accepts only "r" or "w" as mode */
|
||||
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
|
||||
#endif
|
||||
|
||||
|
||||
#define topfile(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
|
||||
#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 /* } */
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
#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");
|
||||
@@ -69,135 +168,160 @@ 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);
|
||||
*p = NULL;
|
||||
return pushresult(L, ok, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int io_fclose (lua_State *L) {
|
||||
FILE **p = topfile(L);
|
||||
int ok = (fclose(*p) == 0);
|
||||
*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 default 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);
|
||||
errno = 0;
|
||||
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);
|
||||
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
|
||||
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, "default %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;
|
||||
}
|
||||
|
||||
@@ -215,35 +339,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -254,93 +411,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 (l == 0 || 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:
|
||||
@@ -350,10 +598,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;
|
||||
}
|
||||
@@ -369,19 +617,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;
|
||||
@@ -392,13 +652,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;
|
||||
@@ -406,7 +670,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -416,7 +681,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -425,12 +692,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);
|
||||
long offset = luaL_optlong(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;
|
||||
}
|
||||
}
|
||||
@@ -442,22 +712,25 @@ static int f_setvbuf (lua_State *L) {
|
||||
FILE *f = tofile(L);
|
||||
int op = luaL_checkoption(L, 2, NULL, modenames);
|
||||
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
|
||||
int res = setvbuf(f, NULL, mode[op], sz);
|
||||
return pushresult(L, res == 0, NULL);
|
||||
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},
|
||||
@@ -474,59 +747,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 (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
|
||||
lua_createtable(L, 2, 1);
|
||||
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_createtable(L, 0, 1);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
112
ljumptab.h
Normal file
112
ljumptab.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
** $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 const void *const 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_LOADFALSE,
|
||||
&&L_OP_LFALSESKIP,
|
||||
&&L_OP_LOADTRUE,
|
||||
&&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
|
||||
|
||||
};
|
||||
540
llex.c
540
llex.c
@@ -1,20 +1,24 @@
|
||||
/*
|
||||
** $Id: llex.c,v 2.19 2006/02/06 18:28:16 roberto Exp roberto $
|
||||
** $Id: llex.c $
|
||||
** Lexical Analyzer
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
#include <locale.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"
|
||||
@@ -25,8 +29,7 @@
|
||||
|
||||
|
||||
|
||||
#define next(ls) (ls->current = zgetc(ls->z))
|
||||
|
||||
#define next(ls) (ls->current = zgetc(ls->z))
|
||||
|
||||
|
||||
|
||||
@@ -34,118 +37,143 @@
|
||||
|
||||
|
||||
/* 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_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? */
|
||||
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 */
|
||||
setbtvalue(o); /* 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) {
|
||||
ls->decpoint = '.';
|
||||
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 */
|
||||
}
|
||||
|
||||
|
||||
@@ -157,56 +185,81 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {
|
||||
*/
|
||||
|
||||
|
||||
|
||||
static int check_next (LexState *ls, const char *set) {
|
||||
if (!strchr(set, ls->current))
|
||||
return 0;
|
||||
save_and_next(ls);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void buffreplace (LexState *ls, char from, char to) {
|
||||
size_t n = luaZ_bufflen(ls->buff);
|
||||
char *p = luaZ_buffer(ls->buff);
|
||||
while (n--)
|
||||
if (p[n] == from) p[n] = to;
|
||||
}
|
||||
|
||||
|
||||
static void trydecpoint (LexState *ls, SemInfo *seminfo) {
|
||||
/* format error: try to update decimal point separator */
|
||||
struct lconv *cv = localeconv();
|
||||
char old = ls->decpoint;
|
||||
ls->decpoint = (cv ? cv->decimal_point[0] : '.');
|
||||
buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */
|
||||
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {
|
||||
/* format error with correct decimal point: no more options */
|
||||
buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */
|
||||
luaX_lexerror(ls, "malformed number", TK_NUMBER);
|
||||
static int check_next1 (LexState *ls, int c) {
|
||||
if (ls->current == c) {
|
||||
next(ls);
|
||||
return 1;
|
||||
}
|
||||
else return 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 */
|
||||
static void read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
lua_assert(isdigit(ls->current));
|
||||
do {
|
||||
save_and_next(ls);
|
||||
} while (isdigit(ls->current) || ls->current == '.');
|
||||
if (check_next(ls, "Ee")) /* `E'? */
|
||||
check_next(ls, "+-"); /* optional exponent sign */
|
||||
while (isalnum(ls->current) || ls->current == '_')
|
||||
save_and_next(ls);
|
||||
/*
|
||||
** 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');
|
||||
buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
|
||||
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */
|
||||
trydecpoint(ls, seminfo); /* try to update decimal point separator */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int skip_sep (LexState *ls) {
|
||||
int count = 0;
|
||||
/*
|
||||
** 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);
|
||||
@@ -214,48 +267,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 */
|
||||
@@ -268,56 +307,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);
|
||||
@@ -333,109 +442,115 @@ 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 (check_next(ls, ".")) {
|
||||
if (check_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -454,8 +569,9 @@ void luaX_next (LexState *ls) {
|
||||
}
|
||||
|
||||
|
||||
void luaX_lookahead (LexState *ls) {
|
||||
int luaX_lookahead (LexState *ls) {
|
||||
lua_assert(ls->lookahead.token == TK_EOS);
|
||||
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
|
||||
return ls->lookahead.token;
|
||||
}
|
||||
|
||||
|
||||
48
llex.h
48
llex.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: llex.h,v 1.57 2005/12/07 15:43:05 roberto Exp roberto $
|
||||
** $Id: llex.h $
|
||||
** Lexical Analyzer
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -7,14 +7,22 @@
|
||||
#ifndef llex_h
|
||||
#define llex_h
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
#define FIRST_RESERVED 257
|
||||
/*
|
||||
** Single-char tokens (terminal symbols) are represented by their own
|
||||
** numeric code. Other tokens start at the following value.
|
||||
*/
|
||||
#define FIRST_RESERVED (UCHAR_MAX + 1)
|
||||
|
||||
/* maximum length of a reserved word */
|
||||
#define TOKEN_LEN (sizeof("function")/sizeof(char))
|
||||
|
||||
#if !defined(LUA_ENV)
|
||||
#define LUA_ENV "_ENV"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -25,23 +33,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,29 +59,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 */
|
||||
char decpoint; /* locale decimal point */
|
||||
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);
|
||||
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 void luaX_lookahead (LexState *ls);
|
||||
LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token);
|
||||
LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s);
|
||||
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);
|
||||
|
||||
|
||||
|
||||
331
llimits.h
331
llimits.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
** $Id: llimits.h,v 1.68 2005/12/22 16:19:56 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,114 +15,343 @@
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/* internal assertions for in-house debugging */
|
||||
#ifdef lua_assert
|
||||
/*
|
||||
** Internal assertions for in-house debugging
|
||||
*/
|
||||
#if defined LUAI_ASSERT
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#define lua_assert(c) assert(c)
|
||||
#endif
|
||||
|
||||
#if defined(lua_assert)
|
||||
#define check_exp(c,e) (lua_assert(c), (e))
|
||||
#define api_check(l,e) lua_assert(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 api_check luai_apicheck
|
||||
#define lua_longassert(c) ((void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** assertion for checking API calls
|
||||
*/
|
||||
#if !defined(luai_apicheck)
|
||||
#define luai_apicheck(l,e) ((void)l, lua_assert(e))
|
||||
#endif
|
||||
|
||||
#define api_check(l,e,msg) luai_apicheck(l,(e) && msg)
|
||||
|
||||
|
||||
/* macro to avoid warnings about unused variables */
|
||||
#if !defined(UNUSED)
|
||||
#define UNUSED(x) ((void)(x))
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef UNUSED
|
||||
#define UNUSED(x) ((void)(x)) /* to avoid warnings */
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef cast
|
||||
/* type casts (a macro highlights casts in the code) */
|
||||
#define cast(t, exp) ((t)(exp))
|
||||
#endif
|
||||
|
||||
#define cast_byte(i) cast(lu_byte, (i))
|
||||
#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
|
||||
|
||||
720
lmathlib.c
720
lmathlib.c
@@ -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,723 @@
|
||||
|
||||
|
||||
#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_floatatt(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). Otherwise, 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) {
|
||||
if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */
|
||||
return ran & n; /* no bias */
|
||||
else {
|
||||
lua_Unsigned lim = n;
|
||||
/* 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 >> 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 +747,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;
|
||||
}
|
||||
|
||||
|
||||
192
lmem.c
192
lmem.c
@@ -1,86 +1,202 @@
|
||||
/*
|
||||
** $Id: lmem.c,v 1.69 2005/02/23 17:30:22 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(EMERGENCYGCTESTS)
|
||||
/*
|
||||
** 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 allocation.)
|
||||
*/
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
84
lmem.h
84
lmem.h
@@ -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
|
||||
|
||||
|
||||
634
lobject.c
634
lobject.c
@@ -1,20 +1,26 @@
|
||||
/*
|
||||
** $Id: lobject.c,v 2.21 2006/01/10 12:50:00 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,110 +43,496 @@ 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(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 to be converted to a number */
|
||||
#if !defined (L_MAXLENNUM)
|
||||
#define L_MAXLENNUM 200
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Convert string 's' to a Lua number (put in 'result'). Return NULL on
|
||||
** fail or the address of the ending '\0' on success. ('mode' == 'x')
|
||||
** means a hexadecimal numeral.
|
||||
*/
|
||||
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 == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */
|
||||
*result = cast_num(strtoul(s, &endptr, 16));
|
||||
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 iff no trailing chars */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert string 's' to a Lua number (put in 'result') handling the
|
||||
** current locale.
|
||||
** 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.
|
||||
** The variable 'mode' checks for special characters in the string:
|
||||
** - 'n' means 'inf' or 'nan' (which should be rejected)
|
||||
** - 'x' means a hexadecimal numeral
|
||||
** - '.' just optimizes the search for the common case (no special chars)
|
||||
*/
|
||||
static const char *l_str2d (const char *s, lua_Number *result) {
|
||||
const char *endptr;
|
||||
const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */
|
||||
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. Must be
|
||||
** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT.
|
||||
** (For a long long int, this is 19 digits plus a sign and a final '\0',
|
||||
** adding to 21. For a long double, it can go to a sign, 33 digits,
|
||||
** the dot, an exponent letter, an exponent sign, 5 exponent digits,
|
||||
** and a final '\0', adding to 43.)
|
||||
*/
|
||||
#define MAXNUMBER2STR 44
|
||||
|
||||
|
||||
/*
|
||||
** 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 200
|
||||
|
||||
/* 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, and
|
||||
** join the 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++;
|
||||
luaV_concat(L, buff->pushed); /* join 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_num(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_num(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 */
|
||||
lua_assert(buff.pushed == 1);
|
||||
return svalue(s2v(L->top - 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -178,37 +545,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
839
lobject.h
839
lobject.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lobject.h,v 2.19 2006/01/10 12:51:53 roberto Exp roberto $
|
||||
** $Id: lobject.h $
|
||||
** Type definitions for Lua objects
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -16,249 +16,490 @@
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/* tags for values visible from Lua */
|
||||
#define LAST_TAG LUA_TTHREAD
|
||||
|
||||
#define NUM_TAGS (LAST_TAG+1)
|
||||
/*
|
||||
** Extra types for collectable non-values
|
||||
*/
|
||||
#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */
|
||||
#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */
|
||||
|
||||
|
||||
/*
|
||||
** Extra tags for non-values
|
||||
** number of all possible types (including LUA_TNONE)
|
||||
*/
|
||||
#define LUA_TPROTO (LAST_TAG+1)
|
||||
#define LUA_TUPVAL (LAST_TAG+2)
|
||||
#define LUA_TDEADKEY (LAST_TAG+3)
|
||||
#define LUA_TOTALTYPES (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* constant)
|
||||
** 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;
|
||||
|
||||
/* add variant bits to a type */
|
||||
#define makevariant(t,v) ((t) | ((v) << 4))
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** 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 */
|
||||
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 */
|
||||
|
||||
/* collectable object has the same tag as the original value */
|
||||
#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt)
|
||||
|
||||
/*
|
||||
** for internal debug only
|
||||
** Any value being manipulated by the program either is non
|
||||
** collectable, or the collectable object has the right tag
|
||||
** and it is not dead. The option 'L == NULL' allows other
|
||||
** macros using this one to be used where L is not available.
|
||||
*/
|
||||
#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 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); }
|
||||
|
||||
|
||||
/* set a value's tag */
|
||||
#define settt_(o,t) ((o)->tt_=(t))
|
||||
|
||||
|
||||
/* main macro to copy values (from 'obj1' to 'obj2') */
|
||||
#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_; settt_(io1, io2->tt_); \
|
||||
checkliveness(L,io1); lua_assert(!isnonstrictnil(io1)); }
|
||||
|
||||
/*
|
||||
** different types of sets, according to destination
|
||||
** Different types of assignments, according to source and destination.
|
||||
** (They are mostly equal now, but may be different in the future.)
|
||||
*/
|
||||
|
||||
/* 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)
|
||||
|
||||
|
||||
|
||||
typedef TValue *StkId; /* index to stack elements */
|
||||
/* to table */
|
||||
#define setobj2t setobj
|
||||
|
||||
|
||||
/*
|
||||
** String headers for string table
|
||||
** Entries in the Lua stack
|
||||
*/
|
||||
typedef union TString {
|
||||
L_Umaxalign dummy; /* ensures maximum alignment for strings */
|
||||
struct {
|
||||
CommonHeader;
|
||||
lu_byte reserved;
|
||||
unsigned int hash;
|
||||
size_t len;
|
||||
} tsv;
|
||||
typedef union StackValue {
|
||||
TValue val;
|
||||
} StackValue;
|
||||
|
||||
|
||||
/* index to stack elements */
|
||||
typedef StackValue *StkId;
|
||||
|
||||
/* convert a 'StackValue' to a 'TValue' */
|
||||
#define s2v(o) (&(o)->val)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Nil
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* Standard nil */
|
||||
#define LUA_VNIL makevariant(LUA_TNIL, 0)
|
||||
|
||||
/* Empty slot (which might be different from a slot containing nil) */
|
||||
#define LUA_VEMPTY makevariant(LUA_TNIL, 1)
|
||||
|
||||
/* Value returned for a key not found in a table (absent key) */
|
||||
#define LUA_VABSTKEY makevariant(LUA_TNIL, 2)
|
||||
|
||||
|
||||
/* macro to test for (any kind of) nil */
|
||||
#define ttisnil(v) checktype((v), LUA_TNIL)
|
||||
|
||||
|
||||
/* macro to test for a standard nil */
|
||||
#define ttisstrictnil(o) checktag((o), LUA_VNIL)
|
||||
|
||||
|
||||
#define setnilvalue(obj) settt_(obj, LUA_VNIL)
|
||||
|
||||
|
||||
#define isabstkey(v) checktag((v), LUA_VABSTKEY)
|
||||
|
||||
|
||||
/*
|
||||
** macro to detect non-standard nils (used only in assertions)
|
||||
*/
|
||||
#define isnonstrictnil(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_VABSTKEY
|
||||
|
||||
|
||||
/* mark an entry as empty */
|
||||
#define setempty(v) settt_(v, LUA_VEMPTY)
|
||||
|
||||
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Booleans
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
|
||||
#define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0)
|
||||
#define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1)
|
||||
|
||||
#define ttisboolean(o) checktype((o), LUA_TBOOLEAN)
|
||||
#define ttisfalse(o) checktag((o), LUA_VFALSE)
|
||||
#define ttistrue(o) checktag((o), LUA_VTRUE)
|
||||
|
||||
|
||||
#define l_isfalse(o) (ttisfalse(o) || ttisnil(o))
|
||||
|
||||
|
||||
#define setbfvalue(obj) settt_(obj, LUA_VFALSE)
|
||||
#define setbtvalue(obj) settt_(obj, LUA_VTRUE)
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Threads
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define LUA_VTHREAD makevariant(LUA_TTHREAD, 0)
|
||||
|
||||
#define ttisthread(o) checktag((o), ctb(LUA_VTHREAD))
|
||||
|
||||
#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_VTHREAD)); \
|
||||
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_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */
|
||||
#define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */
|
||||
|
||||
#define ttisnumber(o) checktype((o), LUA_TNUMBER)
|
||||
#define ttisfloat(o) checktag((o), LUA_VNUMFLT)
|
||||
#define ttisinteger(o) checktag((o), LUA_VNUMINT)
|
||||
|
||||
#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_VNUMFLT); }
|
||||
|
||||
#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_VNUMINT); }
|
||||
|
||||
#define chgivalue(obj,x) \
|
||||
{ TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); }
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Strings
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* Variant tags for strings */
|
||||
#define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */
|
||||
#define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */
|
||||
|
||||
#define ttisstring(o) checktype((o), LUA_TSTRING)
|
||||
#define ttisshrstring(o) checktag((o), ctb(LUA_VSHRSTR))
|
||||
#define ttislngstring(o) checktag((o), ctb(LUA_VLNGSTR))
|
||||
|
||||
#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 a string value.
|
||||
*/
|
||||
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;
|
||||
char contents[1];
|
||||
} TString;
|
||||
|
||||
|
||||
#define getstr(ts) cast(const char *, (ts) + 1)
|
||||
|
||||
/*
|
||||
** Get the actual string (array of bytes) from a 'TString'.
|
||||
*/
|
||||
#define getstr(ts) ((ts)->contents)
|
||||
|
||||
|
||||
/* 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_VSHRSTR ? (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 {
|
||||
|
||||
|
||||
/*
|
||||
** Light userdata should be a variant of userdata, but for compatibility
|
||||
** reasons they are also different types.
|
||||
*/
|
||||
#define LUA_VLIGHTUSERDATA makevariant(LUA_TLIGHTUSERDATA, 0)
|
||||
|
||||
#define LUA_VUSERDATA makevariant(LUA_TUSERDATA, 0)
|
||||
|
||||
#define ttislightuserdata(o) checktag((o), LUA_VLIGHTUSERDATA)
|
||||
#define ttisfulluserdata(o) checktag((o), ctb(LUA_VUSERDATA))
|
||||
|
||||
#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_VLIGHTUSERDATA); }
|
||||
|
||||
#define setuvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); Udata *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VUSERDATA)); \
|
||||
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
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define LUA_VPROTO makevariant(LUA_TPROTO, 0)
|
||||
|
||||
|
||||
/*
|
||||
** 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 */
|
||||
@@ -266,43 +507,129 @@ 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
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define LUA_VUPVAL makevariant(LUA_TUPVAL, 0)
|
||||
|
||||
|
||||
/* Variant tags for functions */
|
||||
#define LUA_VLCL makevariant(LUA_TFUNCTION, 0) /* Lua closure */
|
||||
#define LUA_VLCF makevariant(LUA_TFUNCTION, 1) /* light C function */
|
||||
#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */
|
||||
|
||||
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
|
||||
#define ttisclosure(o) ((rawtt(o) & 0x1F) == LUA_VLCL)
|
||||
#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL))
|
||||
#define ttislcf(o) checktag((o), LUA_VLCF)
|
||||
#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL))
|
||||
|
||||
#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_VLCL)); \
|
||||
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_VLCF); }
|
||||
|
||||
#define setclCvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); CClosure *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \
|
||||
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;
|
||||
|
||||
|
||||
@@ -312,69 +639,149 @@ 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 union TKey {
|
||||
struct {
|
||||
TValuefields;
|
||||
struct Node *next; /* for chaining */
|
||||
} nk;
|
||||
TValue tvk;
|
||||
} TKey;
|
||||
#define LUA_VTABLE makevariant(LUA_TTABLE, 0)
|
||||
|
||||
#define ttistable(o) checktag((o), ctb(LUA_VTABLE))
|
||||
|
||||
#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_VTABLE)); \
|
||||
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)->flags & BITRAS))
|
||||
#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS))
|
||||
#define setnorealasize(t) ((t)->flags |= 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_VNUMINT)
|
||||
#define keyival(node) (keyval(node).i)
|
||||
#define keyisshrstr(node) (keytt(node) == ctb(LUA_VSHRSTR))
|
||||
#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))
|
||||
|
||||
|
||||
#define luaO_nilobject (&luaO_nilobject_)
|
||||
/* 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
|
||||
|
||||
176
lopcodes.c
176
lopcodes.c
@@ -1,102 +1,104 @@
|
||||
/*
|
||||
** $Id: lopcodes.c,v 1.36 2005/10/13 12:22:05 roberto Exp roberto $
|
||||
** $Id: lopcodes.c $
|
||||
** Opcodes for Lua virtual machine
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#define lopcodes_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.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, 1, 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_LOADFALSE */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LFALSESKIP */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADTRUE */
|
||||
,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 */
|
||||
};
|
||||
|
||||
|
||||
418
lopcodes.h
418
lopcodes.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lopcodes.h,v 1.124 2005/12/02 18:42:08 roberto Exp roberto $
|
||||
** $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) |
|
||||
iAsBx 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) check_exp(checkopm(i, iABC), (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
|
||||
|
||||
|
||||
/*
|
||||
@@ -137,9 +183,9 @@ 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)
|
||||
** R[x] - register
|
||||
** K[x] - constant (in constant table)
|
||||
** RK(x) == if k(i) then K[x] else R[x]
|
||||
*/
|
||||
|
||||
|
||||
@@ -149,120 +195,198 @@ enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
|
||||
|
||||
typedef enum {
|
||||
/*----------------------------------------------------------------------
|
||||
name args description
|
||||
name args description
|
||||
------------------------------------------------------------------------*/
|
||||
OP_MOVE,/* A B R(A) := R(B) */
|
||||
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
|
||||
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */
|
||||
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
|
||||
OP_MOVE,/* A B R[A] := R[B] */
|
||||
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_LOADFALSE,/* A R[A] := false */
|
||||
OP_LFALSESKIP,/*A R[A] := false; pc++ */
|
||||
OP_LOADTRUE,/* A R[A] := true */
|
||||
OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
|
||||
OP_GETUPVAL,/* A B R[A] := UpValue[B] */
|
||||
OP_SETUPVAL,/* A B UpValue[B] := R[A] */
|
||||
|
||||
OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */
|
||||
OP_GETTABLE,/* A B C R(A) := R(B)[RK(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_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_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) := {} (size = B,C) */
|
||||
OP_NEWTABLE,/* A B C k R[A] := {} */
|
||||
|
||||
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][RK(C):string] */
|
||||
|
||||
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_NOT,/* A B R(A) := not R(B) */
|
||||
OP_LEN,/* A B R(A) := length of R(B) */
|
||||
OP_ADDI,/* A B sC R[A] := R[B] + sC */
|
||||
|
||||
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
|
||||
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_JMP,/* sBx pc+=sBx */
|
||||
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_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_SHRI,/* A B sC R[A] := R[B] >> sC */
|
||||
OP_SHLI,/* A B sC R[A] := sC << R[B] */
|
||||
|
||||
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_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_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_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_FORLOOP,/* A sBx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
|
||||
OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */
|
||||
OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */
|
||||
OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */
|
||||
|
||||
OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
|
||||
if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
|
||||
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
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_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_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */
|
||||
|
||||
OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
|
||||
OP_CLOSE,/* A close all upvalues >= R[A] */
|
||||
OP_TBC,/* A mark variable A "to be closed" */
|
||||
OP_JMP,/* sJ pc += sJ */
|
||||
OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */
|
||||
OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */
|
||||
OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */
|
||||
|
||||
OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */
|
||||
OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */
|
||||
OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */
|
||||
OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */
|
||||
OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */
|
||||
OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */
|
||||
|
||||
OP_TEST,/* A k if (not R[A] == k) then pc++ */
|
||||
OP_TESTSET,/* A B k 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 k return R[A](R[A+1], ... ,R[A+B-1]) */
|
||||
|
||||
OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */
|
||||
OP_RETURN0,/* return */
|
||||
OP_RETURN1,/* A return R[A] */
|
||||
|
||||
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 k R[A][(C-1)*FPF+i] := R[A+i], 1 <= i <= B */
|
||||
|
||||
OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
|
||||
|
||||
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
|
||||
OP_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).
|
||||
|
||||
(*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped
|
||||
(the constant is the first operand).
|
||||
|
||||
(*) 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. (It must be corrected in case of
|
||||
metamethods.)
|
||||
|
||||
(*) 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
|
||||
|
||||
103
lopnames.h
Normal file
103
lopnames.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
** $Id: lopnames.h $
|
||||
** Opcode names
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#if !defined(lopnames_h)
|
||||
#define lopnames_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
/* ORDER OP */
|
||||
|
||||
static const char *const opnames[] = {
|
||||
"MOVE",
|
||||
"LOADI",
|
||||
"LOADF",
|
||||
"LOADK",
|
||||
"LOADKX",
|
||||
"LOADFALSE",
|
||||
"LFALSESKIP",
|
||||
"LOADTRUE",
|
||||
"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
|
||||
|
||||
341
loslib.c
341
loslib.c
@@ -1,9 +1,14 @@
|
||||
/*
|
||||
** $Id: loslib.c,v 1.18 2006/03/09 18:08:22 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>
|
||||
@@ -11,46 +16,153 @@
|
||||
#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) {
|
||||
int en = errno; /* calls to Lua API may change this value */
|
||||
if (i) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "%s: %s", filename, strerror(en));
|
||||
lua_pushinteger(L, en);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
/*
|
||||
** {==================================================================
|
||||
** 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) {
|
||||
lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));
|
||||
return 1;
|
||||
const char *cmd = luaL_optstring(L, 1, NULL);
|
||||
int stat;
|
||||
errno = 0;
|
||||
stat = system(cmd);
|
||||
if (cmd != NULL)
|
||||
return luaL_execresult(L, stat);
|
||||
else {
|
||||
lua_pushboolean(L, stat); /* true if there is a shell */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,11 +197,25 @@ static int os_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 occur 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 */
|
||||
@@ -97,60 +223,121 @@ 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 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) {
|
||||
const char *s = luaL_optstring(L, 1, "%c");
|
||||
time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
|
||||
struct tm *stm;
|
||||
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;
|
||||
}
|
||||
@@ -164,26 +351,28 @@ static int os_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, (lua_Number)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 os_difftime (lua_State *L) {
|
||||
lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
|
||||
(time_t)(luaL_optnumber(L, 2, 0))));
|
||||
time_t t1 = l_checktime(L, 1);
|
||||
time_t t2 = l_checktime(L, 2);
|
||||
lua_pushnumber(L, (lua_Number)difftime(t1, t2));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -203,10 +392,18 @@ static int os_setlocale (lua_State *L) {
|
||||
|
||||
|
||||
static int os_exit (lua_State *L) {
|
||||
exit(luaL_optint(L, 1, EXIT_SUCCESS));
|
||||
return 0; /* to avoid warnings */
|
||||
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", os_clock},
|
||||
{"date", os_date},
|
||||
@@ -226,8 +423,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;
|
||||
}
|
||||
|
||||
|
||||
164
lparser.h
164
lparser.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lparser.h,v 1.56 2005/10/03 14:02:40 roberto Exp roberto $
|
||||
** $Id: lparser.h $
|
||||
** Lua Parser
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -13,70 +13,158 @@
|
||||
|
||||
|
||||
/*
|
||||
** 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' */
|
||||
VKNUM, /* nval = numerical value */
|
||||
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.sidx = stack index (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;
|
||||
union {
|
||||
struct { int info, aux; } s;
|
||||
lua_Number nval;
|
||||
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; /* compiler index (in 'actvar.arr') */
|
||||
} var;
|
||||
} u;
|
||||
int t; /* patch list of `exit when true' */
|
||||
int f; /* patch list of `exit when false' */
|
||||
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 all 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
|
||||
|
||||
45
lprefix.h
Normal file
45
lprefix.h
Normal file
@@ -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
|
||||
|
||||
470
lstate.c
470
lstate.c
@@ -1,17 +1,21 @@
|
||||
/*
|
||||
** $Id: lstate.c,v 2.35 2005/10/06 20:46:25 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,67 +28,270 @@
|
||||
#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, 1);
|
||||
}
|
||||
|
||||
#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,
|
||||
** keeping the first one.
|
||||
*/
|
||||
void luaE_shrinkCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci->next; /* first free CallInfo */
|
||||
CallInfo *next;
|
||||
if (ci == NULL)
|
||||
return; /* no extra elements */
|
||||
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
|
||||
while ((next = ci->next) != NULL) { /* two extra elements? */
|
||||
CallInfo *next2 = next->next; /* next's next */
|
||||
ci->next = next2; /* remove next from the list */
|
||||
L->nci--;
|
||||
luaM_free(L, next); /* free next */
|
||||
if (next2 == NULL)
|
||||
break; /* no more elements */
|
||||
else {
|
||||
next2->previous = ci;
|
||||
ci = next2; /* continue */
|
||||
}
|
||||
}
|
||||
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, 2)); /* table of globals */
|
||||
sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* 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->hook = NULL;
|
||||
L->hookmask = 0;
|
||||
@@ -92,51 +299,86 @@ static void preinit_state (lua_State *L, global_State *g) {
|
||||
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));
|
||||
L->oldpc = 0;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
lua_State *L1;
|
||||
lua_lock(L);
|
||||
g = G(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_VTHREAD;
|
||||
/* 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->nCcalls = getCcalls(L);
|
||||
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);
|
||||
luai_userstatefree(L1);
|
||||
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);
|
||||
L->ci = ci = &L->base_ci; /* unwind CallInfo list */
|
||||
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
|
||||
ci->func = L->stack;
|
||||
ci->callstatus = CIST_C;
|
||||
status = luaF_close(L, L->stack, CLOSEPROTECT);
|
||||
if (status != CLOSEPROTECT) /* real errors? */
|
||||
luaD_seterrorobj(L, status, L->stack + 1);
|
||||
else {
|
||||
status = LUA_OK;
|
||||
L->top = L->stack + 1;
|
||||
}
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
L->status = status;
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,71 +386,85 @@ 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->tt = LUA_TTHREAD;
|
||||
g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
|
||||
L = &l->l.l;
|
||||
g = &l->g;
|
||||
L->tt = LUA_VTHREAD;
|
||||
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;
|
||||
incnny(L); /* main thread is always non yieldable */
|
||||
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->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
|
||||
g->finobjsur = g->finobjold1 = 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) {
|
||||
L = G(L)->mainthread; /* only the main thread can be closed */
|
||||
lua_lock(L);
|
||||
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);
|
||||
luai_userstateclose(L);
|
||||
L = G(L)->mainthread; /* only the main thread can be closed */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
380
lstate.h
380
lstate.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lstate.h,v 2.23 2005/07/09 13:22:34 roberto Exp roberto $
|
||||
** $Id: lstate.h $
|
||||
** Global State
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -14,115 +14,318 @@
|
||||
#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;
|
||||
** 'old1' -> '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' -> 'finobjold1': survived """";
|
||||
** 'finobjold1' -> 'finobjrold': just old """";
|
||||
** 'finobjrold' -> NULL: really old """".
|
||||
**
|
||||
** All lists can contain elements older than their main ages, due
|
||||
** to 'luaC_checkfinalizer' and 'udata2finalize', which move
|
||||
** objects between the normal lists and the "marked for finalization"
|
||||
** lists. Moreover, barriers can age young objects in young lists as
|
||||
** OLD0, which then become OLD1. However, a list never contains
|
||||
** elements younger than their main ages.
|
||||
**
|
||||
** The generational collector also uses a pointer 'firstold1', which
|
||||
** points to the first OLD1 object in the list. It is used to optimize
|
||||
** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc'
|
||||
** and 'reallyold', but often the list has no OLD1 objects or they are
|
||||
** after 'old1'.) Note the difference between it and 'old1':
|
||||
** 'firstold1': no OLD1 objects before this point; there can be all
|
||||
** ages after it.
|
||||
** 'old1': no objects younger than OLD1 after this point.
|
||||
*/
|
||||
|
||||
/*
|
||||
** 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 (with two exceptions explained below):
|
||||
**
|
||||
** '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.
|
||||
**
|
||||
** The exceptions to that "gray rule" are:
|
||||
** - TOUCHED2 objects in generational mode stay in a gray list (because
|
||||
** they must be visited again at the end of the cycle), but they are
|
||||
** marked black because assignments to them must activate barriers (to
|
||||
** move them back to TOUCHED1).
|
||||
** - Open upvales are kept gray to avoid barriers, but they stay out
|
||||
** of gray lists. (They don't even have a 'gclist' field.)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** 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 */
|
||||
const Instruction *savedpc;
|
||||
int nresults; /* expected number of results from this function */
|
||||
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;
|
||||
volatile 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 */
|
||||
int sweepstrgc; /* position of sweep in `strt' */
|
||||
GCObject *rootgc; /* list of all collectable objects */
|
||||
GCObject **sweepgc; /* position of sweep in `rootgc' */
|
||||
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 *old1; /* start of old1 objects */
|
||||
GCObject *reallyold; /* objects more than one cycle old ("really old") */
|
||||
GCObject *firstold1; /* first OLD1 object in the list (if any) */
|
||||
GCObject *finobjsur; /* list of survival objects with finalizers */
|
||||
GCObject *finobjold1; /* list of old1 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 */
|
||||
StkId stack_last; /* last free slot in the stack */
|
||||
StkId stack; /* stack base */
|
||||
CallInfo *end_ci; /* points after end of ci array*/
|
||||
CallInfo *base_ci; /* array of CallInfo's */
|
||||
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 oldpc; /* last pc traced */
|
||||
int stacksize;
|
||||
int size_ci; /* size of array `base_ci' */
|
||||
unsigned short nCcalls; /* number of nested C calls */
|
||||
lu_byte hookmask;
|
||||
lu_byte allowhook;
|
||||
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) */
|
||||
volatile l_signalT hookmask;
|
||||
};
|
||||
|
||||
|
||||
@@ -130,39 +333,68 @@ struct lua_State {
|
||||
|
||||
|
||||
/*
|
||||
** Union of all collectable objects
|
||||
** Union of all collectable objects (only for conversions)
|
||||
** ISO C99, 6.5.2.3 p.5:
|
||||
** "if a union contains several structures that share a common initial
|
||||
** sequence [...], and if the union object currently contains one
|
||||
** of these structures, it is permitted to inspect the common initial
|
||||
** part of any of them anywhere that a declaration of the complete type
|
||||
** of the union is visible."
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** ISO C99, 6.7.2.1 p.14:
|
||||
** "A pointer to a union object, suitably converted, points to each of
|
||||
** its members [...], and vice versa."
|
||||
*/
|
||||
#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_VUSERDATA, &((cast_u(o))->u))
|
||||
#define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l))
|
||||
#define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((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_VTABLE, &((cast_u(o))->h))
|
||||
#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p))
|
||||
#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th))
|
||||
#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((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
|
||||
|
||||
|
||||
324
lstring.c
324
lstring.c
@@ -1,111 +1,285 @@
|
||||
/*
|
||||
** $Id: lstring.c,v 2.7 2005/02/18 12:40:02 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 long 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_VLNGSTR && b->tt == LUA_VLNGSTR);
|
||||
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,
|
||||
size_t step) {
|
||||
unsigned int h = seed ^ cast_uint(l);
|
||||
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_VLNGSTR);
|
||||
if (ts->extra == 0) { /* no hash? */
|
||||
size_t len = ts->u.lnglen;
|
||||
size_t step = (len >> LUAI_HASHLIMIT) + 1;
|
||||
ts->hash = luaS_hash(getstr(ts), len, ts->hash, step);
|
||||
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_VLNGSTR, 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, 1);
|
||||
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_VSHRSTR, 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_VUSERDATA, 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;
|
||||
}
|
||||
|
||||
|
||||
41
lstring.h
41
lstring.h
@@ -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,52 @@
|
||||
#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)))
|
||||
/*
|
||||
** Size of a TString: Size of the header plus space for the string
|
||||
** itself (including final '\0').
|
||||
*/
|
||||
#define sizelstring(l) (offsetof(TString, contents) + ((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_VSHRSTR && (s)->extra > 0)
|
||||
|
||||
|
||||
/*
|
||||
** equality for short strings, which are always internalized
|
||||
*/
|
||||
#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b))
|
||||
|
||||
|
||||
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l,
|
||||
unsigned int seed, size_t step);
|
||||
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
|
||||
|
||||
46
ltable.h
46
ltable.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: ltable.h,v 2.9 2006/01/10 12:51:53 roberto Exp roberto $
|
||||
** $Id: ltable.h $
|
||||
** Lua tables (hash)
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -11,29 +11,51 @@
|
||||
|
||||
|
||||
#define gnode(t,i) (&(t)->node[i])
|
||||
#define gkey(n) (&(n)->i_key.nk)
|
||||
#define gval(n) (&(n)->i_val)
|
||||
#define gnext(n) ((n)->i_key.nk.next)
|
||||
|
||||
#define key2tval(n) (&(n)->i_key.tvk)
|
||||
#define gnext(n) ((n)->u.next)
|
||||
|
||||
|
||||
LUAI_FUNC const TValue *luaH_getnum (Table *t, int key);
|
||||
LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key);
|
||||
/*
|
||||
** Clear all bits of fast-access metamethods, which means that the table
|
||||
** may have any of these metamethods. (First access that fails after the
|
||||
** clearing will set the bit again.)
|
||||
*/
|
||||
#define invalidateTMcache(t) ((t)->flags &= ~maskflags)
|
||||
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
#if defined(LUA_DEBUG)
|
||||
LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
|
||||
LUAI_FUNC int luaH_isdummy (Node *n);
|
||||
LUAI_FUNC int luaH_isdummy (const Table *t);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
494
ltablib.c
494
ltablib.c
@@ -1,256 +1,408 @@
|
||||
/*
|
||||
** $Id: ltablib.c,v 1.37 2005/10/21 13:47:42 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 */
|
||||
}
|
||||
return 0;
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
static int foreach (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
lua_pushnil(L); /* first key */
|
||||
while (lua_next(L, 1)) {
|
||||
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 */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int maxn (lua_State *L) {
|
||||
lua_Number max = 0;
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_pushnil(L); /* first key */
|
||||
while (lua_next(L, 1)) {
|
||||
lua_pop(L, 1); /* remove value */
|
||||
if (lua_type(L, -1) == LUA_TNUMBER) {
|
||||
lua_Number v = lua_tonumber(L, -1);
|
||||
if (v > max) max = v;
|
||||
/*
|
||||
** 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 */
|
||||
}
|
||||
lua_pushnumber(L, max);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
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 */
|
||||
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: {
|
||||
int i;
|
||||
pos = luaL_checkint(L, 2); /* 2nd argument is the position */
|
||||
if (pos > e) e = pos; /* `grow' array if necessary */
|
||||
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_rawgeti(L, 1, i-1);
|
||||
lua_rawseti(L, 1, i); /* t[i] = t[i-1] */
|
||||
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 " LUA_QL("insert"));
|
||||
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;
|
||||
int i, last;
|
||||
const char *sep = luaL_optlstring(L, 2, "", &lsep);
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
i = luaL_optint(L, 3, 1);
|
||||
last = luaL_opt(L, luaL_checkint, 4, 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;
|
||||
}
|
||||
|
||||
@@ -259,20 +411,18 @@ static int sort (lua_State *L) {
|
||||
|
||||
static const luaL_Reg tab_funcs[] = {
|
||||
{"concat", tconcat},
|
||||
{"foreach", foreach},
|
||||
{"foreachi", foreachi},
|
||||
{"getn", getn},
|
||||
{"maxn", maxn},
|
||||
{"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;
|
||||
}
|
||||
|
||||
|
||||
114
ltests.h
114
ltests.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: ltests.h,v 2.17 2005/12/27 17:12:00 roberto Exp roberto $
|
||||
** $Id: ltests.h $
|
||||
** Internal Header for Debugging of the Lua Implementation
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -8,71 +8,110 @@
|
||||
#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
|
||||
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#define lua_assert(c) assert(c)
|
||||
|
||||
/* turn on assertions */
|
||||
#define LUAI_ASSERT
|
||||
|
||||
|
||||
|
||||
/* 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 {
|
||||
int failnext;
|
||||
unsigned long numblocks;
|
||||
unsigned long total;
|
||||
unsigned long maxmem;
|
||||
unsigned long memlimit;
|
||||
unsigned long countlimit;
|
||||
unsigned long objcount[LUA_NUMTYPES];
|
||||
} 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
|
||||
/*
|
||||
** Function to traverse and check all memory used by Lua
|
||||
*/
|
||||
LUAI_FUNC int lua_checkmemory (lua_State *L);
|
||||
|
||||
|
||||
typedef struct CallInfo *pCallInfo;
|
||||
|
||||
int lua_checkmemory (lua_State *L);
|
||||
int lua_checkpc (lua_State *L, pCallInfo ci);
|
||||
/*
|
||||
** Function to print an object GC-friendly
|
||||
*/
|
||||
struct GCObject;
|
||||
LUAI_FUNC void lua_printobj (lua_State *L, struct GCObject *o);
|
||||
|
||||
|
||||
/* test for lock/unlock */
|
||||
#undef luai_userstateopen
|
||||
#undef luai_userstatethread
|
||||
#undef lua_lock
|
||||
#undef lua_unlock
|
||||
#undef LUAI_EXTRASPACE
|
||||
|
||||
struct L_EXTRA { int lock; int *plock; };
|
||||
#define LUAI_EXTRASPACE sizeof(struct L_EXTRA)
|
||||
#define getlock(l) (cast(struct L_EXTRA *, l) - 1)
|
||||
#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_userstatethread(l,l1) (getlock(l1)->plock = getlock(l)->plock)
|
||||
#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
|
||||
|
||||
|
||||
@@ -80,7 +119,26 @@ int luaB_opentests (lua_State *L);
|
||||
/* 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
|
||||
|
||||
#define STRCACHE_N 23
|
||||
#define STRCACHE_M 5
|
||||
|
||||
#undef LUAI_USER_ALIGNMENT_T
|
||||
#define LUAI_USER_ALIGNMENT_T union { char b[sizeof(void*) * 8]; }
|
||||
|
||||
|
||||
/* make stack-overflow tests run faster */
|
||||
#undef LUAI_MAXSTACK
|
||||
#define LUAI_MAXSTACK 50000
|
||||
|
||||
|
||||
/* force Lua to use its own implementations */
|
||||
#undef lua_strx2number
|
||||
#undef lua_number2strx
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
227
ltm.c
227
ltm.c
@@ -1,44 +1,54 @@
|
||||
/*
|
||||
** $Id: ltm.c,v 2.7 2005/12/22 16:19:56 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_TOTALTYPES] = {
|
||||
"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,9 +58,9 @@ 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? */
|
||||
if (notm(tm)) { /* no tag method? */
|
||||
events->flags |= cast_byte(1u<<event); /* cache this fact */
|
||||
return NULL;
|
||||
}
|
||||
@@ -70,6 +80,191 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls an order tag method.
|
||||
** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old
|
||||
** behavior: if there is no '__le', try '__lt', based on l <= r iff
|
||||
** !(r < l) (assuming a total order). If the metamethod yields during
|
||||
** this substitution, the continuation has to know about it (to negate
|
||||
** the result of r<l); bit CIST_LEQ in the call status keeps that
|
||||
** information.
|
||||
*/
|
||||
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;
|
||||
luaD_checkstack(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 */
|
||||
checkstackGCp(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));
|
||||
}
|
||||
|
||||
|
||||
61
ltm.h
61
ltm.h
@@ -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,91 @@
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
/*
|
||||
** Mask with 1 in all fast-access methods. A 1 in any of these bits
|
||||
** in the flag of a (meta)table means the metatable does not have the
|
||||
** corresponding metamethod field. (Bit 7 of the flag is used for
|
||||
** 'isrealasize'.)
|
||||
*/
|
||||
#define maskflags (~(~0u << (TM_EQ + 1)))
|
||||
|
||||
|
||||
/*
|
||||
** 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_TOTALTYPES];)
|
||||
|
||||
|
||||
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
|
||||
|
||||
711
lua.c
711
lua.c
@@ -1,16 +1,19 @@
|
||||
/*
|
||||
** $Id: lua.c,v 1.159 2006/05/24 14:16:39 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,152 +21,426 @@
|
||||
#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) */
|
||||
lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
|
||||
int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
|
||||
signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
|
||||
lua_sethook(globalL, lstop, flag, 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"
|
||||
" -e stat execute string " LUA_QL("stat") "\n"
|
||||
" -l name require library " LUA_QL("name") "\n"
|
||||
" -i enter interactive mode after executing " LUA_QL("script") "\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"
|
||||
" -E ignore environment variables\n"
|
||||
" -W turn warnings on\n"
|
||||
" -- stop handling options\n"
|
||||
" - execute stdin and 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 */
|
||||
/* force a complete garbage collection in case of errors */
|
||||
if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
|
||||
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_RELEASE " " LUA_COPYRIGHT);
|
||||
lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT));
|
||||
lua_writeline();
|
||||
}
|
||||
|
||||
|
||||
static int getargs (lua_State *L, char **argv, int n) {
|
||||
int narg;
|
||||
int i;
|
||||
int argc = 0;
|
||||
while (argv[argc]) argc++; /* count total number of arguments */
|
||||
narg = argc - (n + 1); /* number of arguments to the script */
|
||||
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_createtable(L, narg, n + 1);
|
||||
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;
|
||||
}
|
||||
@@ -172,219 +449,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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static int handle_script (lua_State *L, char **argv, int n) {
|
||||
int status;
|
||||
const char *fname;
|
||||
int narg = getargs(L, argv, n); /* collect arguments */
|
||||
lua_setglobal(L, "arg");
|
||||
fname = argv[n];
|
||||
if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)
|
||||
fname = NULL; /* stdin */
|
||||
status = luaL_loadfile(L, fname);
|
||||
lua_insert(L, -(narg+1));
|
||||
if (status == 0)
|
||||
status = docall(L, narg, 0);
|
||||
else
|
||||
lua_pop(L, narg);
|
||||
return report(L, status);
|
||||
}
|
||||
|
||||
|
||||
/* check that argument has no extra characters at the end */
|
||||
#define notail(x) {if ((x)[2] != '\0') return -1;}
|
||||
|
||||
|
||||
static int collectargs (char **argv, int *pi, int *pv, int *pe) {
|
||||
int i;
|
||||
for (i = 1; argv[i] != NULL; i++) {
|
||||
if (argv[i][0] != '-') /* not an option? */
|
||||
return i;
|
||||
switch (argv[i][1]) { /* option */
|
||||
case '-':
|
||||
notail(argv[i]);
|
||||
return (argv[i+1] != NULL ? i+1 : 0);
|
||||
case '\0':
|
||||
return i;
|
||||
case 'i':
|
||||
notail(argv[i]);
|
||||
*pi = 1; /* go through */
|
||||
case 'v':
|
||||
notail(argv[i]);
|
||||
*pv = 1;
|
||||
break;
|
||||
case 'e':
|
||||
*pe = 1; /* go through */
|
||||
case 'l':
|
||||
if (argv[i][2] == '\0') {
|
||||
i++;
|
||||
if (argv[i] == NULL) return -1;
|
||||
}
|
||||
break;
|
||||
default: return -1; /* invalid option */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int runargs (lua_State *L, char **argv, int n) {
|
||||
int i;
|
||||
for (i = 1; i < n; i++) {
|
||||
if (argv[i] == NULL) continue;
|
||||
lua_assert(argv[i][0] == '-');
|
||||
switch (argv[i][1]) { /* option */
|
||||
case 'e': {
|
||||
const char *chunk = argv[i] + 2;
|
||||
if (*chunk == '\0') chunk = argv[++i];
|
||||
lua_assert(chunk != NULL);
|
||||
if (dostring(L, chunk, "=(command line)") != 0)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
const char *filename = argv[i] + 2;
|
||||
if (*filename == '\0') filename = argv[++i];
|
||||
lua_assert(filename != NULL);
|
||||
if (dolibrary(L, filename))
|
||||
return 1; /* stop if file fails */
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
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);
|
||||
char **argv = s->argv;
|
||||
int argc = (int)lua_tointeger(L, 1);
|
||||
char **argv = (char **)lua_touserdata(L, 2);
|
||||
int script;
|
||||
int has_i = 0, has_v = 0, has_e = 0;
|
||||
globalL = L;
|
||||
int args = collectargs(argv, &script);
|
||||
luaL_checkversion(L); /* check that interpreter has correct version */
|
||||
if (argv[0] && argv[0][0]) progname = argv[0];
|
||||
lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */
|
||||
luaL_openlibs(L); /* open libraries */
|
||||
lua_gc(L, LUA_GCRESTART, 0);
|
||||
s->status = handle_luainit(L);
|
||||
if (s->status != 0) return 0;
|
||||
script = collectargs(argv, &has_i, &has_v, &has_e);
|
||||
if (script < 0) { /* invalid args? */
|
||||
print_usage();
|
||||
s->status = 1;
|
||||
if (args == has_error) { /* bad arg? */
|
||||
print_usage(argv[script]); /* 'script' has index of bad arg. */
|
||||
return 0;
|
||||
}
|
||||
if (has_v) print_version();
|
||||
s->status = runargs(L, argv, (script > 0) ? script : s->argc);
|
||||
if (s->status != 0) return 0;
|
||||
if (script)
|
||||
s->status = handle_script(L, argv, script);
|
||||
if (s->status != 0) return 0;
|
||||
if (has_i)
|
||||
dotty(L);
|
||||
else if (script == 0 && !has_e && !has_v) {
|
||||
if (lua_stdin_is_tty()) {
|
||||
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();
|
||||
dotty(L);
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
}
|
||||
else dofile(L, NULL); /* executes stdin as a file */
|
||||
}
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
348
lua.h
348
lua.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
** $Id: lua.h,v 1.217 2006/05/31 16:50:40 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,30 +16,37 @@
|
||||
#include "luaconf.h"
|
||||
|
||||
|
||||
#define LUA_VERSION "Lua 5.1"
|
||||
#define LUA_RELEASE "Lua 5.1.1"
|
||||
#define LUA_VERSION_NUM 501
|
||||
#define LUA_COPYRIGHT "Copyright (C) 1994-2006 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 "1"
|
||||
|
||||
#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-2020 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
|
||||
@@ -49,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
|
||||
@@ -81,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_NUMTYPES 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
|
||||
*/
|
||||
@@ -95,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[];
|
||||
|
||||
|
||||
/*
|
||||
@@ -110,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);
|
||||
|
||||
@@ -135,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, ...);
|
||||
@@ -174,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
|
||||
@@ -226,8 +324,11 @@ LUA_API int (lua_status) (lua_State *L);
|
||||
#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, ...);
|
||||
|
||||
|
||||
/*
|
||||
@@ -239,18 +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_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)
|
||||
@@ -259,8 +369,6 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, 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)
|
||||
@@ -270,30 +378,43 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, 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)
|
||||
#endif
|
||||
|
||||
#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0)
|
||||
|
||||
#define lua_Chunkreader lua_Reader
|
||||
#define lua_Chunkwriter lua_Writer
|
||||
#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)
|
||||
|
||||
#define LUA_NUMTAGS LUA_NUMTYPES
|
||||
|
||||
/* }============================================================== */
|
||||
|
||||
/*
|
||||
** {======================================================================
|
||||
@@ -309,7 +430,7 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
|
||||
#define LUA_HOOKRET 1
|
||||
#define LUA_HOOKLINE 2
|
||||
#define LUA_HOOKCOUNT 3
|
||||
#define LUA_HOOKTAILRET 4
|
||||
#define LUA_HOOKTAILCALL 4
|
||||
|
||||
|
||||
/*
|
||||
@@ -323,43 +444,54 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, 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-2006 Lua.org, PUC-Rio. All rights reserved.
|
||||
* Copyright (C) 1994-2020 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
|
||||
|
||||
31
lualib.h
31
lualib.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lualib.h,v 1.35 2005/08/10 18:06:58 roberto Exp roberto $
|
||||
** $Id: lualib.h $
|
||||
** Lua standard libraries
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -11,41 +11,46 @@
|
||||
#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);
|
||||
|
||||
|
||||
|
||||
#ifndef lua_assert
|
||||
#if !defined(lua_assert)
|
||||
#define lua_assert(x) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
466
lundump.c
466
lundump.c
@@ -1,14 +1,18 @@
|
||||
/*
|
||||
** $Id: lundump.c,v 1.60 2006/02/16 15:53:49 lhf Exp $
|
||||
** $Id: lundump.c $
|
||||
** load precompiled Lua chunks
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define lundump_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "ldebug.h"
|
||||
@@ -20,204 +24,310 @@
|
||||
#include "lundump.h"
|
||||
#include "lzio.h"
|
||||
|
||||
typedef struct {
|
||||
lua_State* L;
|
||||
ZIO* Z;
|
||||
Mbuffer* b;
|
||||
const char* name;
|
||||
} LoadState;
|
||||
|
||||
#ifdef LUAC_TRUST_BINARIES
|
||||
#define IF(c,s)
|
||||
#else
|
||||
#define IF(c,s) if (c) error(S,s)
|
||||
|
||||
static void error(LoadState* S, const char* why)
|
||||
{
|
||||
luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why);
|
||||
luaD_throw(S->L,LUA_ERRSYNTAX);
|
||||
}
|
||||
#if !defined(luai_verifycode)
|
||||
#define luai_verifycode(L,f) /* empty */
|
||||
#endif
|
||||
|
||||
#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size))
|
||||
#define LoadByte(S) (lu_byte)LoadChar(S)
|
||||
#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x))
|
||||
#define LoadVector(S,b,n,size) LoadMem(S,b,n,size)
|
||||
|
||||
static void LoadBlock(LoadState* S, void* b, size_t size)
|
||||
{
|
||||
size_t r=luaZ_read(S->Z,b,size);
|
||||
IF (r!=0, "unexpected end");
|
||||
typedef struct {
|
||||
lua_State *L;
|
||||
ZIO *Z;
|
||||
const char *name;
|
||||
} LoadState;
|
||||
|
||||
|
||||
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 LoadChar(LoadState* S)
|
||||
{
|
||||
char x;
|
||||
LoadVar(S,x);
|
||||
return x;
|
||||
|
||||
/*
|
||||
** 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 int LoadInt(LoadState* S)
|
||||
{
|
||||
int x;
|
||||
LoadVar(S,x);
|
||||
IF (x<0, "bad integer");
|
||||
return x;
|
||||
|
||||
#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 lua_Number LoadNumber(LoadState* S)
|
||||
{
|
||||
lua_Number x;
|
||||
LoadVar(S,x);
|
||||
return x;
|
||||
|
||||
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 TString* LoadString(LoadState* S)
|
||||
{
|
||||
size_t size;
|
||||
LoadVar(S,size);
|
||||
if (size==0)
|
||||
return NULL;
|
||||
else
|
||||
{
|
||||
char* s=luaZ_openspace(S->L,S->b,size);
|
||||
LoadBlock(S,s,size);
|
||||
return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */
|
||||
}
|
||||
|
||||
static size_t loadSize (LoadState *S) {
|
||||
return loadUnsigned(S, ~(size_t)0);
|
||||
}
|
||||
|
||||
static void LoadCode(LoadState* S, Proto* f)
|
||||
{
|
||||
int n=LoadInt(S);
|
||||
f->code=luaM_newvector(S->L,n,Instruction);
|
||||
f->sizecode=n;
|
||||
LoadVector(S,f->code,n,sizeof(Instruction));
|
||||
|
||||
static int loadInt (LoadState *S) {
|
||||
return cast_int(loadUnsigned(S, INT_MAX));
|
||||
}
|
||||
|
||||
static Proto* LoadFunction(LoadState* S, TString* p);
|
||||
|
||||
static void LoadConstants(LoadState* S, Proto* f)
|
||||
{
|
||||
int i,n;
|
||||
n=LoadInt(S);
|
||||
f->k=luaM_newvector(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=LoadChar(S);
|
||||
switch (t)
|
||||
{
|
||||
case LUA_TNIL:
|
||||
setnilvalue(o);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
setbvalue(o,LoadChar(S));
|
||||
break;
|
||||
case LUA_TNUMBER:
|
||||
setnvalue(o,LoadNumber(S));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
setsvalue2n(S->L,o,LoadString(S));
|
||||
break;
|
||||
default:
|
||||
IF (1, "bad constant");
|
||||
break;
|
||||
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 into prototype 'p'.
|
||||
*/
|
||||
static TString *loadStringN (LoadState *S, Proto *p) {
|
||||
lua_State *L = S->L;
|
||||
TString *ts;
|
||||
size_t size = loadSize(S);
|
||||
if (size == 0) /* no string? */
|
||||
return NULL;
|
||||
else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */
|
||||
char buff[LUAI_MAXSHORTLEN];
|
||||
loadVector(S, buff, size); /* load string into buffer */
|
||||
ts = luaS_newlstr(L, buff, size); /* create string */
|
||||
}
|
||||
}
|
||||
n=LoadInt(S);
|
||||
f->p=luaM_newvector(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]=LoadFunction(S,f->source);
|
||||
else { /* long string */
|
||||
ts = luaS_createlngstrobj(L, size); /* create string */
|
||||
setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */
|
||||
luaD_inctop(L);
|
||||
loadVector(S, getstr(ts), size); /* load directly in final place */
|
||||
L->top--; /* pop string */
|
||||
}
|
||||
luaC_objbarrier(L, p, ts);
|
||||
return ts;
|
||||
}
|
||||
|
||||
static void LoadDebug(LoadState* S, Proto* f)
|
||||
{
|
||||
int i,n;
|
||||
n=LoadInt(S);
|
||||
f->lineinfo=luaM_newvector(S->L,n,int);
|
||||
f->sizelineinfo=n;
|
||||
LoadVector(S,f->lineinfo,n,sizeof(int));
|
||||
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);
|
||||
}
|
||||
n=LoadInt(S);
|
||||
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)
|
||||
{
|
||||
Proto* f=luaF_newproto(S->L);
|
||||
setptvalue2s(S->L,S->L->top,f); incr_top(S->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);
|
||||
LoadCode(S,f);
|
||||
LoadConstants(S,f);
|
||||
LoadDebug(S,f);
|
||||
IF (!luaG_checkcode(f), "bad code");
|
||||
S->L->top--;
|
||||
return f;
|
||||
}
|
||||
|
||||
static void LoadHeader(LoadState* S)
|
||||
{
|
||||
char h[LUAC_HEADERSIZE];
|
||||
char s[LUAC_HEADERSIZE];
|
||||
luaU_header(h);
|
||||
LoadBlock(S,s,LUAC_HEADERSIZE);
|
||||
IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header");
|
||||
}
|
||||
|
||||
/*
|
||||
** load precompiled chunk
|
||||
** Load a non-nullable string into prototype 'p'.
|
||||
*/
|
||||
Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
|
||||
{
|
||||
LoadState S;
|
||||
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;
|
||||
S.b=buff;
|
||||
LoadHeader(&S);
|
||||
return LoadFunction(&S,luaS_newliteral(L,"=?"));
|
||||
static TString *loadString (LoadState *S, Proto *p) {
|
||||
TString *st = loadStringN(S, p);
|
||||
if (st == NULL)
|
||||
error(S, "bad format for constant string");
|
||||
return st;
|
||||
}
|
||||
|
||||
/*
|
||||
* make header
|
||||
*/
|
||||
void luaU_header (char* h)
|
||||
{
|
||||
int x=1;
|
||||
memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1);
|
||||
h+=sizeof(LUA_SIGNATURE)-1;
|
||||
*h++=(char)LUAC_VERSION;
|
||||
*h++=(char)LUAC_FORMAT;
|
||||
*h++=(char)*(char*)&x; /* endianness */
|
||||
*h++=(char)sizeof(int);
|
||||
*h++=(char)sizeof(size_t);
|
||||
*h++=(char)sizeof(Instruction);
|
||||
*h++=(char)sizeof(lua_Number);
|
||||
*h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */
|
||||
|
||||
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 void loadFunction(LoadState *S, Proto *f, TString *psource);
|
||||
|
||||
|
||||
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_VNIL:
|
||||
setnilvalue(o);
|
||||
break;
|
||||
case LUA_VFALSE:
|
||||
setbfvalue(o);
|
||||
break;
|
||||
case LUA_VTRUE:
|
||||
setbtvalue(o);
|
||||
break;
|
||||
case LUA_VNUMFLT:
|
||||
setfltvalue(o, loadNumber(S));
|
||||
break;
|
||||
case LUA_VNUMINT:
|
||||
setivalue(o, loadInteger(S));
|
||||
break;
|
||||
case LUA_VSHRSTR:
|
||||
case LUA_VLNGSTR:
|
||||
setsvalue2n(S->L, o, loadString(S, f));
|
||||
break;
|
||||
default: lua_assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
luaC_objbarrier(S->L, f, f->p[i]);
|
||||
loadFunction(S, f->p[i], f->source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Load the upvalues for a function. The names must be filled first,
|
||||
** because the filling of the other fields can raise read errors and
|
||||
** the creation of the error message can call an emergency collection;
|
||||
** in that case all prototypes must be consistent for the GC.
|
||||
*/
|
||||
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++) /* make array valid for GC */
|
||||
f->upvalues[i].name = NULL;
|
||||
for (i = 0; i < n; i++) { /* following calls can raise errors */
|
||||
f->upvalues[i].instack = loadByte(S);
|
||||
f->upvalues[i].idx = loadByte(S);
|
||||
f->upvalues[i].kind = loadByte(S);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
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, f);
|
||||
}
|
||||
|
||||
|
||||
static void loadFunction (LoadState *S, Proto *f, TString *psource) {
|
||||
f->source = loadStringN(S, f);
|
||||
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 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 (loadByte(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.
|
||||
*/
|
||||
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);
|
||||
luaC_objbarrier(L, cl, cl->p);
|
||||
loadFunction(&S, cl->p, NULL);
|
||||
lua_assert(cl->nupvalues == cl->p->sizeupvalues);
|
||||
luai_verifycode(L, cl->p);
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
40
lundump.h
40
lundump.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lundump.h,v 1.39 2005/11/01 17:04:55 lhf Exp lhf $
|
||||
** $Id: lundump.h $
|
||||
** load precompiled Lua chunks
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -7,30 +7,30 @@
|
||||
#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);
|
||||
|
||||
/* make header; from lundump.c */
|
||||
LUAI_FUNC void luaU_header (char* h);
|
||||
/* 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)
|
||||
|
||||
/*
|
||||
** Encode major-minor version in one byte, one nibble for each
|
||||
*/
|
||||
#define MYINT(s) (s[0]-'0') /* assume one-digit numerals */
|
||||
#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR))
|
||||
|
||||
#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* f, lua_Writer w, void* data, int strip);
|
||||
|
||||
#ifdef luac_c
|
||||
/* print one chunk; from print.c */
|
||||
LUAI_FUNC void luaU_print (const Proto* f, int full);
|
||||
#endif
|
||||
|
||||
/* for header of binary files -- this is Lua 5.1 */
|
||||
#define LUAC_VERSION 0x51
|
||||
|
||||
/* for header of binary files -- this is the official format */
|
||||
#define LUAC_FORMAT 0
|
||||
|
||||
/* size of header of binary files */
|
||||
#define LUAC_HEADERSIZE 12
|
||||
LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w,
|
||||
void* data, int strip);
|
||||
|
||||
#endif
|
||||
|
||||
289
lutf8lib.c
Normal file
289
lutf8lib.c
Normal file
@@ -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 bounds");
|
||||
luaL_argcheck(L, --posj < (lua_Integer)len, 3,
|
||||
"final position out of bounds");
|
||||
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 bounds");
|
||||
luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds");
|
||||
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 bounds");
|
||||
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;
|
||||
}
|
||||
|
||||
132
lvm.h
132
lvm.h
@@ -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,122 @@
|
||||
#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 F2Ieq
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Rounding modes for float->integer coercion
|
||||
*/
|
||||
typedef enum {
|
||||
F2Ieq, /* no rounding; accepts only integral values */
|
||||
F2Ifloor, /* takes the floor of the number */
|
||||
F2Iceil /* takes the ceil of the number */
|
||||
} F2Imod;
|
||||
|
||||
|
||||
/* 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, F2Imod mode);
|
||||
LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p,
|
||||
F2Imod mode);
|
||||
LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod 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
|
||||
|
||||
52
lzio.c
52
lzio.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
17
lzio.h
17
lzio.h
@@ -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) */
|
||||
};
|
||||
|
||||
|
||||
239
makefile
239
makefile
@@ -1,38 +1,76 @@
|
||||
# makefile for building Lua
|
||||
# see INSTALL for installation instructions
|
||||
# see ../Makefile and luaconf.h for further customization
|
||||
# Developer's makefile for building Lua
|
||||
# see luaconf.h for further customization
|
||||
|
||||
# == 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 \
|
||||
|
||||
# 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)
|
||||
|
||||
# Some useful compiler options for internal tests:
|
||||
# -DLUAI_ASSERT turns on all assertions inside Lua.
|
||||
# -DHARDSTACKTESTS forces a reallocation of the stack at every point where
|
||||
# the stack can be reallocated.
|
||||
# -DHARDMEMTESTS forces a full collection at all points where the collector
|
||||
# can run.
|
||||
# -DEMERGENCYGCTESTS forces an emergency collection at every single allocation.
|
||||
# -DEXTERNMEMCHECK removes internal consistency checking of blocks being
|
||||
# deallocated (useful when an external tool like valgrind does the check).
|
||||
# -DMAXINDEXRK=k limits range of constants in RK instruction operands.
|
||||
# -DLUA_COMPAT_5_3
|
||||
|
||||
# -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
|
||||
|
||||
# -DEXTERNMEMCHECK -DHARDSTACKTESTS
|
||||
# -g -DLUA_USER_H='"ltests.h"'
|
||||
# -fomit-frame-pointer #-pg -malign-double
|
||||
TESTS= -g -DLUA_USER_H='"ltests.h"'
|
||||
|
||||
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= -Wall $(MYCFLAGS) -O2
|
||||
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_LINUX
|
||||
MYLDFLAGS= -Wl,-E
|
||||
MYLIBS= -ldl -lreadline -lhistory -lncurses
|
||||
|
||||
|
||||
|
||||
# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========
|
||||
@@ -41,24 +79,23 @@ 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
|
||||
|
||||
ALL_T= $(CORE_T) $(LUA_T) $(LUAC_T)
|
||||
ALL_O= $(CORE_O) $(LUA_O) $(LUAC_O) $(AUX_O) $(LIB_O)
|
||||
ALL_T= $(CORE_T) $(LUA_T)
|
||||
ALL_O= $(CORE_O) $(LUA_O) $(AUX_O) $(LIB_O)
|
||||
ALL_A= $(CORE_T)
|
||||
|
||||
all: $(ALL_T)
|
||||
touch all
|
||||
|
||||
o: $(ALL_O)
|
||||
|
||||
@@ -71,11 +108,18 @@ $(CORE_T): $(CORE_O) $(AUX_O) $(LIB_O)
|
||||
$(LUA_T): $(LUA_O) $(CORE_T)
|
||||
$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(CORE_T) $(LIBS) $(MYLIBS) $(DL)
|
||||
|
||||
$(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:
|
||||
rcsclean -u
|
||||
$(RM) $(ALL_T) $(ALL_O)
|
||||
|
||||
depend:
|
||||
@@ -92,64 +136,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)
|
||||
|
||||
519
manual/2html
Executable file
519
manual/2html
Executable file
@@ -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>
|
||||
© 2020 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)
|
||||
|
||||
9370
manual/manual.of
Normal file
9370
manual/manual.of
Normal file
File diff suppressed because it is too large
Load Diff
107
onelua.c
Normal file
107
onelua.c
Normal file
@@ -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
testes/all.lua
Normal file
321
testes/all.lua
Normal file
@@ -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.
|
||||
*****************************************************************************
|
||||
]]
|
||||
|
||||
1505
testes/api.lua
Normal file
1505
testes/api.lua
Normal file
File diff suppressed because it is too large
Load Diff
515
testes/attrib.lua
Normal file
515
testes/attrib.lua
Normal file
@@ -0,0 +1,515 @@
|
||||
-- $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 occurrence 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
|
||||
|
||||
|
||||
do print"testing 'require' message"
|
||||
local oldpath = package.path
|
||||
local oldcpath = package.cpath
|
||||
|
||||
package.path = "?.lua;?/?"
|
||||
package.cpath = "?.so;?/init"
|
||||
|
||||
local st, msg = pcall(require, 'XXX')
|
||||
|
||||
local expected = [[module 'XXX' not found:
|
||||
no field package.preload['XXX']
|
||||
no file 'XXX.lua'
|
||||
no file 'XXX/XXX'
|
||||
no file 'XXX.so'
|
||||
no file 'XXX/init']]
|
||||
|
||||
assert(msg == expected)
|
||||
|
||||
package.path = oldpath
|
||||
package.cpath = oldcpath
|
||||
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
|
||||
|
||||
82
testes/big.lua
Normal file
82
testes/big.lua
Normal file
@@ -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 concatenate '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'
|
||||
346
testes/bitwise.lua
Normal file
346
testes/bitwise.lua
Normal file
@@ -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'
|
||||
|
||||
78
testes/bwcoercion.lua
Normal file
78
testes/bwcoercion.lua
Normal file
@@ -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
|
||||
|
||||
481
testes/calls.lua
Normal file
481
testes/calls.lua
Normal file
@@ -0,0 +1,481 @@
|
||||
-- $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)
|
||||
|
||||
|
||||
print"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
|
||||
|
||||
|
||||
|
||||
do -- tail calls x chain of __call
|
||||
local n = 10000 -- depth
|
||||
|
||||
local function foo ()
|
||||
if n == 0 then return 1023
|
||||
else n = n - 1; return foo()
|
||||
end
|
||||
end
|
||||
|
||||
-- build a chain of __call metamethods ending in function 'foo'
|
||||
for i = 1, 100 do
|
||||
foo = setmetatable({}, {__call = foo})
|
||||
end
|
||||
|
||||
-- call the first one as a tail call in a new coroutine
|
||||
-- (to ensure stack is not preallocated)
|
||||
assert(coroutine.wrap(function() return foo() end)() == 1023)
|
||||
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)
|
||||
|
||||
|
||||
do -- another bug (in 5.4.0)
|
||||
-- loading a binary long string interrupted by GC cycles
|
||||
local f = string.dump(function ()
|
||||
return '01234567890123456789012345678901234567890123456789'
|
||||
end)
|
||||
f = load(read1(f))
|
||||
assert(f() == '01234567890123456789012345678901234567890123456789')
|
||||
end
|
||||
|
||||
|
||||
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), "read", "t"))
|
||||
assert(a()(2)(3)(10) == 15)
|
||||
|
||||
-- repeat the test loading a binary chunk
|
||||
x = string.dump(a)
|
||||
a = assert(load(read1(x), "read", "b"))
|
||||
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("c4BBc6BBB",
|
||||
"\27Lua", -- signature
|
||||
0x54, -- version 5.4 (0x54)
|
||||
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)
|
||||
)
|
||||
local c = string.dump(function ()
|
||||
local a = 1; local b = 3;
|
||||
local f = function () return a + b + _ENV.c; end -- upvalues
|
||||
local s1 = "a constant"
|
||||
local s2 = "another constant"
|
||||
return a + b * 3
|
||||
end)
|
||||
|
||||
assert(assert(load(c))() == 10)
|
||||
|
||||
-- check header
|
||||
assert(string.sub(c, 1, #header) == header)
|
||||
-- check LUAC_INT and LUAC_NUM
|
||||
local ci, cn = string.unpack("jn", c, #header + 1)
|
||||
assert(ci == 0x5678 and cn == 370.5)
|
||||
|
||||
-- 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
|
||||
end
|
||||
|
||||
print('OK')
|
||||
return deep
|
||||
270
testes/closure.lua
Normal file
270
testes/closure.lua
Normal file
@@ -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
testes/code.lua
Normal file
443
testes/code.lua
Normal file
@@ -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, 'LOADFALSE', 'RETURN1')
|
||||
check(function () return not not kFalse end, 'LOADFALSE', 'RETURN1')
|
||||
check(function () return not not true end, 'LOADTRUE', 'RETURN1')
|
||||
check(function () return not not k3 end, 'LOADTRUE', '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', 'LOADTRUE', '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'
|
||||
|
||||
377
testes/constructs.lua
Normal file
377
testes/constructs.lua
Normal file
@@ -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'
|
||||
1025
testes/coroutine.lua
Normal file
1025
testes/coroutine.lua
Normal file
File diff suppressed because it is too large
Load Diff
176
testes/cstack.lua
Normal file
176
testes/cstack.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
-- $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 crashes, 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.
|
||||
|
||||
|
||||
-- get and print original limit
|
||||
local origlimit <const> = debug.setcstacklimit(400)
|
||||
print("default stack limit: " .. origlimit)
|
||||
|
||||
|
||||
-- Do the tests using the original limit. Or else you may want to change
|
||||
-- 'currentlimit' to lower values to avoid a seg. fault or to higher
|
||||
-- values to check whether they are reliable.
|
||||
local currentlimit <const> = 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
|
||||
|
||||
-- auxiliary function to keep 'count' on the screen even if the program
|
||||
-- crashes.
|
||||
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) -- erase previous value and write new one
|
||||
end
|
||||
|
||||
|
||||
do print("testing simple recursion:")
|
||||
count = 0
|
||||
local function foo ()
|
||||
progress()
|
||||
foo() -- do recursive calls until a stack error (or crash)
|
||||
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 -- bug in 5.4.0
|
||||
print("testing limits in coroutines inside deep calls")
|
||||
count = 0
|
||||
local lim = 1000
|
||||
local function stack (n)
|
||||
progress()
|
||||
if n > 0 then return stack(n - 1) + 1
|
||||
else coroutine.wrap(function ()
|
||||
stack(lim)
|
||||
end)()
|
||||
end
|
||||
end
|
||||
|
||||
print(xpcall(stack, function () return "ok" end, lim))
|
||||
end
|
||||
|
||||
|
||||
do print("testing changes in C-stack limit")
|
||||
|
||||
-- Just an alternative limit, different from the current one
|
||||
-- (smaller to avoid stack overflows)
|
||||
local alterlimit <const> = currentlimit * 8 // 10
|
||||
|
||||
assert(not debug.setcstacklimit(0)) -- limit too small
|
||||
assert(not debug.setcstacklimit(50000)) -- limit too large
|
||||
local co = coroutine.wrap (function ()
|
||||
return debug.setcstacklimit(alterlimit)
|
||||
end)
|
||||
assert(not co()) -- 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
|
||||
|
||||
-- set limit to 'alterlimit'
|
||||
assert(debug.setcstacklimit(alterlimit) == currentlimit)
|
||||
local limalter <const> = check()
|
||||
-- set a very low limit (given that there are already several active
|
||||
-- calls to arrive here)
|
||||
local lowlimit <const> = 38
|
||||
assert(debug.setcstacklimit(lowlimit) == alterlimit)
|
||||
-- usable limit is much lower, due to active calls
|
||||
local actuallow = check()
|
||||
assert(actuallow < lowlimit - 30)
|
||||
-- now, add 'lowlimit' extra slots, which should all be available
|
||||
assert(debug.setcstacklimit(lowlimit + lowlimit) == lowlimit)
|
||||
local lim2 <const> = check()
|
||||
assert(lim2 == actuallow + lowlimit)
|
||||
|
||||
|
||||
-- 'setcstacklimit' works inside protected calls. (The new stack
|
||||
-- limit is kept when 'pcall' returns.)
|
||||
assert(pcall(function ()
|
||||
assert(debug.setcstacklimit(alterlimit) == lowlimit * 2)
|
||||
assert(check() <= limalter)
|
||||
end))
|
||||
|
||||
assert(check() == limalter)
|
||||
-- restore original limit
|
||||
assert(debug.setcstacklimit(origlimit) == alterlimit)
|
||||
end
|
||||
|
||||
|
||||
print'OK'
|
||||
980
testes/db.lua
Normal file
980
testes/db.lua
Normal file
@@ -0,0 +1,980 @@
|
||||
-- $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")
|
||||
|
||||
t = debug.getinfo(math.sin) -- C function
|
||||
assert(t.isvararg == true and t.nparams == 0 and t.nups == 0)
|
||||
|
||||
t = debug.getinfo(string.gmatch("abc", "a")) -- C closure
|
||||
assert(t.isvararg == true and t.nparams == 0 and t.nups > 0)
|
||||
|
||||
|
||||
|
||||
-- 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 (strip)
|
||||
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 -- bug in 5.4.0: line hooks in stripped code
|
||||
local function foo ()
|
||||
local a = 1
|
||||
local b = 2
|
||||
return b
|
||||
end
|
||||
|
||||
local s = load(string.dump(foo, true))
|
||||
local line = true
|
||||
debug.sethook(function (e, l)
|
||||
assert(e == "line")
|
||||
line = l
|
||||
end, "l")
|
||||
assert(s() == 2); debug.sethook(nil)
|
||||
assert(line == nil) -- hook called withoug debug info for 1st instruction
|
||||
end
|
||||
|
||||
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"
|
||||
|
||||
589
testes/errors.lua
Normal file
589
testes/errors.lua
Normal file
@@ -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')
|
||||
488
testes/events.lua
Normal file
488
testes/events.lua
Normal file
@@ -0,0 +1,488 @@
|
||||
-- $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 comparison 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)
|
||||
|
||||
|
||||
do -- test invalidating flags
|
||||
local mt = {__eq = true}
|
||||
local a = setmetatable({10}, mt)
|
||||
local b = setmetatable({10}, mt)
|
||||
mt.__eq = nil
|
||||
assert(a ~= b) -- no metamethod
|
||||
mt.__eq = function (x,y) return x[1] == y[1] end
|
||||
assert(a == b) -- must use metamethod now
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
assert(rawequal(u1, u1) and not rawequal(u1, u3))
|
||||
|
||||
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
|
||||
|
||||
|
||||
940
testes/files.lua
Normal file
940
testes/files.lua
Normal file
@@ -0,0 +1,940 @@
|
||||
-- $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")
|
||||
-- invalid mode for popen
|
||||
checkerr("invalid mode", io.popen, "cat", "")
|
||||
checkerr("invalid mode", io.popen, "cat", "r+")
|
||||
checkerr("invalid mode", io.popen, "cat", "rw")
|
||||
do -- basic tests for popen
|
||||
local file = os.tmpname()
|
||||
local f = assert(io.popen("cat - > " .. file, "w"))
|
||||
f:write("a line")
|
||||
assert(f:close())
|
||||
local f = assert(io.popen("cat - < " .. file, "r"))
|
||||
assert(f:read("a") == "a line")
|
||||
assert(f:close())
|
||||
assert(os.remove(file))
|
||||
end
|
||||
|
||||
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
testes/gc.lua
Normal file
681
testes/gc.lua
Normal file
@@ -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 = false
|
||||
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 = false
|
||||
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 -- ressurrect 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 = false
|
||||
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 = false
|
||||
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')
|
||||
172
testes/gengc.lua
Normal file
172
testes/gengc.lua
Normal file
@@ -0,0 +1,172 @@
|
||||
-- $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
|
||||
|
||||
|
||||
do
|
||||
-- ensure that 'firstold1' is corrected when object is removed from
|
||||
-- the 'allgc' list
|
||||
local function foo () end
|
||||
local old = {10}
|
||||
collectgarbage() -- make 'old' old
|
||||
assert(not T or T.gcage(old) == "old")
|
||||
setmetatable(old, {}) -- new table becomes OLD0 (barrier)
|
||||
assert(not T or T.gcage(getmetatable(old)) == "old0")
|
||||
collectgarbage("step", 0) -- new table becomes OLD1 and firstold1
|
||||
assert(not T or T.gcage(getmetatable(old)) == "old1")
|
||||
setmetatable(getmetatable(old), {__gc = foo}) -- get it out of allgc list
|
||||
collectgarbage("step", 0) -- should not seg. fault
|
||||
end
|
||||
|
||||
|
||||
do -- bug in 5.4.0
|
||||
-- When an object aged OLD1 is finalized, it is moved from the list
|
||||
-- 'finobj' to the *beginning* of the list 'allgc', but that part of the
|
||||
-- list was not being visited by 'markold'.
|
||||
local A = {}
|
||||
A[1] = false -- old anchor for object
|
||||
|
||||
-- obj finalizer
|
||||
local function gcf (obj)
|
||||
A[1] = obj -- anchor object
|
||||
assert(not T or T.gcage(obj) == "old1")
|
||||
obj = nil -- remove it from the stack
|
||||
collectgarbage("step", 0) -- do a young collection
|
||||
print(getmetatable(A[1]).x) -- metatable was collected
|
||||
end
|
||||
|
||||
collectgarbage() -- make A old
|
||||
local obj = {} -- create a new object
|
||||
collectgarbage("step", 0) -- make it a survival
|
||||
assert(not T or T.gcage(obj) == "survival")
|
||||
setmetatable(obj, {__gc = gcf, x = "+"}) -- create its metatable
|
||||
assert(not T or T.gcage(getmetatable(obj)) == "new")
|
||||
obj = nil -- clear object
|
||||
collectgarbage("step", 0) -- will call obj's finalizer
|
||||
end
|
||||
|
||||
|
||||
do -- another bug in 5.4.0
|
||||
local old = {10}
|
||||
collectgarbage() -- make 'old' old
|
||||
local co = coroutine.create(
|
||||
function ()
|
||||
local x = nil
|
||||
local f = function ()
|
||||
return x[1]
|
||||
end
|
||||
x = coroutine.yield(f)
|
||||
coroutine.yield()
|
||||
end
|
||||
)
|
||||
local _, f = coroutine.resume(co) -- create closure over 'x' in coroutine
|
||||
collectgarbage("step", 0) -- make upvalue a survival
|
||||
old[1] = {"hello"} -- 'old' go to grayagain as 'touched1'
|
||||
coroutine.resume(co, {123}) -- its value will be new
|
||||
co = nil
|
||||
collectgarbage("step", 0) -- hit the barrier
|
||||
assert(f() == 123 and old[1][1] == "hello")
|
||||
collectgarbage("step", 0) -- run the collector once more
|
||||
-- make sure old[1] was not collected
|
||||
assert(f() == 123 and old[1][1] == "hello")
|
||||
end
|
||||
|
||||
|
||||
do -- bug introduced in commit 9cf3299fa
|
||||
local t = setmetatable({}, {__mode = "kv"}) -- all-weak table
|
||||
collectgarbage() -- full collection
|
||||
assert(not T or T.gcage(t) == "old")
|
||||
t[1] = {10}
|
||||
assert(not T or (T.gcage(t) == "touched1" and T.gccolor(t) == "gray"))
|
||||
collectgarbage("step", 0) -- minor collection
|
||||
assert(not T or (T.gcage(t) == "touched2" and T.gccolor(t) == "black"))
|
||||
collectgarbage("step", 0) -- minor collection
|
||||
assert(not T or T.gcage(t) == "old") -- t should be black, but it was gray
|
||||
t[1] = {10} -- no barrier here, so t was still old
|
||||
collectgarbage("step", 0) -- minor collection
|
||||
-- t, being old, is ignored by the collection, so it is not cleared
|
||||
assert(t[1] == nil) -- fails with the bug
|
||||
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')
|
||||
|
||||
|
||||
|
||||
-- just to make sure
|
||||
assert(collectgarbage'isrunning')
|
||||
|
||||
collectgarbage(oldmode)
|
||||
|
||||
print('OK')
|
||||
|
||||
271
testes/goto.lua
Normal file
271
testes/goto.lua
Normal file
@@ -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'
|
||||
173
testes/heavy.lua
Normal file
173
testes/heavy.lua
Normal file
@@ -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"
|
||||
2
testes/libs/P1/dummy
Normal file
2
testes/libs/P1/dummy
Normal file
@@ -0,0 +1,2 @@
|
||||
# This is a dummy file just to make git keep the otherwise empty
|
||||
# directory 'P1' in the repository.
|
||||
44
testes/libs/lib1.c
Normal file
44
testes/libs/lib1.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
10
testes/libs/lib11.c
Normal file
10
testes/libs/lib11.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
23
testes/libs/lib2.c
Normal file
23
testes/libs/lib2.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
10
testes/libs/lib21.c
Normal file
10
testes/libs/lib21.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
25
testes/libs/lib22.c
Normal file
25
testes/libs/lib22.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
27
testes/libs/makefile
Normal file
27
testes/libs/makefile
Normal file
@@ -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
|
||||
318
testes/literals.lua
Normal file
318
testes/literals.lua
Normal file
@@ -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')
|
||||
736
testes/locals.lua
Normal file
736
testes/locals.lua
Normal file
@@ -0,0 +1,736 @@
|
||||
-- $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 fields 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
|
||||
|
||||
|
||||
-- testing to-be-closed x compile-time constants
|
||||
-- (there were some bugs here in Lua 5.4-rc3, due to a confusion
|
||||
-- between compile levels and stack levels of variables)
|
||||
do
|
||||
local flag = false
|
||||
local x = setmetatable({},
|
||||
{__close = function() assert(flag == false); flag = true end})
|
||||
local y <const> = nil
|
||||
local z <const> = nil
|
||||
do
|
||||
local a <close> = x
|
||||
end
|
||||
assert(flag) -- 'x' must be closed here
|
||||
end
|
||||
|
||||
do
|
||||
-- similar problem, but with implicit close in for loops
|
||||
local flag = false
|
||||
local x = setmetatable({},
|
||||
{__close = function () assert(flag == false); flag = true end})
|
||||
-- return an empty iterator, nil, nil, and 'x' to be closed
|
||||
local function a ()
|
||||
return (function () return nil end), nil, nil, x
|
||||
end
|
||||
local v <const> = 1
|
||||
local w <const> = 1
|
||||
local x <const> = 1
|
||||
local y <const> = 1
|
||||
local z <const> = 1
|
||||
for k in a() do
|
||||
a = k
|
||||
end -- ending the loop must close 'x'
|
||||
assert(flag) -- 'x' must be closed here
|
||||
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 == false)
|
||||
warn("@normal")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function checkwarn (msg)
|
||||
if T then
|
||||
assert(string.find(_WARN, msg))
|
||||
_WARN = false -- 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
testes/main.lua
Normal file
468
testes/main.lua
Normal file
@@ -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 = false
|
||||
|
||||
warn("zip", "", " ", "zap")
|
||||
assert(_WARN == "zip zap"); _WARN = false
|
||||
warn("ZIP", "", " ", "ZAP")
|
||||
assert(_WARN == "ZIP ZAP"); _WARN = false
|
||||
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")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user