Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*----------------------------------------------------------------------------*\
- ======================================
- y_inline - Inline functions in PAWN!
- ======================================
- Description:
- Allows you to use basic inline functions to act as callbacks.
- Legal:
- Version: MPL 1.1
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
- The Original Code is the SA:MP inline functions code.
- The Initial Developer of the Original Code is Alex "Y_Less" Cole.
- Portions created by the Initial Developer are Copyright (C) 2008
- the Initial Developer. All Rights Reserved.
- Contributors:
- ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
- Thanks:
- JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
- Peter, Cam - Support.
- ZeeX, g_aSlice/Slice, Popz, others - Very productive conversations.
- koolk - IsPlayerinAreaEx code.
- TheAlpha - Danish translation.
- breadfish - German translation.
- Fireburn - Dutch translation.
- yom - French translation.
- 50p - Polish translation.
- Zamaroht - Spanish translation.
- Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
- for me to strive to better.
- Pixels^ - Running XScripters where the idea was born.
- Matite - Pestering me to release it and using it.
- Very special thanks to:
- Thiadmer - PAWN, whose limits continue to amaze me!
- Kye/Kalcor - SA:MP.
- SA:MP Team past, present and future - SA:MP.
- Version:
- 1.0
- Changelog:
- 02/08/11:
- First version
- \*----------------------------------------------------------------------------*/
- // This file provides the infrastructure to allow PAWN to use inline functions
- // with BASIC closures. I'm not sure how well heap structures will work though
- // when the inline callback is done after the enclosing function has ended - by
- // that point the heap memory will have been cleared, leaving us with a floating
- // pointer in to the heap if we do save the current stack frame. If done, these
- // closures will be based around storing and restoring the stack frame of the
- // calling function. This will mean that all locals are passed by value and all
- // arrays are passed by reference, which makes sense really given that that is
- // how most functions do it. This will of course introduce issues with
- // reference variables in the parent function and it may just be easier to
- // ignore closures entirely, there are just too many problems. Shame...
- #include <YSI\internal\y_version>
- #include <YSI\y_debug>
- #include <YSI\y_utils>
- enum E_INLINE
- {
- // This is the amount of data already on the stack. We need to allocate,
- // but not fill, this much data to make the variable offsets correct in the
- // inline function for it's local varaibles. Note that this is ONLY data IN
- // the stack frame - we don't push parent passed parameters at all. "PAD"
- // is in BYTES.
- E_INLINE_PAD,
- // This is the amount of data that the inline function is expecting to be
- // passed. Can be checked at runtime to ensure it matches the passed
- // parameters. "ALLOCATE" is in BYTES.
- E_INLINE_ALLOCATE,
- // The address of the inline function. This is the address AFTER the first
- // call to the "for" loop variable. Note that the function called in the
- // "while" section for the "for" loop always returns 0, but because of stack
- // manipulations that part of the loop gets run three times and never ends.
- E_INLINE_ADDRESS
- }
- #define inline%0(%1) for(new %1;Inline_Loop();)
- #define using%0inline NULL
- //static
- new
- YSI_g_sFramePointer,
- YSI_g_sJumpTo,
- YSI_g_sReturnTo,
- YSI_g_sDoAddress = 0;
- // This function actually stores the return address of the PREVIOUS function
- // (i.e. the one that called this one) and jumps to it, allowing the code
- // after it to be executed. This will cause a small problem if you do:
- //
- // var[x++] = SomeFuncWithInline(using inline);
- //
- // The "x++" part will get executed twice, we just need to be VERY carful (and
- // be "we", I mean "users"). Writing code to fix that would take A LOT of code
- // especially as their "SomeFuncWithInline" call may be in another function call
- // - I may just ban return values and make people do things by reference.
- // Anyway, this will return to the call point and allow the "inline" function to
- // begin, which will execute the start of the "inline hidden for" and get an
- // address to jump to later on. This will also allocate stack space, which can
- // be used to check allocation sizes are correct. This address being AFTER the
- // data allocation is important as it means the variables exist, but are not
- // blanked out after we allocate the data.
- stock Inline_Get(function[], handle[E_INLINE])
- {
- //printf("Inline_Get");
- if (function[0] == '\1' && function[1] == '\0')
- {
- YSI_g_sDoAddress = 1;
- // Get the return address of the function which called this function.
- #emit LOAD.S.pri 0
- #emit ADD.C 4
- #emit MOVE.alt
- #emit LOAD.I
- #emit MOVE.alt
- // Get the return address for here.
- #emit LCTRL 6
- #emit ADD.C 28
- #emit STOR.pri YSI_g_sReturnTo
- // Jump to after the parent call, should be before the "inline" loop.
- #emit MOVE.pri
- #emit SCTRL 6
- YSI_g_sDoAddress = 0;
- // Now we are here (having subtly called "Inline_Loop"), we have the
- // start address of the code for the "inline" function and the number
- // of parameters it expects, all that's left to do now is get the size
- // of the stack that the inline parent function already has.
- // Get the first frame.
- #emit LOAD.S.alt 0
- // Get the second frame.
- #emit MOVE.pri
- #emit LOAD.I
- // Subtract the two to get allocated byte counts.
- #emit SUB
- #emit ADD.C 0xFFFFFFEC
- #emit STOR.pri YSI_g_sReturnTo
- handle[E_INLINE_PAD] = YSI_g_sReturnTo;
- handle[E_INLINE_ALLOCATE] = YSI_g_sFramePointer;
- handle[E_INLINE_ADDRESS] = YSI_g_sJumpTo;
- return 1;
- }
- else if (function[0])
- {
- return -1;
- }
- // Wasn't passed a valid function.
- return 0;
- }
- stock Inline_Loop()
- {
- switch (YSI_g_sDoAddress)
- {
- case 1:
- {
- // If we detect heap allocation we may have a problem...
- // Get the frame pointer from "Inline_Get". This function is not
- // called inside that function, but IS called inside that function's
- // stack frame, this is so we can carefully control code execution
- // flow.
- #emit LOAD.S.alt 0
- // Get our current frame.
- #emit LCTRL 5
- // Subract the two.
- #emit SUB.alt
- // Subtact additional known parameters (return, count, frames).
- #emit ADD.C 0xFFFFFFF4
- #emit STOR.pri YSI_g_sFramePointer
- // Get the return address so we know where the code starts.
- #emit LOAD.S.pri 4
- #emit STOR.pri YSI_g_sJumpTo
- // Now fix the stack.
- #emit LOAD.S.pri 0
- //#emit ADD.C 0xFFFFFFFC
- #emit SCTRL 4
- //#emit LOAD.S.pri 0
- #emit SCTRL 5
- // And return to "Inline_Get".
- #emit LOAD.pri YSI_g_sReturnTo
- #emit SCTRL 6
- }
- case -1:
- {
- YSI_g_sDoAddress = 0;
- #emit LOAD.pri YSI_g_sReturnTo
- #emit SCTRL 6
- }
- }
- // Always returns 0.
- return 0;
- }
- stock Inline_Call(handle[E_INLINE], GLOBAL_TAG_TYPES:...)
- {
- new
- args = (numargs() - 1) * 4,
- i,
- j = handle[E_INLINE_PAD] / 4;
- if (args != handle[E_INLINE_ALLOCATE])
- {
- // There are not the right number of expected parameters.
- P:E("Incorrect number of parameters passed or expected in inline function (%d != %d)", args / 4, handle[E_INLINE_ALLOCATE] / 4);
- return 0;
- }
- YSI_g_sDoAddress = -1;
- // Push the variables.
- new
- arg_start,
- arg_end;
- // Pad the stack correctly.
- while (i++ != j)
- {
- #emit PUSH.C 0
- }
- // Find the first parameter.
- #emit CONST.alt handle
- #emit LCTRL 5
- #emit ADD
- #emit STOR.S.pri arg_start
- // Find the last parameter.
- #emit LOAD.S.alt args
- #emit ADD
- #emit STOR.S.pri arg_end
- // Do the pushing BACKWARDS to normal!
- while (arg_end > arg_start)
- {
- arg_start += 4;
- #emit LOAD.S.pri arg_start
- // Double unwrap.
- #emit LOAD.I
- #emit LOAD.I
- #emit PUSH.pri
- }
- // Get the function address.
- YSI_g_sJumpTo = handle[E_INLINE_ADDRESS];
- // Set the frame pointer.
- args += handle[E_INLINE_PAD];
- #emit LCTRL 5
- #emit STOR.pri YSI_g_sFramePointer
- #emit LCTRL 4
- #emit LOAD.S.alt args
- #emit ADD
- // DO NOT use ANY local variables beyond this point!
- // If you do they will have the wrong value as they will be from the wrong
- // frame (i.e. the inline function's fake frame)!
- #emit SCTRL 5
- // Set the return address (not on the stack, in a variable).
- //return 1;
- #emit LCTRL 6
- #emit ADD.C 32
- #emit STOR.pri YSI_g_sReturnTo
- // Jump to the target code.
- #emit LOAD.pri YSI_g_sJumpTo
- #emit SCTRL 6
- // Restore the frame pointer FAST!
- #emit LOAD.pri YSI_g_sFramePointer
- #emit SCTRL 5
- // Now wipe the stack. We can't use the "STACK" OpCode as that takes a
- // constant parameter, not a variable (shame!)
- i = 0;
- j = args / 4;
- while (i++ != j)
- {
- #emit POP.pri
- }
- // Remove the extra data from this being called as "Inline_Loop" instead of
- // jumped in to directly.
- #emit STACK 12
- // We're done! Woo!
- return 1;
- }
Advertisement
Add Comment
Please, Sign In to add comment