Y_Less

y_inline

Aug 2nd, 2011
665
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pawn 9.70 KB | None | 0 0
  1. /*----------------------------------------------------------------------------*\
  2.                       ======================================
  3.                        y_inline - Inline functions in PAWN!
  4.                       ======================================
  5. Description:
  6.     Allows you to use basic inline functions to act as callbacks.
  7. Legal:
  8.     Version: MPL 1.1
  9.    
  10.     The contents of this file are subject to the Mozilla Public License Version
  11.     1.1 (the "License"); you may not use this file except in compliance with
  12.     the License. You may obtain a copy of the License at
  13.     http://www.mozilla.org/MPL/
  14.    
  15.     Software distributed under the License is distributed on an "AS IS" basis,
  16.     WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  17.     for the specific language governing rights and limitations under the
  18.     License.
  19.    
  20.     The Original Code is the SA:MP inline functions code.
  21.    
  22.     The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  23.     Portions created by the Initial Developer are Copyright (C) 2008
  24.     the Initial Developer. All Rights Reserved.
  25.    
  26.     Contributors:
  27.         ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  28.    
  29.     Thanks:
  30.         JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  31.         Peter, Cam - Support.
  32.         ZeeX, g_aSlice/Slice, Popz, others - Very productive conversations.
  33.         koolk - IsPlayerinAreaEx code.
  34.         TheAlpha - Danish translation.
  35.         breadfish - German translation.
  36.         Fireburn - Dutch translation.
  37.         yom - French translation.
  38.         50p - Polish translation.
  39.         Zamaroht - Spanish translation.
  40.         Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  41.             for me to strive to better.
  42.         Pixels^ - Running XScripters where the idea was born.
  43.         Matite - Pestering me to release it and using it.
  44.    
  45.     Very special thanks to:
  46.         Thiadmer - PAWN, whose limits continue to amaze me!
  47.         Kye/Kalcor - SA:MP.
  48.         SA:MP Team past, present and future - SA:MP.
  49.    
  50. Version:
  51.     1.0
  52. Changelog:
  53.     02/08/11:
  54.         First version
  55. \*----------------------------------------------------------------------------*/
  56.  
  57. // This file provides the infrastructure to allow PAWN to use inline functions
  58. // with BASIC closures.  I'm not sure how well heap structures will work though
  59. // when the inline callback is done after the enclosing function has ended - by
  60. // that point the heap memory will have been cleared, leaving us with a floating
  61. // pointer in to the heap if we do save the current stack frame.  If done, these
  62. // closures will be based around storing and restoring the stack frame of the
  63. // calling function.  This will mean that all locals are passed by value and all
  64. // arrays are passed by reference, which makes sense really given that that is
  65. // how most functions do it.  This will of course introduce issues with
  66. // reference variables in the parent function and it may just be easier to
  67. // ignore closures entirely, there are just too many problems.  Shame...
  68.  
  69. #include <YSI\internal\y_version>
  70. #include <YSI\y_debug>
  71. #include <YSI\y_utils>
  72.  
  73. enum E_INLINE
  74. {
  75.     // This is the amount of data already on the stack.  We need to allocate,
  76.     // but not fill, this much data to make the variable offsets correct in the
  77.     // inline function for it's local varaibles.  Note that this is ONLY data IN
  78.     // the stack frame - we don't push parent passed parameters at all.  "PAD"
  79.     // is in BYTES.
  80.     E_INLINE_PAD,
  81.     // This is the amount of data that the inline function is expecting to be
  82.     // passed.  Can be checked at runtime to ensure it matches the passed
  83.     // parameters.  "ALLOCATE" is in BYTES.
  84.     E_INLINE_ALLOCATE,
  85.     // The address of the inline function.  This is the address AFTER the first
  86.     // call to the "for" loop variable.  Note that the function called in the
  87.     // "while" section for the "for" loop always returns 0, but because of stack
  88.     // manipulations that part of the loop gets run three times and never ends.
  89.     E_INLINE_ADDRESS
  90. }
  91.  
  92. #define inline%0(%1) for(new %1;Inline_Loop();)
  93.  
  94. #define using%0inline NULL
  95.  
  96. //static
  97. new
  98.     YSI_g_sFramePointer,
  99.     YSI_g_sJumpTo,
  100.     YSI_g_sReturnTo,
  101.     YSI_g_sDoAddress = 0;
  102.  
  103. // This function actually stores the return address of the PREVIOUS function
  104. // (i.e. the one that called this one) and jumps to it, allowing the code
  105. // after it to be executed. This will cause a small problem if you do:
  106. //  
  107. //  var[x++] = SomeFuncWithInline(using inline);
  108. //  
  109. // The "x++" part will get executed twice, we just need to be VERY carful (and
  110. // be "we", I mean "users").  Writing code to fix that would take A LOT of code
  111. // especially as their "SomeFuncWithInline" call may be in another function call
  112. // - I may just ban return values and make people do things by reference.
  113. // Anyway, this will return to the call point and allow the "inline" function to
  114. // begin, which will execute the start of the "inline hidden for" and get an
  115. // address to jump to later on.  This will also allocate stack space, which can
  116. // be used to check allocation sizes are correct.  This address being AFTER the
  117. // data allocation is important as it means the variables exist, but are not
  118. // blanked out after we allocate the data.
  119. stock Inline_Get(function[], handle[E_INLINE])
  120. {
  121.     //printf("Inline_Get");
  122.     if (function[0] == '\1' && function[1] == '\0')
  123.     {
  124.         YSI_g_sDoAddress = 1;
  125.         // Get the return address of the function which called this function.
  126.         #emit LOAD.S.pri   0
  127.         #emit ADD.C        4
  128.         #emit MOVE.alt
  129.         #emit LOAD.I
  130.         #emit MOVE.alt
  131.         // Get the return address for here.
  132.         #emit LCTRL        6
  133.         #emit ADD.C        28
  134.         #emit STOR.pri     YSI_g_sReturnTo
  135.         // Jump to after the parent call, should be before the "inline" loop.
  136.         #emit MOVE.pri
  137.         #emit SCTRL        6
  138.         YSI_g_sDoAddress = 0;
  139.         // Now we are here (having subtly called "Inline_Loop"), we have the
  140.         // start address of the code for the "inline" function and the number
  141.         // of parameters it expects, all that's left to do now is get the size
  142.         // of the stack that the inline parent function already has.
  143.         // Get the first frame.
  144.         #emit LOAD.S.alt   0
  145.         // Get the second frame.
  146.         #emit MOVE.pri
  147.         #emit LOAD.I
  148.         // Subtract the two to get allocated byte counts.
  149.         #emit SUB
  150.         #emit ADD.C        0xFFFFFFEC
  151.         #emit STOR.pri     YSI_g_sReturnTo
  152.         handle[E_INLINE_PAD] = YSI_g_sReturnTo;
  153.         handle[E_INLINE_ALLOCATE] = YSI_g_sFramePointer;
  154.         handle[E_INLINE_ADDRESS] = YSI_g_sJumpTo;
  155.         return 1;
  156.     }
  157.     else if (function[0])
  158.     {
  159.         return -1;
  160.     }
  161.     // Wasn't passed a valid function.
  162.     return 0;
  163. }
  164.  
  165. stock Inline_Loop()
  166. {
  167.     switch (YSI_g_sDoAddress)
  168.     {
  169.         case 1:
  170.         {
  171.             // If we detect heap allocation we may have a problem...
  172.             // Get the frame pointer from "Inline_Get".  This function is not
  173.             // called inside that function, but IS called inside that function's
  174.             // stack frame, this is so we can carefully control code execution
  175.             // flow.
  176.             #emit LOAD.S.alt    0
  177.             // Get our current frame.
  178.             #emit LCTRL         5
  179.             // Subract the two.
  180.             #emit SUB.alt
  181.             // Subtact additional known parameters (return, count, frames).
  182.             #emit ADD.C         0xFFFFFFF4
  183.             #emit STOR.pri      YSI_g_sFramePointer
  184.             // Get the return address so we know where the code starts.
  185.             #emit LOAD.S.pri    4
  186.             #emit STOR.pri      YSI_g_sJumpTo
  187.             // Now fix the stack.
  188.             #emit LOAD.S.pri    0
  189.             //#emit ADD.C         0xFFFFFFFC
  190.             #emit SCTRL         4
  191.             //#emit LOAD.S.pri    0
  192.             #emit SCTRL         5
  193.             // And return to "Inline_Get".
  194.             #emit LOAD.pri      YSI_g_sReturnTo
  195.             #emit SCTRL         6
  196.         }
  197.         case -1:
  198.         {
  199.             YSI_g_sDoAddress = 0;
  200.             #emit LOAD.pri YSI_g_sReturnTo
  201.             #emit SCTRL    6
  202.         }
  203.     }
  204.     // Always returns 0.
  205.     return 0;
  206. }
  207.  
  208. stock Inline_Call(handle[E_INLINE], GLOBAL_TAG_TYPES:...)
  209. {
  210.     new
  211.         args = (numargs() - 1) * 4,
  212.         i,
  213.         j = handle[E_INLINE_PAD] / 4;
  214.     if (args != handle[E_INLINE_ALLOCATE])
  215.     {
  216.         // There are not the right number of expected parameters.
  217.         P:E("Incorrect number of parameters passed or expected in inline function (%d != %d)", args / 4, handle[E_INLINE_ALLOCATE] / 4);
  218.         return 0;
  219.     }
  220.     YSI_g_sDoAddress = -1;
  221.     // Push the variables.
  222.     new
  223.         arg_start,
  224.         arg_end;
  225.     // Pad the stack correctly.
  226.     while (i++ != j)
  227.     {
  228.         #emit PUSH.C    0
  229.     }
  230.     // Find the first parameter.
  231.     #emit CONST.alt                 handle
  232.     #emit LCTRL                     5
  233.     #emit ADD
  234.     #emit STOR.S.pri                arg_start
  235.     // Find the last parameter.
  236.     #emit LOAD.S.alt                args
  237.     #emit ADD
  238.     #emit STOR.S.pri                arg_end
  239.     // Do the pushing BACKWARDS to normal!
  240.     while (arg_end > arg_start)
  241.     {
  242.         arg_start += 4;
  243.         #emit LOAD.S.pri            arg_start
  244.         // Double unwrap.
  245.         #emit LOAD.I
  246.         #emit LOAD.I
  247.         #emit PUSH.pri
  248.     }
  249.     // Get the function address.
  250.     YSI_g_sJumpTo = handle[E_INLINE_ADDRESS];
  251.     // Set the frame pointer.
  252.     args += handle[E_INLINE_PAD];
  253.     #emit LCTRL         5
  254.     #emit STOR.pri      YSI_g_sFramePointer
  255.     #emit LCTRL         4
  256.     #emit LOAD.S.alt    args
  257.     #emit ADD
  258.     // DO NOT use ANY local variables beyond this point!
  259.     // If you do they will have the wrong value as they will be from the wrong
  260.     // frame (i.e. the inline function's fake frame)!
  261.     #emit SCTRL         5
  262.     // Set the return address (not on the stack, in a variable).
  263.     //return 1;
  264.     #emit LCTRL         6
  265.     #emit ADD.C         32
  266.     #emit STOR.pri      YSI_g_sReturnTo
  267.     // Jump to the target code.
  268.     #emit LOAD.pri      YSI_g_sJumpTo
  269.     #emit SCTRL         6
  270.     // Restore the frame pointer FAST!
  271.     #emit LOAD.pri      YSI_g_sFramePointer
  272.     #emit SCTRL         5
  273.     // Now wipe the stack.  We can't use the "STACK" OpCode as that takes a
  274.     // constant parameter, not a variable (shame!)
  275.     i = 0;
  276.     j = args / 4;
  277.     while (i++ != j)
  278.     {
  279.         #emit POP.pri
  280.     }
  281.     // Remove the extra data from this being called as "Inline_Loop" instead of
  282.     // jumped in to directly.
  283.     #emit STACK 12
  284.     // We're done!  Woo!
  285.     return 1;
  286. }
Advertisement
Add Comment
Please, Sign In to add comment