y_inline_impl2.inc 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043
  1. /*
  2. Legal:
  3. Version: MPL 1.1
  4. The contents of this file are subject to the Mozilla Public License Version
  5. 1.1 the "License"; you may not use this file except in compliance with
  6. the License. You may obtain a copy of the License at
  7. http://www.mozilla.org/MPL/
  8. Software distributed under the License is distributed on an "AS IS" basis,
  9. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10. for the specific language governing rights and limitations under the
  11. License.
  12. The Original Code is the YSI framework.
  13. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  14. Portions created by the Initial Developer are Copyright C 2011
  15. the Initial Developer. All Rights Reserved.
  16. Contributors:
  17. Y_Less
  18. koolk
  19. JoeBullet/Google63
  20. g_aSlice/Slice
  21. Misiur
  22. samphunter
  23. tianmeta
  24. maddinat0r
  25. spacemud
  26. Crayder
  27. Dayvison
  28. Ahmad45123
  29. Zeex
  30. irinel1996
  31. Yiin-
  32. Chaprnks
  33. Konstantinos
  34. Masterchen09
  35. Southclaws
  36. PatchwerkQWER
  37. m0k1
  38. paulommu
  39. udan111
  40. Thanks:
  41. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  42. ZeeX - Very productive conversations.
  43. koolk - IsPlayerinAreaEx code.
  44. TheAlpha - Danish translation.
  45. breadfish - German translation.
  46. Fireburn - Dutch translation.
  47. yom - French translation.
  48. 50p - Polish translation.
  49. Zamaroht - Spanish translation.
  50. Los - Portuguese translation.
  51. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
  52. me to strive to better.
  53. Pixels^ - Running XScripters where the idea was born.
  54. Matite - Pestering me to release it and using it.
  55. Very special thanks to:
  56. Thiadmer - PAWN, whose limits continue to amaze me!
  57. Kye/Kalcor - SA:MP.
  58. SA:MP Team past, present and future - SA:MP.
  59. Optional plugins:
  60. Gamer_Z - GPS.
  61. Incognito - Streamer.
  62. Me - sscanf2, fixes2, Whirlpool.
  63. */
  64. // How to convert from old y_inline to new y_inline:
  65. //
  66. // 1) Delete all `[E_CALLBACK_DATA]` everywhere. This can optionally be
  67. // replaced by a specifier tag. So what used to be:
  68. //
  69. // new cb[E_CALLBACK_DATA];
  70. //
  71. // Becomes:
  72. //
  73. // new Func:cb<iiis>;
  74. //
  75. // 2) Replace `Callback_Get` and `Callback_Release` with `Indirect_Claim` and
  76. // `Indirect_Release`. But only if you plan to store the callback for use in
  77. // the future. If you plan to use it immediately, just delete them.
  78. //
  79. // 3) Use `Callback_Find` if you want to search for a callback by name. This
  80. // means the old behviour of `Callback_Call` is now split between two
  81. // functions, both of which are optional depending on what you want to do.
  82. //
  83. // 4) Replace `callback:` tags with specifier tags. So:
  84. //
  85. // MyFunc(callback:x)
  86. //
  87. // Becomes:
  88. //
  89. // MyFunc(Func:cb<iiis>)
  90. //
  91. // 5) Replace:
  92. //
  93. // Callback_Call(cb, your, params);
  94. //
  95. // With:
  96. //
  97. // @.cb(your, params);
  98. //
  99. // 6) If you used plain strings as:
  100. //
  101. // MyFunc(callback_tag:"func");
  102. //
  103. // This has been entirely removed and must be:
  104. //
  105. // MyFunc(using func);
  106. //
  107. // Note the lack of `inline` or `callback`. This was the syntax for plain
  108. // string searches, but plain strings were also possible. Now they are not.
  109. //
  110. // 7) If you used an inline that was not in scope as:
  111. //
  112. // {
  113. // inline Func() {}
  114. // }
  115. // {
  116. // Other(using Func);
  117. // }
  118. //
  119. // You now can't do that - it won't work. The old code was not fully aware
  120. // of scopes and would attempt to find the nearest potential function. The
  121. // new code is fully scope aware and will correct deal with it.
  122. //
  123. // Note that this is all optional. Not doing so will continue to work, but
  124. // give warnings.
  125. #if YSI_KEYWORD(inline)
  126. #define inline INLINE__
  127. #endif
  128. #if YSI_KEYWORD(inline_return)
  129. #define inline_return INLINE_RETURN__
  130. #endif
  131. #if YSI_KEYWORD(@return)
  132. #define @return INLINE_RETURN__
  133. #endif
  134. #define INLINE_RETURN__%0; {Callback_Return_(_:(%0));continue;}
  135. #define callback: F@_@:
  136. #define _F<%0> (#%0)
  137. stock
  138. InlineRet:YSI_gInlineRet;
  139. stock Callback_Return_(value)
  140. {
  141. return value;
  142. }
  143. __COMPILER_STATIC_ENUM e_INLINE_FLAG (<<= 1)
  144. {
  145. e_INLINE_FLAG_NONE = 0,
  146. // Public function, don't do any stack restoration.
  147. e_INLINE_FLAG_PUBLIC = 1,
  148. // Cleared after a non-const function has been called. If the function is
  149. // never called, there's no point restoring. If the function is deferred,
  150. // there's no point restoring. If the function is `const`, there's no point
  151. // restoring.
  152. e_INLINE_FLAG_CONST
  153. // If any more flags are added, Callback_CallHandler_ needs updating, since
  154. // it sets the flags to exactly `0`.
  155. }
  156. __COMPILER_STATIC_ENUM E_INLINE_CALL
  157. {
  158. /* 0 */ E_INLINE_CALL_NULL,
  159. /* 1 */ E_INLINE_CALL_HANDLER,
  160. /* 2 */ E_INLINE_CALL_CLAIM,
  161. /* 3 */ E_INLINE_CALL_RELEASE,
  162. /* 4 */ E_INLINE_CALL_METADATA,
  163. /* 5 */ E_INLINE_CALL_TIMER, // To release unneeded inlines.
  164. /* 6 */ E_INLINE_CALL_FLAGS,
  165. /* 7 */ E_INLINE_CALL_SIZE,
  166. /* 8 */ E_INLINE_CALL_SOURCE,
  167. /* 9 */ E_INLINE_CALL_FUNCTION,
  168. /* 10 */ ResolvedAlloc:E_INLINE_CALL_PARAMS // At least the frame header.
  169. }
  170. __COMPILER_STATIC_ENUM E_PUBLIC_CALL
  171. {
  172. /* 0 */ E_PUBLIC_CALL_NULL,
  173. /* 1 */ E_PUBLIC_CALL_HANDLER,
  174. /* 2 */ E_PUBLIC_CALL_CLAIM,
  175. /* 3 */ E_PUBLIC_CALL_RELEASE,
  176. /* 4 */ E_PUBLIC_CALL_METADATA,
  177. /* 5 */ E_PUBLIC_CALL_TIMER, // To release unneeded remotes.
  178. /* 6 */ E_PUBLIC_CALL_FLAGS,
  179. /* 7 */ E_PUBLIC_CALL_SPECIFIER[32],
  180. /* 39 */ E_PUBLIC_CALL_FUNCTION[32]
  181. }
  182. #if 0
  183. // This:
  184. Func()
  185. {
  186. inline Inner(a, string:b[], c[64], &d)
  187. {
  188. // Code.
  189. }
  190. }
  191. // Becomes:
  192. Func()
  193. {
  194. static const Inner = 0;
  195. while (Inline_Start(Inner)) for (new a, string:b[], c[64], d; Inline_Def(0, cellmax, 64, -1); Inline_End())
  196. {
  197. // Code.
  198. }
  199. }
  200. // Rewrite "Inline_Start()" with entry code and a jump over the whole inline.
  201. // Rewrite "Inline_Def" with
  202. // Where:
  203. Inline_Start(const &name, a = INLINE_PATTERN_1, b = INLINE_PATTERN_2, c = INLINE_PATTERN_3, d = INLINE_PATTERN_4)
  204. {
  205. // The four extra parameters are just for putting unique scannable patterns
  206. // in to the code so that we can locate these function calls and rewrite
  207. // them.
  208. #pragma unused a, b, c, d
  209. // It turns out that "const &" IS valid! Pointless, but valid, which is
  210. // good because we want to bypass the compiler restrictions.
  211. // This allows us to write to a const reference without the compiler
  212. // objecting to it. This is, of course, a TERRIBLE idea! In fact, this is
  213. // only logically what happens, since this function is in reality never
  214. // called, only scanned for and rewritten.
  215. setarg(0, 0, inlineAddress);
  216. // NEVER loop.
  217. return 0;
  218. }
  219. #endif
  220. // Revert to the old scanning design, but using the new code scanner.
  221. const INLINE_PATTERN_1 = _C<Alex>;
  222. const INLINE_PATTERN_2 = _C<_Y_L>;
  223. const INLINE_PATTERN_3 = _C<ess_>;
  224. const INLINE_PATTERN_4 = _C<Cole>;
  225. forward Callback_Release_(ResolvedAlloc:a);
  226. forward Inline_MaybeFree_(Alloc:slot);
  227. forward Inline_MaybeConst_(ResolvedAlloc:slot);
  228. #define CALL@I@F I@F()
  229. #define CALL@I@L I@L()
  230. #define CALL@I@K I@K(0)
  231. #define CALL@I@T I@T(__ARR)
  232. #define CALL@Inline_UI_ Inline_UI_(__REF)
  233. #define CALL@Callback_Return_ Callback_Return_(0)
  234. static stock
  235. YSI_g_sFakeE_INLINE_CALL[E_INLINE_CALL];
  236. #define CALL@Callback_Claim_ Callback_Claim_(YSI_g_sFakeE_INLINE_CALL)
  237. #define MAX_INLINE_PARAMETERS (32)
  238. #define INLINE_DESCRIPTOR_VAR (0)
  239. #define INLINE_DESCRIPTOR_REF (-1)
  240. #define INLINE_DESCRIPTOR_STR (cellmax)
  241. enum E_CALLBACK_DATA
  242. {
  243. // Now only one item.
  244. E_CALLBACK_DATA_ALLOC
  245. }
  246. enum E_INLINE_DATA
  247. {
  248. // The fake "parameters" for the inline function.
  249. E_INLINE_DATA_PARAMETERS[MAX_INLINE_PARAMETERS],
  250. E_INLINE_DATA_PARAMETER_COUNT,
  251. E_INLINE_DATA_NAME, // The address of the string with the name in.
  252. E_INLINE_DATA_STATE, // Which part of the header scanning we are on.
  253. E_INLINE_DATA_LOCALS, // Closure size.
  254. E_INLINE_DATA_STACK, // Count of all locals.
  255. E_INLINE_DATA_POINTER, // The struct to store the inline data in.
  256. E_INLINE_DATA_START, // The start of the writable code space.
  257. E_INLINE_DATA_USER, // The location of the user code.
  258. E_INLINE_DATA_CLEANUP // The final address at which all inline local cleanup is done.
  259. }
  260. static stock
  261. YSI_g_sInlineEndPoint,
  262. YSI_g_sJumpOffset,
  263. YSI_g_sCallbackCallAddress,
  264. // This is the start of the linked list of inline functions. Each time a
  265. // function goes in to scope, the address of the local pointing to the
  266. // function header data is pushed to this stack. Each time a function goes
  267. // out of scope a destructor is used to remove that inline from the list
  268. // again. TODO: If there is a crash, clear this list.
  269. YSI_g_sInlineLinkedList;
  270. stock I@T:I@T(const str[])
  271. {
  272. #pragma unused str
  273. return I@T:0;
  274. }
  275. stock operator~(I@T:inlines[], size)
  276. {
  277. {}
  278. // This destructor is actually only ever called once, even though it might
  279. // exist many times in the compiled code; the rest are replaced at init time
  280. // using information gathered here.
  281. #pragma unused inlines
  282. // Get the return address.
  283. #emit LOAD.S.alt 4
  284. // Read the code before the return.
  285. #emit LCTRL 0
  286. #emit ADD
  287. #emit MOVE.alt
  288. #emit LCTRL 1
  289. #emit SUB.alt
  290. #emit ADD.C 0xFFFFFFFC
  291. #emit STOR.S.pri size
  292. #emit LREF.S.pri size
  293. #emit STOR.S.pri size
  294. {}
  295. // This code:
  296. //
  297. // YSI_g_sInlineLinkedList = DisasmReloc(size);
  298. //
  299. // For some reason crashes with:
  300. //
  301. // https://github.com/pawn-lang/compiler/issues/318
  302. //
  303. // Interestingly, it turns out the variable `YSI_g_sJumpOffset` already
  304. // existed locally with exactly the same value as the one used in
  305. // `DisasmReloc`, so the fix ends up actually being faster than the
  306. // original code (but less explicit). For reference - it gets the previous
  307. // in-scope inline from the linked list, as a data segment address.
  308. YSI_g_sInlineLinkedList = size - YSI_g_sJumpOffset;
  309. }
  310. public Inline_MaybeFree_(Alloc:slot)
  311. {
  312. // If this function is called, the given inline left the call stack without
  313. // being claimed.
  314. free(slot);
  315. }
  316. public Inline_MaybeConst_(ResolvedAlloc:slot)
  317. {
  318. // If this function is called, the given inline has been claimed, but has
  319. // left the current scope, so can't have its closure written back to.
  320. KillTimer(AMX_Read(_:slot + _:E_INLINE_CALL_TIMER * cellbytes));
  321. AMX_Write(_:slot + _:E_INLINE_CALL_TIMER * cellbytes, 0);
  322. }
  323. static stock Inline_FoundStart(const scanner[CodeScanner], data[E_INLINE_DATA] = "")
  324. {
  325. P:4("Inline_FoundStart called");
  326. if (data[E_INLINE_DATA_STATE] != 0)
  327. return 0;
  328. P:5("Inline_FoundStart OK");
  329. data[E_INLINE_DATA_LOCALS] = CodeScanGetMatchStack(scanner);
  330. data[E_INLINE_DATA_START] = CodeScanGetMatchAddress(scanner);
  331. data[E_INLINE_DATA_NAME] = CodeScanGetMatchHole(scanner, 0);
  332. data[E_INLINE_DATA_STATE] = 1;
  333. data[E_INLINE_DATA_PARAMETER_COUNT] = 0;
  334. return 0;
  335. }
  336. static stock Inline_FoundMid(const scanner[CodeScanner], data[E_INLINE_DATA] = "")
  337. {
  338. P:4("Inline_FoundMid called");
  339. if (data[E_INLINE_DATA_STATE] != 1)
  340. return 0;
  341. P:5("Inline_FoundMid OK");
  342. data[E_INLINE_DATA_STACK] = CodeScanGetMatchStack(scanner);
  343. data[E_INLINE_DATA_STATE] = 2;
  344. data[E_INLINE_DATA_CLEANUP] = CodeScanGetMatchHole(scanner, 0);
  345. return 0;
  346. }
  347. static stock Inline_FoundDescriptor(size, data[E_INLINE_DATA])
  348. {
  349. if (data[E_INLINE_DATA_PARAMETER_COUNT] == MAX_INLINE_PARAMETERS)
  350. P:F("y_inline: Max inline parameter count exceeded (%d).", MAX_INLINE_PARAMETERS);
  351. else switch (size)
  352. {
  353. case INLINE_DESCRIPTOR_VAR:
  354. {
  355. data[E_INLINE_DATA_PARAMETERS][data[E_INLINE_DATA_PARAMETER_COUNT]++] = INLINE_DESCRIPTOR_VAR;
  356. }
  357. case INLINE_DESCRIPTOR_REF:
  358. {
  359. data[E_INLINE_DATA_PARAMETERS][data[E_INLINE_DATA_PARAMETER_COUNT]++] = INLINE_DESCRIPTOR_REF;
  360. data[E_INLINE_DATA_STATE] |= 16;
  361. }
  362. case INLINE_DESCRIPTOR_STR:
  363. {
  364. data[E_INLINE_DATA_PARAMETERS][data[E_INLINE_DATA_PARAMETER_COUNT]++] = INLINE_DESCRIPTOR_STR;
  365. data[E_INLINE_DATA_STATE] |= 8;
  366. }
  367. default:
  368. {
  369. data[E_INLINE_DATA_PARAMETERS][data[E_INLINE_DATA_PARAMETER_COUNT]++] = size * cellbytes;
  370. data[E_INLINE_DATA_STATE] |= 8;
  371. }
  372. }
  373. }
  374. static stock Inline_FoundConst(const scanner[CodeScanner], data[E_INLINE_DATA] = "")
  375. {
  376. P:4("Inline_FoundConst called");
  377. if (data[E_INLINE_DATA_STATE] != 2)
  378. return 0;
  379. P:5("Inline_FoundConst OK");
  380. data[E_INLINE_DATA_STATE] = 3 + CodeScanGetMatchHole(scanner, 0);
  381. return 0;
  382. }
  383. static stock Inline_FoundConst2(const scanner[CodeScanner], data[E_INLINE_DATA] = "")
  384. {
  385. P:4("Inline_FoundConst2 called");
  386. #pragma unused scanner
  387. // Can't use size to determine this match as two pieces of code are the same
  388. // size in the same place, but mean very different things.
  389. if (data[E_INLINE_DATA_STATE] != 2)
  390. return 0;
  391. P:5("Inline_FoundConst2 OK");
  392. data[E_INLINE_DATA_STATE] = 3;
  393. return 0;
  394. }
  395. static stock Inline_FoundVar(const scanner[CodeScanner], data[E_INLINE_DATA] = "")
  396. {
  397. P:4("Inline_FoundVar called");
  398. #pragma unused scanner
  399. if (data[E_INLINE_DATA_STATE] < 3)
  400. return 0;
  401. P:5("Inline_FoundVar OK");
  402. Inline_FoundDescriptor(0, data);
  403. return 0;
  404. }
  405. static stock Inline_FoundRef(const scanner[CodeScanner], data[E_INLINE_DATA] = "")
  406. {
  407. P:4("Inline_FoundRef called");
  408. if (data[E_INLINE_DATA_STATE] < 3)
  409. return 0;
  410. P:5("Inline_FoundRef OK");
  411. Inline_FoundDescriptor(CodeScanGetMatchHole(scanner, 0), data);
  412. return 0;
  413. }
  414. static stock Inline_FoundEnd(const scanner[CodeScanner], data[E_INLINE_DATA] = "")
  415. {
  416. P:4("Inline_FoundEnd called");
  417. if (data[E_INLINE_DATA_STATE] < 3)
  418. return 0;
  419. P:5("Inline_FoundEnd OK");
  420. data[E_INLINE_DATA_USER] = CodeScanGetMatchAddress(scanner) + CodeScanGetMatchLength(scanner);
  421. // Do the actual codegen here.
  422. Inline_DoCodeGen(scanner, data);
  423. Inline_StoreData(data);
  424. // Restart scanning for the next inline.
  425. data[E_INLINE_DATA_STATE] = 0;
  426. return 0;
  427. }
  428. static stock Inline_StoreData(const data[E_INLINE_DATA])
  429. {
  430. // `data[E_INLINE_DATA_NAME]` stores the address of a string with the inline
  431. // function's name, followed by extra space for storing: a pointer to the
  432. // name, a pointer to the function, a pointer to the next inline in the
  433. // linked list of names, and something else?
  434. new
  435. header = data[E_INLINE_DATA_NAME];
  436. // Add a pointer to the function itself (after `JUMP`).
  437. AMX_Write(header, data[E_INLINE_DATA_START] + 10 * cellbytes);
  438. // Store the local stack size at this point.
  439. AMX_Write(header + cellbytes, data[E_INLINE_DATA_LOCALS]);
  440. // Store the tag data. TODO: Put this in the compiler with
  441. // `InlineName = tagof (InlineName);`.
  442. new
  443. tag[32] = "F@_@";
  444. for (new cur = 0; cur != data[E_INLINE_DATA_PARAMETER_COUNT]; ++cur)
  445. {
  446. switch (data[E_INLINE_DATA_PARAMETERS][cur])
  447. {
  448. case INLINE_DESCRIPTOR_VAR:
  449. tag[cur + 4] = 'i';
  450. case INLINE_DESCRIPTOR_REF:
  451. tag[cur + 4] = 'v';
  452. case INLINE_DESCRIPTOR_STR:
  453. tag[cur + 4] = 's';
  454. default:
  455. tag[cur + 4] = 'a';
  456. }
  457. }
  458. AMX_Write(header + 2 * cellbytes, GetTagIDFromName(tag));
  459. }
  460. static stock Inline_FoundUsingInline(const scanner[CodeScanner])
  461. {
  462. // Found a call to `using inline`. Change it from (the equivalent of)
  463. // `(a = Inline_UI_(a), a)` to `Inline_UI_(a)`. First, check that the two
  464. // holes are the same. If they're not, this isn't what we want to optimise.
  465. if (CodeScanGetMatchHole(scanner, 0) != CodeScanGetMatchHole(scanner, 1))
  466. return 0;
  467. new
  468. ctx[AsmContext];
  469. switch (CodeScanGetMatchLength(scanner))
  470. {
  471. case 8 * cellbytes:
  472. {
  473. CodeScanGetMatchAsm(scanner, ctx, 6 * cellbytes);
  474. @emit PUSH.pri
  475. @emit NOP
  476. }
  477. case 10 * cellbytes:
  478. {
  479. CodeScanGetMatchAsm(scanner, ctx, 7 * cellbytes);
  480. @emit NOP
  481. @emit NOP
  482. }
  483. }
  484. return 0;
  485. }
  486. static stock Inline_FoundDestructor(const scanner[CodeScanner])
  487. {
  488. // Found a call to `operator~(I@T:inlines[], size)`. Get the offset of the
  489. // variable we store the linked list in. Note that the linked list doesn't
  490. // point to this variable, but the next variable on the stack, the variable
  491. // containing the function header data. So when we use the list, we need to
  492. // take that in to account; but not here.
  493. new
  494. func = CodeScanGetMatchHole(scanner, 0),
  495. ctx[AsmContext];
  496. CodeScanGetMatchAsm(scanner, ctx);
  497. if (YSI_g_sInlineEndPoint > CodeScanGetMatchAddress(scanner))
  498. {
  499. // Do not call inline function destructors in inlines.
  500. @emit NOP
  501. @emit NOP
  502. @emit NOP
  503. @emit NOP
  504. }
  505. else
  506. {
  507. // Remove this inline function from the linked list of inlines.
  508. @emit LOAD.S.pri func
  509. @emit STOR.pri ref(YSI_g_sInlineLinkedList)
  510. }
  511. @emit NOP
  512. @emit NOP
  513. @emit NOP
  514. @emit NOP
  515. @emit NOP
  516. return 0;
  517. }
  518. static stock Inline_Found@return(const scanner[CodeScanner])
  519. {
  520. new
  521. ctx[AsmContext];
  522. CodeScanGetMatchAsm(scanner, ctx);
  523. @emit POP.pri
  524. @emit STACK CodeScanGetMatchStack(scanner)
  525. @emit RETN
  526. return 0;
  527. }
  528. #if !defined _ALS_OnRuntimeError
  529. forward OnRuntimeError(code, &bool:suppress);
  530. #endif
  531. public OnRuntimeError(code, &bool:suppress)
  532. {
  533. // No inlines are in scope, because the scope just crashed. Clear the list.
  534. YSI_g_sInlineLinkedList = 0;
  535. Inline_OnRuntimeError(code, suppress);
  536. return 1;
  537. }
  538. CHAIN_FORWARD:Inline_OnRuntimeError(code, &bool:suppress) = 1;
  539. #if defined _ALS_OnRuntimeError
  540. #undef OnRuntimeError
  541. #else
  542. #define _ALS_OnRuntimeError
  543. #endif
  544. #define OnRuntimeError(%0) CHAIN_PUBLIC:Inline_OnRuntimeError(%0)
  545. public OnCodeInit()
  546. {
  547. P:2("Inline_OnCodeInit called");
  548. new
  549. hdr[AMX_HDR];
  550. GetAmxHeader(hdr);
  551. YSI_g_sJumpOffset = GetAmxBaseAddress() + hdr[AMX_HDR_COD];
  552. if (FALSE)
  553. Callback_CallHandler_();
  554. {}
  555. #emit CONST.pri Callback_CallHandler_
  556. #emit STOR.pri YSI_g_sCallbackCallAddress
  557. YSI_g_sCallbackCallAddress += YSI_g_sJumpOffset;
  558. new scanner[CodeScanner];
  559. CodeScanInit(scanner);
  560. // Allocate the inline scanning data on the stack, instead of globally.
  561. new data[E_INLINE_DATA];
  562. // Optimised.
  563. new csm1a[CodeScanMatcher];
  564. CodeScanMatcherInit(csm1a, &Inline_FoundStart);
  565. CodeScanMatcherData(csm1a, ref(data));
  566. CodeScanMatcherPattern(csm1a,
  567. OP(STACK, -4)
  568. OP(CONST_PRI, ???)
  569. OP(INVERT)
  570. OP(INVERT)
  571. OP(PUSH_PRI)
  572. OP(PUSH_C, 4)
  573. OP(CALL, &I@T)
  574. OP(STOR_S_PRI, ???)
  575. OP(STACK, -4)
  576. OP(LOAD_S_PRI, ???)
  577. OP(STOR_S_PRI, ???)
  578. );
  579. CodeScanAddMatcher(scanner, csm1a);
  580. // Unoptimised.
  581. new csm1b[CodeScanMatcher];
  582. CodeScanMatcherInit(csm1b, &Inline_FoundStart);
  583. CodeScanMatcherData(csm1b, ref(data));
  584. CodeScanMatcherPattern(csm1b,
  585. OP(STACK, -4)
  586. OP(CONST_PRI, ???)
  587. OP(INVERT)
  588. OP(INVERT)
  589. OP(PUSH_PRI)
  590. OP(PUSH_C, 4)
  591. OP(CALL, &I@T)
  592. OP(STOR_S_PRI, ???)
  593. OP(STACK, -4)
  594. OP(LOAD_S_PRI, ???)
  595. OP(STOR_S_PRI, ???)
  596. );
  597. CodeScanAddMatcher(scanner, csm1b);
  598. // Mid point.
  599. new csm2a[CodeScanMatcher];
  600. CodeScanMatcherInit(csm2a, &Inline_FoundMid);
  601. CodeScanMatcherData(csm2a, ref(data));
  602. CodeScanMatcherPattern(csm2a,
  603. OP(PUSH_C, 0)
  604. OP(CALL, &I@F)
  605. OP(JZER, ???)
  606. );
  607. CodeScanAddMatcher(scanner, csm2a);
  608. // Normal parameter.
  609. //
  610. // ZERO.pri
  611. // HEAP 4
  612. // STOR.I
  613. // PUSH.alt
  614. //
  615. new csm3a[CodeScanMatcher];
  616. CodeScanMatcherInit(csm3a, &Inline_FoundVar);
  617. CodeScanMatcherData(csm3a, ref(data));
  618. CodeScanMatcherPattern(csm3a,
  619. OP(ZERO_PRI)
  620. OP(HEAP, 4)
  621. OP(STOR_I)
  622. OP(PUSH_ALT)
  623. );
  624. CodeScanAddMatcher(scanner, csm3a);
  625. new csm3b[CodeScanMatcher];
  626. CodeScanMatcherInit(csm3b, &Inline_FoundVar);
  627. CodeScanMatcherData(csm3b, ref(data));
  628. CodeScanMatcherPattern(csm3b,
  629. OP(ZERO_PRI)
  630. OP(HEAP, 4)
  631. OP(STOR_I)
  632. OP(MOVE_PRI)
  633. OP(PUSH_PRI)
  634. );
  635. CodeScanAddMatcher(scanner, csm3b);
  636. // Reference parameter.
  637. //
  638. // CONST.pri ffffffff
  639. // HEAP 4
  640. // STOR.I
  641. // PUSH.alt
  642. //
  643. // Array (with size in CELLS).
  644. //
  645. // CONST.pri a
  646. // HEAP 4
  647. // STOR.I
  648. // PUSH.alt
  649. //
  650. // String
  651. //
  652. // CONST.pri 80000000
  653. // HEAP 4
  654. // STOR.I
  655. // PUSH.alt
  656. //
  657. new csm4a[CodeScanMatcher];
  658. CodeScanMatcherInit(csm4a, &Inline_FoundRef);
  659. CodeScanMatcherData(csm4a, ref(data));
  660. CodeScanMatcherPattern(csm4a,
  661. OP(CONST_PRI, ???)
  662. OP(HEAP, 4)
  663. OP(STOR_I)
  664. OP(PUSH_ALT)
  665. );
  666. CodeScanAddMatcher(scanner, csm4a);
  667. new csm4b[CodeScanMatcher];
  668. CodeScanMatcherInit(csm4b, &Inline_FoundRef);
  669. CodeScanMatcherData(csm4b, ref(data));
  670. CodeScanMatcherPattern(csm4b,
  671. OP(CONST_PRI, ???)
  672. OP(HEAP, 4)
  673. OP(STOR_I)
  674. OP(MOVE_PRI)
  675. OP(PUSH_PRI)
  676. );
  677. CodeScanAddMatcher(scanner, csm4b);
  678. // End
  679. new csm5a[CodeScanMatcher];
  680. CodeScanMatcherInit(csm5a, &Inline_FoundEnd);
  681. CodeScanMatcherData(csm5a, ref(data));
  682. CodeScanMatcherPattern(csm5a,
  683. OP(CALL, &I@L)
  684. OP(HEAP, ???)
  685. OP(JZER, ???)
  686. );
  687. CodeScanAddMatcher(scanner, csm5a);
  688. // Constness
  689. new csm6a[CodeScanMatcher];
  690. CodeScanMatcherInit(csm6a, &Inline_FoundConst);
  691. CodeScanMatcherData(csm6a, ref(data));
  692. CodeScanMatcherPattern(csm6a,
  693. OP(PUSH_C, ???)
  694. OP(PUSH_C, 4)
  695. OP(CALL, &I@K)
  696. );
  697. CodeScanAddMatcher(scanner, csm6a);
  698. new csm6b[CodeScanMatcher];
  699. CodeScanMatcherInit(csm6b, &Inline_FoundConst);
  700. CodeScanMatcherData(csm6b, ref(data));
  701. CodeScanMatcherPattern(csm6b,
  702. OP(CONST_PRI, ???)
  703. OP(PUSH_PRI)
  704. OP(PUSH_C, 4)
  705. OP(CALL, &I@K)
  706. );
  707. CodeScanAddMatcher(scanner, csm6b);
  708. new csm6c[CodeScanMatcher];
  709. CodeScanMatcherInit(csm6c, &Inline_FoundConst2);
  710. CodeScanMatcherData(csm6c, ref(data));
  711. CodeScanMatcherPattern(csm6c,
  712. OP(ZERO_PRI)
  713. OP(PUSH_PRI)
  714. OP(PUSH_C, 4)
  715. OP(CALL, &I@K)
  716. );
  717. CodeScanAddMatcher(scanner, csm6c);
  718. // Replace code that was `(Inline_UI_(x), x)` with just `Inline_UI_()`.
  719. new csm7a[CodeScanMatcher];
  720. CodeScanMatcherInit(csm7a, &Inline_FoundUsingInline);
  721. CodeScanMatcherPattern(csm7a,
  722. OP(ADDR_PRI, ???)
  723. OP(PUSH_PRI)
  724. OP(PUSH_C, 4)
  725. OP(CALL, &Inline_UI_)
  726. OP(LOAD_S_PRI, ???)
  727. OP(PUSH_PRI)
  728. );
  729. CodeScanAddMatcher(scanner, csm7a);
  730. new csm7b[CodeScanMatcher];
  731. CodeScanMatcherInit(csm7b, &Inline_FoundUsingInline);
  732. CodeScanMatcherPattern(csm7b,
  733. OP(PUSH_ADR, ???)
  734. OP(PUSH_C, 4)
  735. OP(CALL, &Inline_UI_)
  736. OP(PUSH_S, ???)
  737. );
  738. CodeScanAddMatcher(scanner, csm7b);
  739. // Destructors.
  740. {
  741. // We can't directly get the address of a destructor. Instead, we need
  742. // to create a local that will be destroyed and read the call from that.
  743. // Use `[2]` so it doesn't match the scanner later.
  744. new I@T:search[2];
  745. // THIS BLOCK IS NOT POINTLESS! IT CALLS A REQUIRED DESTRUCTOR.
  746. }
  747. // Now we have the destructor address, replace all calls to it. I've never
  748. // seen this using `PUSH.adr` instead of `ADDR.pri`/`PUSH.pri`, even on
  749. // optimising builds. `YSI_g_sInlineLinkedList` currently holds the address
  750. // of the `I@T:` destructor. We used that as a temporary to return the
  751. // function address from the (seemingly) pointless block directly above
  752. // because `addressof` and `&` don't work for operators (I don't know a
  753. // generic solution to this problem, but it has only ever come up once and I
  754. // could get a non-portable solution in that one instance).
  755. new csm8a[CodeScanMatcher];
  756. CodeScanMatcherInit(csm8a, &Inline_FoundDestructor);
  757. CodeScanMatcherPattern(csm8a,
  758. OP(PUSH_C, 1)
  759. OP(ADDR_PRI, ???)
  760. OP(PUSH_PRI)
  761. OP(PUSH_C, 8)
  762. OP(CALL, YSI_g_sInlineLinkedList)
  763. );
  764. CodeScanAddMatcher(scanner, csm8a);
  765. // Detect `@return` and make it a real return.
  766. new csm9a[CodeScanMatcher];
  767. CodeScanMatcherInit(csm9a, &Inline_Found@return);
  768. CodeScanMatcherPattern(csm9a,
  769. OP(PUSH_C, 4)
  770. OP(CALL, &Callback_Return_)
  771. );
  772. CodeScanAddMatcher(scanner, csm9a);
  773. // Reset the linked list whose variable we borrowed as a temporary.
  774. YSI_g_sInlineLinkedList = 0;
  775. // Run all the scanners in parallel.
  776. // TODO: Try and determine rough types for parent function parameters, using
  777. // Opcodes like LREF, SREF, and IDXADDR (IDXARRAY? Can't remember off the
  778. // top of my head).
  779. CodeScanRunFast(scanner, &I@T);
  780. #if defined Inline_OnCodeInit
  781. Inline_OnCodeInit();
  782. #endif
  783. return 1;
  784. }
  785. #undef OnCodeInit
  786. #define OnCodeInit Inline_OnCodeInit
  787. #if defined Inline_OnCodeInit
  788. forward Inline_OnCodeInit();
  789. #endif
  790. /*
  791. At maximum optimisation we get...
  792. Via parameter passing:
  793. 6 cells for a reference variable.
  794. 6 cells for an array.
  795. 6 cells for a string.
  796. 5 cells for a normal variable.
  797. Via initial declaration:
  798. 2 cells for a reference variable.
  799. 7 cells for an array.
  800. 7 cells for a string.
  801. 2 cells for a normal variable.
  802. For a total of:
  803. 8 cells for a reference variable.
  804. 13 cells for an array.
  805. 13 cells for a string.
  806. 7 cells for a normal variable.
  807. Plus:
  808. 8 cells for the call to `I@F` (the address after which is the loop repeat).
  809. 20 cells for the call to `I@L` (the address after which is the start of
  810. code, and whose final part jumps to just after the code return jump,
  811. that we can co-opt for `RETN` and do away with the bounds check code).
  812. N useless cells at the end.
  813. Note:
  814. I just added two variables, so their declaration also exists, but the maths
  815. above hasn't been updated to reflect this fact.
  816. */
  817. // Make sure there's a space after the "return".
  818. #define return%0({%1}%2)%3; I@=%0({%1}%2)%3;return I@;
  819. // The "INLINE" in the types here will override "PARSER@" to "PARSE@INLINE",
  820. // because there is no colon (well not "because", but it helps).
  821. #define INLINE__%0(%1) MAKE_PARSER(INLINE,ARR:REF:STR:NUM:QAL::INLINE)(%0(%1))()0()#
  822. #define INLINE_CONST__%0(%1) MAKE_PARSER(INLINE,ARR:REF:STR:NUM:QAL::INLINE)(%0(%1))()1()#
  823. // Follows the "code-parse.inc" internal structure. Ugly but required, since we
  824. // are parsing functions, but not at a top level.
  825. #define PARSER@INLINE:%0(%5)%6(%7)$ new I@T:_@%6=I@T(_:%0(%5)%6(%7) I@O$
  826. #define INLINE_STR(%9,%9,%2,%9)%8$(%0)%1(%3)%4# %8$(%0,%2[YSI_MAX_INLINE_STRING])%1(cellmax,%3)%4s#
  827. #define INLINE_ARR(%9,%9,%2,%9)%8$(%0)%1(%3)%4# %8$(%0,%2[%9])%1(%9,%3)%4a#
  828. #define INLINE_NUM(%9,%9,%2)%8$(%0)%1(%3)%4# %8$(%0,%2)%1(0,%3)%4i#
  829. #define INLINE_REF(%9,%9,%2)%8$(%0)%1(%3)%4# %8$(%0,%2)%1(-1,%3)%4v#
  830. // ".." is used to reserve memory at the start of the string for:
  831. //
  832. // +0 - Inline function start pointer.
  833. // +1 - Stack size.
  834. #define INLINE_END(%9)%8$(,%0)%1(%3)%4# %8$#...#%9),F@_@%4:%9=F@_@%4:_@%9;for(new %0;I@F();)while(I@L(%3I@K(%1)))
  835. #define INLINE_NUL(%9)%8$()%1()%4# %8$#...#%9),F@_@%4:%9=F@_@%4:_@%9;for(;I@F();)while(I@L(I@K(%1)))
  836. #define I@O$
  837. // Detect `const` in the function name, and strip it from the variable name.
  838. #define INLINE_const(%9)%8$(%0)%1(%2) %8$(%0)1(%2)
  839. #define _@const%0\32; _@
  840. #define USING_INLINE__ (@Ik:@Il:Inline_UI_(),)
  841. #define USING__%0\32; (@Ip:@Iq:@Io:@Iu:@Ik:@Il:@Im:Callback_Find_(,I@),F@_@:I@)
  842. #define USING_PUBLIC__ (@Ik:@Il:Inline_UP_(),)
  843. #define USING_CALLBACK__ (@Ik:@Il:Inline_UP_(),)
  844. #define USING_RESOLVED__ (@Ik:@Il:0,)
  845. // Old `using`.
  846. #if YSI_KEYWORD(callback_tag)
  847. #define callback_tag: (@Ik:@Il:@Im:Callback_Find_(,I@),F@_@:I@)
  848. #endif
  849. #if YSI_KEYWORD(using)
  850. #define using USING__
  851. #endif
  852. // Parameter type for passing callbacks about.
  853. #define @Ip:@Iq:@Io:@Iu:%0)inline @Ik:@Il:Inline_UI_(),)
  854. #define @Iq:@Io:@Iu:%0)callback @Ik:@Il:Inline_UP_(),)
  855. #define @Io:@Iu:%0)public @Ik:@Il:Inline_UP_(),)
  856. #define @Iu:%0)resolved @Ik:@Il:0,)
  857. // Detect a `using` parameter that is not the last parameter. Actually handles
  858. // nested calls quite nicely - `X(Y(using inline Z), 5)` should be detected as a
  859. // final parameter, but this detects it instead and gives
  860. // `X(Y(Inline_UI_(Z)), 5)`, which is still correct. This only falls down on
  861. // code like `X(Y(using inline Z) - 10, 5)`, which currently becomes
  862. // `X(Y(Inline_UI_(Z - 10)), 5)`
  863. #define @Ik:@Il:%0)%1, %0%1),
  864. // Detect a `using` parameter that is the last parameter.
  865. #define @Il:%0)%1) %0%1))
  866. // Detect a callback with a tag.
  867. #define @Ir:%9#%1<%0>),F@_@:%2$) @It:%9#%1,#%0),F@_@%0:%2$)
  868. // Normally `F@_@` doesn't consume spaces because it is a valid tag on its own.
  869. // However, in this case we know that extra specifiers after the tag prefix
  870. // exist so we can check fairly safely.
  871. #define @It:%0),F@_@%9\32;%1:%2$) @It:%0),F@_@%9%1:%2$)
  872. // Detect a callback that starts with "On". These are often redefined and we
  873. // want to keep the original.
  874. #define @Is:%9#On%0),%2$) %9#On#%0),%2$)
  875. // Callbacks with additional parameters (MUST have matching parameters (y_ini)).
  876. #define @In:%0(%1)%3),%2$) %0%3),%2$),.bExtra=true,.extra=%1)
  877. // Move the callback parameter inside the brackets.
  878. #define Inline_UI_(),%0) Inline_UI_(_:%0),%0)
  879. #define Inline_UP_(),%0) Inline_UP_(_:@Ir:@Is:@In:#%0),F@_@:I@ I@O$)
  880. #define @Im:Callback_Find_(,I@),F@_@:I@%0) Callback_Find_(#%0,I@),F@_@:I@)
  881. stock I@F()
  882. {
  883. return 0;
  884. }
  885. stock I@L(...)
  886. {
  887. return 0;
  888. }
  889. stock I@K(n)
  890. {
  891. #pragma unused n
  892. return 0;
  893. }
  894. /*-------------------------------------------------------------------------*//**
  895. * <param name="func">Public function to get.</param>
  896. * <param name="spec">The structure of the function's parameters.</param>
  897. * <returns>
  898. * A pointer to the function.
  899. * </returns>
  900. * <remarks>
  901. * Accepts the following parameter specifiers:
  902. *
  903. * i - Integer (also x/c/d/h)
  904. * f - Float (also g)
  905. * s - String
  906. * ai - Array (followed by length)
  907. * v - Reference (&amp;var, any tag)
  908. *
  909. * </remarks>
  910. *//*------------------------------------------------------------------------**/
  911. #define Function: F@_@:
  912. stock Function:GetRemoteFunction(const func[], const spec[])
  913. {
  914. new
  915. Alloc:closure = malloc(_:E_PUBLIC_CALL);
  916. if (!closure)
  917. return Function:0;
  918. mset(closure, _:E_PUBLIC_CALL_NULL, 0);
  919. mset(closure, _:E_PUBLIC_CALL_HANDLER, _:addressof (Callback_RemoteHandler_<x>));
  920. mset(closure, _:E_PUBLIC_CALL_CLAIM, _:addressof (Callback_Claim_));
  921. mset(closure, _:E_PUBLIC_CALL_RELEASE, _:addressof (Callback_Release_<tResolvedAlloc:>));
  922. mset(closure, _:E_PUBLIC_CALL_TIMER, SetTimerEx("Inline_MaybeFree_", 0, false, "i", _:closure));
  923. mset(closure, _:E_PUBLIC_CALL_FLAGS, e_INLINE_FLAG_PUBLIC);
  924. mset(closure, _:E_PUBLIC_CALL_METADATA, 0);
  925. msets(closure, _:E_PUBLIC_CALL_SPECIFIER, spec);
  926. msets(closure, _:E_PUBLIC_CALL_FUNCTION, func);
  927. return Function:Indirect_Ptr(Malloc_Resolve(closure));
  928. }
  929. #define GetRemoteFunction(&%0<%1>) (F@_@%1:GetRemoteFunction(#%0, #%1))
  930. /*-------------------------------------------------------------------------*//**
  931. * <param name="func">Public function to get.</param>
  932. * <param name="spec">The structure of the function's parameters.</param>
  933. * <returns>
  934. * A pointer to the function.
  935. * </returns>
  936. * <remarks>
  937. * Accepts the following parameter specifiers:
  938. *
  939. * i - Integer (also x/c/d/h)
  940. * f - Float (also g)
  941. * s - String
  942. * ai - Array (followed by length)
  943. * v - Reference (&amp;var, any tag)
  944. *
  945. * </remarks>
  946. *//*------------------------------------------------------------------------**/
  947. stock Function:GetLocalFunction(const func[], const spec[])
  948. {
  949. if (funcidx(func) == -1)
  950. return Function:0;
  951. new
  952. Alloc:closure = malloc(_:E_PUBLIC_CALL);
  953. if (!closure)
  954. return Function:0;
  955. mset(closure, _:E_PUBLIC_CALL_NULL, 0);
  956. mset(closure, _:E_PUBLIC_CALL_HANDLER, _:addressof (Callback_LocalHandler_<x>));
  957. mset(closure, _:E_PUBLIC_CALL_CLAIM, _:addressof (Callback_Claim_));
  958. mset(closure, _:E_PUBLIC_CALL_RELEASE, _:addressof (Callback_Release_<tResolvedAlloc:>));
  959. mset(closure, _:E_PUBLIC_CALL_TIMER, SetTimerEx("Inline_MaybeFree_", 0, false, "i", _:closure));
  960. mset(closure, _:E_PUBLIC_CALL_FLAGS, e_INLINE_FLAG_PUBLIC);
  961. mset(closure, _:E_PUBLIC_CALL_METADATA, 0);
  962. msets(closure, _:E_PUBLIC_CALL_SPECIFIER, spec);
  963. msets(closure, _:E_PUBLIC_CALL_FUNCTION, func);
  964. new tmp[32];
  965. mgets(tmp, 32, closure, _:E_PUBLIC_CALL_SPECIFIER);
  966. return Function:Indirect_Ptr(Malloc_Resolve(closure));
  967. }
  968. #define GetLocalFunction(&%0<%1>) (F@_@%1:GetLocalFunction(#%0, #%1))
  969. /*-------------------------------------------------------------------------*//**
  970. * <param name="func">Function pointer to call.</param>
  971. * <param name="">The function's parameters.</param>
  972. * <remarks>
  973. * Call the function in the given pointer with the given parameters.
  974. * </remarks>
  975. *//*------------------------------------------------------------------------**/
  976. #pragma deprecated Use `@.func(params);`.
  977. stock CallStoredFunction(Function:func, GLOBAL_TAG_TYPES:...)
  978. {
  979. return Indirect_Call(_:func, 0, ___(1));
  980. }
  981. /*-------------------------------------------------------------------------*//**
  982. * <param name="string1">First string as a pointer.</param>
  983. * <param name="string2">Second string as a string.</param>
  984. * <param name="ignorecase">Do a case-insensitive search.</param>
  985. * <param name="length">Length of string to compare over.</param>
  986. * <remarks>
  987. * Just <c>strcmp</c>, but pretending the first parameter is a value not an
  988. * array so that we can trick the compiler in to accepting a pointer.
  989. * </remarks>
  990. *//*------------------------------------------------------------------------**/
  991. native Inline_Strcmp(const string1, const string2[], bool:ignorecase=false, length=cellmax) = strcmp;
  992. /*-------------------------------------------------------------------------*//**
  993. * <param name="frm">Frame to get the parameters from.</param>
  994. * <remarks>
  995. * Deals with y_hooks parameter count mangling. Stolen from <c>Hooks_NumArgs</c>.
  996. * </remarks>
  997. *//*------------------------------------------------------------------------**/
  998. static stock Inline_NumArgs(frm)
  999. {
  1000. #emit LOAD.S.alt frm
  1001. Inline_NumArgs_load:
  1002. #emit CONST.pri 8
  1003. #emit ADD
  1004. #emit LOAD.I
  1005. #emit ZERO.alt
  1006. #emit PUSH.pri
  1007. #emit SGEQ
  1008. #emit LREF.S.alt frm
  1009. #emit JZER Inline_NumArgs_load
  1010. #emit POP.pri
  1011. #emit RETN
  1012. __COMPILER_NAKED
  1013. }
  1014. /*-------------------------------------------------------------------------*//**
  1015. * <param name="name">Inline to find by name.</param>
  1016. * <remarks>
  1017. * Traverses up the stack to find an in-scope inline with the same name AND
  1018. * SIGNATURE.
  1019. * </remarks>
  1020. *//*------------------------------------------------------------------------**/
  1021. static stock Callback_InlineByName(const name[], tag)
  1022. {
  1023. new
  1024. ptr = YSI_g_sInlineLinkedList;
  1025. while (ptr)
  1026. {
  1027. // Get the pointer to the inline function data.
  1028. new
  1029. ret = AMX_Read(ptr);
  1030. // Compare the tags.
  1031. if (tag == 0 || tag == AMX_Read(ret + 2 * cellbytes))
  1032. {
  1033. // Compare the names.
  1034. if (!Inline_Strcmp(ret + 3 * cellbytes, name))
  1035. {
  1036. new
  1037. frm = ptr + AMX_Read(ret + cellbytes),
  1038. args = Inline_NumArgs(frm),
  1039. size = frm - ptr + 12 + args,
  1040. Alloc:closure = malloc(size / cellbytes + _:E_INLINE_CALL - 1);
  1041. if (!closure)
  1042. return 0;
  1043. mset(closure, _:E_INLINE_CALL_NULL, 0);
  1044. mset(closure, _:E_INLINE_CALL_HANDLER, _:addressof (Callback_CallHandler_<x>));
  1045. mset(closure, _:E_INLINE_CALL_CLAIM, _:addressof (Callback_Claim_));
  1046. mset(closure, _:E_INLINE_CALL_RELEASE, _:addressof (Callback_Release_<tResolvedAlloc:>));
  1047. mset(closure, _:E_INLINE_CALL_TIMER, SetTimerEx("Inline_MaybeFree_", 0, false, "i", _:closure));
  1048. mset(closure, _:E_INLINE_CALL_FLAGS, e_INLINE_FLAG_CONST);
  1049. mset(closure, _:E_INLINE_CALL_METADATA, 0);
  1050. mset(closure, _:E_INLINE_CALL_SIZE, size);
  1051. mset(closure, _:E_INLINE_CALL_SOURCE, ptr);
  1052. mset(closure, _:E_INLINE_CALL_FUNCTION, AMX_Read(ret));
  1053. new
  1054. ResolvedAlloc:ra = Malloc_Resolve(closure);
  1055. rawMemcpy(_:ra + _:E_INLINE_CALL_PARAMS * cellbytes, ptr, size);
  1056. AMX_Write(_:ra + (_:E_INLINE_CALL_PARAMS + 2) * cellbytes + frm - ptr, args);
  1057. return Indirect_Ptr(ra);
  1058. }
  1059. }
  1060. // Read the next data.
  1061. ptr = AMX_Read(ptr + cellbytes);
  1062. }
  1063. return 0;
  1064. }
  1065. /*-------------------------------------------------------------------------*//**
  1066. * <param name="name">Callback to find by name.</param>
  1067. * <param name="dest">Where to store the function.</param>
  1068. * <param name="remote">Is this function called on one or all scripts?</param>
  1069. * <param name="tag">The tag of the return value for type reasons.</param>
  1070. * <remarks>
  1071. * Replacement for `Callback_Get`. Just returns a pointer, and no longer
  1072. * relies on strict control of where is called from.
  1073. * </remarks>
  1074. *//*------------------------------------------------------------------------**/
  1075. stock bool:Callback_Find_(const name[], &dest, bool:remote = false, tag = 0)
  1076. {
  1077. // Remote calls must always be publics. Indirection handles publics
  1078. // natively. However, it doesn't handle it amazingly since it needs to
  1079. // handle it very generically.
  1080. if (remote)
  1081. {
  1082. new
  1083. tagname[32];
  1084. GetTagNameFromID(tag, tagname);
  1085. return !!(dest = _:GetRemoteFunction(name, tagname[4]));
  1086. }
  1087. else if (funcidx(name) != -1)
  1088. {
  1089. new
  1090. tagname[32];
  1091. GetTagNameFromID(tag, tagname);
  1092. return !!(dest = _:GetLocalFunction(name, tagname[4]));
  1093. }
  1094. // Otherwise iterate through the in-scope inlines stack to see if any match
  1095. // this name.
  1096. return !!(dest = Callback_InlineByName(name, tag));
  1097. }
  1098. #define Callback_Find(%0,%1) Callback_Find_(%0,_:%1,.tag=tagof(%1))
  1099. #define Callback_Find_(%0,_:%1,%2,.tag=tagof(%3)) Callback_Find_(%0,_:%1,%2,tagof(%1))
  1100. // Old API.
  1101. /*-------------------------------------------------------------------------*//**
  1102. * <param name="func">Info on the function to be called.</param>
  1103. * <remarks>
  1104. * Takes an inline function handler and parameters, and either calls the
  1105. * public function while passing through the parameters, or just jumps to the
  1106. * carefully crafted inline function code.
  1107. * </remarks>
  1108. *//*------------------------------------------------------------------------**/
  1109. #pragma deprecated Use `@.func(params);`.
  1110. stock Callback_Call(const func[E_CALLBACK_DATA], GLOBAL_TAG_TYPES:...)
  1111. {
  1112. return Indirect_Call(func[E_CALLBACK_DATA_ALLOC], 0, ___(1));
  1113. }
  1114. /*-------------------------------------------------------------------------*//**
  1115. * <param name="func">Info on the function to be called.</param>
  1116. * <param name="params">Array of data pointers.</param>
  1117. * <param name="num">Size of the array.</param>
  1118. * <remarks>
  1119. * This is very similar to Callback_Call, but takes an array of ADDRESSES
  1120. * instead of normal parameters. This is designed to help support some
  1121. * experimental OO code I was working on...
  1122. *
  1123. * If the target is a public function, the parameters are resolved and passed
  1124. * normally. If the target is an inline function we are optimised for the
  1125. * common case, so move the data on to the stack (currently done value-by-value
  1126. * not all at once) and call "Callback_Call".
  1127. * </remarks>
  1128. *//*------------------------------------------------------------------------**/
  1129. #pragma deprecated Use `Indirect_Array(_:func, tagof (func), params);`.
  1130. stock Callback_Array(const func[E_CALLBACK_DATA], const params[], num = sizeof (params))
  1131. {
  1132. return Indirect_Array(func[E_CALLBACK_DATA_ALLOC], 0, params, num);
  1133. }
  1134. #pragma deprecated No longer required.
  1135. stock Inline_Reset(callback[E_CALLBACK_DATA])
  1136. {
  1137. return (callback[E_CALLBACK_DATA_ALLOC] = 0);
  1138. }
  1139. native Callback_Strcat_(dest, const src[], len) = strcat;
  1140. /*-------------------------------------------------------------------------*//**
  1141. * <remarks>
  1142. * Looks up the callback by name. If the name has the correct data embedded
  1143. * within it that's great and we use that directly. Otherwise this function
  1144. * loops backwards over the callbacks currently in scope (mostly) to the start
  1145. * of the parent function. If a match is still not found this looks for a
  1146. * public function of the same name. If that isn't found either it gives up.
  1147. *
  1148. * The new "remote" parameter returns instantly with a remote public function
  1149. * stub, and no stored data.
  1150. * </remarks>
  1151. *//*------------------------------------------------------------------------**/
  1152. #pragma deprecated Remove or use `Indirect_Claim(func);`.
  1153. #if __COMPILER_BUG_317
  1154. stock bool:Callback_Get_(__DUMMY_COMPILER_BUG_317_FIX__, {F@_@, _}:...)
  1155. {
  1156. #pragma unused __DUMMY_COMPILER_BUG_317_FIX__
  1157. #else
  1158. stock bool:Callback_Get({F@_@, _}:...)
  1159. {
  1160. #endif
  1161. // Construct the old API arguments from the new prototype. The new
  1162. // prototype can take both old-style strings and new-style resolved
  1163. // callbacks from `using inline`.
  1164. new
  1165. name[32],
  1166. numArgs = numargs(),
  1167. ret,
  1168. expect[32] = "F@_@",
  1169. bool:remote = false;
  1170. assert(2 + __COMPILER_BUG_317 <= numArgs <= 4 + __COMPILER_BUG_317);
  1171. getstringarg(name, 0 + __COMPILER_BUG_317);
  1172. if (numArgs >= 3 + __COMPILER_BUG_317)
  1173. {
  1174. getstringarg(expect[4], 2 + __COMPILER_BUG_317, sizeof (expect) - 4);
  1175. if (numArgs == 4 + __COMPILER_BUG_317)
  1176. remote = bool:getarg(3 + __COMPILER_BUG_317);
  1177. }
  1178. P:2("Callback_Get called: %d %s %s", numArgs, name, expect);
  1179. // Check that the assumption made here is valid. I can't imagine a
  1180. // case, given the number of libraries in use here, where it couldn't
  1181. // be! We check this since any inline data pointer will point in to the
  1182. // `DAT` segment relative to the start of `COD`. Any bare string will
  1183. // just be a normal string starting with an ASCII letter. This is
  1184. // different to in the indirection include which knows to resolve the
  1185. // pointer before checking this, unlike here were we don't know the
  1186. // type.
  1187. assert(AMX_HEADER_DAT - AMX_HEADER_COD > 128);
  1188. ret = getarg(0 + __COMPILER_BUG_317);
  1189. if (ret == 0)
  1190. return false;
  1191. if (!(0 < ret < 128))
  1192. {
  1193. // Resolved inline.
  1194. P:3("Callback_Get: Found resolved callback: %08x", ret);
  1195. setarg(1 + __COMPILER_BUG_317, 0, ret);
  1196. // Direct function pointer.
  1197. Indirect_Claim(ret);
  1198. ret = Indirect_DePtr_(ret);
  1199. if (ret < 0 || AMX_Read(ret))
  1200. return true;
  1201. // Check the format, since we might have a new one to use here.
  1202. numArgs = AMX_Read(ret + _:E_PUBLIC_CALL_HANDLER * cellbytes);
  1203. if (numArgs == _:addressof (Callback_LocalHandler_<x>) || numArgs == _:addressof (Callback_RemoteHandler_<x>))
  1204. {
  1205. if (expect[4] && AMX_Read(ret + _:E_PUBLIC_CALL_SPECIFIER * cellbytes) == '\0')
  1206. {
  1207. // Given an explicit specifier here, not before.
  1208. Callback_Strcat_(ret + _:E_PUBLIC_CALL_SPECIFIER * cellbytes, expect[4], 32);
  1209. }
  1210. }
  1211. return true;
  1212. }
  1213. getstringarg(name, 0 + __COMPILER_BUG_317);
  1214. // Wrap the new string-find API.
  1215. if (Callback_Find_(name, ret, remote, GetTagIDFromName(expect)))
  1216. {
  1217. setarg(1 + __COMPILER_BUG_317, 0, ret);
  1218. Indirect_Claim(ret);
  1219. return true;
  1220. }
  1221. return false;
  1222. #if __COMPILER_BUG_317
  1223. }
  1224. #define Callback_Get(%0) Callback_Get_(0,%0)
  1225. #else
  1226. // Make brace finders and code folders happy.
  1227. }
  1228. #endif
  1229. /*-------------------------------------------------------------------------*//**
  1230. * <param name="input">Callback to release.</param>
  1231. * <remarks>
  1232. * Releases all the data associated with a given callback (closure storage).
  1233. * </remarks>
  1234. *//*------------------------------------------------------------------------**/
  1235. #pragma deprecated Remove or use `Indirect_Release(func);`.
  1236. stock Callback_Release(const input[E_CALLBACK_DATA])
  1237. {
  1238. Indirect_Release(input[E_CALLBACK_DATA_ALLOC]);
  1239. return 0;
  1240. }
  1241. static stock Callback_Claim_(func[E_INLINE_CALL])
  1242. {
  1243. KillTimer(func[E_INLINE_CALL_TIMER]);
  1244. func[E_INLINE_CALL_TIMER] = SetTimerEx("Inline_MaybeConst_", 0, false, "i", ref(func));
  1245. }
  1246. /*-------------------------------------------------------------------------*//**
  1247. * <param name="">The parameters passed to this function via <see>@</see>.</param>
  1248. * <remarks>
  1249. * When you call an inline function using <c>@.name</c>, the underlying
  1250. * <c>Indirect_Call</c> function jumps to a handler. This is that handler.
  1251. * The code here copies the function closure then jumps to the start of the
  1252. * inline code. Once that is completed, the inline jumps back to midway
  1253. * through this function to store the closure back again (if required).
  1254. * </remarks>
  1255. *//*------------------------------------------------------------------------**/
  1256. static Callback_CallHandler_(...)
  1257. {
  1258. // This is called by `@`, and given the above enum in `INDIRECTION_DATA`.
  1259. // Get the size, and update `INDIRECTION_DATA` to point to the data.
  1260. #emit LOAD.alt INDIRECTION_DATA // 2
  1261. #emit MOVE.pri // 3
  1262. #emit ADD.C 40 // 5, E_INLINE_CALL_PARAMS * cellbytes
  1263. #emit PUSH.pri // 6
  1264. #emit CONST.pri 7 // 8, E_INLINE_CALL_SIZE
  1265. #emit LIDX // 9
  1266. #emit PUSH.pri // 10
  1267. #emit STACK 0 // 12
  1268. #emit SUB.alt // 13
  1269. #emit SCTRL 4 // 15
  1270. // Grow the stack.
  1271. // Call memcpy (put the native's parameters on the stack beyond `dest`).
  1272. #emit PUSH.S 0xFFFFFFF8 // 17
  1273. #emit PUSH.S 0xFFFFFFF8 // 19
  1274. #emit PUSH.C 0 // 21
  1275. #emit PUSH.S 0xFFFFFFFC // 23
  1276. #emit PUSH.pri // 24
  1277. #emit PUSH.C 20 // 26
  1278. #emit SYSREQ.C memcpy // 28
  1279. #emit STACK 24 // 30
  1280. // "jump" in to the code.
  1281. #emit LOAD.S.pri 0xFFFFFFFC // 32
  1282. #emit ADD.C 0xFFFFFFFC // 34
  1283. #emit LOAD.I // 35
  1284. #emit SCTRL 6 // 37
  1285. //Callback_Call_restore_stack:
  1286. // The stack hasn't been restored yet, and we need it.
  1287. #emit NOP // 38
  1288. #emit NOP // 39
  1289. // Padding to account for debug builds.
  1290. // If the code decides to jump back to here, save the stack out again.
  1291. #emit LCTRL 4 // 41
  1292. #emit PUSH.S 0xFFFFFFF8 // 43
  1293. #emit PUSH.S 0xFFFFFFF8 // 45
  1294. #emit PUSH.C 0 // 47
  1295. #emit PUSH.pri // 48
  1296. #emit PUSH.S 0xFFFFFFFC // 50
  1297. #emit PUSH.C 20 // 52
  1298. #emit SYSREQ.C memcpy // 54
  1299. // At this point, we've written the closure back to the structure.
  1300. // Indicate to the original user that this is the case by clearing
  1301. // `e_INLINE_FLAG_CONST` from the flags (actually clears all the flags,
  1302. // since `e_INLINE_FLAG_PUBLIC` isn't set here either).
  1303. #emit LOAD.S.pri 0xFFFFFFFC // 56
  1304. #emit ADD.C 0xFFFFFFF0 // 58 , E_INLINE_CALL_FLAGS - E_INLINE_CALL_PARAMS
  1305. #emit MOVE.alt // 59
  1306. #emit ZERO.pri // 60
  1307. #emit STOR.I // 61
  1308. #emit LCTRL 5 // 63
  1309. #emit SCTRL 4 // 65
  1310. #emit LOAD.pri I@ // 67
  1311. #emit RETN // 68
  1312. __COMPILER_NAKED
  1313. }
  1314. /*-------------------------------------------------------------------------*//**
  1315. * <param name="">The parameters passed to this function via <see>@</see>.</param>
  1316. * <remarks>
  1317. * When you call a remote function using <c>@.name</c>, the underlying
  1318. * <c>Indirect_Call</c> function jumps to a handler. This is that handler.
  1319. * The code here simply wraps <c>CallRemoteFunction</c>, passing the specifier
  1320. * and function name stored in <c>INDIRECTION_DATA</c>, which is assumed to be
  1321. * a pointer to <c>enum E_PUBLIC_CALL</c>.
  1322. * </remarks>
  1323. *//*------------------------------------------------------------------------**/
  1324. static stock Callback_RemoteHandler_(...)
  1325. {
  1326. #emit LOAD.S.alt 8
  1327. #emit LCTRL 4
  1328. #emit SUB
  1329. #emit SCTRL 4
  1330. // Call `memcpy` to make a new copy of the parameters for
  1331. // `CallRemoteFunction`.
  1332. #emit PUSH.alt
  1333. #emit PUSH.alt
  1334. #emit PUSH.C 0
  1335. #emit PUSH.adr 12
  1336. #emit PUSH.pri
  1337. #emit PUSH.C 20
  1338. #emit SYSREQ.C memcpy
  1339. #emit STACK 24
  1340. // Push the specifier and function name.
  1341. #emit LOAD.pri INDIRECTION_DATA
  1342. #emit ADD.C 28 // E_PUBLIC_CALL_SPECIFIER * cellbytes
  1343. #emit PUSH.pri
  1344. #emit ADD.C 128 // (E_PUBLIC_CALL_FUNCTION - E_PUBLIC_CALL_SPECIFIER) * cellbytes
  1345. #emit PUSH.pri
  1346. #emit LOAD.S.pri 8
  1347. #emit ADD.C 8
  1348. #emit PUSH.pri
  1349. #emit SYSREQ.C CallRemoteFunction
  1350. // Store the return, then do the return.
  1351. #emit MOVE.alt
  1352. #emit LCTRL 5
  1353. #emit SCTRL 4
  1354. #emit MOVE.pri
  1355. #emit RETN
  1356. __COMPILER_NAKED
  1357. }
  1358. /*-------------------------------------------------------------------------*//**
  1359. * <param name="">The parameters passed to this function via <see>@</see>.</param>
  1360. * <remarks>
  1361. * When you call a local function using <c>@.name</c>, the underlying
  1362. * <c>Indirect_Call</c> function jumps to a handler. This is that handler.
  1363. * The code here simply wraps <c>CallLocalFunction</c>, passing the specifier
  1364. * and function name stored in <c>INDIRECTION_DATA</c>.
  1365. * </remarks>
  1366. *//*------------------------------------------------------------------------**/
  1367. static stock Callback_LocalHandler_(...)
  1368. {
  1369. #emit LOAD.S.alt 8
  1370. #emit LCTRL 4
  1371. #emit SUB
  1372. #emit SCTRL 4
  1373. // Call `memcpy` to make a new copy of the parameters for
  1374. // `CallLocalFunction`.
  1375. #emit PUSH.alt
  1376. #emit PUSH.alt
  1377. #emit PUSH.C 0
  1378. #emit PUSH.adr 12
  1379. #emit PUSH.pri
  1380. #emit PUSH.C 20
  1381. #emit SYSREQ.C memcpy
  1382. #emit STACK 24
  1383. // Push the specifier and function name.
  1384. #emit LOAD.pri INDIRECTION_DATA
  1385. #emit ADD.C 28 // E_PUBLIC_CALL_SPECIFIER * cellbytes
  1386. #emit PUSH.pri
  1387. #emit ADD.C 128 // (E_PUBLIC_CALL_FUNCTION - E_PUBLIC_CALL_SPECIFIER) * cellbytes
  1388. #emit PUSH.pri
  1389. #emit LOAD.S.pri 8
  1390. #emit ADD.C 8
  1391. #emit PUSH.pri
  1392. #emit SYSREQ.C CallLocalFunction
  1393. // Store the return, then do the return.
  1394. #emit MOVE.alt
  1395. #emit LCTRL 5
  1396. #emit SCTRL 4
  1397. #emit MOVE.pri
  1398. #emit RETN
  1399. __COMPILER_NAKED
  1400. }
  1401. /*-------------------------------------------------------------------------*//**
  1402. * <param name="ctx">The code generation output context.</param>
  1403. * <param name="parameters">Information on the fake parameter types.</param>
  1404. * <param name="count">How many parameters there are.</param>
  1405. * <remarks>
  1406. * Generates the code which copies the parameters from `Callback_Call` in to
  1407. * the local stack. All those parameters are passed by reference, since the
  1408. * function is a varargs function. Since an inline function's "input"
  1409. * parameters are just regular variables on the stack, they all need resolving,
  1410. * which is what this code does. Regular variables are dereferenced, and
  1411. * arryas and strings are fully copied over.
  1412. *
  1413. * Technically this doesn't ACTUALLY do the copy, but generates the code for
  1414. * the copy.
  1415. * </remarks>
  1416. *//*------------------------------------------------------------------------**/
  1417. static stock Inline_GenerateLocalsCopy(ctx[AsmContext], const parameters[], count)
  1418. {
  1419. new
  1420. input = 12;
  1421. for (new i = 0; i != count; ++i)
  1422. {
  1423. switch (parameters[i])
  1424. {
  1425. case -1, 0:
  1426. {
  1427. @emit LREF.S.pri input
  1428. @emit PUSH.pri
  1429. }
  1430. case cellmax:
  1431. {
  1432. @emit STACK -(YSI_MAX_INLINE_STRING * cellbytes)
  1433. @emit STACK 0
  1434. @emit PUSH.C YSI_MAX_INLINE_STRING
  1435. @emit PUSH.S input
  1436. @emit PUSH.alt
  1437. @emit PUSH.C 12
  1438. @emit SYSREQ "strunpack"
  1439. @emit STACK 16
  1440. }
  1441. default:
  1442. {
  1443. @emit STACK -(parameters[i])
  1444. @emit STACK 0
  1445. @emit LOAD.S.pri input
  1446. @emit MOVS (parameters[i])
  1447. }
  1448. }
  1449. input += cellbytes;
  1450. }
  1451. // Jump past the postamble, which puts a return address on the stack.
  1452. @emit CALL.label Inline_Start
  1453. }
  1454. /*-------------------------------------------------------------------------*//**
  1455. * <param name="ctx">The code generation output context.</param>
  1456. * <param name="parameters">Information on the fake parameter types.</param>
  1457. * <param name="count">How many parameters there are.</param>
  1458. * <remarks>
  1459. * When the inline function ends, any parameters that were defined as being
  1460. * passed by reference are copied back. This is because true locals are never
  1461. * by reference, so we fake it. There is one bug with this method - aliased
  1462. * variables won't work correctly:
  1463. *
  1464. * <c>
  1465. * inline Func(&a, &b)
  1466. * {
  1467. * ++a;
  1468. * printf("%d", b);
  1469. * }
  1470. *
  1471. * new a = 10;
  1472. * Callback_Call(using inline Func, a, a);
  1473. * </c>
  1474. *
  1475. * That will print <c>10</c> while the same code with a normal function will
  1476. * print <c>11</c> thanks to <c>a</c> and <c>b</c> being aliased. Maybe I
  1477. * should add a <c>restrict</c> keyword, but even then I don't know how to
  1478. * solve unrestricted variables (at best I can warn for them). And this is not
  1479. * a totally unheard of situation. I have at least seen this for getting only
  1480. * a player's height:
  1481. *
  1482. * <c>
  1483. * new z;
  1484. * GetPlayerPos(playerid, z, z, z);
  1485. * </c>
  1486. *
  1487. * </remarks>
  1488. *//*------------------------------------------------------------------------**/
  1489. static stock Inline_GenerateLocalsStore(ctx[AsmContext], const parameters[], count)
  1490. {
  1491. new
  1492. accumulate = 0;
  1493. while (count--)
  1494. {
  1495. switch (parameters[count])
  1496. {
  1497. case -1:
  1498. {
  1499. // Only use `alt` in here, since `Inline_GeneratePostamble` holds
  1500. // the final stack value in `pri`.
  1501. if (accumulate)
  1502. @emit STACK accumulate
  1503. accumulate = 0;
  1504. @emit POP.alt
  1505. @emit SREF.S.alt count * cellbytes + 12
  1506. }
  1507. case 0:
  1508. accumulate += cellbytes;
  1509. case cellmax:
  1510. accumulate += YSI_MAX_INLINE_STRING * cellbytes;
  1511. default:
  1512. accumulate += parameters[count];
  1513. }
  1514. }
  1515. // Return how much of this data was left on the stack. We might need to
  1516. // clear it, we might not...
  1517. return accumulate;
  1518. }
  1519. static stock Inline_GeneratePreamble(ctx[AsmContext], locals)
  1520. {
  1521. // This is sort of the start of the code.
  1522. @emit Inline_Start:
  1523. // Set the local frame.
  1524. @emit STACK 0
  1525. @emit LCTRL 5
  1526. @emit XCHG
  1527. @emit ADD.C locals + 4
  1528. @emit SCTRL 5
  1529. @emit STOR.S.alt 0
  1530. // Get the return address.
  1531. @emit POP.pri
  1532. @emit STOR.S.pri 4
  1533. }
  1534. static stock Inline_GeneratePostamble(ctx[AsmContext], const parameters[], const count, bool:isConst, locals, inlineParams)
  1535. {
  1536. new
  1537. bool:needStore = false,
  1538. accumulate = 0;
  1539. for (new i = 0; i != count; ++i)
  1540. {
  1541. if (parameters[i] == -1)
  1542. {
  1543. needStore = true;
  1544. break;
  1545. }
  1546. }
  1547. // When we "return" from the function, we end up here, with all our stack
  1548. // data removed. Put it back (which is easy, because the top should still
  1549. // be in `alt` - actually, it might not in some cases). Turns out that's
  1550. // irrelevant, since the only time it can happen is if there are no locals
  1551. // to save!
  1552. if (isConst)
  1553. {
  1554. if (needStore)
  1555. {
  1556. // Back up `pri` somewhere for returning later.
  1557. @emit STOR.pri ref(I@)
  1558. // This used to exploit the fact that the bottom of the stack was
  1559. // still in `alt`, as in:
  1560. //
  1561. // https://github.com/compuphase/pawn/issues/35
  1562. //
  1563. // However, that was brittle (it has already been changed in the
  1564. // official distribution), and was inaccurate if there were extra
  1565. // locals declared in the inline.
  1566. @emit LOAD.S.pri 0xFFFFFFF8
  1567. @emit STACK 0
  1568. @emit ADD.C locals - inlineParams
  1569. @emit SUB.alt
  1570. @emit SCTRL 4
  1571. // We will remove everything from the stack later, as well as the
  1572. // two values on the stack for `memcpy` restoration.
  1573. @emit CONST.pri 8
  1574. @emit ADD
  1575. accumulate = Inline_GenerateLocalsStore(ctx, parameters, count);
  1576. // Restore the return value.
  1577. @emit SCTRL 4
  1578. @emit LOAD.pri ref(I@)
  1579. @emit RETN
  1580. }
  1581. else
  1582. {
  1583. // If we are here, we are in the context of `Callback_Call`, with no
  1584. // locals on the stack.
  1585. @emit STACK 8
  1586. @emit RETN
  1587. }
  1588. }
  1589. else
  1590. {
  1591. @emit STOR.pri ref(I@)
  1592. if (needStore)
  1593. {
  1594. @emit LOAD.S.alt 0xFFFFFFF8
  1595. @emit LCTRL 4
  1596. @emit ADD.C inlineParams - locals
  1597. @emit SUB
  1598. @emit SCTRL 4
  1599. accumulate = Inline_GenerateLocalsStore(ctx, parameters, count);
  1600. if (accumulate)
  1601. @emit STACK accumulate
  1602. }
  1603. else
  1604. {
  1605. // Go back down the stack, up to where the inline parameters began,
  1606. // but not including them. This will set us up the bomb (sorry,
  1607. // couldn't resist). This will set us up nicely for the jump in to
  1608. // `Callback_CallHandler` for copying the stack back out again.
  1609. @emit LOAD.S.alt 0xFFFFFFF8
  1610. @emit LCTRL 4
  1611. @emit SUB
  1612. @emit SCTRL 4
  1613. }
  1614. // Jump to `Callback_Call_restore_stack:` to perform common cleanup.
  1615. @emit JUMP YSI_g_sCallbackCallAddress + 39 * cellbytes
  1616. }
  1617. }
  1618. static stock Inline_DoRetnGen(ctx[AsmContext], const scanner[CodeScanner], const data[E_INLINE_DATA])
  1619. {
  1620. // Remove the return for the inner loop, since it may now point to an
  1621. // invalid address (in the middle of an OpCode we just wrote out).
  1622. new
  1623. startaddr = data[E_INLINE_DATA_USER],
  1624. endaddrDAT = CodeScanGetMatchHole(scanner, 1) - 8,
  1625. endaddrCOD = endaddrDAT + YSI_g_sJumpOffset,
  1626. nop = _:RelocateOpcode(OP_NOP),
  1627. dctx[DisasmContext];
  1628. // Using the local decompiler, go through the code and remove any jumps to
  1629. // outside of [startaddr, endaddrDAT]. Convert them all to `RETN; NOP`.
  1630. CodeScanGetMatchDisasm(scanner, dctx, CodeScanGetMatchLength(scanner));
  1631. dctx[DisasmContext_end_ip] = endaddrDAT + AMX_HEADER_COD + 16;
  1632. while (DisasmNext(dctx) != DISASM_DONE)
  1633. {
  1634. // Is this a jump? The only jumps that can get out of this constraint
  1635. // are `JUMP` ones - all others like `JNEQ` etc would be generated by
  1636. // `if` statements and so constrained by syntax. `JUMP` would come from
  1637. // `break`, `continue`, or `goto`.
  1638. if (DisasmGetOpcode(dctx) == OP_JUMP && !(startaddr <= DisasmGetOperandReloc(dctx) < endaddrDAT))
  1639. {
  1640. AMX_Write(DisasmGetCurIp(dctx) + 4, endaddrCOD);
  1641. }
  1642. }
  1643. // Save this end address for when we detect inline function destructor calls. This way we don't
  1644. // destruct the inline within itself.
  1645. YSI_g_sInlineEndPoint = endaddrDAT;
  1646. endaddrDAT += AMX_HEADER_COD;
  1647. // Add the current inline to the linked list of in-scope inlines. Also push
  1648. // the inline address and current frame data.
  1649. @emit PUSH ref(YSI_g_sInlineLinkedList)
  1650. @emit PUSH.C data[E_INLINE_DATA_NAME]
  1651. @emit ADDR.alt -data[E_INLINE_DATA_LOCALS]
  1652. @emit STOR.alt ref(YSI_g_sInlineLinkedList)
  1653. // Size of inline parameters.
  1654. startaddr = data[E_INLINE_DATA_STACK] - data[E_INLINE_DATA_LOCALS];
  1655. if (startaddr)
  1656. {
  1657. // Look for the next `stack`.
  1658. dctx[DisasmContext_end_ip] = 0;
  1659. while (DisasmNext(dctx) != DISASM_DONE && DisasmGetOpcode(dctx) != OP_STACK)
  1660. {
  1661. AMX_Write(endaddrDAT, _:DisasmGetOpcode(dctx));
  1662. endaddrDAT += 4;
  1663. for (startaddr = 0; startaddr < DisasmGetNumOperands(dctx); ++startaddr)
  1664. {
  1665. AMX_Write(endaddrDAT, _:DisasmGetOperand(dctx, startaddr));
  1666. endaddrDAT += 4;
  1667. }
  1668. startaddr = 0;
  1669. }
  1670. @emit JUMP endaddrDAT - AMX_HEADER_COD + YSI_g_sJumpOffset + 24
  1671. AMX_Write(endaddrDAT , _:RelocateOpcode(OP_STACK));
  1672. AMX_Write(endaddrDAT + 4 , data[E_INLINE_DATA_STACK]);
  1673. AMX_Write(endaddrDAT + 8 , _:RelocateOpcode(OP_ZERO_PRI));
  1674. AMX_Write(endaddrDAT + 12, _:RelocateOpcode(OP_RETN));
  1675. if (!startaddr)
  1676. {
  1677. // The write extended beyond the limit of where codescan was up to. Adjust the stack
  1678. // back down again by the size of the closure data (i.e. the data outside the current
  1679. // inline). This is AFTER the return, since we don't actually want the code to run,
  1680. // just correct codescan's view of the world.
  1681. AMX_Write(DisasmGetCurIp(dctx) + 4, -data[E_INLINE_DATA_LOCALS]);
  1682. }
  1683. }
  1684. else if (data[E_INLINE_DATA_STACK])
  1685. {
  1686. // No parameters, but some stack.
  1687. @emit JUMP endaddrCOD + 16
  1688. AMX_Write(endaddrDAT , _:RelocateOpcode(OP_STACK));
  1689. AMX_Write(endaddrDAT + 4 , data[E_INLINE_DATA_STACK]);
  1690. AMX_Write(endaddrDAT + 8 , _:RelocateOpcode(OP_ZERO_PRI));
  1691. AMX_Write(endaddrDAT + 12, _:RelocateOpcode(OP_RETN));
  1692. }
  1693. else
  1694. {
  1695. // No cleanup, no stack correction, no need to satisfy codescan.
  1696. @emit JUMP endaddrCOD + 16
  1697. AMX_Write(endaddrDAT , _:RelocateOpcode(OP_ZERO_PRI));
  1698. AMX_Write(endaddrDAT + 4 , _:RelocateOpcode(OP_RETN));
  1699. AMX_Write(endaddrDAT + 8 , nop);
  1700. AMX_Write(endaddrDAT + 12, nop);
  1701. }
  1702. }
  1703. #define CALL@Inline_OnAsmError Inline_OnAsmError("", ASM_ERROR_NONE)
  1704. static stock Inline_OnAsmError(const ctx[AsmContext], AsmError:error)
  1705. {
  1706. switch (numargs() == 1 ? AsmGetError(ctx) : error)
  1707. {
  1708. case ASM_ERROR_OPCODE: P:E("ASM_ERROR_OPCODE in Inline_Main.");
  1709. case ASM_ERROR_OPERAND: P:E("ASM_ERROR_OPERAND in Inline_Main.");
  1710. case ASM_ERROR_SPACE: P:E("ASM_ERROR_SPACE in Inline_Main.");
  1711. case ASM_ERROR_LABEL_OVERFLOW: P:E("ASM_ERROR_LABEL_OVERFLOW in Inline_Main.");
  1712. case ASM_ERROR_LABEL_DUPLICATE: P:E("ASM_ERROR_LABEL_DUPLICATE in Inline_Main.");
  1713. case ASM_ERROR_NONE: return;
  1714. default: P:E("Unknown error in Inline_Main.");
  1715. }
  1716. // TODO: Abort codegen.
  1717. }
  1718. static stock Inline_DoCodeGen(const scanner[CodeScanner], const data[E_INLINE_DATA])
  1719. {
  1720. new
  1721. ctx[AsmContext];
  1722. AsmInitPtr(ctx, data[E_INLINE_DATA_START] + AMX_HEADER_COD, data[E_INLINE_DATA_USER] - data[E_INLINE_DATA_START]);
  1723. AsmSetErrorHandler(ctx, addressof (Inline_OnAsmError));
  1724. Inline_DoRetnGen(ctx, scanner, data);
  1725. Inline_GenerateLocalsCopy(ctx, data[E_INLINE_DATA_PARAMETERS], data[E_INLINE_DATA_PARAMETER_COUNT]);
  1726. Inline_GeneratePostamble(ctx, data[E_INLINE_DATA_PARAMETERS], data[E_INLINE_DATA_PARAMETER_COUNT], bool:(data[E_INLINE_DATA_STATE] & 4), data[E_INLINE_DATA_STACK], data[E_INLINE_DATA_LOCALS]);
  1727. Inline_GeneratePreamble(ctx, data[E_INLINE_DATA_STACK]);
  1728. AsmEmitPadding(ctx);
  1729. }
  1730. public Callback_Release_(ResolvedAlloc:a)
  1731. {
  1732. KillTimer(AMX_Read(_:a + _:E_INLINE_CALL_TIMER * cellbytes));
  1733. free(Malloc_Reconcile(a));
  1734. }
  1735. #if __COMPILER_CONST_REF
  1736. #pragma warning push
  1737. #pragma warning disable 238
  1738. #endif
  1739. static stock Inline_Ref(const &ptr)
  1740. {
  1741. #emit LOAD.S.pri ptr
  1742. #emit RETN
  1743. __COMPILER_NAKED
  1744. }
  1745. #if __COMPILER_CONST_REF
  1746. #pragma warning pop
  1747. #endif
  1748. #if __COMPILER_CONST_REF
  1749. #pragma warning push
  1750. #pragma warning disable 238
  1751. #endif
  1752. stock Inline_UI_(const &header)
  1753. {
  1754. // We use `&header` instead of `return` so that we can keep the tags
  1755. // correct. This also passes an address that we know is the bottom of the
  1756. // part of the stack that we need to save for the closure. The bottom two
  1757. // variables in the stack must be preserved, since they store information
  1758. // about the inline function (name and address), which may be required even
  1759. // in the inline if it is called recursively. The code is compiled to pass
  1760. // `header` as the inline structure. However, that is just for tag
  1761. // checking, and we change that in assembly so that the return from this
  1762. // function is truly what is pushed.
  1763. //
  1764. // We can't use `ref()` here because passing a `const &` to a `...` function
  1765. // gets the address of a heap temporary, instead of the original. Even
  1766. // `const ...` doesn't solve this.
  1767. P:3("Inline_UI_ called: %d", header);
  1768. new
  1769. ptr = Inline_Ref(header),
  1770. frm = AMX_Read(header + cellbytes),
  1771. args = Inline_NumArgs(ptr + frm),
  1772. size = frm + 12 + args,
  1773. // The size of the allocation makes very little difference, so even if
  1774. // it isn't needed, we allocate extra memory here for the closure
  1775. // storage. The other option would be allocating it separately later
  1776. // when we determine it is needed, but that would be no faster in the
  1777. // common case, and slower in the uncommon case, so just do it together.
  1778. Alloc:closure = malloc(size / cellbytes + _:E_INLINE_CALL - 1);
  1779. P:5("Inline_UI_: %d %d %d %d %d", ptr, frm, size, _:closure, AMX_Read(header));
  1780. if (!closure)
  1781. return 0;
  1782. mset(closure, _:E_INLINE_CALL_NULL, 0);
  1783. mset(closure, _:E_INLINE_CALL_HANDLER, _:addressof (Callback_CallHandler_<x>));
  1784. mset(closure, _:E_INLINE_CALL_CLAIM, _:addressof (Callback_Claim_));
  1785. mset(closure, _:E_INLINE_CALL_RELEASE, _:addressof (Callback_Release_<tResolvedAlloc:>));
  1786. mset(closure, _:E_INLINE_CALL_TIMER, SetTimerEx("Inline_MaybeFree_", 0, false, "i", _:closure));
  1787. mset(closure, _:E_INLINE_CALL_FLAGS, e_INLINE_FLAG_CONST);
  1788. mset(closure, _:E_INLINE_CALL_METADATA, 0);
  1789. mset(closure, _:E_INLINE_CALL_SIZE, size);
  1790. mset(closure, _:E_INLINE_CALL_SOURCE, ptr);
  1791. mset(closure, _:E_INLINE_CALL_FUNCTION, AMX_Read(header));
  1792. new
  1793. ResolvedAlloc:ra = Malloc_Resolve(closure);
  1794. rawMemcpy(_:ra + _:E_INLINE_CALL_PARAMS * cellbytes, ptr, size);
  1795. AMX_Write(_:ra + (_:E_INLINE_CALL_PARAMS + 2) * cellbytes + frm, args);
  1796. return Indirect_Ptr(ra);
  1797. }
  1798. #if __COMPILER_CONST_REF
  1799. #pragma warning pop
  1800. #endif
  1801. stock Inline_UP_(const func[], const spec[] = "")
  1802. {
  1803. // Convert a function name to a pointer.
  1804. I@ = _:GetLocalFunction(func, spec);
  1805. }
  1806. /*-------------------------------------------------------------------------*//**
  1807. * <param name="">Info on the restoration function.</param>
  1808. * <remarks>
  1809. * Makes variables referenced, instead of valued. When used after
  1810. * "Callback_Call" the values of any variables in the enclosing function that
  1811. * were modified in the inline function will be propgated so that their new
  1812. * values are seen by the original parent function (rather than that function
  1813. * still seeing the original values prior to the inline function modifying
  1814. * them). Note that this does no checks at all at the minute - if you call an
  1815. * inline function whose parent is not currently on the stack, this will
  1816. * probably fail catastrophically!
  1817. * </remarks>
  1818. *//*------------------------------------------------------------------------**/
  1819. stock Callback_Restore_(...)
  1820. {
  1821. assert(numargs() == 1);
  1822. // Maintain the frame header.
  1823. new
  1824. closure = getarg(0);
  1825. if (closure < 128)
  1826. return false;
  1827. closure = Indirect_DePtr(closure);
  1828. if (closure < 0 || AMX_Read(closure))
  1829. return true;
  1830. // Called via a timer, or is const, or wasn't actually called, or is a public.
  1831. if (AMX_Read(closure + _:E_INLINE_CALL_FLAGS * cellbytes) || !AMX_Read(closure + _:E_INLINE_CALL_TIMER * cellbytes))
  1832. {
  1833. return 0;
  1834. }
  1835. new
  1836. ptr = AMX_Read(closure + _:E_INLINE_CALL_SOURCE * cellbytes),
  1837. frm = ptr + AMX_Read(AMX_Read(ptr) + cellbytes),
  1838. ret = GetFrameReturn(frm),
  1839. pfr = GetFramePreviousFrame(frm);
  1840. rawMemcpy(ptr, closure + _:E_INLINE_CALL_PARAMS * cellbytes, AMX_Read(closure + _:E_INLINE_CALL_SIZE * cellbytes)),
  1841. SetFrameReturn(frm, ret),
  1842. SetFramePreviousFrame(frm, pfr);
  1843. return 0;
  1844. }
  1845. #define Callback_Restore(%0) Callback_Restore_(_:%0)
  1846. stock Inline_Debug(ptr)
  1847. {
  1848. // Get back to normal memory.
  1849. new Alloc:data = Malloc_Reconcile(ResolvedAlloc:Indirect_DePtr(ptr));
  1850. printf("ptr: %d", _:ptr);
  1851. printf("resolved: %d", _:Indirect_DePtr(ptr));
  1852. #emit CONST.alt YSI_gMallocMemory
  1853. #emit STOR.S.alt ptr
  1854. printf("memory: %d", _:ptr);
  1855. printf("slot: %d", _:data);
  1856. printf("E_INLINE_CALL_NULL: %d", mget(data, E_INLINE_CALL_NULL));
  1857. printf("E_INLINE_CALL_HANDLER: %d", mget(data, E_INLINE_CALL_HANDLER));
  1858. printf("E_INLINE_CALL_CLAIM: %d", mget(data, E_INLINE_CALL_CLAIM));
  1859. printf("E_INLINE_CALL_RELEASE: %d", mget(data, E_INLINE_CALL_RELEASE));
  1860. printf("E_INLINE_CALL_METADATA: %d", mget(data, E_INLINE_CALL_METADATA));
  1861. printf("E_INLINE_CALL_TIMER: %d", mget(data, E_INLINE_CALL_TIMER));
  1862. printf("E_INLINE_CALL_FLAGS: %d", mget(data, E_INLINE_CALL_FLAGS));
  1863. printf("E_INLINE_CALL_SIZE: %d", mget(data, E_INLINE_CALL_SIZE));
  1864. printf("E_INLINE_CALL_SOURCE: %d", mget(data, E_INLINE_CALL_SOURCE));
  1865. printf("E_INLINE_CALL_FUNCTION: %d", mget(data, E_INLINE_CALL_FUNCTION));
  1866. printf("E_INLINE_CALL_PARAMS: %d", _:Malloc_Reconcile(ResolvedAlloc:mget(data, E_INLINE_CALL_FUNCTION)));
  1867. }